Initial Import
authorPrajwal Mohan <prajwal.karur.mohan@intel.com>
Fri, 27 Apr 2012 23:29:25 +0000 (16:29 -0700)
committerPrajwal Mohan <prajwal.karur.mohan@intel.com>
Fri, 27 Apr 2012 23:29:25 +0000 (16:29 -0700)
595 files changed:
COPYING [new file with mode: 0644]
README [new file with mode: 0644]
packaging/wpa_supplicant.changes [new file with mode: 0644]
packaging/wpa_supplicant.config [new file with mode: 0644]
packaging/wpa_supplicant.spec [new file with mode: 0644]
patches/0001-eap_peer-create-a-libeap-library-with-header-files-a.patch [new file with mode: 0644]
patches/0001-events-Custom-event-for-broadcom-proprietary-driver.patch [new file with mode: 0644]
patches/0002-nl80211-Add-a-get_country-hook.patch [new file with mode: 0644]
patches/0003-dbus-Add-a-Country-global-property.patch [new file with mode: 0644]
patches/openssl-0.9.8-tls-extensions.patch [new file with mode: 0644]
patches/openssl-0.9.8d-tls-extensions.patch [new file with mode: 0644]
patches/openssl-0.9.8e-tls-extensions.patch [new file with mode: 0644]
patches/openssl-0.9.8g-tls-extensions.patch [new file with mode: 0644]
patches/openssl-0.9.8h-tls-extensions.patch [new file with mode: 0644]
patches/openssl-0.9.8i-tls-extensions.patch [new file with mode: 0644]
patches/openssl-0.9.9-session-ticket.patch [new file with mode: 0644]
patches/wpa_s-alldrivers.patch [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/ap/Makefile [new file with mode: 0644]
src/ap/accounting.c [new file with mode: 0644]
src/ap/accounting.h [new file with mode: 0644]
src/ap/ap_config.c [new file with mode: 0644]
src/ap/ap_config.h [new file with mode: 0644]
src/ap/ap_drv_ops.c [new file with mode: 0644]
src/ap/ap_drv_ops.h [new file with mode: 0644]
src/ap/ap_list.c [new file with mode: 0644]
src/ap/ap_list.h [new file with mode: 0644]
src/ap/ap_mlme.c [new file with mode: 0644]
src/ap/ap_mlme.h [new file with mode: 0644]
src/ap/authsrv.c [new file with mode: 0644]
src/ap/authsrv.h [new file with mode: 0644]
src/ap/beacon.c [new file with mode: 0644]
src/ap/beacon.h [new file with mode: 0644]
src/ap/ctrl_iface_ap.c [new file with mode: 0644]
src/ap/ctrl_iface_ap.h [new file with mode: 0644]
src/ap/drv_callbacks.c [new file with mode: 0644]
src/ap/hostapd.c [new file with mode: 0644]
src/ap/hostapd.h [new file with mode: 0644]
src/ap/hw_features.c [new file with mode: 0644]
src/ap/hw_features.h [new file with mode: 0644]
src/ap/iapp.c [new file with mode: 0644]
src/ap/iapp.h [new file with mode: 0644]
src/ap/ieee802_11.c [new file with mode: 0644]
src/ap/ieee802_11.h [new file with mode: 0644]
src/ap/ieee802_11_auth.c [new file with mode: 0644]
src/ap/ieee802_11_auth.h [new file with mode: 0644]
src/ap/ieee802_11_ht.c [new file with mode: 0644]
src/ap/ieee802_1x.c [new file with mode: 0644]
src/ap/ieee802_1x.h [new file with mode: 0644]
src/ap/peerkey_auth.c [new file with mode: 0644]
src/ap/pmksa_cache_auth.c [new file with mode: 0644]
src/ap/pmksa_cache_auth.h [new file with mode: 0644]
src/ap/preauth_auth.c [new file with mode: 0644]
src/ap/preauth_auth.h [new file with mode: 0644]
src/ap/sta_info.c [new file with mode: 0644]
src/ap/sta_info.h [new file with mode: 0644]
src/ap/tkip_countermeasures.c [new file with mode: 0644]
src/ap/tkip_countermeasures.h [new file with mode: 0644]
src/ap/utils.c [new file with mode: 0644]
src/ap/vlan_init.c [new file with mode: 0644]
src/ap/vlan_init.h [new file with mode: 0644]
src/ap/wmm.c [new file with mode: 0644]
src/ap/wmm.h [new file with mode: 0644]
src/ap/wpa_auth.c [new file with mode: 0644]
src/ap/wpa_auth.h [new file with mode: 0644]
src/ap/wpa_auth_ft.c [new file with mode: 0644]
src/ap/wpa_auth_glue.c [new file with mode: 0644]
src/ap/wpa_auth_glue.h [new file with mode: 0644]
src/ap/wpa_auth_i.h [new file with mode: 0644]
src/ap/wpa_auth_ie.c [new file with mode: 0644]
src/ap/wpa_auth_ie.h [new file with mode: 0644]
src/ap/wps_hostapd.c [new file with mode: 0644]
src/ap/wps_hostapd.h [new file with mode: 0644]
src/common/Makefile [new file with mode: 0644]
src/common/defs.h [new file with mode: 0644]
src/common/eapol_common.h [new file with mode: 0644]
src/common/ieee802_11_common.c [new file with mode: 0644]
src/common/ieee802_11_common.h [new file with mode: 0644]
src/common/ieee802_11_defs.h [new file with mode: 0644]
src/common/privsep_commands.h [new file with mode: 0644]
src/common/version.h [new file with mode: 0644]
src/common/wpa_common.c [new file with mode: 0644]
src/common/wpa_common.h [new file with mode: 0644]
src/common/wpa_ctrl.c [new file with mode: 0644]
src/common/wpa_ctrl.h [new file with mode: 0644]
src/crypto/.gitignore [new file with mode: 0644]
src/crypto/Makefile [new file with mode: 0644]
src/crypto/aes-cbc.c [new file with mode: 0644]
src/crypto/aes-ctr.c [new file with mode: 0644]
src/crypto/aes-eax.c [new file with mode: 0644]
src/crypto/aes-encblock.c [new file with mode: 0644]
src/crypto/aes-internal-dec.c [new file with mode: 0644]
src/crypto/aes-internal-enc.c [new file with mode: 0644]
src/crypto/aes-internal.c [new file with mode: 0644]
src/crypto/aes-omac1.c [new file with mode: 0644]
src/crypto/aes-unwrap.c [new file with mode: 0644]
src/crypto/aes-wrap.c [new file with mode: 0644]
src/crypto/aes.h [new file with mode: 0644]
src/crypto/aes_i.h [new file with mode: 0644]
src/crypto/aes_wrap.h [new file with mode: 0644]
src/crypto/crypto.h [new file with mode: 0644]
src/crypto/crypto_cryptoapi.c [new file with mode: 0644]
src/crypto/crypto_gnutls.c [new file with mode: 0644]
src/crypto/crypto_internal-cipher.c [new file with mode: 0644]
src/crypto/crypto_internal-modexp.c [new file with mode: 0644]
src/crypto/crypto_internal-rsa.c [new file with mode: 0644]
src/crypto/crypto_internal.c [new file with mode: 0644]
src/crypto/crypto_libtomcrypt.c [new file with mode: 0644]
src/crypto/crypto_none.c [new file with mode: 0644]
src/crypto/crypto_nss.c [new file with mode: 0644]
src/crypto/crypto_openssl.c [new file with mode: 0644]
src/crypto/des-internal.c [new file with mode: 0644]
src/crypto/des_i.h [new file with mode: 0644]
src/crypto/dh_group5.c [new file with mode: 0644]
src/crypto/dh_group5.h [new file with mode: 0644]
src/crypto/dh_groups.c [new file with mode: 0644]
src/crypto/dh_groups.h [new file with mode: 0644]
src/crypto/fips_prf_cryptoapi.c [new file with mode: 0644]
src/crypto/fips_prf_gnutls.c [new file with mode: 0644]
src/crypto/fips_prf_internal.c [new file with mode: 0644]
src/crypto/fips_prf_nss.c [new file with mode: 0644]
src/crypto/fips_prf_openssl.c [new file with mode: 0644]
src/crypto/md4-internal.c [new file with mode: 0644]
src/crypto/md5-internal.c [new file with mode: 0644]
src/crypto/md5-non-fips.c [new file with mode: 0644]
src/crypto/md5.c [new file with mode: 0644]
src/crypto/md5.h [new file with mode: 0644]
src/crypto/md5_i.h [new file with mode: 0644]
src/crypto/milenage.c [new file with mode: 0644]
src/crypto/milenage.h [new file with mode: 0644]
src/crypto/ms_funcs.c [new file with mode: 0644]
src/crypto/ms_funcs.h [new file with mode: 0644]
src/crypto/rc4.c [new file with mode: 0644]
src/crypto/sha1-internal.c [new file with mode: 0644]
src/crypto/sha1-pbkdf2.c [new file with mode: 0644]
src/crypto/sha1-tlsprf.c [new file with mode: 0644]
src/crypto/sha1-tprf.c [new file with mode: 0644]
src/crypto/sha1.c [new file with mode: 0644]
src/crypto/sha1.h [new file with mode: 0644]
src/crypto/sha1_i.h [new file with mode: 0644]
src/crypto/sha256-internal.c [new file with mode: 0644]
src/crypto/sha256.c [new file with mode: 0644]
src/crypto/sha256.h [new file with mode: 0644]
src/crypto/tls.h [new file with mode: 0644]
src/crypto/tls_gnutls.c [new file with mode: 0644]
src/crypto/tls_internal.c [new file with mode: 0644]
src/crypto/tls_none.c [new file with mode: 0644]
src/crypto/tls_nss.c [new file with mode: 0644]
src/crypto/tls_openssl.c [new file with mode: 0644]
src/crypto/tls_schannel.c [new file with mode: 0644]
src/drivers/.gitignore [new file with mode: 0644]
src/drivers/Apple80211.h [new file with mode: 0644]
src/drivers/Makefile [new file with mode: 0644]
src/drivers/MobileApple80211.c [new file with mode: 0644]
src/drivers/MobileApple80211.h [new file with mode: 0644]
src/drivers/driver.h [new file with mode: 0644]
src/drivers/driver_atheros.c [new file with mode: 0644]
src/drivers/driver_atmel.c [new file with mode: 0644]
src/drivers/driver_broadcom.c [new file with mode: 0644]
src/drivers/driver_bsd.c [new file with mode: 0644]
src/drivers/driver_hostap.c [new file with mode: 0644]
src/drivers/driver_hostap.h [new file with mode: 0644]
src/drivers/driver_iphone.m [new file with mode: 0644]
src/drivers/driver_ipw.c [new file with mode: 0644]
src/drivers/driver_madwifi.c [new file with mode: 0644]
src/drivers/driver_ndis.c [new file with mode: 0644]
src/drivers/driver_ndis.h [new file with mode: 0644]
src/drivers/driver_ndis_.c [new file with mode: 0644]
src/drivers/driver_ndiswrapper.c [new file with mode: 0644]
src/drivers/driver_nl80211.c [new file with mode: 0644]
src/drivers/driver_none.c [new file with mode: 0644]
src/drivers/driver_osx.m [new file with mode: 0644]
src/drivers/driver_privsep.c [new file with mode: 0644]
src/drivers/driver_ralink.c [new file with mode: 0644]
src/drivers/driver_ralink.h [new file with mode: 0644]
src/drivers/driver_roboswitch.c [new file with mode: 0644]
src/drivers/driver_test.c [new file with mode: 0644]
src/drivers/driver_wext.c [new file with mode: 0644]
src/drivers/driver_wext.h [new file with mode: 0644]
src/drivers/driver_wired.c [new file with mode: 0644]
src/drivers/drivers.c [new file with mode: 0644]
src/drivers/drivers.mak [new file with mode: 0644]
src/drivers/linux_ioctl.c [new file with mode: 0644]
src/drivers/linux_ioctl.h [new file with mode: 0644]
src/drivers/ndis_events.c [new file with mode: 0644]
src/drivers/netlink.c [new file with mode: 0644]
src/drivers/netlink.h [new file with mode: 0644]
src/drivers/nl80211_copy.h [new file with mode: 0644]
src/drivers/priv_netlink.h [new file with mode: 0644]
src/drivers/wireless_copy.h [new file with mode: 0644]
src/eap_common/Makefile [new file with mode: 0644]
src/eap_common/chap.c [new file with mode: 0644]
src/eap_common/chap.h [new file with mode: 0644]
src/eap_common/eap_common.c [new file with mode: 0644]
src/eap_common/eap_common.h [new file with mode: 0644]
src/eap_common/eap_defs.h [new file with mode: 0644]
src/eap_common/eap_fast_common.c [new file with mode: 0644]
src/eap_common/eap_fast_common.h [new file with mode: 0644]
src/eap_common/eap_gpsk_common.c [new file with mode: 0644]
src/eap_common/eap_gpsk_common.h [new file with mode: 0644]
src/eap_common/eap_ikev2_common.c [new file with mode: 0644]
src/eap_common/eap_ikev2_common.h [new file with mode: 0644]
src/eap_common/eap_pax_common.c [new file with mode: 0644]
src/eap_common/eap_pax_common.h [new file with mode: 0644]
src/eap_common/eap_peap_common.c [new file with mode: 0644]
src/eap_common/eap_peap_common.h [new file with mode: 0644]
src/eap_common/eap_psk_common.c [new file with mode: 0644]
src/eap_common/eap_psk_common.h [new file with mode: 0644]
src/eap_common/eap_sake_common.c [new file with mode: 0644]
src/eap_common/eap_sake_common.h [new file with mode: 0644]
src/eap_common/eap_sim_common.c [new file with mode: 0644]
src/eap_common/eap_sim_common.h [new file with mode: 0644]
src/eap_common/eap_tlv_common.h [new file with mode: 0644]
src/eap_common/eap_ttls.h [new file with mode: 0644]
src/eap_common/eap_wsc_common.c [new file with mode: 0644]
src/eap_common/eap_wsc_common.h [new file with mode: 0644]
src/eap_common/ikev2_common.c [new file with mode: 0644]
src/eap_common/ikev2_common.h [new file with mode: 0644]
src/eap_peer/Makefile [new file with mode: 0644]
src/eap_peer/eap.c [new file with mode: 0644]
src/eap_peer/eap.h [new file with mode: 0644]
src/eap_peer/eap_aka.c [new file with mode: 0644]
src/eap_peer/eap_config.h [new file with mode: 0644]
src/eap_peer/eap_fast.c [new file with mode: 0644]
src/eap_peer/eap_fast_pac.c [new file with mode: 0644]
src/eap_peer/eap_fast_pac.h [new file with mode: 0644]
src/eap_peer/eap_gpsk.c [new file with mode: 0644]
src/eap_peer/eap_gtc.c [new file with mode: 0644]
src/eap_peer/eap_i.h [new file with mode: 0644]
src/eap_peer/eap_ikev2.c [new file with mode: 0644]
src/eap_peer/eap_leap.c [new file with mode: 0644]
src/eap_peer/eap_md5.c [new file with mode: 0644]
src/eap_peer/eap_methods.c [new file with mode: 0644]
src/eap_peer/eap_methods.h [new file with mode: 0644]
src/eap_peer/eap_mschapv2.c [new file with mode: 0644]
src/eap_peer/eap_otp.c [new file with mode: 0644]
src/eap_peer/eap_pax.c [new file with mode: 0644]
src/eap_peer/eap_peap.c [new file with mode: 0644]
src/eap_peer/eap_psk.c [new file with mode: 0644]
src/eap_peer/eap_sake.c [new file with mode: 0644]
src/eap_peer/eap_sim.c [new file with mode: 0644]
src/eap_peer/eap_tls.c [new file with mode: 0644]
src/eap_peer/eap_tls_common.c [new file with mode: 0644]
src/eap_peer/eap_tls_common.h [new file with mode: 0644]
src/eap_peer/eap_tnc.c [new file with mode: 0644]
src/eap_peer/eap_ttls.c [new file with mode: 0644]
src/eap_peer/eap_vendor_test.c [new file with mode: 0644]
src/eap_peer/eap_wsc.c [new file with mode: 0644]
src/eap_peer/ikev2.c [new file with mode: 0644]
src/eap_peer/ikev2.h [new file with mode: 0644]
src/eap_peer/mschapv2.c [new file with mode: 0644]
src/eap_peer/mschapv2.h [new file with mode: 0644]
src/eap_peer/tncc.c [new file with mode: 0644]
src/eap_peer/tncc.h [new file with mode: 0644]
src/eap_server/Makefile [new file with mode: 0644]
src/eap_server/eap.h [new file with mode: 0644]
src/eap_server/eap_i.h [new file with mode: 0644]
src/eap_server/eap_methods.h [new file with mode: 0644]
src/eap_server/eap_server.c [new file with mode: 0644]
src/eap_server/eap_server_aka.c [new file with mode: 0644]
src/eap_server/eap_server_fast.c [new file with mode: 0644]
src/eap_server/eap_server_gpsk.c [new file with mode: 0644]
src/eap_server/eap_server_gtc.c [new file with mode: 0644]
src/eap_server/eap_server_identity.c [new file with mode: 0644]
src/eap_server/eap_server_ikev2.c [new file with mode: 0644]
src/eap_server/eap_server_md5.c [new file with mode: 0644]
src/eap_server/eap_server_methods.c [new file with mode: 0644]
src/eap_server/eap_server_mschapv2.c [new file with mode: 0644]
src/eap_server/eap_server_pax.c [new file with mode: 0644]
src/eap_server/eap_server_peap.c [new file with mode: 0644]
src/eap_server/eap_server_psk.c [new file with mode: 0644]
src/eap_server/eap_server_sake.c [new file with mode: 0644]
src/eap_server/eap_server_sim.c [new file with mode: 0644]
src/eap_server/eap_server_tls.c [new file with mode: 0644]
src/eap_server/eap_server_tls_common.c [new file with mode: 0644]
src/eap_server/eap_server_tnc.c [new file with mode: 0644]
src/eap_server/eap_server_ttls.c [new file with mode: 0644]
src/eap_server/eap_server_vendor_test.c [new file with mode: 0644]
src/eap_server/eap_server_wsc.c [new file with mode: 0644]
src/eap_server/eap_sim_db.c [new file with mode: 0644]
src/eap_server/eap_sim_db.h [new file with mode: 0644]
src/eap_server/eap_tls_common.h [new file with mode: 0644]
src/eap_server/ikev2.c [new file with mode: 0644]
src/eap_server/ikev2.h [new file with mode: 0644]
src/eap_server/tncs.c [new file with mode: 0644]
src/eap_server/tncs.h [new file with mode: 0644]
src/eapol_auth/Makefile [new file with mode: 0644]
src/eapol_auth/eapol_auth_dump.c [new file with mode: 0644]
src/eapol_auth/eapol_auth_sm.c [new file with mode: 0644]
src/eapol_auth/eapol_auth_sm.h [new file with mode: 0644]
src/eapol_auth/eapol_auth_sm_i.h [new file with mode: 0644]
src/eapol_supp/Makefile [new file with mode: 0644]
src/eapol_supp/eapol_supp_sm.c [new file with mode: 0644]
src/eapol_supp/eapol_supp_sm.h [new file with mode: 0644]
src/l2_packet/Makefile [new file with mode: 0644]
src/l2_packet/l2_packet.h [new file with mode: 0644]
src/l2_packet/l2_packet_freebsd.c [new file with mode: 0644]
src/l2_packet/l2_packet_linux.c [new file with mode: 0644]
src/l2_packet/l2_packet_ndis.c [new file with mode: 0644]
src/l2_packet/l2_packet_none.c [new file with mode: 0644]
src/l2_packet/l2_packet_pcap.c [new file with mode: 0644]
src/l2_packet/l2_packet_privsep.c [new file with mode: 0644]
src/l2_packet/l2_packet_winpcap.c [new file with mode: 0644]
src/lib.rules [new file with mode: 0644]
src/radius/.gitignore [new file with mode: 0644]
src/radius/Makefile [new file with mode: 0644]
src/radius/radius.c [new file with mode: 0644]
src/radius/radius.h [new file with mode: 0644]
src/radius/radius_client.c [new file with mode: 0644]
src/radius/radius_client.h [new file with mode: 0644]
src/radius/radius_server.c [new file with mode: 0644]
src/radius/radius_server.h [new file with mode: 0644]
src/rsn_supp/Makefile [new file with mode: 0644]
src/rsn_supp/peerkey.c [new file with mode: 0644]
src/rsn_supp/peerkey.h [new file with mode: 0644]
src/rsn_supp/pmksa_cache.c [new file with mode: 0644]
src/rsn_supp/pmksa_cache.h [new file with mode: 0644]
src/rsn_supp/preauth.c [new file with mode: 0644]
src/rsn_supp/preauth.h [new file with mode: 0644]
src/rsn_supp/wpa.c [new file with mode: 0644]
src/rsn_supp/wpa.h [new file with mode: 0644]
src/rsn_supp/wpa_ft.c [new file with mode: 0644]
src/rsn_supp/wpa_i.h [new file with mode: 0644]
src/rsn_supp/wpa_ie.c [new file with mode: 0644]
src/rsn_supp/wpa_ie.h [new file with mode: 0644]
src/tls/.gitignore [new file with mode: 0644]
src/tls/Makefile [new file with mode: 0644]
src/tls/asn1.c [new file with mode: 0644]
src/tls/asn1.h [new file with mode: 0644]
src/tls/bignum.c [new file with mode: 0644]
src/tls/bignum.h [new file with mode: 0644]
src/tls/libtommath.c [new file with mode: 0644]
src/tls/pkcs1.c [new file with mode: 0644]
src/tls/pkcs1.h [new file with mode: 0644]
src/tls/pkcs5.c [new file with mode: 0644]
src/tls/pkcs5.h [new file with mode: 0644]
src/tls/pkcs8.c [new file with mode: 0644]
src/tls/pkcs8.h [new file with mode: 0644]
src/tls/rsa.c [new file with mode: 0644]
src/tls/rsa.h [new file with mode: 0644]
src/tls/tlsv1_client.c [new file with mode: 0644]
src/tls/tlsv1_client.h [new file with mode: 0644]
src/tls/tlsv1_client_i.h [new file with mode: 0644]
src/tls/tlsv1_client_read.c [new file with mode: 0644]
src/tls/tlsv1_client_write.c [new file with mode: 0644]
src/tls/tlsv1_common.c [new file with mode: 0644]
src/tls/tlsv1_common.h [new file with mode: 0644]
src/tls/tlsv1_cred.c [new file with mode: 0644]
src/tls/tlsv1_cred.h [new file with mode: 0644]
src/tls/tlsv1_record.c [new file with mode: 0644]
src/tls/tlsv1_record.h [new file with mode: 0644]
src/tls/tlsv1_server.c [new file with mode: 0644]
src/tls/tlsv1_server.h [new file with mode: 0644]
src/tls/tlsv1_server_i.h [new file with mode: 0644]
src/tls/tlsv1_server_read.c [new file with mode: 0644]
src/tls/tlsv1_server_write.c [new file with mode: 0644]
src/tls/x509v3.c [new file with mode: 0644]
src/tls/x509v3.h [new file with mode: 0644]
src/utils/.gitignore [new file with mode: 0644]
src/utils/Makefile [new file with mode: 0644]
src/utils/base64.c [new file with mode: 0644]
src/utils/base64.h [new file with mode: 0644]
src/utils/build_config.h [new file with mode: 0644]
src/utils/common.c [new file with mode: 0644]
src/utils/common.h [new file with mode: 0644]
src/utils/eloop.c [new file with mode: 0644]
src/utils/eloop.h [new file with mode: 0644]
src/utils/eloop_none.c [new file with mode: 0644]
src/utils/eloop_win.c [new file with mode: 0644]
src/utils/includes.h [new file with mode: 0644]
src/utils/ip_addr.c [new file with mode: 0644]
src/utils/ip_addr.h [new file with mode: 0644]
src/utils/list.h [new file with mode: 0644]
src/utils/os.h [new file with mode: 0644]
src/utils/os_internal.c [new file with mode: 0644]
src/utils/os_none.c [new file with mode: 0644]
src/utils/os_unix.c [new file with mode: 0644]
src/utils/os_win32.c [new file with mode: 0644]
src/utils/pcsc_funcs.c [new file with mode: 0644]
src/utils/pcsc_funcs.h [new file with mode: 0644]
src/utils/radiotap.c [new file with mode: 0644]
src/utils/radiotap.h [new file with mode: 0644]
src/utils/radiotap_iter.h [new file with mode: 0644]
src/utils/state_machine.h [new file with mode: 0644]
src/utils/trace.c [new file with mode: 0644]
src/utils/trace.h [new file with mode: 0644]
src/utils/uuid.c [new file with mode: 0644]
src/utils/uuid.h [new file with mode: 0644]
src/utils/wpa_debug.c [new file with mode: 0644]
src/utils/wpa_debug.h [new file with mode: 0644]
src/utils/wpabuf.c [new file with mode: 0644]
src/utils/wpabuf.h [new file with mode: 0644]
src/wps/Makefile [new file with mode: 0644]
src/wps/http.h [new file with mode: 0644]
src/wps/http_client.c [new file with mode: 0644]
src/wps/http_client.h [new file with mode: 0644]
src/wps/http_server.c [new file with mode: 0644]
src/wps/http_server.h [new file with mode: 0644]
src/wps/httpread.c [new file with mode: 0644]
src/wps/httpread.h [new file with mode: 0644]
src/wps/ndef.c [new file with mode: 0644]
src/wps/upnp_xml.c [new file with mode: 0644]
src/wps/upnp_xml.h [new file with mode: 0644]
src/wps/wps.c [new file with mode: 0644]
src/wps/wps.h [new file with mode: 0644]
src/wps/wps_attr_build.c [new file with mode: 0644]
src/wps/wps_attr_parse.c [new file with mode: 0644]
src/wps/wps_attr_process.c [new file with mode: 0644]
src/wps/wps_common.c [new file with mode: 0644]
src/wps/wps_defs.h [new file with mode: 0644]
src/wps/wps_dev_attr.c [new file with mode: 0644]
src/wps/wps_dev_attr.h [new file with mode: 0644]
src/wps/wps_enrollee.c [new file with mode: 0644]
src/wps/wps_er.c [new file with mode: 0644]
src/wps/wps_er.h [new file with mode: 0644]
src/wps/wps_er_ssdp.c [new file with mode: 0644]
src/wps/wps_i.h [new file with mode: 0644]
src/wps/wps_nfc.c [new file with mode: 0644]
src/wps/wps_nfc_pn531.c [new file with mode: 0644]
src/wps/wps_registrar.c [new file with mode: 0644]
src/wps/wps_ufd.c [new file with mode: 0644]
src/wps/wps_upnp.c [new file with mode: 0644]
src/wps/wps_upnp.h [new file with mode: 0644]
src/wps/wps_upnp_ap.c [new file with mode: 0644]
src/wps/wps_upnp_event.c [new file with mode: 0644]
src/wps/wps_upnp_i.h [new file with mode: 0644]
src/wps/wps_upnp_ssdp.c [new file with mode: 0644]
src/wps/wps_upnp_web.c [new file with mode: 0644]
wpa_supplicant/.gitignore [new file with mode: 0644]
wpa_supplicant/ChangeLog [new file with mode: 0644]
wpa_supplicant/Makefile [new file with mode: 0644]
wpa_supplicant/README [new file with mode: 0644]
wpa_supplicant/README-WPS [new file with mode: 0644]
wpa_supplicant/README-Windows.txt [new file with mode: 0644]
wpa_supplicant/ap.c [new file with mode: 0644]
wpa_supplicant/ap.h [new file with mode: 0644]
wpa_supplicant/bgscan.c [new file with mode: 0644]
wpa_supplicant/bgscan.h [new file with mode: 0644]
wpa_supplicant/bgscan_simple.c [new file with mode: 0644]
wpa_supplicant/blacklist.c [new file with mode: 0644]
wpa_supplicant/blacklist.h [new file with mode: 0644]
wpa_supplicant/bss.c [new file with mode: 0644]
wpa_supplicant/bss.h [new file with mode: 0644]
wpa_supplicant/config.c [new file with mode: 0644]
wpa_supplicant/config.h [new file with mode: 0644]
wpa_supplicant/config_file.c [new file with mode: 0644]
wpa_supplicant/config_none.c [new file with mode: 0644]
wpa_supplicant/config_ssid.h [new file with mode: 0644]
wpa_supplicant/config_winreg.c [new file with mode: 0644]
wpa_supplicant/ctrl_iface.c [new file with mode: 0644]
wpa_supplicant/ctrl_iface.h [new file with mode: 0644]
wpa_supplicant/ctrl_iface_named_pipe.c [new file with mode: 0644]
wpa_supplicant/ctrl_iface_udp.c [new file with mode: 0644]
wpa_supplicant/ctrl_iface_unix.c [new file with mode: 0644]
wpa_supplicant/dbus/.gitignore [new file with mode: 0644]
wpa_supplicant/dbus/Makefile [new file with mode: 0644]
wpa_supplicant/dbus/dbus-wpa_supplicant.conf [new file with mode: 0644]
wpa_supplicant/dbus/dbus_common.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_common.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_common_i.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_dict_helpers.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_dict_helpers.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_handlers.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_handlers.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_handlers_wps.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_helpers.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_helpers.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_new_introspect.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_old.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_old.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_old_handlers.c [new file with mode: 0644]
wpa_supplicant/dbus/dbus_old_handlers.h [new file with mode: 0644]
wpa_supplicant/dbus/dbus_old_handlers_wps.c [new file with mode: 0644]
wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service [new file with mode: 0644]
wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service [new file with mode: 0644]
wpa_supplicant/defconfig [new file with mode: 0644]
wpa_supplicant/doc/docbook/.gitignore [new file with mode: 0644]
wpa_supplicant/doc/docbook/Makefile [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_background.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_cli.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_gui.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_passphrase.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_priv.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_supplicant.sgml [new file with mode: 0644]
wpa_supplicant/driver_i.h [new file with mode: 0644]
wpa_supplicant/eap_register.c [new file with mode: 0644]
wpa_supplicant/eap_testing.txt [new file with mode: 0644]
wpa_supplicant/eapol_test.c [new file with mode: 0644]
wpa_supplicant/events.c [new file with mode: 0644]
wpa_supplicant/examples/60_wpa_supplicant [new file with mode: 0755]
wpa_supplicant/examples/ieee8021x.conf [new file with mode: 0644]
wpa_supplicant/examples/openCryptoki.conf [new file with mode: 0644]
wpa_supplicant/examples/plaintext.conf [new file with mode: 0644]
wpa_supplicant/examples/wep.conf [new file with mode: 0644]
wpa_supplicant/examples/wpa-psk-tkip.conf [new file with mode: 0644]
wpa_supplicant/examples/wpa2-eap-ccmp.conf [new file with mode: 0644]
wpa_supplicant/examples/wpas-dbus-new-getall.py [new file with mode: 0755]
wpa_supplicant/examples/wpas-dbus-new-signals.py [new file with mode: 0755]
wpa_supplicant/examples/wpas-dbus-new-wps.py [new file with mode: 0755]
wpa_supplicant/examples/wpas-dbus-new.py [new file with mode: 0755]
wpa_supplicant/examples/wpas-test.py [new file with mode: 0755]
wpa_supplicant/ibss_rsn.c [new file with mode: 0644]
wpa_supplicant/ibss_rsn.h [new file with mode: 0644]
wpa_supplicant/main.c [new file with mode: 0644]
wpa_supplicant/main_none.c [new file with mode: 0644]
wpa_supplicant/main_symbian.cpp [new file with mode: 0644]
wpa_supplicant/main_winmain.c [new file with mode: 0644]
wpa_supplicant/main_winsvc.c [new file with mode: 0644]
wpa_supplicant/mlme.c [new file with mode: 0644]
wpa_supplicant/mlme.h [new file with mode: 0644]
wpa_supplicant/nmake.mak [new file with mode: 0644]
wpa_supplicant/notify.c [new file with mode: 0644]
wpa_supplicant/notify.h [new file with mode: 0644]
wpa_supplicant/preauth_test.c [new file with mode: 0644]
wpa_supplicant/scan.c [new file with mode: 0644]
wpa_supplicant/scan.h [new file with mode: 0644]
wpa_supplicant/sme.c [new file with mode: 0644]
wpa_supplicant/sme.h [new file with mode: 0644]
wpa_supplicant/symbian/README.symbian [new file with mode: 0644]
wpa_supplicant/symbian/bld.inf [new file with mode: 0644]
wpa_supplicant/symbian/wpa_supplicant.mmp [new file with mode: 0644]
wpa_supplicant/tests/link_test.c [new file with mode: 0644]
wpa_supplicant/tests/test_eap_sim_common.c [new file with mode: 0644]
wpa_supplicant/tests/test_wpa.c [new file with mode: 0644]
wpa_supplicant/todo.txt [new file with mode: 0644]
wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj [new file with mode: 0755]
wpa_supplicant/vs2005/wpa_supplicant.sln [new file with mode: 0755]
wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj [new file with mode: 0755]
wpa_supplicant/win_example.reg [new file with mode: 0755]
wpa_supplicant/win_if_list.c [new file with mode: 0644]
wpa_supplicant/wpa_cli.c [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/.gitignore [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/addinterface.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/addinterface.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/eventhistory.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/eventhistory.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/eventhistory.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons.qrc [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons/README [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons/ap.svg [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons/laptop.svg [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/icons_png.qrc [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/lang/.gitignore [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/main.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/networkconfig.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/networkconfig.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/networkconfig.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/peers.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/peers.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/peers.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/scanresults.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/scanresults.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/scanresults.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/stringquery.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/stringquery.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/userdatarequest.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/userdatarequest.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/wpa_gui.pro [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/wpagui.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/wpagui.h [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/wpagui.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui-qt4/wpamsg.h [new file with mode: 0644]
wpa_supplicant/wpa_gui/.gitignore [new file with mode: 0644]
wpa_supplicant/wpa_gui/eventhistory.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui/eventhistory.ui.h [new file with mode: 0644]
wpa_supplicant/wpa_gui/main.cpp [new file with mode: 0644]
wpa_supplicant/wpa_gui/networkconfig.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui/networkconfig.ui.h [new file with mode: 0644]
wpa_supplicant/wpa_gui/scanresults.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui/scanresults.ui.h [new file with mode: 0644]
wpa_supplicant/wpa_gui/setup-mingw-cross-compiling [new file with mode: 0755]
wpa_supplicant/wpa_gui/userdatarequest.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui/userdatarequest.ui.h [new file with mode: 0644]
wpa_supplicant/wpa_gui/wpa_gui.pro [new file with mode: 0644]
wpa_supplicant/wpa_gui/wpagui.ui [new file with mode: 0644]
wpa_supplicant/wpa_gui/wpagui.ui.h [new file with mode: 0644]
wpa_supplicant/wpa_gui/wpamsg.h [new file with mode: 0644]
wpa_supplicant/wpa_passphrase.c [new file with mode: 0644]
wpa_supplicant/wpa_priv.c [new file with mode: 0644]
wpa_supplicant/wpa_supplicant.c [new file with mode: 0644]
wpa_supplicant/wpa_supplicant.conf [new file with mode: 0644]
wpa_supplicant/wpa_supplicant.nsi [new file with mode: 0644]
wpa_supplicant/wpa_supplicant_i.h [new file with mode: 0644]
wpa_supplicant/wpas_glue.c [new file with mode: 0644]
wpa_supplicant/wpas_glue.h [new file with mode: 0644]
wpa_supplicant/wps_supplicant.c [new file with mode: 0644]
wpa_supplicant/wps_supplicant.h [new file with mode: 0644]
wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..14f5453
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19yy name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..9c6be85
--- /dev/null
+++ b/README
@@ -0,0 +1,19 @@
+wpa_supplicant and hostapd v0.6.x
+---------------------------------
+
+Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+These program is dual-licensed under both the GPL version 2 and BSD
+license. Either license may be used at your option.
+
+
+This package may include either wpa_supplicant, hostapd, or both. See
+README file respective subdirectories (wpa_supplicant/README or
+hostapd/README) for more details.
+
+Source code files have been moved around in v0.6.x releases and
+compared to earlier releases, the programs are now build by first
+going to a subdirectory (wpa_supplicant or hostapd) and creating
+build configuration (.config) and running 'make' there (for
+Linux/BSD/cygwin builds).
diff --git a/packaging/wpa_supplicant.changes b/packaging/wpa_supplicant.changes
new file mode 100644 (file)
index 0000000..1877ccd
--- /dev/null
@@ -0,0 +1,89 @@
+* Tue Jan 18 2011 Martin Xu <martin.xu@intel.com> - 0.7.3
+- add patch wpa_s-alldrivers.patch to fix BMC #12576
+
+* Fri Jan 14 2011 Martin Xu <martin.xu@intel.com> - 0.7.3
+- upgrade to 0.7.3 to partly support FEA #7696
+
+* Fri Oct 08 2010 Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - 0.7.2
+- Fix mis-generated patch file that does not apply correctly.
+
+* Fri Oct 01 2010 Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com> - 0.7.2
+- Add patch to generate the libeap0 and libeap0-devel packges; this
+  does not modify the wpa_supplicant binaries at all.
+
+- Add Makefile to generate .spec from .yaml
+
+* Thu Aug 26 2010 Martin Xu <martin.xu@intel.com> - 0.7.2
+- Add patch 0002-sme-Check-for-prev_bssid-from-the-disassociation-eve.patch
+- To fix BMC #5111
+- Add patch 0001-sme-Try-all-authentication-algorithms-when-the-first.patch
+- To fix BMC #5191 
+
+* Wed Jul 21 2010 Martin Xu <martin.xu@intel.com> - 0.7.2
+- add patch 0001-Skip-D-Bus-signals-if-the-dbus_path-is-not-yet-set.patch
+- to fix wpa_s start up fail issue
+- add patch bcm-custom_event-0.7.2.patch
+- it is Broadcome driver specific patch
+
+* Mon Jul 19 2010 Martin Xu <martin.xu@intel.com> - 0.7.2
+- use spectacle
+
+* Mon Jul 19 2010 Martin Xu <martin.xu@intel.com> - 0.7.2
+- upgrade to 0.7.2
+- enable CONFIG_DRIVER_NL80211=y
+
+* Fri Mar 26 2010 Anas Nashif <anas.nashif@intel.com> - 0.7.1
+- Remove Epoch
+
+* Mon Jan 18 2010 Martin Xu <martin.xu@intel.com> - 0.7.1
+- upgrade to 0.7.1
+
+* Thu Oct 01 2009 Anas Nashif <anas.nashif@intel.com> - 0.6.9
+- Remove dependency on python through examples
+
+* Tue Sep 15 2009 Martin Xu <martin.xu@intel.com> 0.6.9
+- add wpa_s-drv-disassociate-on-disassoc-event.patch 
+
+* Thu Sep 11 2009 Martin Xu <martin.xu@intel.com> 0.6.9
+- add wpa_s-disconnected.patch 
+
+* Tue Sep 8 2009 Martin Xu <martin.xu@intel.com> 0.6.9
+- add wpa_s-encode_disable-0.6.9.patch
+- add wpa_s-discard-eapol.patch
+- remove other unused patches
+- clean up spec file and remove unused config file
+- remove the gui-qt package
+- clean up changfile
+- install exec file to /sbin
+
+* Wed May 27 2009 Martin Xu <martin.xu@intel.com> 0.6.9
+- Upgrade to 0.6.9
+- Add WPS support in config file
+
+* Wed Feb 18 2009 Yin Kangkai <kangkai.yin@intel.com> 0.6.8
+- Upgrade to 0.6.8
+
+* Sat Feb 14 2009 Anas Nashif <anas.nashif@intel.com> 0.6.7
+- Update to 2.6.7 and apply fixes and patches from fedora
+
+* Sat Feb 14 2009 Anas Nashif <anas.nashif@intel.com> 0.6.7
+- Apply fixes from fedora:
+ - Fix scan result retrieval in very dense wifi environments
+ - Ensure that drivers don't retry association when they aren't supposed to
+ - Fix PEAP connections to Windows Server 2008 authenticators (rh #465022)
+ - Stop supplicant on uninstall (rh #447843)
+ - Suppress scan results message in logs (rh #466601)
+ - Handle encryption keys correctly when switching 802.11 modes (rh #459399)
+ - Better scanning behavior on resume from suspend/hibernate
+ - Better interaction with newer kernels and drivers
+ - Remove 'hostap', 'madwifi', and 'prism54' drivers; use standard 'wext' instead
+
+* Mon Sep 22 2008 Anas Nashif <anas.nashif@intel.com> 0.6.3
+- do not install examples
+
+* Tue Aug 12 2008 Anas Nashif <anas.nashif@intel.com>
+- Do not install gui stuff is disabled
+
+* Tue Aug 12 2008 Anas Nashif <anas.nashif@intel.com> 
+- Enable/disable gui via macro
+
diff --git a/packaging/wpa_supplicant.config b/packaging/wpa_supplicant.config
new file mode 100644 (file)
index 0000000..5df6bca
--- /dev/null
@@ -0,0 +1,37 @@
+CONFIG_WPS=y
+CONFIG_AP=y
+CONFIG_CTRL_IFACE=y
+CONFIG_CTRL_IFACE_DBUS=y
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+CONFIG_CTRL_IFACE_DBUS_INTRO=y
+//CONFIG_DRIVER_HOSTAP=y
+//CONFIG_DRIVER_HERMES=y
+//CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+//CONFIG_DRIVER_PRISM54=y
+CONFIG_DRIVER_WIRED=y
+//CONFIG_DRIVER_BROADCOM=y
+//CONFIG_DRIVER_IPW=y
+//CONFIG_DRIVER_BSD=y
+//CONFIG_DRIVER_NDIS=y
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+//CONFIG_PCSC=y
+CONFIG_PKCS12=y
+CONFIG_SMARTCARD=y
+CONFIG_DEBUG_FILE=y
+CONFIG_DRIVER_NL80211=y
diff --git a/packaging/wpa_supplicant.spec b/packaging/wpa_supplicant.spec
new file mode 100644 (file)
index 0000000..337ad9e
--- /dev/null
@@ -0,0 +1,143 @@
+Name:       wpa_supplicant
+Summary:    WPA/WPA2/IEEE 802.1X Supplicant
+Version:    0.7.3
+Release:    1
+Group:      System/Base
+License:    BSD
+URL:        http://w1.fi/wpa_supplicant/
+Source0:    http://w1.fi/releases/%{name}-%{version}.tar.gz
+Source1:    %{name}.config
+Patch0:     0001-eap_peer-create-a-libeap-library-with-header-files-a.patch
+Patch1:     0001-events-Custom-event-for-broadcom-proprietary-driver.patch
+Patch2:     0002-nl80211-Add-a-get_country-hook.patch
+Patch3:     0003-dbus-Add-a-Country-global-property.patch
+Patch4:     wpa_s-alldrivers.patch
+BuildRequires:  pkgconfig(openssl)
+BuildRequires:  pkgconfig(libnl-1)
+BuildRequires:  pkgconfig(dbus-1)
+BuildRequires:  readline
+
+
+%description
+wpa_supplicant is a WPA Supplicant for Linux, BSD and Windows with support 
+for WPA and WPA2 (IEEE 802.11i / RSN). Supplicant is the IEEE 802.1X/WPA 
+component that is used in the client stations. It implements key negotiation 
+with a WPA Authenticator and it controls the roaming and IEEE 802.11 
+authentication/association of the wlan driver.
+
+
+
+%package -n libeap0
+Summary:    EAP-Peer runtime library
+Group:      System/Libraries
+Requires:   %{name} = %{version}-%{release}
+Requires(post): /sbin/ldconfig
+Requires(postun): /sbin/ldconfig
+
+%description -n libeap0
+This is just the runtime for EAP-Peer authentication.
+
+
+%package -n libeap0-devel
+Summary:    EAP-Peer development support
+Group:      Development/Libraries
+Requires:   %{name} = %{version}-%{release}
+Requires:   libeap0
+
+%description -n libeap0-devel
+Development support for for EAP-Peer authentication library
+(header files and package config).
+
+
+
+%prep
+%setup -q -n %{name}-%{version}
+
+# 0001-eap_peer-create-a-libeap-library-with-header-files-a.patch
+%patch0 -p1
+# 0001-events-Custom-event-for-broadcom-proprietary-driver.patch
+%patch1 -p1
+# 0002-nl80211-Add-a-get_country-hook.patch
+%patch2 -p1
+# 0003-dbus-Add-a-Country-global-property.patch
+%patch3 -p1
+# wpa_s-alldrivers.patch
+%patch4 -p1
+
+%build
+pushd wpa_supplicant
+cp %{SOURCE1} ./.config
+CFLAGS="${CFLAGS:-%optflags}" ; export CFLAGS ;
+CXXFLAGS="${CXXFLAGS:-%optflags}" ; export CXXFLAGS ;
+
+
+
+make %{?jobs:-j%jobs} V=1
+popd
+# Ugly, but otherwise ARM will FAIL. Force all objects to be rebuilt
+# from the eap_peer subdirectory, so they include -fPIC for the shared
+# library and it won't affect the other built binaries  -- this is an
+# ugly hack, but until the build system in wpa_supplicant is not
+# revamped, I can't think of a better way.
+rm -f src/utils/*.o src/eap_common/*.o src/crypto/*.o src/eap_peer/*.o
+make -C src/eap_peer %{?jobs:-j%jobs}
+%install
+rm -rf %{buildroot}
+
+rm -rf %{buildroot}
+
+# binary
+install -d %{buildroot}/sbin
+install -m 0755 %{name}/wpa_passphrase %{buildroot}/sbin
+install -m 0755 %{name}/wpa_cli %{buildroot}/sbin
+install -m 0755 %{name}/wpa_supplicant %{buildroot}/sbin
+install -d %{buildroot}/%{_sysconfdir}/dbus-1/system.d/
+install -m 0644 %{name}/dbus/dbus-wpa_supplicant.conf %{buildroot}/%{_sysconfdir}/dbus-1/system.d/wpa_supplicant.conf
+install -d %{buildroot}/%{_datadir}/dbus-1/system-services/
+install -m 0644 %{name}/dbus/fi.epitest.hostap.WPASupplicant.service %{buildroot}/%{_datadir}/dbus-1/system-services/fi.epitest.hostap.WPASupplicant.service
+install -m 0644 %{name}/dbus/fi.w1.wpa_supplicant1.service %{buildroot}/%{_datadir}/dbus-1/system-services/fi.w1.wpa_supplicant1.service
+make -C src/eap_peer install DESTDIR=%{buildroot}
+
+# running
+mkdir -p %{buildroot}/%{_localstatedir}/run/%{name}
+
+# man pages
+install -d %{buildroot}%{_mandir}/man{5,8}
+install -m 0644 %{name}/doc/docbook/*.8 %{buildroot}%{_mandir}/man8
+install -m 0644 %{name}/doc/docbook/*.5 %{buildroot}%{_mandir}/man5
+
+# some cleanup in docs
+rm -f  %{name}/doc/.cvsignore
+rm -rf %{name}/doc/docbook
+
+
+
+
+
+
+
+
+%post -n libeap0 -p /sbin/ldconfig
+
+%postun -n libeap0 -p /sbin/ldconfig
+
+
+%docs_package
+
+%files
+%{_sysconfdir}/dbus-1/system.d/%{name}.conf
+%{_datadir}/dbus-1/system-services/fi.epitest.hostap.WPASupplicant.service
+%{_datadir}/dbus-1/system-services/fi.w1.wpa_supplicant1.service
+/sbin/wpa_passphrase
+/sbin/wpa_supplicant
+/sbin/wpa_cli
+%dir %{_localstatedir}/run/%{name}
+
+%files -n libeap0
+/usr/lib/libeap.so.0.0.0
+
+%files -n libeap0-devel
+/usr/lib/libeap.so
+/usr/lib/pkgconfig/libeap0.pc
+/usr/include/eap_peer
+
diff --git a/patches/0001-eap_peer-create-a-libeap-library-with-header-files-a.patch b/patches/0001-eap_peer-create-a-libeap-library-with-header-files-a.patch
new file mode 100644 (file)
index 0000000..c4bff18
--- /dev/null
@@ -0,0 +1,395 @@
+From 3de5e59b291b6f58317bb16736f8c0271754378e Mon Sep 17 00:00:00 2001
+From: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+Date: Sat, 2 Oct 2010 00:11:51 -0700
+Subject: [PATCH] eap_peer: create a libeap library, with header files and pkg-config [v2]
+
+This adds infrastructe in src/eap_peer to make libeap.so and install
+the needed header files and pkg-config files.
+
+Now, this is quite dirty and probably not what we want in the long
+term, but serves as an starting point:
+
+ - we don't build from the wpa_supplicant directory because the
+   objects the .so have to be built with -fPIC. So if you need to
+   build both the binary and the library:
+
+   make -C wpa_supplicant
+   make -C src/eap_peer clean
+   make -C src/eap_peer
+
+   As I said, it's dirty -- we'd need either wpa_supplicant linking
+   against the library properly (but that seems not to be desirable)
+   or a multiple object build approach ala automake.
+
+ - need to use 'override CFLAGS' in src/eap_peer/Makefile, otherwise
+   any CFLAGS setting will kill the build infrastructure. I miss
+   AM_CFLAGS.
+
+ - adds 'eap_register_methods()' that will register every compiled in
+   method.
+
+Signed-off-by: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
+---
+ build_release              |   12 +++
+ src/eap_peer/Makefile      |  191 ++++++++++++++++++++++++++++++++++++++++++--
+ src/eap_peer/eap_methods.c |  114 ++++++++++++++++++++++++++
+ src/eap_peer/eap_methods.h |    1 +
+ src/eap_peer/libeap0.pc    |   10 +++
+ 5 files changed, 320 insertions(+), 8 deletions(-)
+ create mode 100644 src/eap_peer/libeap0.pc
+
+diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
+index 3651056..58c067a 100644
+--- a/src/eap_peer/Makefile
++++ b/src/eap_peer/Makefile
+@@ -1,11 +1,186 @@
+-all:
+-      @echo Nothing to be made.
++LIBEAP_NAME = libeap
++LIBEAP_CURRENT = 0
++LIBEAP_REVISION = 0
++LIBEAP_AGE = 0
++
++LIBEAP = $(LIBEAP_NAME).so.$(LIBEAP_CURRENT).$(LIBEAP_REVISION).$(LIBEAP_AGE)
++LIBEAP_SO = $(LIBEAP_NAME).so.$(LIBEAP_CURRENT)
++
++.PHONY: all clean install uninstall
++
++all: $(LIBEAP)
++
++ifndef CC
++CC=gcc
++endif
++
++ifndef CFLAGS
++CFLAGS = -MMD -O0 -Wall -g
++endif
++
++CONFIG_TLS=openssl
++
++INCLUDE_INSTALL_DIR=/usr/include/eap_peer
++
++# Got to use override all across the board, otherwise a 'make
++# CFLAGS=XX' will kill us because the command line's CFLAGS will
++# overwrite Make's and we'll loose all the infrastructure it sets.
++override CFLAGS += -I. -I.. -I../crypto -I../utils -I../common
++
++# at least for now, need to include config_ssid.h and config_blob.h from
++# wpa_supplicant directory
++override CFLAGS += -I ../../wpa_supplicant
++
++OBJS_both += ../utils/common.o
++OBJS_both += ../utils/os_unix.o
++OBJS_both += ../utils/wpa_debug.o
++OBJS_both += ../utils/base64.o
++OBJS_both += ../utils/wpabuf.o
++OBJS_both += ../crypto/md5.o
++OBJS_both += ../crypto/sha1.o
++OBJS_both += ../crypto/sha1-tlsprf.o
++OBJS_both += ../crypto/aes-encblock.o
++OBJS_both += ../crypto/aes-wrap.o
++OBJS_both += ../crypto/aes-ctr.o
++OBJS_both += ../crypto/aes-eax.o
++OBJS_both += ../crypto/aes-omac1.o
++OBJS_both += ../crypto/ms_funcs.o
++OBJS_both += ../crypto/sha256.o
++
++
++OBJS_both += ../eap_common/eap_peap_common.o
++OBJS_both += ../eap_common/eap_psk_common.o
++OBJS_both += ../eap_common/eap_pax_common.o
++OBJS_both += ../eap_common/eap_sake_common.o
++OBJS_both += ../eap_common/eap_gpsk_common.o
++OBJS_both += ../eap_common/chap.o
++
++OBJS_peer += ../eap_peer/eap_tls.o
++OBJS_peer += ../eap_peer/eap_peap.o
++OBJS_peer += ../eap_peer/eap_ttls.o
++OBJS_peer += ../eap_peer/eap_md5.o
++OBJS_peer += ../eap_peer/eap_mschapv2.o
++OBJS_peer += ../eap_peer/mschapv2.o
++OBJS_peer += ../eap_peer/eap_otp.o
++OBJS_peer += ../eap_peer/eap_gtc.o
++OBJS_peer += ../eap_peer/eap_leap.o
++OBJS_peer += ../eap_peer/eap_psk.o
++OBJS_peer += ../eap_peer/eap_pax.o
++OBJS_peer += ../eap_peer/eap_sake.o
++OBJS_peer += ../eap_peer/eap_gpsk.o
++OBJS_peer += ../eap_peer/eap.o
++OBJS_peer += ../eap_common/eap_common.o
++OBJS_peer += ../eap_peer/eap_methods.o
++OBJS_peer += ../eap_peer/eap_tls_common.o
++
++override CFLAGS += -DEAP_TLS
++override CFLAGS += -DEAP_PEAP
++override CFLAGS += -DEAP_TTLS
++override CFLAGS += -DEAP_MD5
++override CFLAGS += -DEAP_MSCHAPv2
++override CFLAGS += -DEAP_GTC
++override CFLAGS += -DEAP_OTP
++override CFLAGS += -DEAP_LEAP
++override CFLAGS += -DEAP_PSK
++override CFLAGS += -DEAP_PAX
++override CFLAGS += -DEAP_SAKE
++override CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
++override CFLAGS += -DEAP_TLS_FUNCS
++
++override CFLAGS += -DIEEE8021X_EAPOL
++
++ifeq ($(CONFIG_TLS), openssl)
++override CFLAGS += -DEAP_TLS_OPENSSL
++OBJS_both += ../crypto/tls_openssl.o
++OBJS_both += ../crypto/crypto_openssl.o
++LIBS += -lssl -lcrypto
++override CFLAGS += -DINTERNAL_SHA256
++endif
++
++ifeq ($(CONFIG_TLS), internal)
++OBJS_both += ../crypto/tls_internal.o
++OBJS_both += ../tls/tlsv1_common.o ../../tls/tlsv1_record.o
++OBJS_both += ../tls/tlsv1_cred.o
++OBJS_both += ../tls/asn1.o ../../tls/x509v3.o
++OBJS_both += ../crypto/crypto_internal.o ../../tls/rsa.o ../../tls/bignum.o
++
++OBJS_peer += ../tls/tlsv1_client.o
++OBJS_peer += ../tls/tlsv1_client_write.o ../../tls/tlsv1_client_read.o
++override CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
++
++OBJS_server += ../tls/tlsv1_server.o
++OBJS_server += ../tls/tlsv1_server_write.o ../../tls/tlsv1_server_read.o
++override CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
++
++override CFLAGS += -DCONFIG_TLS_INTERNAL
++override CFLAGS += -DCONFIG_CRYPTO_INTERNAL
++override CFLAGS += -DCONFIG_INTERNAL_X509
++override CFLAGS += -DINTERNAL_AES
++override CFLAGS += -DINTERNAL_SHA1
++override CFLAGS += -DINTERNAL_SHA256
++override CFLAGS += -DINTERNAL_MD5
++override CFLAGS += -DINTERNAL_MD4
++override CFLAGS += -DINTERNAL_DES
++ifdef CONFIG_INTERNAL_LIBTOMMATH
++override CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
++else
++LIBS += -ltommath
++endif
++endif
++
++ifndef LDO
++LDO=$(CC)
++endif
++
++
++OBJS_lib=$(OBJS_both) $(OBJS_peer)
++
++ #$(OBJS_server)
++
++override CFLAGS  += -fPIC -DPIC
++LDFLAGS += -shared
++
++$(LIBEAP): $(OBJS_lib)
++      $(LDO) $(LDFLAGS) $(OBJS_lib) -Wl,-soname -Wl,$(LIBEAP_SO) -o $(LIBEAP) $(LIBS)
++
++
++UTIL_HEADERS = ../utils/includes.h ../utils/common.h \
++      ../utils/wpabuf.h ../utils/build_config.h \
++      ../utils/os.h ../utils/wpa_debug.h
++COMMON_HEADERS = ../common/defs.h 
++EAP_COMMON_HEADERS = ../eap_common/eap_defs.h 
++MAIN_HEADERS = eap.h eap_methods.h eap_config.h
++CRYPTO_HEADERS =  ../crypto/tls.h  
++
++install: 
++
++      mkdir -p $(DESTDIR)/usr/lib
++#     copy the lib file to std lib location
++      cp $(LIBEAP) $(DESTDIR)/usr/lib
++      ln -fs $(LIBEAP_SO) $(DESTDIR)/usr/lib/$(LIBEAP_NAME).so
++
++#     copy the headers reqd by apps using eap peer library in its own subfolder under /usr/include
++      mkdir -p \
++              $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/eap_common \
++              $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/common \
++              $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/util \
++              $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/crypto
++      install -m 0644 $(EAP_COMMON_HEADERS) $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/eap_common
++      install -m 0644 $(COMMON_HEADERS) $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/common
++      install -m 0644 $(CRYPTO_HEADERS) $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/crypto
++      install -m 0644 $(UTIL_HEADERS) $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/util
++      install -m 0644 $(MAIN_HEADERS) $(DESTDIR)/$(INCLUDE_INSTALL_DIR)/
++
++      mkdir -p $(DESTDIR)/usr/lib/pkgconfig
++      cp libeap0.pc $(DESTDIR)/usr/lib/pkgconfig
++
++uninstall: 
++
++      rm $(DESTDIR)/usr/lib/$(LIBEAP)
++      rm -fr $(DESTDIR)/$(INCLUDE_INSTALL_DIR)
++      rm -f $(DESTDIR)/usr/lib/pkgconfig/libeap0.pc
+ clean:
+-      rm -f *~ *.o *.so *.d
++      rm -f *~ *.o *.so *.d libeap.a $(LIBEAP) $(OBJS_lib)
+-install:
+-      if ls *.so >/dev/null 2>&1; then \
+-              install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+-              cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+-      ; fi
++-include $(OBJS:%.o=%.d)
+diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
+index 3b0af05..092f266 100644
+--- a/src/eap_peer/eap_methods.c
++++ b/src/eap_peer/eap_methods.c
+@@ -340,6 +340,120 @@ int eap_peer_method_register(struct eap_method *method)
+ /**
++ * eap_peer_register_methods - Register all known EAP peer methods
++ *
++ * This function is called at program start to register all compiled
++ * in EAP peer methods.
++ */
++int eap_peer_register_methods(void)
++{
++      int ret = 0;
++
++#ifdef EAP_MD5
++      if (ret == 0)
++              ret = eap_peer_md5_register();
++#endif /* EAP_MD5 */
++
++#ifdef EAP_TLS
++      if (ret == 0)
++              ret = eap_peer_tls_register();
++#endif /* EAP_TLS */
++
++#ifdef EAP_MSCHAPv2
++      if (ret == 0)
++              ret = eap_peer_mschapv2_register();
++#endif /* EAP_MSCHAPv2 */
++
++#ifdef EAP_PEAP
++      if (ret == 0)
++              ret = eap_peer_peap_register();
++#endif /* EAP_PEAP */
++
++#ifdef EAP_TTLS
++      if (ret == 0)
++              ret = eap_peer_ttls_register();
++#endif /* EAP_TTLS */
++
++#ifdef EAP_GTC
++      if (ret == 0)
++              ret = eap_peer_gtc_register();
++#endif /* EAP_GTC */
++
++#ifdef EAP_OTP
++      if (ret == 0)
++              ret = eap_peer_otp_register();
++#endif /* EAP_OTP */
++
++#ifdef EAP_SIM
++      if (ret == 0)
++              ret = eap_peer_sim_register();
++#endif /* EAP_SIM */
++
++#ifdef EAP_LEAP
++      if (ret == 0)
++              ret = eap_peer_leap_register();
++#endif /* EAP_LEAP */
++
++#ifdef EAP_PSK
++      if (ret == 0)
++              ret = eap_peer_psk_register();
++#endif /* EAP_PSK */
++
++#ifdef EAP_AKA
++      if (ret == 0)
++              ret = eap_peer_aka_register();
++#endif /* EAP_AKA */
++
++#ifdef EAP_AKA_PRIME
++      if (ret == 0)
++              ret = eap_peer_aka_prime_register();
++#endif /* EAP_AKA_PRIME */
++
++#ifdef EAP_FAST
++      if (ret == 0)
++              ret = eap_peer_fast_register();
++#endif /* EAP_FAST */
++
++#ifdef EAP_PAX
++      if (ret == 0)
++              ret = eap_peer_pax_register();
++#endif /* EAP_PAX */
++
++#ifdef EAP_SAKE
++      if (ret == 0)
++              ret = eap_peer_sake_register();
++#endif /* EAP_SAKE */
++
++#ifdef EAP_GPSK
++      if (ret == 0)
++              ret = eap_peer_gpsk_register();
++#endif /* EAP_GPSK */
++
++#ifdef EAP_WSC
++      if (ret == 0)
++              ret = eap_peer_wsc_register();
++#endif /* EAP_WSC */
++
++#ifdef EAP_IKEV2
++      if (ret == 0)
++              ret = eap_peer_ikev2_register();
++#endif /* EAP_IKEV2 */
++
++#ifdef EAP_VENDOR_TEST
++      if (ret == 0)
++              ret = eap_peer_vendor_test_register();
++#endif /* EAP_VENDOR_TEST */
++
++#ifdef EAP_TNC
++      if (ret == 0)
++              ret = eap_peer_tnc_register();
++#endif /* EAP_TNC */
++
++      return ret;
++}
++
++
++/**
+  * eap_peer_unregister_methods - Unregister EAP peer methods
+  *
+  * This function is called at program termination to unregister all EAP peer
+diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
+index 384c61b..b83a46f 100644
+--- a/src/eap_peer/eap_methods.h
++++ b/src/eap_peer/eap_methods.h
+@@ -32,6 +32,7 @@ EapType eap_peer_get_type(const char *name, int *vendor);
+ const char * eap_get_name(int vendor, EapType type);
+ size_t eap_get_names(char *buf, size_t buflen);
+ char ** eap_get_names_as_string_array(size_t *num);
++int eap_peer_register_methods(void);
+ void eap_peer_unregister_methods(void);
+ #else /* IEEE8021X_EAPOL */
+diff --git a/src/eap_peer/libeap0.pc b/src/eap_peer/libeap0.pc
+new file mode 100644
+index 0000000..2f8463a
+--- /dev/null
++++ b/src/eap_peer/libeap0.pc
+@@ -0,0 +1,10 @@
++prefix=/usr
++exec_prefix=/usr
++libdir=${exec_prefix}/lib
++includedir=${prefix}/include
++
++Name: libeap0
++Description: EAP Peer Library API
++Version: 0.7.2
++Libs: -L${libdir} -leap
++Cflags: -I${includedir}
+-- 
+1.6.6.1
+
diff --git a/patches/0001-events-Custom-event-for-broadcom-proprietary-driver.patch b/patches/0001-events-Custom-event-for-broadcom-proprietary-driver.patch
new file mode 100644 (file)
index 0000000..3e05d84
--- /dev/null
@@ -0,0 +1,69 @@
+From bf47542f9db46e7bd9a79f66e8d919c583991c5b Mon Sep 17 00:00:00 2001
+From: Samuel Ortiz <sameo@linux.intel.com>
+Date: Wed, 15 Dec 2010 20:36:04 +0100
+Subject: [PATCH 1/3] events: Custom event for broadcom proprietary driver
+
+---
+ src/drivers/driver.h      |   10 +++++++++-
+ src/drivers/driver_wext.c |    3 +++
+ wpa_supplicant/events.c   |    9 +++++++++
+ 3 files changed, 21 insertions(+), 1 deletions(-)
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index fa49da4..511f613 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -2046,7 +2046,15 @@ enum wpa_event_type {
+        * observed in frames received from the current AP if signal strength
+        * monitoring has been enabled with signal_monitor().
+        */
+-      EVENT_SIGNAL_CHANGE
++      EVENT_SIGNAL_CHANGE,
++
++      /**
++       * EVENT_BROADCOM_CUSTOM - Broadcom custom event
++       *
++       * This event is sent when failing to associate while running the
++       * initial scan.
++       */
++      EVENT_BROADCOM_CUSTOM
+ };
+diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
+index 2614f23..04094ee 100644
+--- a/src/drivers/driver_wext.c
++++ b/src/drivers/driver_wext.c
+@@ -299,6 +299,9 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
+               }
+               wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+ #endif /* CONFIG_PEERKEY */
++      } else if (os_strncmp(custom, "Conn NoNetworks", 15) == 0) {
++              wpa_printf(MSG_DEBUG, "WEXT: Broadcom custom event");
++              wpa_supplicant_event(ctx, EVENT_BROADCOM_CUSTOM, &data);
+       }
+ }
+diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
+index 85dcfb2..e925447 100644
+--- a/wpa_supplicant/events.c
++++ b/wpa_supplicant/events.c
+@@ -1722,6 +1722,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+               bgscan_notify_signal_change(
+                       wpa_s, data->signal_change.above_threshold);
+               break;
++      case EVENT_BROADCOM_CUSTOM:
++              wpa_printf(MSG_DEBUG, "Broadcom event in state %d",
++                         wpa_s->wpa_state);
++              if (wpa_s->wpa_state == WPA_SCANNING) {
++                      /* Force scanning */
++                      wpa_s->scan_req = 1;
++                      wpa_supplicant_req_scan(wpa_s, 0, 0);
++              }
++              break;
+       default:
+               wpa_printf(MSG_INFO, "Unknown event %d", event);
+               break;
+-- 
+1.7.2.3
+
diff --git a/patches/0002-nl80211-Add-a-get_country-hook.patch b/patches/0002-nl80211-Add-a-get_country-hook.patch
new file mode 100644 (file)
index 0000000..2499777
--- /dev/null
@@ -0,0 +1,120 @@
+From 0c62f3723c3c4782d67266b467ce7296eae99b7b Mon Sep 17 00:00:00 2001
+From: Samuel Ortiz <sameo@linux.intel.com>
+Date: Wed, 15 Dec 2010 20:36:41 +0100
+Subject: [PATCH 2/3] nl80211: Add a get_country hook
+
+Get the current regulatory domain through nl80211.
+---
+ src/drivers/driver.h         |    8 ++++++
+ src/drivers/driver_nl80211.c |   50 ++++++++++++++++++++++++++++++++++++++++++
+ wpa_supplicant/driver_i.h    |    7 ++++++
+ 3 files changed, 65 insertions(+), 0 deletions(-)
+
+diff --git a/src/drivers/driver.h b/src/drivers/driver.h
+index 511f613..31dcbb4 100644
+--- a/src/drivers/driver.h
++++ b/src/drivers/driver.h
+@@ -1081,6 +1081,14 @@ struct wpa_driver_ops {
+        struct wpa_scan_results * (*get_scan_results2)(void *priv);
+       /**
++       * get_country - get country
++       * @priv: Private driver interface data
++       * Returns: Allocated alpha2 null terminated string (caller is
++       * responsible for freeing the data structure), NULL on failure.
++       */
++      char * (*get_country)(void *priv);
++
++      /**
+        * set_country - Set country
+        * @priv: Private driver interface data
+        * @alpha2: country to which to switch to
+diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
+index 364158c..1a3c4ac 100644
+--- a/src/drivers/driver_nl80211.c
++++ b/src/drivers/driver_nl80211.c
+@@ -1097,6 +1097,55 @@ static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+ }
++static int nl80211_get_country(struct nl_msg *msg, void *arg)
++{
++      char **alpha2 = arg;
++      struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
++      struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
++
++      nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
++                genlmsg_attrlen(gnlh, 0), NULL);
++      if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
++          !tb_msg[NL80211_ATTR_REG_RULES]) {
++              wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
++                         "available");
++              return NL_SKIP;
++      }
++
++      *alpha2 = os_strdup((char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
++
++      return NL_SKIP;
++}
++
++/**
++ * wpa_driver_nl80211_get_country - ask nl80211 to get the regulatory domain
++ * @priv: driver_nl80211 private data.
++ * Returns: An allocated alpha2 null terminated string, NULL on failure.
++ *
++ * This asks nl80211 to get the regulatory domain.
++ */
++static char *wpa_driver_nl80211_get_country(void *priv)
++{
++      struct i802_bss *bss = priv;
++      struct wpa_driver_nl80211_data *drv = bss->drv;
++      char *alpha2 = NULL;
++      struct nl_msg *msg;
++
++      msg = nlmsg_alloc();
++      if (!msg)
++              return NULL;
++
++      genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
++                  0, NL80211_CMD_GET_REG, 0);
++
++      if (send_and_recv_msgs(drv, msg, nl80211_get_country, &alpha2))
++              return NULL;
++
++      wpa_printf(MSG_DEBUG, "nl80211: Country=%s", alpha2);
++
++      return alpha2;
++}
++
+ /**
+  * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+  * @priv: driver_nl80211 private data
+@@ -5345,6 +5394,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+       .get_capa = wpa_driver_nl80211_get_capa,
+       .set_operstate = wpa_driver_nl80211_set_operstate,
+       .set_supp_port = wpa_driver_nl80211_set_supp_port,
++      .get_country = wpa_driver_nl80211_get_country,
+       .set_country = wpa_driver_nl80211_set_country,
+       .set_beacon = wpa_driver_nl80211_set_beacon,
+       .if_add = wpa_driver_nl80211_if_add,
+diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
+index a70aa6a..3058afa 100644
+--- a/wpa_supplicant/driver_i.h
++++ b/wpa_supplicant/driver_i.h
+@@ -265,6 +265,13 @@ static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s,
+       return -1;
+ }
++static inline char *wpa_drv_get_country(struct wpa_supplicant *wpa_s)
++{
++      if (wpa_s->driver->get_country)
++              return wpa_s->driver->get_country(wpa_s->drv_priv);
++      return NULL;
++}
++
+ static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+                                     const char *alpha2)
+ {
+-- 
+1.7.2.3
+
diff --git a/patches/0003-dbus-Add-a-Country-global-property.patch b/patches/0003-dbus-Add-a-Country-global-property.patch
new file mode 100644 (file)
index 0000000..ccc7354
--- /dev/null
@@ -0,0 +1,121 @@
+From 079bd8b4cbe03d0beb2f7e17e0df4b851b2e6318 Mon Sep 17 00:00:00 2001
+From: Samuel Ortiz <sameo@linux.intel.com>
+Date: Wed, 15 Dec 2010 20:37:16 +0100
+Subject: [PATCH 3/3] dbus: Add a Country global property
+
+This read-write property allows to pass an ISO 3166-1 alpha2 string to the
+cfg80211 layer, through D-Bus.
+---
+ wpa_supplicant/dbus/dbus_new.c          |    5 +++
+ wpa_supplicant/dbus/dbus_new_handlers.c |   61 +++++++++++++++++++++++++++++++
+ wpa_supplicant/dbus/dbus_new_handlers.h |    6 +++
+ 3 files changed, 72 insertions(+), 0 deletions(-)
+
+diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
+index bdfbbac..64e2887 100644
+--- a/wpa_supplicant/dbus/dbus_new.c
++++ b/wpa_supplicant/dbus/dbus_new.c
+@@ -879,6 +879,11 @@ static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
+         NULL,
+         R
+       },
++      { "Country", WPAS_DBUS_NEW_INTERFACE, "s",
++        (WPADBusPropertyAccessor) wpas_dbus_getter_country,
++        (WPADBusPropertyAccessor) wpas_dbus_setter_country,
++        RW
++      },
+       { NULL, NULL, NULL, NULL, NULL, 0 }
+ };
+diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
+index e2b5e50..6a0fe10 100644
+--- a/wpa_supplicant/dbus/dbus_new_handlers.c
++++ b/wpa_supplicant/dbus/dbus_new_handlers.c
+@@ -923,6 +923,67 @@ DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing)
+       return reply;
+ }
++/**
++ * wpas_dbus_getter_country - Get the ISO/IEC alpha2 country code
++ * @message: Pointer to incoming dbus message
++ * @global: %wpa_supplicant global data structure
++ * Returns: DBus message with the alpha2 string
++ *
++ * Getter for "Country" property.
++ */
++DBusMessage * wpas_dbus_getter_country(DBusMessage *message,
++                                     struct wpa_global *global)
++{
++      DBusMessage *reply;
++      struct wpa_supplicant *wpa_s;
++      char *country;
++
++      wpa_s = global->ifaces;
++
++      if (wpa_s == NULL)
++              return NULL;
++
++      country = wpa_drv_get_country(wpa_s);
++
++      reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
++                                              &country);
++
++      os_free(country);
++
++      return reply;
++}
++
++
++/**
++ * wpas_dbus_setter_country - Set the ISO/IEC alpha2 country code
++ * @message: Pointer to incoming dbus message
++ * @global: %wpa_supplicant global data structure
++ * Returns: %NULL or DBus error message
++ *
++ * Setter function for the "Country" property.
++ */
++DBusMessage * wpas_dbus_setter_country(DBusMessage *message,
++                                     struct wpa_global *global)
++{
++      struct wpa_supplicant *wpa_s;
++      DBusMessage *reply = NULL;
++      char *country;
++
++      wpa_s = global->ifaces;
++
++      if (wpa_s == NULL)
++              return NULL;
++
++      reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING,
++                                               &country);
++      if (reply)
++              return reply;
++
++      if (wpa_drv_set_country(wpa_s, country))
++              return wpas_dbus_error_invalid_args(message, "Invalid country");
++
++      return NULL;
++}
+ static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var,
+                                  char **type, DBusMessage **reply)
+diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
+index 3cdf9cb..3d0b9d6 100644
+--- a/wpa_supplicant/dbus/dbus_new_handlers.h
++++ b/wpa_supplicant/dbus/dbus_new_handlers.h
+@@ -71,6 +71,12 @@ DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
+ DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message,
+                                          void *nothing);
++DBusMessage * wpas_dbus_getter_country(DBusMessage *message,
++                                     struct wpa_global *global);
++
++DBusMessage * wpas_dbus_setter_country(DBusMessage *message,
++                                     struct wpa_global *global);
++
+ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
+                                    struct wpa_supplicant *wpa_s);
+-- 
+1.7.2.3
+
diff --git a/patches/openssl-0.9.8-tls-extensions.patch b/patches/openssl-0.9.8-tls-extensions.patch
new file mode 100644 (file)
index 0000000..44490cc
--- /dev/null
@@ -0,0 +1,429 @@
+This patch is adding support for TLS hello extensions and externally
+generated pre-shared key material to OpenSSL 0.9.8. This is
+based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+
+
+diff -uprN openssl-0.9.8.orig/include/openssl/ssl.h openssl-0.9.8/include/openssl/ssl.h
+--- openssl-0.9.8.orig/include/openssl/ssl.h   2005-06-10 12:51:16.000000000 -0700
++++ openssl-0.9.8/include/openssl/ssl.h        2005-07-19 20:02:15.000000000 -0700
+@@ -340,6 +340,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -968,6 +971,15 @@ struct ssl_st
+       int first_packet;
+       int client_version;     /* what was passed, used for
+                                * SSLv3/TLS rollback check */
++
++      /* TLS externsions */
++      TLS_EXTENSION *tls_extension;
++      int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg);
++      void *tls_extension_cb_arg;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
+       };
+ #ifdef __cplusplus
+@@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -uprN openssl-0.9.8.orig/include/openssl/tls1.h openssl-0.9.8/include/openssl/tls1.h
+--- openssl-0.9.8.orig/include/openssl/tls1.h  2003-07-22 05:34:21.000000000 -0700
++++ openssl-0.9.8/include/openssl/tls1.h       2005-07-19 20:02:15.000000000 -0700
+@@ -282,6 +282,14 @@ extern "C" {
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -uprN openssl-0.9.8.orig/ssl/Makefile openssl-0.9.8/ssl/Makefile
+--- openssl-0.9.8.orig/ssl/Makefile    2005-05-30 16:20:30.000000000 -0700
++++ openssl-0.9.8/ssl/Makefile 2005-07-19 20:02:15.000000000 -0700
+@@ -24,7 +24,7 @@ LIBSRC=      \
+       s2_meth.c   s2_srvr.c s2_clnt.c  s2_lib.c  s2_enc.c s2_pkt.c \
+       s3_meth.c   s3_srvr.c s3_clnt.c  s3_lib.c  s3_enc.c s3_pkt.c s3_both.c \
+       s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c          s23_pkt.c \
+-      t1_meth.c   t1_srvr.c t1_clnt.c  t1_lib.c  t1_enc.c \
++      t1_meth.c   t1_srvr.c t1_clnt.c  t1_lib.c  t1_enc.c                    t1_ext.c \
+       d1_meth.c   d1_srvr.c d1_clnt.c  d1_lib.c  d1_pkt.c \
+       d1_both.c d1_enc.c \
+       ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \
+@@ -35,7 +35,7 @@ LIBOBJ= \
+       s2_meth.o  s2_srvr.o  s2_clnt.o  s2_lib.o  s2_enc.o s2_pkt.o \
+       s3_meth.o  s3_srvr.o  s3_clnt.o  s3_lib.o  s3_enc.o s3_pkt.o s3_both.o \
+       s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o          s23_pkt.o \
+-      t1_meth.o   t1_srvr.o t1_clnt.o  t1_lib.o  t1_enc.o \
++      t1_meth.o   t1_srvr.o t1_clnt.o  t1_lib.o  t1_enc.o                    t1_ext.o \
+       d1_meth.o   d1_srvr.o d1_clnt.o  d1_lib.o  d1_pkt.o \
+       d1_both.o d1_enc.o \
+       ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \
+@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h ..
+ t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
+ t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h
+ t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c
++t1_ext.o: t1_ext.c ssl_locl.h
+diff -uprN openssl-0.9.8.orig/ssl/s3_clnt.c openssl-0.9.8/ssl/s3_clnt.c
+--- openssl-0.9.8.orig/ssl/s3_clnt.c   2005-05-16 03:11:03.000000000 -0700
++++ openssl-0.9.8/ssl/s3_clnt.c        2005-07-19 20:02:15.000000000 -0700
+@@ -606,6 +606,20 @@ int ssl3_client_hello(SSL *s)
+                       }
+               *(p++)=0; /* Add the NULL method */
+               
++              /* send client hello extensions if any */
++              if (s->version >= TLS1_VERSION && s->tls_extension)
++              {
++                      // set the total extensions length
++                      s2n(s->tls_extension->length + 4, p);
++
++                      // put the extensions with type and length
++                      s2n(s->tls_extension->type, p);
++                      s2n(s->tls_extension->length, p);
++                      
++                      memcpy(p, s->tls_extension->data, s->tls_extension->length);
++                      p+=s->tls_extension->length;
++              }
++
+               l=(p-d);
+               d=buf;
+               *(d++)=SSL3_MT_CLIENT_HELLO;
+@@ -628,7 +642,7 @@ int ssl3_get_server_hello(SSL *s)
+       STACK_OF(SSL_CIPHER) *sk;
+       SSL_CIPHER *c;
+       unsigned char *p,*d;
+-      int i,al,ok;
++      int i,al,ok,pre_shared;
+       unsigned int j;
+       long n;
+       SSL_COMP *comp;
+@@ -693,7 +707,24 @@ int ssl3_get_server_hello(SSL *s)
+               goto f_err;
+               }
+-      if (j != 0 && j == s->session->session_id_length
++      /* check if we want to resume the session based on external pre-shared secret */
++      pre_shared = 0;
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j);
++                      s->session->session_id_length = j;
++                      memcpy(s->session->session_id, p, j);
++                      pre_shared = 1;
++              }
++      }
++
++      if ((pre_shared || j != 0) && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+           if(s->sid_ctx_length != s->session->sid_ctx_length
+diff -uprN openssl-0.9.8.orig/ssl/s3_srvr.c openssl-0.9.8/ssl/s3_srvr.c
+--- openssl-0.9.8.orig/ssl/s3_srvr.c   2005-05-22 17:32:55.000000000 -0700
++++ openssl-0.9.8/ssl/s3_srvr.c        2005-07-19 20:02:15.000000000 -0700
+@@ -955,6 +955,75 @@ int ssl3_get_client_hello(SSL *s)
+               }
+ #endif
++      /* Check for TLS client hello extension here */
++      if (p < (d+n) && s->version >= TLS1_VERSION)
++      {
++              if (s->tls_extension_cb)
++              {
++                      TLS_EXTENSION tls_ext;
++                      unsigned short ext_total_len;
++                      
++                      n2s(p, ext_total_len);
++                      n2s(p, tls_ext.type);
++                      n2s(p, tls_ext.length);
++
++                      // sanity check in TLS extension len
++                      if (tls_ext.length > (d+n) - p)
++                      {
++                              // just cut the lenth to packet border
++                              tls_ext.length = (d+n) - p;
++                      }
++
++                      tls_ext.data = p;
++
++                      // returns an alert code or 0
++                      al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg);
++                      if (al != 0)
++                      {
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR);
++                              goto f_err;
++                      }
++              }
++      }
++
++      /* Check if we want to use external pre-shared secret for this handshake */
++      /* for not reused session only */
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++                      
++                      ciphers=NULL;
++                      
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++              }
++      }
++
+       /* Given s->session->ciphers and SSL_get_ciphers, we must
+        * pick a cipher */
+diff -uprN openssl-0.9.8.orig/ssl/ssl_err.c openssl-0.9.8/ssl/ssl_err.c
+--- openssl-0.9.8.orig/ssl/ssl_err.c   2005-06-10 12:51:16.000000000 -0700
++++ openssl-0.9.8/ssl/ssl_err.c        2005-07-19 20:02:15.000000000 -0700
+@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC),    "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"},
+ {0,NULL}
+       };
+diff -uprN openssl-0.9.8.orig/ssl/ssl.h openssl-0.9.8/ssl/ssl.h
+--- openssl-0.9.8.orig/ssl/ssl.h       2005-06-10 12:51:16.000000000 -0700
++++ openssl-0.9.8/ssl/ssl.h    2005-07-19 20:02:15.000000000 -0700
+@@ -340,6 +340,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -361,6 +362,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -968,6 +971,15 @@ struct ssl_st
+       int first_packet;
+       int client_version;     /* what was passed, used for
+                                * SSLv3/TLS rollback check */
++
++      /* TLS externsions */
++      TLS_EXTENSION *tls_extension;
++      int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg);
++      void *tls_extension_cb_arg;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
+       };
+ #ifdef __cplusplus
+@@ -1533,6 +1545,13 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1714,6 +1733,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -uprN openssl-0.9.8.orig/ssl/ssl_sess.c openssl-0.9.8/ssl/ssl_sess.c
+--- openssl-0.9.8.orig/ssl/ssl_sess.c  2005-04-29 13:10:06.000000000 -0700
++++ openssl-0.9.8/ssl/ssl_sess.c       2005-07-19 20:02:15.000000000 -0700
+@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+       return(s->session_timeout);
+       }
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, 
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++{
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++}
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+diff -uprN openssl-0.9.8.orig/ssl/t1_ext.c openssl-0.9.8/ssl/t1_ext.c
+--- openssl-0.9.8.orig/ssl/t1_ext.c    1969-12-31 16:00:00.000000000 -0800
++++ openssl-0.9.8/ssl/t1_ext.c 2005-07-19 20:03:29.000000000 -0700
+@@ -0,0 +1,48 @@
++
++#include <stdio.h>
++#include "ssl_locl.h"
++
++
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              if(s->tls_extension)
++              {
++                      OPENSSL_free(s->tls_extension);
++                      s->tls_extension = NULL;
++              }
++
++              if(ext_data)
++              {
++                      s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len);
++                      if(!s->tls_extension)
++                      {
++                              SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE);
++                              return 0;
++                      }
++
++                      s->tls_extension->type = ext_type;
++                      s->tls_extension->length = ext_len;
++                      s->tls_extension->data = s->tls_extension + 1;
++                      memcpy(s->tls_extension->data, ext_data, ext_len);
++              }
++
++              return 1;
++      }
++
++      return 0;
++}
++
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              s->tls_extension_cb = cb;
++              s->tls_extension_cb_arg = arg;
++
++              return 1;
++      }
++
++      return 0;
++}
+diff -uprN openssl-0.9.8.orig/ssl/t1_lib.c openssl-0.9.8/ssl/t1_lib.c
+--- openssl-0.9.8.orig/ssl/t1_lib.c    2005-04-26 09:02:40.000000000 -0700
++++ openssl-0.9.8/ssl/t1_lib.c 2005-07-19 20:02:15.000000000 -0700
+@@ -131,6 +131,10 @@ int tls1_new(SSL *s)
+ void tls1_free(SSL *s)
+       {
++      if(s->tls_extension)
++      {
++              OPENSSL_free(s->tls_extension);
++      }
+       ssl3_free(s);
+       }
+diff -uprN openssl-0.9.8.orig/ssl/tls1.h openssl-0.9.8/ssl/tls1.h
+--- openssl-0.9.8.orig/ssl/tls1.h      2003-07-22 05:34:21.000000000 -0700
++++ openssl-0.9.8/ssl/tls1.h   2005-07-19 20:02:15.000000000 -0700
+@@ -282,6 +282,14 @@ extern "C" {
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -uprN openssl-0.9.8.orig/util/ssleay.num openssl-0.9.8/util/ssleay.num
+--- openssl-0.9.8.orig/util/ssleay.num 2005-05-08 17:22:02.000000000 -0700
++++ openssl-0.9.8/util/ssleay.num      2005-07-19 20:02:15.000000000 -0700
+@@ -226,3 +226,6 @@ DTLSv1_server_method                    
+ SSL_COMP_get_compression_methods        276   EXIST:!VMS:FUNCTION:COMP
+ SSL_COMP_get_compress_methods           276   EXIST:VMS:FUNCTION:COMP
+ SSL_SESSION_get_id                      277   EXIST::FUNCTION:
++SSL_set_hello_extension                       278     EXIST::FUNCTION:
++SSL_set_hello_extension_cb            279     EXIST::FUNCTION:
++SSL_set_session_secret_cb             280     EXIST::FUNCTION:
diff --git a/patches/openssl-0.9.8d-tls-extensions.patch b/patches/openssl-0.9.8d-tls-extensions.patch
new file mode 100644 (file)
index 0000000..eec6db8
--- /dev/null
@@ -0,0 +1,429 @@
+This patch is adding support for TLS hello extensions and externally
+generated pre-shared key material to OpenSSL 0.9.8d. This is
+based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+
+
+diff -uprN openssl-0.9.8d.orig/include/openssl/ssl.h openssl-0.9.8d/include/openssl/ssl.h
+--- openssl-0.9.8d.orig/include/openssl/ssl.h  2006-06-14 06:52:49.000000000 -0700
++++ openssl-0.9.8d/include/openssl/ssl.h       2006-12-10 08:20:02.000000000 -0800
+@@ -345,6 +345,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -973,6 +976,15 @@ struct ssl_st
+       int first_packet;
+       int client_version;     /* what was passed, used for
+                                * SSLv3/TLS rollback check */
++
++      /* TLS externsions */
++      TLS_EXTENSION *tls_extension;
++      int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg);
++      void *tls_extension_cb_arg;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
+       };
+ #ifdef __cplusplus
+@@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -uprN openssl-0.9.8d.orig/include/openssl/tls1.h openssl-0.9.8d/include/openssl/tls1.h
+--- openssl-0.9.8d.orig/include/openssl/tls1.h 2006-06-14 10:52:01.000000000 -0700
++++ openssl-0.9.8d/include/openssl/tls1.h      2006-12-10 08:20:02.000000000 -0800
+@@ -296,6 +296,14 @@ extern "C" {
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -uprN openssl-0.9.8d.orig/ssl/Makefile openssl-0.9.8d/ssl/Makefile
+--- openssl-0.9.8d.orig/ssl/Makefile   2006-02-03 17:49:35.000000000 -0800
++++ openssl-0.9.8d/ssl/Makefile        2006-12-10 08:20:02.000000000 -0800
+@@ -24,7 +24,7 @@ LIBSRC=      \
+       s2_meth.c   s2_srvr.c s2_clnt.c  s2_lib.c  s2_enc.c s2_pkt.c \
+       s3_meth.c   s3_srvr.c s3_clnt.c  s3_lib.c  s3_enc.c s3_pkt.c s3_both.c \
+       s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c          s23_pkt.c \
+-      t1_meth.c   t1_srvr.c t1_clnt.c  t1_lib.c  t1_enc.c \
++      t1_meth.c   t1_srvr.c t1_clnt.c  t1_lib.c  t1_enc.c                    t1_ext.c \
+       d1_meth.c   d1_srvr.c d1_clnt.c  d1_lib.c  d1_pkt.c \
+       d1_both.c d1_enc.c \
+       ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \
+@@ -35,7 +35,7 @@ LIBOBJ= \
+       s2_meth.o  s2_srvr.o  s2_clnt.o  s2_lib.o  s2_enc.o s2_pkt.o \
+       s3_meth.o  s3_srvr.o  s3_clnt.o  s3_lib.o  s3_enc.o s3_pkt.o s3_both.o \
+       s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o          s23_pkt.o \
+-      t1_meth.o   t1_srvr.o t1_clnt.o  t1_lib.o  t1_enc.o \
++      t1_meth.o   t1_srvr.o t1_clnt.o  t1_lib.o  t1_enc.o                    t1_ext.o \
+       d1_meth.o   d1_srvr.o d1_clnt.o  d1_lib.o  d1_pkt.o \
+       d1_both.o d1_enc.o \
+       ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \
+@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h ..
+ t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
+ t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h
+ t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c
++t1_ext.o: t1_ext.c ssl_locl.h
+diff -uprN openssl-0.9.8d.orig/ssl/s3_clnt.c openssl-0.9.8d/ssl/s3_clnt.c
+--- openssl-0.9.8d.orig/ssl/s3_clnt.c  2005-12-12 23:41:46.000000000 -0800
++++ openssl-0.9.8d/ssl/s3_clnt.c       2006-12-10 08:20:02.000000000 -0800
+@@ -601,6 +601,20 @@ int ssl3_client_hello(SSL *s)
+ #endif
+               *(p++)=0; /* Add the NULL method */
+               
++              /* send client hello extensions if any */
++              if (s->version >= TLS1_VERSION && s->tls_extension)
++              {
++                      // set the total extensions length
++                      s2n(s->tls_extension->length + 4, p);
++
++                      // put the extensions with type and length
++                      s2n(s->tls_extension->type, p);
++                      s2n(s->tls_extension->length, p);
++                      
++                      memcpy(p, s->tls_extension->data, s->tls_extension->length);
++                      p+=s->tls_extension->length;
++              }
++
+               l=(p-d);
+               d=buf;
+               *(d++)=SSL3_MT_CLIENT_HELLO;
+@@ -623,7 +637,7 @@ int ssl3_get_server_hello(SSL *s)
+       STACK_OF(SSL_CIPHER) *sk;
+       SSL_CIPHER *c;
+       unsigned char *p,*d;
+-      int i,al,ok;
++      int i,al,ok,pre_shared;
+       unsigned int j;
+       long n;
+ #ifndef OPENSSL_NO_COMP
+@@ -690,7 +704,24 @@ int ssl3_get_server_hello(SSL *s)
+               goto f_err;
+               }
+-      if (j != 0 && j == s->session->session_id_length
++      /* check if we want to resume the session based on external pre-shared secret */
++      pre_shared = 0;
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j);
++                      s->session->session_id_length = j;
++                      memcpy(s->session->session_id, p, j);
++                      pre_shared = 1;
++              }
++      }
++
++      if ((pre_shared || j != 0) && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+           if(s->sid_ctx_length != s->session->sid_ctx_length
+diff -uprN openssl-0.9.8d.orig/ssl/s3_srvr.c openssl-0.9.8d/ssl/s3_srvr.c
+--- openssl-0.9.8d.orig/ssl/s3_srvr.c  2006-09-28 04:29:03.000000000 -0700
++++ openssl-0.9.8d/ssl/s3_srvr.c       2006-12-10 08:20:02.000000000 -0800
+@@ -943,6 +943,75 @@ int ssl3_get_client_hello(SSL *s)
+               }
+ #endif
++      /* Check for TLS client hello extension here */
++      if (p < (d+n) && s->version >= TLS1_VERSION)
++      {
++              if (s->tls_extension_cb)
++              {
++                      TLS_EXTENSION tls_ext;
++                      unsigned short ext_total_len;
++                      
++                      n2s(p, ext_total_len);
++                      n2s(p, tls_ext.type);
++                      n2s(p, tls_ext.length);
++
++                      // sanity check in TLS extension len
++                      if (tls_ext.length > (d+n) - p)
++                      {
++                              // just cut the lenth to packet border
++                              tls_ext.length = (d+n) - p;
++                      }
++
++                      tls_ext.data = p;
++
++                      // returns an alert code or 0
++                      al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg);
++                      if (al != 0)
++                      {
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR);
++                              goto f_err;
++                      }
++              }
++      }
++
++      /* Check if we want to use external pre-shared secret for this handshake */
++      /* for not reused session only */
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++                      
++                      ciphers=NULL;
++                      
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++              }
++      }
++
+       /* Given s->session->ciphers and SSL_get_ciphers, we must
+        * pick a cipher */
+diff -uprN openssl-0.9.8d.orig/ssl/ssl.h openssl-0.9.8d/ssl/ssl.h
+--- openssl-0.9.8d.orig/ssl/ssl.h      2006-06-14 06:52:49.000000000 -0700
++++ openssl-0.9.8d/ssl/ssl.h   2006-12-10 08:20:02.000000000 -0800
+@@ -345,6 +345,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -973,6 +976,15 @@ struct ssl_st
+       int first_packet;
+       int client_version;     /* what was passed, used for
+                                * SSLv3/TLS rollback check */
++
++      /* TLS externsions */
++      TLS_EXTENSION *tls_extension;
++      int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg);
++      void *tls_extension_cb_arg;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
+       };
+ #ifdef __cplusplus
+@@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -uprN openssl-0.9.8d.orig/ssl/ssl_err.c openssl-0.9.8d/ssl/ssl_err.c
+--- openssl-0.9.8d.orig/ssl/ssl_err.c  2006-01-08 13:52:46.000000000 -0800
++++ openssl-0.9.8d/ssl/ssl_err.c       2006-12-10 08:20:02.000000000 -0800
+@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC),    "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"},
+ {0,NULL}
+       };
+diff -uprN openssl-0.9.8d.orig/ssl/ssl_sess.c openssl-0.9.8d/ssl/ssl_sess.c
+--- openssl-0.9.8d.orig/ssl/ssl_sess.c 2005-12-30 15:51:57.000000000 -0800
++++ openssl-0.9.8d/ssl/ssl_sess.c      2006-12-10 08:20:02.000000000 -0800
+@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+       return(s->session_timeout);
+       }
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, 
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++{
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++}
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+diff -uprN openssl-0.9.8d.orig/ssl/t1_ext.c openssl-0.9.8d/ssl/t1_ext.c
+--- openssl-0.9.8d.orig/ssl/t1_ext.c   1969-12-31 16:00:00.000000000 -0800
++++ openssl-0.9.8d/ssl/t1_ext.c        2006-12-10 08:20:02.000000000 -0800
+@@ -0,0 +1,48 @@
++
++#include <stdio.h>
++#include "ssl_locl.h"
++
++
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              if(s->tls_extension)
++              {
++                      OPENSSL_free(s->tls_extension);
++                      s->tls_extension = NULL;
++              }
++
++              if(ext_data)
++              {
++                      s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len);
++                      if(!s->tls_extension)
++                      {
++                              SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE);
++                              return 0;
++                      }
++
++                      s->tls_extension->type = ext_type;
++                      s->tls_extension->length = ext_len;
++                      s->tls_extension->data = s->tls_extension + 1;
++                      memcpy(s->tls_extension->data, ext_data, ext_len);
++              }
++
++              return 1;
++      }
++
++      return 0;
++}
++
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              s->tls_extension_cb = cb;
++              s->tls_extension_cb_arg = arg;
++
++              return 1;
++      }
++
++      return 0;
++}
+diff -uprN openssl-0.9.8d.orig/ssl/t1_lib.c openssl-0.9.8d/ssl/t1_lib.c
+--- openssl-0.9.8d.orig/ssl/t1_lib.c   2005-08-05 16:52:07.000000000 -0700
++++ openssl-0.9.8d/ssl/t1_lib.c        2006-12-10 08:20:02.000000000 -0800
+@@ -97,6 +97,10 @@ int tls1_new(SSL *s)
+ void tls1_free(SSL *s)
+       {
++      if(s->tls_extension)
++      {
++              OPENSSL_free(s->tls_extension);
++      }
+       ssl3_free(s);
+       }
+diff -uprN openssl-0.9.8d.orig/ssl/tls1.h openssl-0.9.8d/ssl/tls1.h
+--- openssl-0.9.8d.orig/ssl/tls1.h     2006-06-14 10:52:01.000000000 -0700
++++ openssl-0.9.8d/ssl/tls1.h  2006-12-10 08:20:02.000000000 -0800
+@@ -296,6 +296,14 @@ extern "C" {
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -uprN openssl-0.9.8d.orig/util/ssleay.num openssl-0.9.8d/util/ssleay.num
+--- openssl-0.9.8d.orig/util/ssleay.num        2005-05-08 17:22:02.000000000 -0700
++++ openssl-0.9.8d/util/ssleay.num     2006-12-10 08:20:02.000000000 -0800
+@@ -226,3 +226,6 @@ DTLSv1_server_method                    
+ SSL_COMP_get_compression_methods        276   EXIST:!VMS:FUNCTION:COMP
+ SSL_COMP_get_compress_methods           276   EXIST:VMS:FUNCTION:COMP
+ SSL_SESSION_get_id                      277   EXIST::FUNCTION:
++SSL_set_hello_extension                       278     EXIST::FUNCTION:
++SSL_set_hello_extension_cb            279     EXIST::FUNCTION:
++SSL_set_session_secret_cb             280     EXIST::FUNCTION:
diff --git a/patches/openssl-0.9.8e-tls-extensions.patch b/patches/openssl-0.9.8e-tls-extensions.patch
new file mode 100644 (file)
index 0000000..ede053f
--- /dev/null
@@ -0,0 +1,353 @@
+This patch is adding support for TLS hello extensions and externally
+generated pre-shared key material to OpenSSL 0.9.8e. This is
+based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+
+
+diff -uprN openssl-0.9.8e.orig/ssl/Makefile openssl-0.9.8e/ssl/Makefile
+--- openssl-0.9.8e.orig/ssl/Makefile   2006-02-03 17:49:35.000000000 -0800
++++ openssl-0.9.8e/ssl/Makefile        2007-03-22 20:23:19.000000000 -0700
+@@ -24,7 +24,7 @@ LIBSRC=      \
+       s2_meth.c   s2_srvr.c s2_clnt.c  s2_lib.c  s2_enc.c s2_pkt.c \
+       s3_meth.c   s3_srvr.c s3_clnt.c  s3_lib.c  s3_enc.c s3_pkt.c s3_both.c \
+       s23_meth.c s23_srvr.c s23_clnt.c s23_lib.c          s23_pkt.c \
+-      t1_meth.c   t1_srvr.c t1_clnt.c  t1_lib.c  t1_enc.c \
++      t1_meth.c   t1_srvr.c t1_clnt.c  t1_lib.c  t1_enc.c                    t1_ext.c \
+       d1_meth.c   d1_srvr.c d1_clnt.c  d1_lib.c  d1_pkt.c \
+       d1_both.c d1_enc.c \
+       ssl_lib.c ssl_err2.c ssl_cert.c ssl_sess.c \
+@@ -35,7 +35,7 @@ LIBOBJ= \
+       s2_meth.o  s2_srvr.o  s2_clnt.o  s2_lib.o  s2_enc.o s2_pkt.o \
+       s3_meth.o  s3_srvr.o  s3_clnt.o  s3_lib.o  s3_enc.o s3_pkt.o s3_both.o \
+       s23_meth.o s23_srvr.o s23_clnt.o s23_lib.o          s23_pkt.o \
+-      t1_meth.o   t1_srvr.o t1_clnt.o  t1_lib.o  t1_enc.o \
++      t1_meth.o   t1_srvr.o t1_clnt.o  t1_lib.o  t1_enc.o                    t1_ext.o \
+       d1_meth.o   d1_srvr.o d1_clnt.o  d1_lib.o  d1_pkt.o \
+       d1_both.o d1_enc.o \
+       ssl_lib.o ssl_err2.o ssl_cert.o ssl_sess.o \
+@@ -968,3 +968,4 @@ t1_srvr.o: ../include/openssl/ssl23.h ..
+ t1_srvr.o: ../include/openssl/stack.h ../include/openssl/symhacks.h
+ t1_srvr.o: ../include/openssl/tls1.h ../include/openssl/x509.h
+ t1_srvr.o: ../include/openssl/x509_vfy.h ssl_locl.h t1_srvr.c
++t1_ext.o: t1_ext.c ssl_locl.h
+diff -uprN openssl-0.9.8e.orig/ssl/s3_clnt.c openssl-0.9.8e/ssl/s3_clnt.c
+--- openssl-0.9.8e.orig/ssl/s3_clnt.c  2006-09-28 05:23:15.000000000 -0700
++++ openssl-0.9.8e/ssl/s3_clnt.c       2007-03-22 20:23:19.000000000 -0700
+@@ -601,6 +601,20 @@ int ssl3_client_hello(SSL *s)
+ #endif
+               *(p++)=0; /* Add the NULL method */
+               
++              /* send client hello extensions if any */
++              if (s->version >= TLS1_VERSION && s->tls_extension)
++              {
++                      // set the total extensions length
++                      s2n(s->tls_extension->length + 4, p);
++
++                      // put the extensions with type and length
++                      s2n(s->tls_extension->type, p);
++                      s2n(s->tls_extension->length, p);
++                      
++                      memcpy(p, s->tls_extension->data, s->tls_extension->length);
++                      p+=s->tls_extension->length;
++              }
++
+               l=(p-d);
+               d=buf;
+               *(d++)=SSL3_MT_CLIENT_HELLO;
+@@ -623,7 +637,7 @@ int ssl3_get_server_hello(SSL *s)
+       STACK_OF(SSL_CIPHER) *sk;
+       SSL_CIPHER *c;
+       unsigned char *p,*d;
+-      int i,al,ok;
++      int i,al,ok,pre_shared;
+       unsigned int j;
+       long n;
+ #ifndef OPENSSL_NO_COMP
+@@ -690,7 +704,24 @@ int ssl3_get_server_hello(SSL *s)
+               goto f_err;
+               }
+-      if (j != 0 && j == s->session->session_id_length
++      /* check if we want to resume the session based on external pre-shared secret */
++      pre_shared = 0;
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j);
++                      s->session->session_id_length = j;
++                      memcpy(s->session->session_id, p, j);
++                      pre_shared = 1;
++              }
++      }
++
++      if ((pre_shared || j != 0) && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+           if(s->sid_ctx_length != s->session->sid_ctx_length
+diff -uprN openssl-0.9.8e.orig/ssl/s3_srvr.c openssl-0.9.8e/ssl/s3_srvr.c
+--- openssl-0.9.8e.orig/ssl/s3_srvr.c  2007-02-07 12:36:40.000000000 -0800
++++ openssl-0.9.8e/ssl/s3_srvr.c       2007-03-22 20:23:19.000000000 -0700
+@@ -945,6 +945,75 @@ int ssl3_get_client_hello(SSL *s)
+               }
+ #endif
++      /* Check for TLS client hello extension here */
++      if (p < (d+n) && s->version >= TLS1_VERSION)
++      {
++              if (s->tls_extension_cb)
++              {
++                      TLS_EXTENSION tls_ext;
++                      unsigned short ext_total_len;
++                      
++                      n2s(p, ext_total_len);
++                      n2s(p, tls_ext.type);
++                      n2s(p, tls_ext.length);
++
++                      // sanity check in TLS extension len
++                      if (tls_ext.length > (d+n) - p)
++                      {
++                              // just cut the lenth to packet border
++                              tls_ext.length = (d+n) - p;
++                      }
++
++                      tls_ext.data = p;
++
++                      // returns an alert code or 0
++                      al = s->tls_extension_cb(s, &tls_ext, s->tls_extension_cb_arg);
++                      if (al != 0)
++                      {
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_PEER_ERROR);
++                              goto f_err;
++                      }
++              }
++      }
++
++      /* Check if we want to use external pre-shared secret for this handshake */
++      /* for not reused session only */
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++                      
++                      ciphers=NULL;
++                      
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++              }
++      }
++
+       /* Given s->session->ciphers and SSL_get_ciphers, we must
+        * pick a cipher */
+diff -uprN openssl-0.9.8e.orig/ssl/ssl.h openssl-0.9.8e/ssl/ssl.h
+--- openssl-0.9.8e.orig/ssl/ssl.h      2007-02-19 09:55:07.000000000 -0800
++++ openssl-0.9.8e/ssl/ssl.h   2007-03-22 20:23:19.000000000 -0700
+@@ -345,6 +345,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -366,6 +367,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -973,6 +976,15 @@ struct ssl_st
+       int first_packet;
+       int client_version;     /* what was passed, used for
+                                * SSLv3/TLS rollback check */
++
++      /* TLS externsions */
++      TLS_EXTENSION *tls_extension;
++      int (*tls_extension_cb)(SSL *s, TLS_EXTENSION *tls_ext, void *arg);
++      void *tls_extension_cb_arg;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
+       };
+ #ifdef __cplusplus
+@@ -1538,6 +1550,13 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1719,6 +1738,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -uprN openssl-0.9.8e.orig/ssl/ssl_err.c openssl-0.9.8e/ssl/ssl_err.c
+--- openssl-0.9.8e.orig/ssl/ssl_err.c  2006-11-21 12:14:46.000000000 -0800
++++ openssl-0.9.8e/ssl/ssl_err.c       2007-03-22 20:23:19.000000000 -0700
+@@ -242,6 +242,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC),    "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"},
+ {0,NULL}
+       };
+diff -uprN openssl-0.9.8e.orig/ssl/ssl_sess.c openssl-0.9.8e/ssl/ssl_sess.c
+--- openssl-0.9.8e.orig/ssl/ssl_sess.c 2007-02-10 02:40:24.000000000 -0800
++++ openssl-0.9.8e/ssl/ssl_sess.c      2007-03-22 20:23:19.000000000 -0700
+@@ -656,6 +656,15 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+       return(s->session_timeout);
+       }
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, 
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++{
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++}
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+diff -uprN openssl-0.9.8e.orig/ssl/t1_ext.c openssl-0.9.8e/ssl/t1_ext.c
+--- openssl-0.9.8e.orig/ssl/t1_ext.c   1969-12-31 16:00:00.000000000 -0800
++++ openssl-0.9.8e/ssl/t1_ext.c        2007-03-22 20:23:19.000000000 -0700
+@@ -0,0 +1,48 @@
++
++#include <stdio.h>
++#include "ssl_locl.h"
++
++
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              if(s->tls_extension)
++              {
++                      OPENSSL_free(s->tls_extension);
++                      s->tls_extension = NULL;
++              }
++
++              if(ext_data)
++              {
++                      s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len);
++                      if(!s->tls_extension)
++                      {
++                              SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE);
++                              return 0;
++                      }
++
++                      s->tls_extension->type = ext_type;
++                      s->tls_extension->length = ext_len;
++                      s->tls_extension->data = s->tls_extension + 1;
++                      memcpy(s->tls_extension->data, ext_data, ext_len);
++              }
++
++              return 1;
++      }
++
++      return 0;
++}
++
++int SSL_set_hello_extension_cb(SSL *s, int (*cb)(SSL *, TLS_EXTENSION *, void *), void *arg)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              s->tls_extension_cb = cb;
++              s->tls_extension_cb_arg = arg;
++
++              return 1;
++      }
++
++      return 0;
++}
+diff -uprN openssl-0.9.8e.orig/ssl/t1_lib.c openssl-0.9.8e/ssl/t1_lib.c
+--- openssl-0.9.8e.orig/ssl/t1_lib.c   2007-01-21 08:07:25.000000000 -0800
++++ openssl-0.9.8e/ssl/t1_lib.c        2007-03-22 20:23:19.000000000 -0700
+@@ -97,6 +97,10 @@ int tls1_new(SSL *s)
+ void tls1_free(SSL *s)
+       {
++      if(s->tls_extension)
++      {
++              OPENSSL_free(s->tls_extension);
++      }
+       ssl3_free(s);
+       }
+diff -uprN openssl-0.9.8e.orig/ssl/tls1.h openssl-0.9.8e/ssl/tls1.h
+--- openssl-0.9.8e.orig/ssl/tls1.h     2006-06-14 10:52:01.000000000 -0700
++++ openssl-0.9.8e/ssl/tls1.h  2007-03-22 20:23:19.000000000 -0700
+@@ -296,6 +296,14 @@ extern "C" {
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -uprN openssl-0.9.8e.orig/util/ssleay.num openssl-0.9.8e/util/ssleay.num
+--- openssl-0.9.8e.orig/util/ssleay.num        2006-11-30 05:04:43.000000000 -0800
++++ openssl-0.9.8e/util/ssleay.num     2007-03-22 20:24:07.000000000 -0700
+@@ -238,3 +238,6 @@ SSL_CTX_set_info_callback               
+ SSL_CTX_sess_get_new_cb                 287   EXIST::FUNCTION:
+ SSL_CTX_get_client_cert_cb              288   EXIST::FUNCTION:
+ SSL_CTX_sess_get_remove_cb              289   EXIST::FUNCTION:
++SSL_set_hello_extension                       290     EXIST::FUNCTION:
++SSL_set_hello_extension_cb            291     EXIST::FUNCTION:
++SSL_set_session_secret_cb             292     EXIST::FUNCTION:
diff --git a/patches/openssl-0.9.8g-tls-extensions.patch b/patches/openssl-0.9.8g-tls-extensions.patch
new file mode 100644 (file)
index 0000000..8ccbfaa
--- /dev/null
@@ -0,0 +1,330 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+OpenSSL 0.9.8g does not enable TLS extension support by default, so it
+will need to be enabled by adding enable-tlsext to config script
+command line.
+
+
+diff -upr openssl-0.9.8g.orig/ssl/s3_clnt.c openssl-0.9.8g/ssl/s3_clnt.c
+--- openssl-0.9.8g.orig/ssl/s3_clnt.c  2007-08-31 03:28:51.000000000 +0300
++++ openssl-0.9.8g/ssl/s3_clnt.c       2008-04-15 17:11:46.000000000 +0300
+@@ -727,6 +727,20 @@ int ssl3_get_server_hello(SSL *s)
+               goto f_err;
+               }
++#ifndef OPENSSL_NO_TLSEXT
++      /* check if we want to resume the session based on external pre-shared secret */
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j);
++              }
++      }
++#endif /* OPENSSL_NO_TLSEXT */
++
+       if (j != 0 && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+diff -upr openssl-0.9.8g.orig/ssl/s3_srvr.c openssl-0.9.8g/ssl/s3_srvr.c
+--- openssl-0.9.8g.orig/ssl/s3_srvr.c  2007-09-30 21:55:59.000000000 +0300
++++ openssl-0.9.8g/ssl/s3_srvr.c       2008-04-15 17:10:37.000000000 +0300
+@@ -928,6 +928,59 @@ int ssl3_get_client_hello(SSL *s)
+                       SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+                       goto err;
+               }
++
++      /* Check if we want to use external pre-shared secret for this
++       * handshake for not reused session only. We need to generate
++       * server_random before calling tls_session_secret_cb in order to allow
++       * SessionTicket processing to use it in key derivation. */
++      {
++              unsigned long Time;
++              unsigned char *pos;
++              Time=(unsigned long)time(NULL);                 /* Time */
++              pos=s->s3->server_random;
++              l2n(Time,pos);
++              if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
++              {
++                      al=SSL_AD_INTERNAL_ERROR;
++                      goto f_err;
++              }
++      }
++
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++                      
++                      ciphers=NULL;
++                      
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++              }
++      }
+ #endif
+       /* Worst case, we will use the NULL compression, but if we have other
+        * options, we will now look for them.  We have i-1 compression
+@@ -1066,16 +1119,22 @@ int ssl3_send_server_hello(SSL *s)
+       unsigned char *buf;
+       unsigned char *p,*d;
+       int i,sl;
+-      unsigned long l,Time;
++      unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++      unsigned long Time;
++#endif
+       if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+               {
+               buf=(unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+               p=s->s3->server_random;
++              /* Generate server_random if it was not needed previously */
+               Time=(unsigned long)time(NULL);                 /* Time */
+               l2n(Time,p);
+               if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+                       return -1;
++#endif
+               /* Do the message type and length last */
+               d=p= &(buf[4]);
+diff -upr openssl-0.9.8g.orig/ssl/ssl.h openssl-0.9.8g/ssl/ssl.h
+--- openssl-0.9.8g.orig/ssl/ssl.h      2007-10-19 10:42:38.000000000 +0300
++++ openssl-0.9.8g/ssl/ssl.h   2008-04-15 17:10:37.000000000 +0300
+@@ -342,6 +342,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -363,6 +364,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -1004,6 +1007,14 @@ struct ssl_st
+                              */
+       /* RFC4507 session ticket expected to be received or sent */
+       int tlsext_ticket_expected;
++
++      /* TLS extensions */
++      TLS_EXTENSION *tls_extension;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
++
+       SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+ #define session_ctx initial_ctx
+ #else
+@@ -1589,6 +1600,12 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1778,6 +1795,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION                  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -upr openssl-0.9.8g.orig/ssl/ssl_err.c openssl-0.9.8g/ssl/ssl_err.c
+--- openssl-0.9.8g.orig/ssl/ssl_err.c  2007-10-11 17:36:59.000000000 +0300
++++ openssl-0.9.8g/ssl/ssl_err.c       2008-04-15 17:10:37.000000000 +0300
+@@ -250,6 +250,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC),    "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"},
+ {0,NULL}
+       };
+diff -upr openssl-0.9.8g.orig/ssl/ssl_sess.c openssl-0.9.8g/ssl/ssl_sess.c
+--- openssl-0.9.8g.orig/ssl/ssl_sess.c 2007-10-19 10:36:34.000000000 +0300
++++ openssl-0.9.8g/ssl/ssl_sess.c      2008-04-15 17:10:37.000000000 +0300
+@@ -704,6 +704,52 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+       return(s->session_timeout);
+       }
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, 
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++{
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++}
++
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              if(s->tls_extension)
++              {
++                      OPENSSL_free(s->tls_extension);
++                      s->tls_extension = NULL;
++              }
++
++              s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len);
++              if(!s->tls_extension)
++              {
++                      SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE);
++                      return 0;
++              }
++
++              s->tls_extension->type = ext_type;
++
++              if(ext_data)
++              {
++                      s->tls_extension->length = ext_len;
++                      s->tls_extension->data = s->tls_extension + 1;
++                      memcpy(s->tls_extension->data, ext_data, ext_len);
++              } else {
++                      s->tls_extension->length = 0;
++                      s->tls_extension->data = NULL;
++              }
++
++              return 1;
++      }
++
++      return 0;
++}
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+diff -upr openssl-0.9.8g.orig/ssl/t1_lib.c openssl-0.9.8g/ssl/t1_lib.c
+--- openssl-0.9.8g.orig/ssl/t1_lib.c   2007-10-19 10:44:10.000000000 +0300
++++ openssl-0.9.8g/ssl/t1_lib.c        2008-04-15 17:10:37.000000000 +0300
+@@ -105,6 +105,12 @@ int tls1_new(SSL *s)
+ void tls1_free(SSL *s)
+       {
++#ifndef OPENSSL_NO_TLSEXT
++      if(s->tls_extension)
++      {
++              OPENSSL_free(s->tls_extension);
++      }
++#endif
+       ssl3_free(s);
+       }
+@@ -174,8 +180,24 @@ unsigned char *ssl_add_clienthello_tlsex
+               int ticklen;
+               if (s->session && s->session->tlsext_tick)
+                       ticklen = s->session->tlsext_ticklen;
++              else if (s->session && s->tls_extension &&
++                      s->tls_extension->type == TLSEXT_TYPE_session_ticket &&
++                      s->tls_extension->data)
++              {
++                      ticklen = s->tls_extension->length;
++                      s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++                      if (!s->session->tlsext_tick)
++                              return NULL;
++                      memcpy(s->session->tlsext_tick, s->tls_extension->data,
++                             ticklen);
++                      s->session->tlsext_ticklen = ticklen;
++              }
+               else
+                       ticklen = 0;
++              if (ticklen == 0 && s->tls_extension &&
++                  s->tls_extension->type == TLSEXT_TYPE_session_ticket &&
++                  s->tls_extension->data == NULL)
++                      goto skip_ext;
+               /* Check for enough room 2 for extension type, 2 for len
+                * rest for ticket
+                */
+@@ -189,6 +211,7 @@ unsigned char *ssl_add_clienthello_tlsex
+                       ret += ticklen;
+                       }
+               }
++              skip_ext:
+       if ((extdatalen = ret-p-2)== 0) 
+               return p;
+@@ -543,6 +566,8 @@ int tls1_process_ticket(SSL *s, unsigned
+                               s->tlsext_ticket_expected = 1;
+                               return 0;       /* Cache miss */
+                               }
++                      if (s->tls_session_secret_cb)
++                              return 0;
+                       return tls_decrypt_ticket(s, p, size, session_id, len,
+                                                                       ret);
+                       }
+diff -upr openssl-0.9.8g.orig/ssl/tls1.h openssl-0.9.8g/ssl/tls1.h
+--- openssl-0.9.8g.orig/ssl/tls1.h     2007-08-28 04:12:44.000000000 +0300
++++ openssl-0.9.8g/ssl/tls1.h  2008-04-15 17:10:37.000000000 +0300
+@@ -365,6 +365,14 @@ SSL_CTX_ctrl(ctx,SSL_CTRL_SET_TLSEXT_SER
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -upr openssl-0.9.8g.orig/util/ssleay.num openssl-0.9.8g/util/ssleay.num
+--- openssl-0.9.8g.orig/util/ssleay.num        2007-08-13 01:31:16.000000000 +0300
++++ openssl-0.9.8g/util/ssleay.num     2008-04-15 17:10:37.000000000 +0300
+@@ -241,3 +241,5 @@ SSL_CTX_sess_get_remove_cb              
+ SSL_set_SSL_CTX                         290   EXIST::FUNCTION:
+ SSL_get_servername                      291   EXIST::FUNCTION:TLSEXT
+ SSL_get_servername_type                 292   EXIST::FUNCTION:TLSEXT
++SSL_set_hello_extension                       305     EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb             306     EXIST::FUNCTION:TLSEXT
diff --git a/patches/openssl-0.9.8h-tls-extensions.patch b/patches/openssl-0.9.8h-tls-extensions.patch
new file mode 100644 (file)
index 0000000..c68f227
--- /dev/null
@@ -0,0 +1,344 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+OpenSSL 0.9.8h does not enable TLS extension support by default, so it
+will need to be enabled by adding enable-tlsext to config script
+command line.
+
+
+diff -upr openssl-0.9.8h.orig/ssl/s3_clnt.c openssl-0.9.8h/ssl/s3_clnt.c
+--- openssl-0.9.8h.orig/ssl/s3_clnt.c  2008-05-28 10:29:27.000000000 +0300
++++ openssl-0.9.8h/ssl/s3_clnt.c       2008-05-29 10:44:25.000000000 +0300
+@@ -752,6 +752,20 @@ int ssl3_get_server_hello(SSL *s)
+               goto f_err;
+               }
++#ifndef OPENSSL_NO_TLSEXT
++      /* check if we want to resume the session based on external pre-shared secret */
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->session->cipher=pref_cipher ? pref_cipher : ssl_get_cipher_by_char(s,p+j);
++              }
++      }
++#endif /* OPENSSL_NO_TLSEXT */
++
+       if (j != 0 && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+@@ -2693,11 +2707,8 @@ static int ssl3_check_finished(SSL *s)
+       {
+       int ok;
+       long n;
+-      /* If we have no ticket or session ID is non-zero length (a match of
+-       * a non-zero session length would never reach here) it cannot be a
+-       * resumed session.
+-       */
+-      if (!s->session->tlsext_tick || s->session->session_id_length)
++      /* If we have no ticket it cannot be a resumed session. */
++      if (!s->session->tlsext_tick)
+               return 1;
+       /* this function is called when we really expect a Certificate
+        * message, so permit appropriate message length */
+diff -upr openssl-0.9.8h.orig/ssl/s3_srvr.c openssl-0.9.8h/ssl/s3_srvr.c
+--- openssl-0.9.8h.orig/ssl/s3_srvr.c  2008-04-30 19:11:32.000000000 +0300
++++ openssl-0.9.8h/ssl/s3_srvr.c       2008-05-28 18:49:34.000000000 +0300
+@@ -959,6 +959,59 @@ int ssl3_get_client_hello(SSL *s)
+                       SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+                       goto err;
+               }
++
++      /* Check if we want to use external pre-shared secret for this
++       * handshake for not reused session only. We need to generate
++       * server_random before calling tls_session_secret_cb in order to allow
++       * SessionTicket processing to use it in key derivation. */
++      {
++              unsigned long Time;
++              unsigned char *pos;
++              Time=(unsigned long)time(NULL);                 /* Time */
++              pos=s->s3->server_random;
++              l2n(Time,pos);
++              if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
++              {
++                      al=SSL_AD_INTERNAL_ERROR;
++                      goto f_err;
++              }
++      }
++
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++      {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++              {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++                      
++                      ciphers=NULL;
++                      
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++              }
++      }
+ #endif
+       /* Worst case, we will use the NULL compression, but if we have other
+        * options, we will now look for them.  We have i-1 compression
+@@ -1097,16 +1150,22 @@ int ssl3_send_server_hello(SSL *s)
+       unsigned char *buf;
+       unsigned char *p,*d;
+       int i,sl;
+-      unsigned long l,Time;
++      unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++      unsigned long Time;
++#endif
+       if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+               {
+               buf=(unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+               p=s->s3->server_random;
++              /* Generate server_random if it was not needed previously */
+               Time=(unsigned long)time(NULL);                 /* Time */
+               l2n(Time,p);
+               if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+                       return -1;
++#endif
+               /* Do the message type and length last */
+               d=p= &(buf[4]);
+diff -upr openssl-0.9.8h.orig/ssl/ssl.h openssl-0.9.8h/ssl/ssl.h
+--- openssl-0.9.8h.orig/ssl/ssl.h      2008-04-30 19:11:32.000000000 +0300
++++ openssl-0.9.8h/ssl/ssl.h   2008-05-28 18:49:34.000000000 +0300
+@@ -343,6 +343,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_extension_st TLS_EXTENSION;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -364,6 +365,8 @@ DECLARE_STACK_OF(SSL_CIPHER)
+ typedef struct ssl_st SSL;
+ typedef struct ssl_ctx_st SSL_CTX;
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -1027,6 +1030,14 @@ struct ssl_st
+       /* RFC4507 session ticket expected to be received or sent */
+       int tlsext_ticket_expected;
++
++      /* TLS extensions */
++      TLS_EXTENSION *tls_extension;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
++
+       SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+ #define session_ctx initial_ctx
+ #else
+@@ -1625,6 +1636,12 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1815,6 +1832,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_HELLO_EXTENSION                  213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+diff -upr openssl-0.9.8h.orig/ssl/ssl_err.c openssl-0.9.8h/ssl/ssl_err.c
+--- openssl-0.9.8h.orig/ssl/ssl_err.c  2007-10-12 03:00:30.000000000 +0300
++++ openssl-0.9.8h/ssl/ssl_err.c       2008-05-28 18:49:34.000000000 +0300
+@@ -251,6 +251,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_ENC),    "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_HELLO_EXTENSION), "SSL_set_hello_extension"},
+ {0,NULL}
+       };
+diff -upr openssl-0.9.8h.orig/ssl/ssl_sess.c openssl-0.9.8h/ssl/ssl_sess.c
+--- openssl-0.9.8h.orig/ssl/ssl_sess.c 2007-10-17 20:30:15.000000000 +0300
++++ openssl-0.9.8h/ssl/ssl_sess.c      2008-05-28 18:49:34.000000000 +0300
+@@ -704,6 +704,52 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+       return(s->session_timeout);
+       }
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len, 
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++{
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++}
++
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len)
++{
++      if(s->version >= TLS1_VERSION)
++      {
++              if(s->tls_extension)
++              {
++                      OPENSSL_free(s->tls_extension);
++                      s->tls_extension = NULL;
++              }
++
++              s->tls_extension = OPENSSL_malloc(sizeof(TLS_EXTENSION) + ext_len);
++              if(!s->tls_extension)
++              {
++                      SSLerr(SSL_F_SSL_SET_HELLO_EXTENSION, ERR_R_MALLOC_FAILURE);
++                      return 0;
++              }
++
++              s->tls_extension->type = ext_type;
++
++              if(ext_data)
++              {
++                      s->tls_extension->length = ext_len;
++                      s->tls_extension->data = s->tls_extension + 1;
++                      memcpy(s->tls_extension->data, ext_data, ext_len);
++              } else {
++                      s->tls_extension->length = 0;
++                      s->tls_extension->data = NULL;
++              }
++
++              return 1;
++      }
++
++      return 0;
++}
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+diff -upr openssl-0.9.8h.orig/ssl/t1_lib.c openssl-0.9.8h/ssl/t1_lib.c
+--- openssl-0.9.8h.orig/ssl/t1_lib.c   2008-05-28 10:26:33.000000000 +0300
++++ openssl-0.9.8h/ssl/t1_lib.c        2008-05-28 18:49:34.000000000 +0300
+@@ -106,6 +106,12 @@ int tls1_new(SSL *s)
+ void tls1_free(SSL *s)
+       {
++#ifndef OPENSSL_NO_TLSEXT
++      if(s->tls_extension)
++      {
++              OPENSSL_free(s->tls_extension);
++      }
++#endif
+       ssl3_free(s);
+       }
+@@ -175,8 +181,24 @@ unsigned char *ssl_add_clienthello_tlsex
+               int ticklen;
+               if (s->session && s->session->tlsext_tick)
+                       ticklen = s->session->tlsext_ticklen;
++              else if (s->session && s->tls_extension &&
++                      s->tls_extension->type == TLSEXT_TYPE_session_ticket &&
++                      s->tls_extension->data)
++              {
++                      ticklen = s->tls_extension->length;
++                      s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++                      if (!s->session->tlsext_tick)
++                              return NULL;
++                      memcpy(s->session->tlsext_tick, s->tls_extension->data,
++                             ticklen);
++                      s->session->tlsext_ticklen = ticklen;
++              }
+               else
+                       ticklen = 0;
++              if (ticklen == 0 && s->tls_extension &&
++                  s->tls_extension->type == TLSEXT_TYPE_session_ticket &&
++                  s->tls_extension->data == NULL)
++                      goto skip_ext;
+               /* Check for enough room 2 for extension type, 2 for len
+                * rest for ticket
+                */
+@@ -190,6 +212,7 @@ unsigned char *ssl_add_clienthello_tlsex
+                       ret += ticklen;
+                       }
+               }
++              skip_ext:
+       if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp)
+               {
+@@ -774,6 +797,8 @@ int tls1_process_ticket(SSL *s, unsigned
+                               s->tlsext_ticket_expected = 1;
+                               return 0;       /* Cache miss */
+                               }
++                      if (s->tls_session_secret_cb)
++                              return 0;
+                       return tls_decrypt_ticket(s, p, size, session_id, len,
+                                                                       ret);
+                       }
+diff -upr openssl-0.9.8h.orig/ssl/tls1.h openssl-0.9.8h/ssl/tls1.h
+--- openssl-0.9.8h.orig/ssl/tls1.h     2008-04-30 19:11:33.000000000 +0300
++++ openssl-0.9.8h/ssl/tls1.h  2008-05-28 18:49:34.000000000 +0300
+@@ -398,6 +398,14 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_extension_st
++{
++      unsigned short type;
++      unsigned short length;
++      void *data;
++};
++
+ #ifdef  __cplusplus
+ }
+ #endif
+diff -upr openssl-0.9.8h.orig/util/ssleay.num openssl-0.9.8h/util/ssleay.num
+--- openssl-0.9.8h.orig/util/ssleay.num        2007-08-13 01:31:16.000000000 +0300
++++ openssl-0.9.8h/util/ssleay.num     2008-05-28 18:49:34.000000000 +0300
+@@ -241,3 +241,5 @@ SSL_CTX_sess_get_remove_cb              
+ SSL_set_SSL_CTX                         290   EXIST::FUNCTION:
+ SSL_get_servername                      291   EXIST::FUNCTION:TLSEXT
+ SSL_get_servername_type                 292   EXIST::FUNCTION:TLSEXT
++SSL_set_hello_extension                       305     EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb             306     EXIST::FUNCTION:TLSEXT
diff --git a/patches/openssl-0.9.8i-tls-extensions.patch b/patches/openssl-0.9.8i-tls-extensions.patch
new file mode 100644 (file)
index 0000000..90bff54
--- /dev/null
@@ -0,0 +1,404 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+OpenSSL 0.9.8i does not enable TLS extension support by default, so it
+will need to be enabled by adding enable-tlsext to config script
+command line.
+
+
+Index: openssl-0.9.8i/ssl/s3_clnt.c
+===================================================================
+--- openssl-0.9.8i.orig/ssl/s3_clnt.c  2008-06-16 19:56:41.000000000 +0300
++++ openssl-0.9.8i/ssl/s3_clnt.c       2008-11-23 20:39:40.000000000 +0200
+@@ -759,6 +759,21 @@
+               goto f_err;
+               }
++#ifndef OPENSSL_NO_TLSEXT
++      /* check if we want to resume the session based on external pre-shared secret */
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++              {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      NULL, &pref_cipher, s->tls_session_secret_cb_arg))
++                      {
++                      s->session->cipher=pref_cipher ?
++                              pref_cipher : ssl_get_cipher_by_char(s,p+j);
++                      }
++              }
++#endif /* OPENSSL_NO_TLSEXT */
++
+       if (j != 0 && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+@@ -2701,11 +2716,8 @@
+       {
+       int ok;
+       long n;
+-      /* If we have no ticket or session ID is non-zero length (a match of
+-       * a non-zero session length would never reach here) it cannot be a
+-       * resumed session.
+-       */
+-      if (!s->session->tlsext_tick || s->session->session_id_length)
++      /* If we have no ticket it cannot be a resumed session. */
++      if (!s->session->tlsext_tick)
+               return 1;
+       /* this function is called when we really expect a Certificate
+        * message, so permit appropriate message length */
+Index: openssl-0.9.8i/ssl/s3_srvr.c
+===================================================================
+--- openssl-0.9.8i.orig/ssl/s3_srvr.c  2008-09-14 21:16:09.000000000 +0300
++++ openssl-0.9.8i/ssl/s3_srvr.c       2008-11-23 20:37:40.000000000 +0200
+@@ -959,6 +959,59 @@
+                       SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+                       goto err;
+               }
++
++      /* Check if we want to use external pre-shared secret for this
++       * handshake for not reused session only. We need to generate
++       * server_random before calling tls_session_secret_cb in order to allow
++       * SessionTicket processing to use it in key derivation. */
++      {
++              unsigned long Time;
++              unsigned char *pos;
++              Time=(unsigned long)time(NULL);                 /* Time */
++              pos=s->s3->server_random;
++              l2n(Time,pos);
++              if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
++                      {
++                      al=SSL_AD_INTERNAL_ERROR;
++                      goto f_err;
++                      }
++      }
++
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++              {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length, 
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++                      {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++                      
++                      ciphers=NULL;
++                      
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      }
++              }
+ #endif
+       /* Worst case, we will use the NULL compression, but if we have other
+        * options, we will now look for them.  We have i-1 compression
+@@ -1097,16 +1150,22 @@
+       unsigned char *buf;
+       unsigned char *p,*d;
+       int i,sl;
+-      unsigned long l,Time;
++      unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++      unsigned long Time;
++#endif
+       if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+               {
+               buf=(unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+               p=s->s3->server_random;
++              /* Generate server_random if it was not needed previously */
+               Time=(unsigned long)time(NULL);                 /* Time */
+               l2n(Time,p);
+               if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+                       return -1;
++#endif
+               /* Do the message type and length last */
+               d=p= &(buf[4]);
+Index: openssl-0.9.8i/ssl/ssl_err.c
+===================================================================
+--- openssl-0.9.8i.orig/ssl/ssl_err.c  2008-08-13 22:44:44.000000000 +0300
++++ openssl-0.9.8i/ssl/ssl_err.c       2008-11-23 20:33:43.000000000 +0200
+@@ -253,6 +253,7 @@
+ {ERR_FUNC(SSL_F_TLS1_ENC),    "TLS1_ENC"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+ {0,NULL}
+       };
+Index: openssl-0.9.8i/ssl/ssl.h
+===================================================================
+--- openssl-0.9.8i.orig/ssl/ssl.h      2008-08-13 22:44:44.000000000 +0300
++++ openssl-0.9.8i/ssl/ssl.h   2008-11-23 20:35:41.000000000 +0200
+@@ -344,6 +344,7 @@
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -362,6 +363,9 @@
+ DECLARE_STACK_OF(SSL_CIPHER)
++typedef int (*tls_session_ticket_ext_cb_fn)(SSL *s, const unsigned char *data, int len, void *arg);
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -1034,6 +1038,18 @@
+       /* RFC4507 session ticket expected to be received or sent */
+       int tlsext_ticket_expected;
++
++      /* TLS Session Ticket extension override */
++      TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
++
++      /* TLS Session Ticket extension callback */
++      tls_session_ticket_ext_cb_fn tls_session_ticket_ext_cb;
++      void *tls_session_ticket_ext_cb_arg;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
++
+       SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+ #define session_ctx initial_ctx
+ #else
+@@ -1632,6 +1648,15 @@
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* TLS extensions functions */
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++                                void *arg);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1824,6 +1849,7 @@
+ #define SSL_F_TLS1_ENC                                         210
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_SESSION_TICKET_EXT               213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+Index: openssl-0.9.8i/ssl/ssl_sess.c
+===================================================================
+--- openssl-0.9.8i.orig/ssl/ssl_sess.c 2008-06-04 21:35:27.000000000 +0300
++++ openssl-0.9.8i/ssl/ssl_sess.c      2008-11-23 20:32:24.000000000 +0200
+@@ -707,6 +707,61 @@
+       return(s->session_timeout);
+       }
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++      {
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++      }
++
++int SSL_set_session_ticket_ext_cb(SSL *s, tls_session_ticket_ext_cb_fn cb,
++                                void *arg)
++      {
++      if (s == NULL) return(0);
++      s->tls_session_ticket_ext_cb = cb;
++      s->tls_session_ticket_ext_cb_arg = arg;
++      return(1);
++      }
++
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
++      {
++      if (s->version >= TLS1_VERSION)
++              {
++              if (s->tlsext_session_ticket)
++                      {
++                      OPENSSL_free(s->tlsext_session_ticket);
++                      s->tlsext_session_ticket = NULL;
++                      }
++
++              s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
++              if (!s->tlsext_session_ticket)
++                      {
++                      SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
++                      return 0;
++                      }
++
++              if (ext_data)
++                      {
++                      s->tlsext_session_ticket->length = ext_len;
++                      s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
++                      memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
++                      }
++              else
++                      {
++                      s->tlsext_session_ticket->length = 0;
++                      s->tlsext_session_ticket->data = NULL;
++                      }
++
++              return 1;
++              }
++
++      return 0;
++      }
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+Index: openssl-0.9.8i/ssl/t1_lib.c
+===================================================================
+--- openssl-0.9.8i.orig/ssl/t1_lib.c   2008-09-04 01:13:04.000000000 +0300
++++ openssl-0.9.8i/ssl/t1_lib.c        2008-11-23 20:31:20.000000000 +0200
+@@ -106,6 +106,12 @@
+ void tls1_free(SSL *s)
+       {
++#ifndef OPENSSL_NO_TLSEXT
++      if (s->tlsext_session_ticket)
++              {
++              OPENSSL_free(s->tlsext_session_ticket);
++              }
++#endif
+       ssl3_free(s);
+       }
+@@ -175,8 +181,23 @@
+               int ticklen;
+               if (s->session && s->session->tlsext_tick)
+                       ticklen = s->session->tlsext_ticklen;
++              else if (s->session && s->tlsext_session_ticket &&
++                       s->tlsext_session_ticket->data)
++                      {
++                      ticklen = s->tlsext_session_ticket->length;
++                      s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++                      if (!s->session->tlsext_tick)
++                              return NULL;
++                      memcpy(s->session->tlsext_tick,
++                             s->tlsext_session_ticket->data,
++                             ticklen);
++                      s->session->tlsext_ticklen = ticklen;
++                      }
+               else
+                       ticklen = 0;
++              if (ticklen == 0 && s->tlsext_session_ticket &&
++                  s->tlsext_session_ticket->data == NULL)
++                      goto skip_ext;
+               /* Check for enough room 2 for extension type, 2 for len
+                * rest for ticket
+                */
+@@ -190,6 +211,7 @@
+                       ret += ticklen;
+                       }
+               }
++              skip_ext:
+       if (s->tlsext_status_type == TLSEXT_STATUSTYPE_ocsp)
+               {
+@@ -407,6 +429,15 @@
+                               }
+                       }
++              else if (type == TLSEXT_TYPE_session_ticket)
++                      {
++                      if (s->tls_session_ticket_ext_cb &&
++                          !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
++                              {
++                              *al = TLS1_AD_INTERNAL_ERROR;
++                              return 0;
++                              }
++                      }
+               else if (type == TLSEXT_TYPE_status_request
+                                               && s->ctx->tlsext_status_cb)
+                       {
+@@ -553,6 +584,12 @@
+                       }
+               else if (type == TLSEXT_TYPE_session_ticket)
+                       {
++                      if (s->tls_session_ticket_ext_cb &&
++                          !s->tls_session_ticket_ext_cb(s, data, size, s->tls_session_ticket_ext_cb_arg))
++                              {
++                              *al = TLS1_AD_INTERNAL_ERROR;
++                              return 0;
++                              }
+                       if ((SSL_get_options(s) & SSL_OP_NO_TICKET)
+                               || (size > 0))
+                               {
+@@ -776,6 +813,15 @@
+                               s->tlsext_ticket_expected = 1;
+                               return 0;       /* Cache miss */
+                               }
++                      if (s->tls_session_secret_cb)
++                              {
++                              /* Indicate cache miss here and instead of
++                               * generating the session from ticket now,
++                               * trigger abbreviated handshake based on
++                               * external mechanism to calculate the master
++                               * secret later. */
++                              return 0;
++                              }
+                       return tls_decrypt_ticket(s, p, size, session_id, len,
+                                                                       ret);
+                       }
+Index: openssl-0.9.8i/ssl/tls1.h
+===================================================================
+--- openssl-0.9.8i.orig/ssl/tls1.h     2008-04-30 19:11:33.000000000 +0300
++++ openssl-0.9.8i/ssl/tls1.h  2008-11-23 20:22:38.000000000 +0200
+@@ -398,6 +398,13 @@
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS extension struct */
++struct tls_session_ticket_ext_st
++      {
++      unsigned short length;
++      void *data;
++      };
++
+ #ifdef  __cplusplus
+ }
+ #endif
+Index: openssl-0.9.8i/util/ssleay.num
+===================================================================
+--- openssl-0.9.8i.orig/util/ssleay.num        2008-06-05 13:57:21.000000000 +0300
++++ openssl-0.9.8i/util/ssleay.num     2008-11-23 20:22:05.000000000 +0200
+@@ -242,3 +242,5 @@
+ SSL_get_servername                      291   EXIST::FUNCTION:TLSEXT
+ SSL_get_servername_type                 292   EXIST::FUNCTION:TLSEXT
+ SSL_CTX_set_client_cert_engine          293   EXIST::FUNCTION:ENGINE
++SSL_set_session_ticket_ext            306     EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb             307     EXIST::FUNCTION:TLSEXT
diff --git a/patches/openssl-0.9.9-session-ticket.patch b/patches/openssl-0.9.9-session-ticket.patch
new file mode 100644 (file)
index 0000000..3afa639
--- /dev/null
@@ -0,0 +1,374 @@
+This patch adds support for TLS SessionTicket extension (RFC 5077) for
+the parts used by EAP-FAST (RFC 4851).
+
+This is based on the patch from Alexey Kobozev <akobozev@cisco.com>
+(sent to openssl-dev mailing list on Tue, 07 Jun 2005 15:40:58 +0300).
+
+NOTE: This patch (without SSL_set_hello_extension() wrapper) was
+merged into the upstream OpenSSL 0.9.9 tree and as such, an external
+patch for EAP-FAST support is not needed anymore.
+
+
+
+Index: openssl-SNAP-20081111/ssl/s3_clnt.c
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/s3_clnt.c
++++ openssl-SNAP-20081111/ssl/s3_clnt.c
+@@ -788,6 +788,23 @@ int ssl3_get_server_hello(SSL *s)
+               goto f_err;
+               }
++#ifndef OPENSSL_NO_TLSEXT
++      /* check if we want to resume the session based on external pre-shared secret */
++      if (s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++              {
++              SSL_CIPHER *pref_cipher=NULL;
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if (s->tls_session_secret_cb(s, s->session->master_key,
++                                           &s->session->master_key_length,
++                                           NULL, &pref_cipher,
++                                           s->tls_session_secret_cb_arg))
++                      {
++                      s->session->cipher = pref_cipher ?
++                              pref_cipher : ssl_get_cipher_by_char(s, p+j);
++                      }
++              }
++#endif /* OPENSSL_NO_TLSEXT */
++
+       if (j != 0 && j == s->session->session_id_length
+           && memcmp(p,s->session->session_id,j) == 0)
+           {
+@@ -2927,11 +2944,8 @@ static int ssl3_check_finished(SSL *s)
+       {
+       int ok;
+       long n;
+-      /* If we have no ticket or session ID is non-zero length (a match of
+-       * a non-zero session length would never reach here) it cannot be a
+-       * resumed session.
+-       */
+-      if (!s->session->tlsext_tick || s->session->session_id_length)
++      /* If we have no ticket it cannot be a resumed session. */
++      if (!s->session->tlsext_tick)
+               return 1;
+       /* this function is called when we really expect a Certificate
+        * message, so permit appropriate message length */
+Index: openssl-SNAP-20081111/ssl/s3_srvr.c
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/s3_srvr.c
++++ openssl-SNAP-20081111/ssl/s3_srvr.c
+@@ -1010,6 +1010,59 @@ int ssl3_get_client_hello(SSL *s)
+                       SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_CLIENTHELLO_TLSEXT);
+                       goto err;
+               }
++
++      /* Check if we want to use external pre-shared secret for this
++       * handshake for not reused session only. We need to generate
++       * server_random before calling tls_session_secret_cb in order to allow
++       * SessionTicket processing to use it in key derivation. */
++      {
++              unsigned long Time;
++              unsigned char *pos;
++              Time=(unsigned long)time(NULL);                 /* Time */
++              pos=s->s3->server_random;
++              l2n(Time,pos);
++              if (RAND_pseudo_bytes(pos,SSL3_RANDOM_SIZE-4) <= 0)
++                      {
++                      al=SSL_AD_INTERNAL_ERROR;
++                      goto f_err;
++                      }
++      }
++
++      if (!s->hit && s->version >= TLS1_VERSION && s->tls_session_secret_cb)
++              {
++              SSL_CIPHER *pref_cipher=NULL;
++
++              s->session->master_key_length=sizeof(s->session->master_key);
++              if(s->tls_session_secret_cb(s, s->session->master_key, &s->session->master_key_length,
++                      ciphers, &pref_cipher, s->tls_session_secret_cb_arg))
++                      {
++                      s->hit=1;
++                      s->session->ciphers=ciphers;
++                      s->session->verify_result=X509_V_OK;
++
++                      ciphers=NULL;
++
++                      /* check if some cipher was preferred by call back */
++                      pref_cipher=pref_cipher ? pref_cipher : ssl3_choose_cipher(s, s->session->ciphers, SSL_get_ciphers(s));
++                      if (pref_cipher == NULL)
++                              {
++                              al=SSL_AD_HANDSHAKE_FAILURE;
++                              SSLerr(SSL_F_SSL3_GET_CLIENT_HELLO,SSL_R_NO_SHARED_CIPHER);
++                              goto f_err;
++                              }
++
++                      s->session->cipher=pref_cipher;
++
++                      if (s->cipher_list)
++                              sk_SSL_CIPHER_free(s->cipher_list);
++
++                      if (s->cipher_list_by_id)
++                              sk_SSL_CIPHER_free(s->cipher_list_by_id);
++
++                      s->cipher_list = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      s->cipher_list_by_id = sk_SSL_CIPHER_dup(s->session->ciphers);
++                      }
++              }
+ #endif
+       /* Worst case, we will use the NULL compression, but if we have other
+@@ -1134,16 +1187,22 @@ int ssl3_send_server_hello(SSL *s)
+       unsigned char *buf;
+       unsigned char *p,*d;
+       int i,sl;
+-      unsigned long l,Time;
++      unsigned long l;
++#ifdef OPENSSL_NO_TLSEXT
++      unsigned long Time;
++#endif
+       if (s->state == SSL3_ST_SW_SRVR_HELLO_A)
+               {
+               buf=(unsigned char *)s->init_buf->data;
++#ifdef OPENSSL_NO_TLSEXT
+               p=s->s3->server_random;
++              /* Generate server_random if it was not needed previously */
+               Time=(unsigned long)time(NULL);                 /* Time */
+               l2n(Time,p);
+               if (RAND_pseudo_bytes(p,SSL3_RANDOM_SIZE-4) <= 0)
+                       return -1;
++#endif
+               /* Do the message type and length last */
+               d=p= &(buf[4]);
+Index: openssl-SNAP-20081111/ssl/ssl_err.c
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/ssl_err.c
++++ openssl-SNAP-20081111/ssl/ssl_err.c
+@@ -263,6 +263,7 @@ static ERR_STRING_DATA SSL_str_functs[]=
+ {ERR_FUNC(SSL_F_TLS1_PRF),    "tls1_prf"},
+ {ERR_FUNC(SSL_F_TLS1_SETUP_KEY_BLOCK),        "TLS1_SETUP_KEY_BLOCK"},
+ {ERR_FUNC(SSL_F_WRITE_PENDING),       "WRITE_PENDING"},
++{ERR_FUNC(SSL_F_SSL_SET_SESSION_TICKET_EXT), "SSL_set_session_ticket_ext"},
+ {0,NULL}
+       };
+Index: openssl-SNAP-20081111/ssl/ssl.h
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/ssl.h
++++ openssl-SNAP-20081111/ssl/ssl.h
+@@ -355,6 +355,7 @@ extern "C" {
+  * 'struct ssl_st *' function parameters used to prototype callbacks
+  * in SSL_CTX. */
+ typedef struct ssl_st *ssl_crock_st;
++typedef struct tls_session_ticket_ext_st TLS_SESSION_TICKET_EXT;
+ /* used to hold info on the particular ciphers used */
+ typedef struct ssl_cipher_st
+@@ -378,6 +379,8 @@ typedef struct ssl_cipher_st
+ DECLARE_STACK_OF(SSL_CIPHER)
++typedef int (*tls_session_secret_cb_fn)(SSL *s, void *secret, int *secret_len, STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg);
++
+ /* Used to hold functions for SSLv2 or SSLv3/TLSv1 functions */
+ typedef struct ssl_method_st
+       {
+@@ -1145,6 +1148,13 @@ struct ssl_st
+       void *tlsext_opaque_prf_input;
+       size_t tlsext_opaque_prf_input_len;
++      /* TLS Session Ticket extension override */
++      TLS_SESSION_TICKET_EXT *tlsext_session_ticket;
++
++      /* TLS pre-shared secret session resumption */
++      tls_session_secret_cb_fn tls_session_secret_cb;
++      void *tls_session_secret_cb_arg;
++
+       SSL_CTX * initial_ctx; /* initial ctx, used to store sessions */
+ #define session_ctx initial_ctx
+ #else
+@@ -1746,6 +1756,16 @@ void *SSL_COMP_get_compression_methods(v
+ int SSL_COMP_add_compression_method(int id,void *cm);
+ #endif
++/* NOTE: This function will be removed; it is only here for backwards
++ * compatibility for the API during testing. */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len);
++
++/* TLS extensions functions */
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len);
++
++/* Pre-shared secret session resumption functions */
++int SSL_set_session_secret_cb(SSL *s, tls_session_secret_cb_fn tls_session_secret_cb, void *arg);
++
+ /* BEGIN ERROR CODES */
+ /* The following lines are auto generated by the script mkerr.pl. Any changes
+  * made after this point may be overwritten when the script is next run.
+@@ -1948,6 +1968,7 @@ void ERR_load_SSL_strings(void);
+ #define SSL_F_TLS1_PRF                                         284
+ #define SSL_F_TLS1_SETUP_KEY_BLOCK                     211
+ #define SSL_F_WRITE_PENDING                            212
++#define SSL_F_SSL_SET_SESSION_TICKET_EXT               213
+ /* Reason codes. */
+ #define SSL_R_APP_DATA_IN_HANDSHAKE                    100
+Index: openssl-SNAP-20081111/ssl/ssl_sess.c
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/ssl_sess.c
++++ openssl-SNAP-20081111/ssl/ssl_sess.c
+@@ -834,6 +834,62 @@ long SSL_CTX_get_timeout(const SSL_CTX *
+       return(s->session_timeout);
+       }
++#ifndef OPENSSL_NO_TLSEXT
++int SSL_set_session_secret_cb(SSL *s, int (*tls_session_secret_cb)(SSL *s, void *secret, int *secret_len,
++      STACK_OF(SSL_CIPHER) *peer_ciphers, SSL_CIPHER **cipher, void *arg), void *arg)
++      {
++      if (s == NULL) return(0);
++      s->tls_session_secret_cb = tls_session_secret_cb;
++      s->tls_session_secret_cb_arg = arg;
++      return(1);
++      }
++
++int SSL_set_session_ticket_ext(SSL *s, void *ext_data, int ext_len)
++      {
++      if (s->version >= TLS1_VERSION)
++              {
++              if (s->tlsext_session_ticket)
++                      {
++                      OPENSSL_free(s->tlsext_session_ticket);
++                      s->tlsext_session_ticket = NULL;
++                      }
++
++              s->tlsext_session_ticket = OPENSSL_malloc(sizeof(TLS_SESSION_TICKET_EXT) + ext_len);
++              if (!s->tlsext_session_ticket)
++                      {
++                      SSLerr(SSL_F_SSL_SET_SESSION_TICKET_EXT, ERR_R_MALLOC_FAILURE);
++                      return 0;
++                      }
++
++              if (ext_data)
++                      {
++                      s->tlsext_session_ticket->length = ext_len;
++                      s->tlsext_session_ticket->data = s->tlsext_session_ticket + 1;
++                      memcpy(s->tlsext_session_ticket->data, ext_data, ext_len);
++                      }
++              else
++                      {
++                      s->tlsext_session_ticket->length = 0;
++                      s->tlsext_session_ticket->data = NULL;
++                      }
++
++              return 1;
++              }
++
++      return 0;
++      }
++
++/* NOTE: This function will be removed; it is only here for backwards
++ * compatibility for the API during testing. */
++int SSL_set_hello_extension(SSL *s, int ext_type, void *ext_data, int ext_len)
++      {
++      if (ext_type != TLSEXT_TYPE_session_ticket)
++              return 0;
++
++      return SSL_set_session_ticket_ext(s, ext_data, ext_len);
++      }
++#endif /* OPENSSL_NO_TLSEXT */
++
+ typedef struct timeout_param_st
+       {
+       SSL_CTX *ctx;
+Index: openssl-SNAP-20081111/ssl/t1_lib.c
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/t1_lib.c
++++ openssl-SNAP-20081111/ssl/t1_lib.c
+@@ -154,6 +154,12 @@ int tls1_new(SSL *s)
+ void tls1_free(SSL *s)
+       {
++#ifndef OPENSSL_NO_TLSEXT
++      if (s->tlsext_session_ticket)
++              {
++              OPENSSL_free(s->tlsext_session_ticket);
++              }
++#endif /* OPENSSL_NO_TLSEXT */
+       ssl3_free(s);
+       }
+@@ -357,8 +363,23 @@ unsigned char *ssl_add_clienthello_tlsex
+               int ticklen;
+               if (s->session && s->session->tlsext_tick)
+                       ticklen = s->session->tlsext_ticklen;
++              else if (s->session && s->tlsext_session_ticket &&
++                       s->tlsext_session_ticket->data)
++                      {
++                      ticklen = s->tlsext_session_ticket->length;
++                      s->session->tlsext_tick = OPENSSL_malloc(ticklen);
++                      if (!s->session->tlsext_tick)
++                              return NULL;
++                      memcpy(s->session->tlsext_tick,
++                             s->tlsext_session_ticket->data,
++                             ticklen);
++                      s->session->tlsext_ticklen = ticklen;
++                      }
+               else
+                       ticklen = 0;
++              if (ticklen == 0 && s->tlsext_session_ticket &&
++                  s->tlsext_session_ticket->data == NULL)
++                      goto skip_ext;
+               /* Check for enough room 2 for extension type, 2 for len
+                * rest for ticket
+                */
+@@ -371,6 +392,7 @@ unsigned char *ssl_add_clienthello_tlsex
+                       ret += ticklen;
+                       }
+               }
++              skip_ext:
+ #ifdef TLSEXT_TYPE_opaque_prf_input
+       if (s->s3->client_opaque_prf_input != NULL)
+@@ -1435,6 +1457,15 @@ int tls1_process_ticket(SSL *s, unsigned
+                               s->tlsext_ticket_expected = 1;
+                               return 0;       /* Cache miss */
+                               }
++                      if (s->tls_session_secret_cb)
++                              {
++                              /* Indicate cache miss here and instead of
++                               * generating the session from ticket now,
++                               * trigger abbreviated handshake based on
++                               * external mechanism to calculate the master
++                               * secret later. */
++                              return 0;
++                              }
+                       return tls_decrypt_ticket(s, p, size, session_id, len,
+                                                                       ret);
+                       }
+Index: openssl-SNAP-20081111/ssl/tls1.h
+===================================================================
+--- openssl-SNAP-20081111.orig/ssl/tls1.h
++++ openssl-SNAP-20081111/ssl/tls1.h
+@@ -512,6 +512,13 @@ SSL_CTX_callback_ctrl(ssl,SSL_CTRL_SET_T
+ #define TLS_MD_MASTER_SECRET_CONST    "\x6d\x61\x73\x74\x65\x72\x20\x73\x65\x63\x72\x65\x74"  /*master secret*/
+ #endif
++/* TLS Session Ticket extension struct */
++struct tls_session_ticket_ext_st
++      {
++      unsigned short length;
++      void *data;
++      };
++
+ #ifdef  __cplusplus
+ }
+ #endif
+Index: openssl-SNAP-20081111/util/ssleay.num
+===================================================================
+--- openssl-SNAP-20081111.orig/util/ssleay.num
++++ openssl-SNAP-20081111/util/ssleay.num
+@@ -254,3 +254,5 @@ PEM_read_bio_SSL_SESSION                
+ SSL_CTX_set_psk_server_callback         303   EXIST::FUNCTION:PSK
+ SSL_get_psk_identity                    304   EXIST::FUNCTION:PSK
+ PEM_write_SSL_SESSION                   305   EXIST:!WIN16:FUNCTION:
++SSL_set_session_ticket_ext            306     EXIST::FUNCTION:TLSEXT
++SSL_set_session_secret_cb             307     EXIST::FUNCTION:TLSEXT
diff --git a/patches/wpa_s-alldrivers.patch b/patches/wpa_s-alldrivers.patch
new file mode 100644 (file)
index 0000000..0a6d7db
--- /dev/null
@@ -0,0 +1,63 @@
+commit 74b1c84a0b1218a33ce7089e11172e7f39273158
+Author: Samuel Ortiz <sameo@linux.intel.com>
+Date:   Tue Nov 9 16:45:27 2010 +0200
+
+    wpa_supplicant: Test all compiled drivers before failing
+    
+    wpa_supplicant_set_driver() is returning an error if the first driver
+    in the driver list is not built in. It should continue through the
+    driver list until it finds one that's built in.
+
+diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
+index ff1cfd3..0a603af 100644
+--- a/wpa_supplicant/wpa_supplicant.c
++++ b/wpa_supplicant/wpa_supplicant.c
+@@ -1702,7 +1702,7 @@ static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
+ {
+       int i;
+       size_t len;
+-      const char *pos;
++      const char *pos, *driver = name;
+       if (wpa_s == NULL)
+               return -1;
+@@ -1720,20 +1720,26 @@ static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
+               return 0;
+       }
+-      pos = os_strchr(name, ',');
+-      if (pos)
+-              len = pos - name;
+-      else
+-              len = os_strlen(name);
+-      for (i = 0; wpa_drivers[i]; i++) {
+-              if (os_strlen(wpa_drivers[i]->name) == len &&
+-                  os_strncmp(name, wpa_drivers[i]->name, len) ==
+-                  0) {
+-                      wpa_s->driver = wpa_drivers[i];
+-                      wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];
+-                      return 0;
++      do {
++              pos = os_strchr(driver, ',');
++              if (pos)
++                      len = pos - driver;
++              else
++                      len = os_strlen(driver);
++
++              for (i = 0; wpa_drivers[i]; i++) {
++                      if (os_strlen(wpa_drivers[i]->name) == len &&
++                          os_strncmp(driver, wpa_drivers[i]->name, len) ==
++                          0) {
++                              wpa_s->driver = wpa_drivers[i];
++                              wpa_s->global_drv_priv =
++                                      wpa_s->global->drv_priv[i];
++                              return 0;
++                      }
+               }
+-      }
++
++              driver = pos + 1;
++      } while (pos);
+       wpa_printf(MSG_ERROR, "Unsupported driver '%s'.", name);
+       return -1;
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..f47da7b
--- /dev/null
@@ -0,0 +1,11 @@
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps
+
+all:
+       for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
+
+clean:
+       for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done
+       rm -f *~
+
+install:
+       for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d install; done
diff --git a/src/ap/Makefile b/src/ap/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/ap/accounting.c b/src/ap/accounting.c
new file mode 100644 (file)
index 0000000..7939c68
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * hostapd / RADIUS Accounting
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "accounting.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);
+       return NULL;
+}
+
+
+static int accounting_sta_update_stats(struct hostapd_data *hapd,
+                                      struct sta_info *sta,
+                                      struct hostap_sta_driver_data *data)
+{
+       if (hapd->drv.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;
+       hapd->drv.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);
+}
+
+
+/**
+ * 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 (radius_msg_get_hdr(msg)->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);
+               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);
+}
diff --git a/src/ap/accounting.h b/src/ap/accounting.h
new file mode 100644 (file)
index 0000000..f3d60f0
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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_interim(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_NO_ACCOUNTING
+static inline void accounting_sta_start(struct hostapd_data *hapd,
+                                       struct sta_info *sta)
+{
+}
+
+static inline void accounting_sta_stop(struct hostapd_data *hapd,
+                                      struct sta_info *sta)
+{
+}
+
+static inline int accounting_init(struct hostapd_data *hapd)
+{
+       return 0;
+}
+
+static inline void accounting_deinit(struct hostapd_data *hapd)
+{
+}
+#else /* CONFIG_NO_ACCOUNTING */
+void accounting_sta_start(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);
+#endif /* CONFIG_NO_ACCOUNTING */
+
+#endif /* ACCOUNTING_H */
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
new file mode 100644 (file)
index 0000000..5996993
--- /dev/null
@@ -0,0 +1,605 @@
+/*
+ * hostapd / Configuration helper functions
+ * Copyright (c) 2003-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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/sha1.h"
+#include "radius/radius_client.h"
+#include "common/ieee802_11_defs.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_server/eap.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_config.h"
+
+
+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;
+}
+
+
+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_SERVER_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_SERVER_FAST */
+}
+
+
+struct hostapd_config * hostapd_config_defaults(void)
+{
+       struct hostapd_config *conf;
+       struct hostapd_bss_config *bss;
+       int i;
+       const int aCWmin = 4, aCWmax = 10;
+       const struct hostapd_wmm_ac_params ac_bk =
+               { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */
+       const struct hostapd_wmm_ac_params ac_be =
+               { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
+       const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
+               { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 };
+       const struct hostapd_wmm_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;
+       }
+
+       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;
+
+       for (i = 0; i < NUM_TX_QUEUES; i++)
+               conf->tx_queue[i].aifs = -1; /* use hw default */
+
+       conf->wmm_ac_params[0] = ac_be;
+       conf->wmm_ac_params[1] = ac_bk;
+       conf->wmm_ac_params[2] = ac_vi;
+       conf->wmm_ac_params[3] = ac_vo;
+
+       conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED;
+
+       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_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;
+}
+
+
+static int hostapd_derive_psk(struct hostapd_ssid *ssid)
+{
+       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_key(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_key(MSG_DEBUG, "PSK (from passphrase)",
+                       ssid->wpa_psk->psk, PMK_LEN);
+       return 0;
+}
+
+
+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_DEBUG, "Using pre-configured WPA PSK "
+                                  "instead of passphrase");
+               } else {
+                       wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on "
+                                  "passphrase");
+                       if (hostapd_derive_psk(ssid) < 0)
+                               return -1;
+               }
+               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;
+}
+
+
+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);
+       hostapd_config_free_wep(&conf->ssid.wep);
+#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->supported_rates);
+       os_free(conf->basic_rates);
+
+       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 && 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 = conf->ap_pin ?
+                       os_strlen(conf->ap_pin) : 0;
+               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/src/ap/ap_config.h b/src/ap/ap_config.h
new file mode 100644 (file)
index 0000000..f509b5b
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * hostapd / Configuration definitions and helpers functions
+ * Copyright (c) 2003-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.
+ */
+
+#ifndef HOSTAPD_CONFIG_H
+#define HOSTAPD_CONFIG_H
+
+#include "common/defs.h"
+#include "ip_addr.h"
+#include "common/wpa_common.h"
+
+#define MAX_STA_COUNT 2007
+#define MAX_VLAN_ID 4094
+
+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_wmm_ac_params {
+       int cwmin;
+       int cwmax;
+       int aifs;
+       int txop_limit; /* 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;
+       int acct_interim_interval;
+
+       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 wds_sta;
+
+       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 mfp_options 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 */
+#ifndef CONFIG_NATIVE_WINDOWS
+       gid_t ctrl_interface_gid;
+#endif /* CONFIG_NATIVE_WINDOWS */
+       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 wmm_enabled;
+       int wmm_uapsd;
+
+       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 */
+};
+
+
+/**
+ * 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;
+       enum 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 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];
+
+       /*
+        * WMM 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_wmm_ac_params wmm_ac_params[4];
+
+       int ht_op_mode_fixed;
+       u16 ht_capab;
+       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_defaults(void);
+void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+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 /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.c b/src/ap/ap_drv_ops.c
new file mode 100644 (file)
index 0000000..f264a3e
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 2009-2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+
+
+static int hostapd_sta_flags_to_drv(int flags)
+{
+       int res = 0;
+       if (flags & WLAN_STA_AUTHORIZED)
+               res |= WPA_STA_AUTHORIZED;
+       if (flags & WLAN_STA_WMM)
+               res |= WPA_STA_WMM;
+       if (flags & WLAN_STA_SHORT_PREAMBLE)
+               res |= WPA_STA_SHORT_PREAMBLE;
+       if (flags & WLAN_STA_MFP)
+               res |= WPA_STA_MFP;
+       return res;
+}
+
+
+static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd)
+{
+       struct wpabuf *beacon, *proberesp;
+       int ret;
+
+       if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL)
+               return 0;
+
+       beacon = hapd->wps_beacon_ie;
+       proberesp = hapd->wps_probe_resp_ie;
+
+       ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp);
+
+       return ret;
+}
+
+
+static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg,
+                          size_t len)
+{
+       if (hapd->driver == NULL || hapd->driver->send_mlme == NULL)
+               return 0;
+       return hapd->driver->send_mlme(hapd->drv_priv, msg, len);
+}
+
+
+static 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->hapd_send_eapol == NULL)
+               return 0;
+       return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data,
+                                            data_len, encrypt,
+                                            hapd->own_addr);
+}
+
+
+static int hostapd_set_authorized(struct hostapd_data *hapd,
+                                 struct sta_info *sta, int authorized)
+{
+       if (authorized) {
+               return hostapd_sta_set_flags(hapd, sta->addr,
+                                            hostapd_sta_flags_to_drv(
+                                                    sta->flags),
+                                            WPA_STA_AUTHORIZED, ~0);
+       }
+
+       return hostapd_sta_set_flags(hapd, sta->addr,
+                                    hostapd_sta_flags_to_drv(sta->flags),
+                                    0, ~WPA_STA_AUTHORIZED);
+}
+
+
+static int hostapd_set_key(const char *ifname, struct hostapd_data *hapd,
+                          enum wpa_alg alg, const u8 *addr, int key_idx,
+                          int set_tx, const u8 *seq, size_t seq_len,
+                          const u8 *key, size_t key_len)
+{
+       if (hapd->driver == NULL || hapd->driver->set_key == NULL)
+               return 0;
+       return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr,
+                                    key_idx, set_tx, seq, seq_len, key,
+                                    key_len);
+}
+
+
+static 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 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 int hostapd_set_sta_flags(struct hostapd_data *hapd,
+                                struct sta_info *sta)
+{
+       int set_flags, total_flags, flags_and, flags_or;
+       total_flags = hostapd_sta_flags_to_drv(sta->flags);
+       set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP;
+       if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+            sta->auth_alg == WLAN_AUTH_FT) &&
+           sta->flags & WLAN_STA_AUTHORIZED)
+               set_flags |= WPA_STA_AUTHORIZED;
+       flags_or = total_flags & set_flags;
+       flags_and = total_flags | ~set_flags;
+       return hostapd_sta_set_flags(hapd, sta->addr, total_flags,
+                                    flags_or, flags_and);
+}
+
+
+static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd,
+                                    const char *ifname, int enabled)
+{
+       struct wpa_bss_params params;
+       os_memset(&params, 0, sizeof(params));
+       params.ifname = ifname;
+       params.enabled = enabled;
+       if (enabled) {
+               params.wpa = hapd->conf->wpa;
+               params.ieee802_1x = hapd->conf->ieee802_1x;
+               params.wpa_group = hapd->conf->wpa_group;
+               params.wpa_pairwise = hapd->conf->wpa_pairwise;
+               params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
+               params.rsn_preauth = hapd->conf->rsn_preauth;
+       }
+       return hostapd_set_ieee8021x(hapd, &params);
+}
+
+
+static 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 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);
+}
+
+
+static int hostapd_set_bss_params(struct hostapd_data *hapd,
+                                 int use_protection)
+{
+       int ret = 0;
+       int preamble;
+#ifdef CONFIG_IEEE80211N
+       u8 buf[60], *ht_capab, *ht_oper, *pos;
+
+       pos = buf;
+       ht_capab = pos;
+       pos = hostapd_eid_ht_capabilities(hapd, pos);
+       ht_oper = pos;
+       pos = hostapd_eid_ht_operation(hapd, pos);
+       if (pos > ht_oper && ht_oper > ht_capab &&
+           hostapd_set_ht_params(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");
+               ret = -1;
+       }
+
+#endif /* CONFIG_IEEE80211N */
+
+       if (hostapd_set_cts_protect(hapd, use_protection)) {
+               wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel "
+                          "driver");
+               ret = -1;
+       }
+
+       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");
+               ret = -1;
+       }
+
+       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");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+static int hostapd_set_beacon(struct hostapd_data *hapd,
+                             const u8 *head, size_t head_len,
+                             const u8 *tail, size_t tail_len, int dtim_period,
+                             int beacon_int)
+{
+       if (hapd->driver == NULL || hapd->driver->set_beacon == NULL)
+               return 0;
+       return hapd->driver->set_beacon(hapd->drv_priv,
+                                       head, head_len, tail, tail_len,
+                                       dtim_period, beacon_int);
+}
+
+
+static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
+{
+       char force_ifname[IFNAMSIZ];
+       u8 if_addr[ETH_ALEN];
+       return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, NULL, NULL, NULL,
+                             force_ifname, if_addr);
+}
+
+static int hostapd_vlan_if_remove(struct hostapd_data *hapd,
+                                 const char *ifname)
+{
+       return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname);
+}
+
+
+static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr,
+                              int aid, int val)
+{
+       if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
+               return 0;
+       return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val);
+}
+
+
+static 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 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 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, hapd->own_addr, addr,
+                                       reason);
+}
+
+
+static 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, hapd->own_addr, addr,
+                                         reason);
+}
+
+
+static int hostapd_sta_add(struct hostapd_data *hapd,
+                          const u8 *addr, u16 aid, u16 capability,
+                          const u8 *supp_rates, size_t supp_rates_len,
+                          u16 listen_interval,
+                          const struct ieee80211_ht_capabilities *ht_capab)
+{
+       struct hostapd_sta_add_params params;
+
+       if (hapd->driver == NULL)
+               return 0;
+       if (hapd->driver->sta_add == NULL)
+               return 0;
+
+       os_memset(&params, 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.listen_interval = listen_interval;
+       params.ht_capabilities = ht_capab;
+       return hapd->driver->sta_add(hapd->drv_priv, &params);
+}
+
+
+static 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 int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled)
+{
+       if (hapd->driver == NULL ||
+           hapd->driver->hapd_set_countermeasures == NULL)
+               return 0;
+       return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled);
+}
+
+
+void hostapd_set_driver_ops(struct hostapd_driver_ops *ops)
+{
+       ops->set_ap_wps_ie = hostapd_set_ap_wps_ie;
+       ops->send_mgmt_frame = hostapd_send_mgmt_frame;
+       ops->send_eapol = hostapd_send_eapol;
+       ops->set_authorized = hostapd_set_authorized;
+       ops->set_key = hostapd_set_key;
+       ops->read_sta_data = hostapd_read_sta_data;
+       ops->sta_clear_stats = hostapd_sta_clear_stats;
+       ops->set_sta_flags = hostapd_set_sta_flags;
+       ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x;
+       ops->set_radius_acl_auth = hostapd_set_radius_acl_auth;
+       ops->set_radius_acl_expire = hostapd_set_radius_acl_expire;
+       ops->set_bss_params = hostapd_set_bss_params;
+       ops->set_beacon = hostapd_set_beacon;
+       ops->vlan_if_add = hostapd_vlan_if_add;
+       ops->vlan_if_remove = hostapd_vlan_if_remove;
+       ops->set_wds_sta = hostapd_set_wds_sta;
+       ops->set_sta_vlan = hostapd_set_sta_vlan;
+       ops->get_inact_sec = hostapd_get_inact_sec;
+       ops->sta_deauth = hostapd_sta_deauth;
+       ops->sta_disassoc = hostapd_sta_disassoc;
+       ops->sta_add = hostapd_sta_add;
+       ops->sta_remove = hostapd_sta_remove;
+       ops->set_countermeasures = hostapd_set_countermeasures;
+}
+
+
+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->drv_priv, enabled);
+}
+
+
+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->drv_priv, elem, elem_len);
+}
+
+
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len)
+{
+       if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL)
+               return 0;
+       return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
+{
+       if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL)
+               return 0;
+       return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len);
+}
+
+
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+                  const char *ifname, const u8 *addr, void *bss_ctx,
+                  void **drv_priv, char *force_ifname, u8 *if_addr)
+{
+       if (hapd->driver == NULL || hapd->driver->if_add == NULL)
+               return -1;
+       return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
+                                   bss_ctx, drv_priv, force_ifname, if_addr);
+}
+
+
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+                     const char *ifname)
+{
+       if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+               return -1;
+       return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
+}
+
+
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+                         struct wpa_bss_params *params)
+{
+       if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL)
+               return 0;
+       return hapd->driver->set_ieee8021x(hapd->drv_priv, params);
+}
+
+
+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);
+}
+
+
+int hostapd_flush(struct hostapd_data *hapd)
+{
+       if (hapd->driver == NULL || hapd->driver->flush == NULL)
+               return 0;
+       return hapd->driver->flush(hapd->drv_priv);
+}
+
+
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+                    int channel, int ht_enabled, int sec_channel_offset)
+{
+       struct hostapd_freq_params data;
+       if (hapd->driver == NULL)
+               return 0;
+       if (hapd->driver->set_freq == NULL)
+               return 0;
+       os_memset(&data, 0, sizeof(data));
+       data.mode = mode;
+       data.freq = freq;
+       data.channel = channel;
+       data.ht_enabled = ht_enabled;
+       data.sec_channel_offset = sec_channel_offset;
+       return hapd->driver->set_freq(hapd->drv_priv, &data);
+}
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+int hostapd_set_ht_params(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(hapd->drv_priv,
+                                          ht_capab, ht_capab_len,
+                                          ht_oper, ht_oper_len);
+}
+
+
+int hostapd_drv_none(struct hostapd_data *hapd)
+{
+       return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0;
+}
+
+
+int hostapd_driver_scan(struct hostapd_data *hapd,
+                       struct wpa_driver_scan_params *params)
+{
+       if (hapd->driver && hapd->driver->scan2)
+               return hapd->driver->scan2(hapd->drv_priv, params);
+       return -1;
+}
+
+
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+       struct hostapd_data *hapd)
+{
+       if (hapd->driver && hapd->driver->get_scan_results2)
+               return hapd->driver->get_scan_results2(hapd->drv_priv);
+       return NULL;
+}
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
new file mode 100644 (file)
index 0000000..9b75d09
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * hostapd - Driver operations
+ * Copyright (c) 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.
+ */
+
+#ifndef AP_DRV_OPS
+#define AP_DRV_OPS
+
+enum wpa_driver_if_type;
+struct wpa_bss_params;
+struct wpa_driver_scan_params;
+
+void hostapd_set_driver_ops(struct hostapd_driver_ops *ops);
+int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
+int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
+                            size_t elem_len);
+int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len);
+int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
+int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+                  const char *ifname, const u8 *addr, void *bss_ctx,
+                  void **drv_priv, char *force_ifname, u8 *if_addr);
+int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
+                     const char *ifname);
+int hostapd_set_ieee8021x(struct hostapd_data *hapd,
+                         struct wpa_bss_params *params);
+int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
+                      const u8 *addr, int idx, u8 *seq);
+int hostapd_flush(struct hostapd_data *hapd);
+int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
+                    int channel, int ht_enabled, int sec_channel_offset);
+int hostapd_set_rts(struct hostapd_data *hapd, int rts);
+int hostapd_set_frag(struct hostapd_data *hapd, int frag);
+int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr,
+                         int total_flags, int flags_or, int flags_and);
+int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates,
+                         int *basic_rates, int mode);
+int hostapd_set_country(struct hostapd_data *hapd, const char *country);
+int hostapd_set_cts_protect(struct hostapd_data *hapd, int value);
+int hostapd_set_preamble(struct hostapd_data *hapd, int value);
+int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value);
+int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs,
+                               int cw_min, int cw_max, int burst_time);
+int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr,
+                          const u8 *mask);
+struct hostapd_hw_modes *
+hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes,
+                           u16 *flags);
+int hostapd_driver_commit(struct hostapd_data *hapd);
+int hostapd_set_ht_params(struct hostapd_data *hapd,
+                         const u8 *ht_capab, size_t ht_capab_len,
+                         const u8 *ht_oper, size_t ht_oper_len);
+int hostapd_drv_none(struct hostapd_data *hapd);
+int hostapd_driver_scan(struct hostapd_data *hapd,
+                       struct wpa_driver_scan_params *params);
+struct wpa_scan_results * hostapd_driver_get_scan_results(
+       struct hostapd_data *hapd);
+
+#endif /* AP_DRV_OPS */
diff --git a/src/ap/ap_list.c b/src/ap/ap_list.c
new file mode 100644 (file)
index 0000000..5297dbf
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * hostapd / AP table
+ * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ap_list.h"
+
+
+/* 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 int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
+{
+       int i;
+
+       if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+           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;
+}
+
+
+struct ap_info * ap_get_ap(struct hostapd_iface *iface, const 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, const 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));
+               ap_free_ap(iface, ap->prev);
+       }
+
+       return ap;
+}
+
+
+void ap_list_process_beacon(struct hostapd_iface *iface,
+                           const 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->ssi_signal = fi->ssi_signal;
+               ap->datarate = fi->datarate;
+       }
+
+       if (!new_ap && 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->ht_support) {
+               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);
+
+       while (iface->ap_list) {
+               ap = iface->ap_list->prev;
+               if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
+                   now)
+                       break;
+
+               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;
+                       if (!ap->ht_support)
+                               olbc_ht = 1;
+                       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);
+}
diff --git a/src/ap/ap_list.h b/src/ap/ap_list.h
new file mode 100644 (file)
index 0000000..f49f58b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ *
+ * 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 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, const 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,
+                           const struct ieee80211_mgmt *mgmt,
+                           struct ieee802_11_elems *elems,
+                           struct hostapd_frame_info *fi);
+#ifdef NEED_AP_MLME
+int ap_list_init(struct hostapd_iface *iface);
+void ap_list_deinit(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline int ap_list_init(struct hostapd_iface *iface)
+{
+       return 0;
+}
+
+static inline void ap_list_deinit(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* AP_LIST_H */
diff --git a/src/ap/ap_mlme.c b/src/ap/ap_mlme.c
new file mode 100644 (file)
index 0000000..2b09b11
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+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";
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+
+/**
+ * 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/src/ap/ap_mlme.h b/src/ap/ap_mlme.h
new file mode 100644 (file)
index 0000000..c77a939
--- /dev/null
@@ -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/src/ap/authsrv.c b/src/ap/authsrv.c
new file mode 100644 (file)
index 0000000..0ab0668
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Authentication server setup
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "eap_server/eap_sim_db.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "radius/radius_server.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "authsrv.h"
+
+
+#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA)
+#define EAP_SIM_DB
+#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */
+
+
+#ifdef EAP_SIM_DB
+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) {
+#ifdef RADIUS_SERVER
+               radius_server_eap_pending_cb(hapd->radius_srv, session_ctx);
+#endif /* RADIUS_SERVER */
+       }
+}
+#endif /* EAP_SIM_DB */
+
+
+#ifdef RADIUS_SERVER
+
+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 hostapd_setup_radius_srv(struct hostapd_data *hapd)
+{
+       struct radius_server_conf srv;
+       struct hostapd_bss_config *conf = hapd->conf;
+       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.msg_ctx = hapd->msg_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;
+}
+
+#endif /* RADIUS_SERVER */
+
+
+int authsrv_init(struct hostapd_data *hapd)
+{
+#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");
+                       authsrv_deinit(hapd);
+                       return -1;
+               }
+
+               os_memset(&params, 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, &params)) {
+                       wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+                       authsrv_deinit(hapd);
+                       return -1;
+               }
+
+               if (tls_global_set_verify(hapd->ssl_ctx,
+                                         hapd->conf->check_crl)) {
+                       wpa_printf(MSG_ERROR, "Failed to enable check_crl");
+                       authsrv_deinit(hapd);
+                       return -1;
+               }
+       }
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+       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");
+                       authsrv_deinit(hapd);
+                       return -1;
+               }
+       }
+#endif /* EAP_SIM_DB */
+
+#ifdef RADIUS_SERVER
+       if (hapd->conf->radius_server_clients &&
+           hostapd_setup_radius_srv(hapd))
+               return -1;
+#endif /* RADIUS_SERVER */
+
+       return 0;
+}
+
+
+void authsrv_deinit(struct hostapd_data *hapd)
+{
+#ifdef RADIUS_SERVER
+       radius_server_deinit(hapd->radius_srv);
+       hapd->radius_srv = NULL;
+#endif /* RADIUS_SERVER */
+
+#ifdef EAP_TLS_FUNCS
+       if (hapd->ssl_ctx) {
+               tls_deinit(hapd->ssl_ctx);
+               hapd->ssl_ctx = NULL;
+       }
+#endif /* EAP_TLS_FUNCS */
+
+#ifdef EAP_SIM_DB
+       if (hapd->eap_sim_db_priv) {
+               eap_sim_db_deinit(hapd->eap_sim_db_priv);
+               hapd->eap_sim_db_priv = NULL;
+       }
+#endif /* EAP_SIM_DB */
+}
diff --git a/src/ap/authsrv.h b/src/ap/authsrv.h
new file mode 100644 (file)
index 0000000..be3051e
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Authentication server setup
+ * 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.
+ */
+
+#ifndef AUTHSRV_H
+#define AUTHSRV_H
+
+int authsrv_init(struct hostapd_data *hapd);
+void authsrv_deinit(struct hostapd_data *hapd);
+
+#endif /* AUTHSRV_H */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
new file mode 100644 (file)
index 0000000..004cc8a
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ * 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-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 "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.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 ||
+           hapd->iconf->preamble == LONG_PREAMBLE)
+               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,
+                     const struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct ieee80211_mgmt *resp;
+       struct ieee802_11_elems elems;
+       char *ssid;
+       u8 *pos, *epos;
+       const u8 *ie;
+       size_t ssid_len, ie_len;
+       struct sta_info *sta = NULL;
+       size_t buflen;
+       size_t i;
+
+       ie = mgmt->u.probe_req.variable;
+       ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+
+       for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
+               if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+                                           mgmt->sa, ie, ie_len) > 0)
+                       return;
+
+       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
+       buflen = MAX_PROBERESP_LEN;
+#ifdef CONFIG_WPS
+       if (hapd->wps_probe_resp_ie)
+               buflen += wpabuf_len(hapd->wps_probe_resp_ie);
+#endif /* CONFIG_WPS */
+       resp = os_zalloc(buflen);
+       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);
+
+       /* RSN, MDIE, WPA */
+       pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta);
+
+#ifdef CONFIG_IEEE80211N
+       pos = hostapd_eid_ht_capabilities(hapd, pos);
+       pos = hostapd_eid_ht_operation(hapd, pos);
+#endif /* CONFIG_IEEE80211N */
+
+       /* Wi-Fi Alliance WMM */
+       pos = hostapd_eid_wmm(hapd, pos);
+
+#ifdef CONFIG_WPS
+       if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) {
+               os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie),
+                         wpabuf_len(hapd->wps_probe_resp_ie));
+               pos += wpabuf_len(hapd->wps_probe_resp_ie);
+       }
+#endif /* CONFIG_WPS */
+
+       if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 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;
+       u16 capab_info;
+       size_t head_len, tail_len;
+
+#define BEACON_HEAD_BUF_SIZE 256
+#define BEACON_TAIL_BUF_SIZE 512
+       head = os_zalloc(BEACON_HEAD_BUF_SIZE);
+       tail_len = BEACON_TAIL_BUF_SIZE;
+#ifdef CONFIG_WPS
+       if (hapd->conf->wps_state && hapd->wps_beacon_ie)
+               tail_len += wpabuf_len(hapd->wps_beacon_ie);
+#endif /* CONFIG_WPS */
+       tailpos = tail = os_malloc(tail_len);
+       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);
+
+       /* RSN, MDIE, WPA */
+       tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
+                                 tailpos, NULL);
+
+#ifdef CONFIG_IEEE80211N
+       tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
+       tailpos = hostapd_eid_ht_operation(hapd, tailpos);
+#endif /* CONFIG_IEEE80211N */
+
+       /* Wi-Fi Alliance WMM */
+       tailpos = hostapd_eid_wmm(hapd, tailpos);
+
+#ifdef CONFIG_WPS
+       if (hapd->conf->wps_state && hapd->wps_beacon_ie) {
+               os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie),
+                         wpabuf_len(hapd->wps_beacon_ie));
+               tailpos += wpabuf_len(hapd->wps_beacon_ie);
+       }
+#endif /* CONFIG_WPS */
+
+       tail_len = tailpos > tail ? tailpos - tail : 0;
+
+       if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len,
+                                tail, tail_len, hapd->conf->dtim_period,
+                                hapd->iconf->beacon_int))
+               wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM "
+                          "period");
+
+       os_free(tail);
+       os_free(head);
+
+       hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) &
+                                         ERP_INFO_USE_PROTECTION));
+}
+
+
+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/src/ap/beacon.h b/src/ap/beacon.h
new file mode 100644 (file)
index 0000000..c1510e1
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
+
+struct ieee80211_mgmt;
+
+void handle_probe_req(struct hostapd_data *hapd,
+                     const struct ieee80211_mgmt *mgmt, size_t len);
+#ifdef NEED_AP_MLME
+void ieee802_11_set_beacon(struct hostapd_data *hapd);
+void ieee802_11_set_beacons(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline void ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+}
+
+static inline void ieee802_11_set_beacons(struct hostapd_iface *iface)
+{
+}
+#endif /* NEED_AP_MLME */
+
+#endif /* BEACON_H */
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
new file mode 100644 (file)
index 0000000..e50b0a7
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Control interface for shared AP commands
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "wps_hostapd.h"
+#include "ctrl_iface_ap.h"
+
+
+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;
+       res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len,
+                                     buflen - len);
+       if (res >= 0)
+               len += res;
+
+       return len;
+}
+
+
+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);
+}
+
+
+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);
+}
+
+
+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);
+}
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
new file mode 100644 (file)
index 0000000..8690bea
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Control interface for shared AP commands
+ * 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.
+ */
+
+#ifndef CTRL_IFACE_AP_H
+#define CTRL_IFACE_AP_H
+
+int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd,
+                                char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
+                          char *buf, size_t buflen);
+int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
+                               char *buf, size_t buflen);
+
+#endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
new file mode 100644 (file)
index 0000000..26ef584
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * hostapd / Callback functions for driver wrappers
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "radius/radius.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "tkip_countermeasures.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "wps_hostapd.h"
+#include "ap_config.h"
+
+
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+                       const u8 *ie, size_t ielen)
+{
+       struct sta_info *sta;
+       int new_assoc, res;
+       struct ieee802_11_elems elems;
+
+       if (addr == NULL) {
+               /*
+                * This could potentially happen with unexpected event from the
+                * driver wrapper. This was seen at least in one case where the
+                * driver ended up being set to station mode while hostapd was
+                * running, so better make sure we stop processing such an
+                * event here.
+                */
+               wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with "
+                          "no address");
+               return -1;
+       }
+
+       hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_INFO, "associated");
+
+       ieee802_11_parse_elems(ie, ielen, &elems, 0);
+       if (elems.wps_ie) {
+               ie = elems.wps_ie - 2;
+               ielen = elems.wps_ie_len + 2;
+               wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq");
+       } else if (elems.rsn_ie) {
+               ie = elems.rsn_ie - 2;
+               ielen = elems.rsn_ie_len + 2;
+               wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq");
+       } else if (elems.wpa_ie) {
+               ie = elems.wpa_ie - 2;
+               ielen = elems.wpa_ie_len + 2;
+               wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+       } else {
+               ie = NULL;
+               ielen = 0;
+               wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in "
+                          "(Re)AssocReq");
+       }
+
+       sta = ap_get_sta(hapd, addr);
+       if (sta) {
+               accounting_sta_stop(hapd, sta);
+       } else {
+               sta = ap_sta_add(hapd, addr);
+               if (sta == NULL)
+                       return -1;
+       }
+       sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS);
+
+       if (hapd->conf->wpa) {
+               if (ie == NULL || ielen == 0) {
+                       if (hapd->conf->wps_state) {
+                               wpa_printf(MSG_DEBUG, "STA did not include "
+                                          "WPA/RSN IE in (Re)Association "
+                                          "Request - possible WPS use");
+                               sta->flags |= WLAN_STA_MAYBE_WPS;
+                               goto skip_wpa_check;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA");
+                       return -1;
+               }
+               if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 &&
+                   os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+                       sta->flags |= WLAN_STA_WPS;
+                       goto skip_wpa_check;
+               }
+
+               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_ERROR, "Failed to initialize WPA state "
+                                  "machine");
+                       return -1;
+               }
+               res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+                                         ie, ielen, NULL, 0);
+               if (res != WPA_IE_OK) {
+                       int resp;
+                       wpa_printf(MSG_DEBUG, "WPA/RSN information element "
+                                  "rejected? (res %u)", res);
+                       wpa_hexdump(MSG_DEBUG, "IE", ie, ielen);
+                       if (res == WPA_INVALID_GROUP)
+                               resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+                       else if (res == WPA_INVALID_PAIRWISE)
+                               resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID;
+                       else if (res == WPA_INVALID_AKMP)
+                               resp = WLAN_REASON_AKMP_NOT_VALID;
+#ifdef CONFIG_IEEE80211W
+                       else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION)
+                               resp = WLAN_REASON_INVALID_IE;
+                       else if (res == WPA_INVALID_MGMT_GROUP_CIPHER)
+                               resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID;
+#endif /* CONFIG_IEEE80211W */
+                       else
+                               resp = WLAN_REASON_INVALID_IE;
+                       hapd->drv.sta_disassoc(hapd, sta->addr, resp);
+                       ap_free_sta(hapd, sta);
+                       return -1;
+               }
+       } else if (hapd->conf->wps_state) {
+               if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 &&
+                   os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) {
+                       sta->flags |= WLAN_STA_WPS;
+               } else
+                       sta->flags |= WLAN_STA_MAYBE_WPS;
+       }
+skip_wpa_check:
+
+       new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+       sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+       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);
+
+       return 0;
+}
+
+
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
+{
+       struct sta_info *sta;
+
+       hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_INFO, "disassociated");
+
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "Disassociation notification for "
+                          "unknown STA " MACSTR, MAC2STR(addr));
+               return;
+       }
+
+       sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+               MAC2STR(sta->addr));
+       wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+       sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+       ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+       ap_free_sta(hapd, sta);
+}
+
+
+#ifdef HOSTAPD
+
+#ifdef NEED_AP_MLME
+
+static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len)
+{
+       u16 fc, type, stype;
+
+       /*
+        * PS-Poll frames are 16 bytes. All other frames are
+        * 24 bytes or longer.
+        */
+       if (len < 16)
+               return NULL;
+
+       fc = le_to_host16(hdr->frame_control);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       switch (type) {
+       case WLAN_FC_TYPE_DATA:
+               if (len < 24)
+                       return NULL;
+               switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+               case WLAN_FC_FROMDS | WLAN_FC_TODS:
+               case WLAN_FC_TODS:
+                       return hdr->addr1;
+               case WLAN_FC_FROMDS:
+                       return hdr->addr2;
+               default:
+                       return NULL;
+               }
+       case WLAN_FC_TYPE_CTRL:
+               if (stype != WLAN_FC_STYPE_PSPOLL)
+                       return NULL;
+               return hdr->addr1;
+       case WLAN_FC_TYPE_MGMT:
+               return hdr->addr3;
+       default:
+               return NULL;
+       }
+}
+
+
+#define HAPD_BROADCAST ((struct hostapd_data *) -1)
+
+static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface,
+                                           const u8 *bssid)
+{
+       size_t i;
+
+       if (bssid == NULL)
+               return NULL;
+       if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff &&
+           bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff)
+               return HAPD_BROADCAST;
+
+       for (i = 0; i < iface->num_bss; i++) {
+               if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0)
+                       return iface->bss[i];
+       }
+
+       return NULL;
+}
+
+
+static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
+                                       const u8 *frame, size_t len)
+{
+       const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame;
+       u16 fc = le_to_host16(hdr->frame_control);
+       hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+       if (hapd == NULL || hapd == HAPD_BROADCAST)
+               return;
+
+       ieee802_11_rx_from_unknown(hapd, hdr->addr2,
+                                  (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+                                  (WLAN_FC_TODS | WLAN_FC_FROMDS));
+}
+
+
+static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       const struct ieee80211_hdr *hdr;
+       const u8 *bssid;
+       struct hostapd_frame_info fi;
+
+       hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
+       bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
+       if (bssid == NULL)
+               return;
+
+       hapd = get_hapd_bssid(iface, bssid);
+       if (hapd == NULL) {
+               u16 fc;
+               fc = le_to_host16(hdr->frame_control);
+
+               /*
+                * Drop frames to unknown BSSIDs except for Beacon frames which
+                * could be used to update neighbor information.
+                */
+               if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
+                       hapd = iface->bss[0];
+               else
+                       return;
+       }
+
+       os_memset(&fi, 0, sizeof(fi));
+       fi.datarate = rx_mgmt->datarate;
+       fi.ssi_signal = rx_mgmt->ssi_signal;
+
+       if (hapd == HAPD_BROADCAST) {
+               size_t i;
+               for (i = 0; i < iface->num_bss; i++)
+                       ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+                                       rx_mgmt->frame_len, &fi);
+       } else
+               ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+}
+
+
+static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf,
+                              size_t len, u16 stype, int ok)
+{
+       struct ieee80211_hdr *hdr;
+       hdr = (struct ieee80211_hdr *) buf;
+       hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len));
+       if (hapd == NULL || hapd == HAPD_BROADCAST)
+               return;
+       ieee802_11_mgmt_cb(hapd, buf, len, stype, ok);
+}
+
+#endif /* NEED_AP_MLME */
+
+
+static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa,
+                               const u8 *ie, size_t ie_len)
+{
+       size_t i;
+       int ret = 0;
+
+       for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) {
+               if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
+                                           sa, ie, ie_len) > 0) {
+                       ret = 1;
+                       break;
+               }
+       }
+       return ret;
+}
+
+
+static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr)
+{
+       struct sta_info *sta = ap_get_sta(hapd, addr);
+       if (sta)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR
+                  " - adding a new STA", MAC2STR(addr));
+       sta = ap_sta_add(hapd, addr);
+       if (sta) {
+               hostapd_new_assoc_sta(hapd, sta, 0);
+       } else {
+               wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR,
+                          MAC2STR(addr));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
+                                  const u8 *data, size_t data_len)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       size_t j;
+
+       for (j = 0; j < iface->num_bss; j++) {
+               if (ap_get_sta(iface->bss[j], src)) {
+                       hapd = iface->bss[j];
+                       break;
+               }
+       }
+
+       ieee802_1x_receive(hapd, src, data, data_len);
+}
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+                         union wpa_event_data *data)
+{
+       struct hostapd_data *hapd = ctx;
+
+       switch (event) {
+       case EVENT_MICHAEL_MIC_FAILURE:
+               michael_mic_failure(hapd, data->michael_mic_failure.src, 1);
+               break;
+       case EVENT_SCAN_RESULTS:
+               if (hapd->iface->scan_cb)
+                       hapd->iface->scan_cb(hapd->iface);
+               break;
+#ifdef CONFIG_IEEE80211R
+       case EVENT_FT_RRB_RX:
+               wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src,
+                             data->ft_rrb_rx.data, data->ft_rrb_rx.data_len);
+               break;
+#endif /* CONFIG_IEEE80211R */
+       case EVENT_WPS_BUTTON_PUSHED:
+               hostapd_wps_button_pushed(hapd);
+               break;
+#ifdef NEED_AP_MLME
+       case EVENT_TX_STATUS:
+               switch (data->tx_status.type) {
+               case WLAN_FC_TYPE_MGMT:
+                       hostapd_mgmt_tx_cb(hapd, data->tx_status.data,
+                                          data->tx_status.data_len,
+                                          data->tx_status.stype,
+                                          data->tx_status.ack);
+                       break;
+               case WLAN_FC_TYPE_DATA:
+                       hostapd_tx_status(hapd, data->tx_status.dst,
+                                         data->tx_status.data,
+                                         data->tx_status.data_len,
+                                         data->tx_status.ack);
+                       break;
+               }
+               break;
+       case EVENT_RX_FROM_UNKNOWN:
+               hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame,
+                                           data->rx_from_unknown.len);
+               break;
+       case EVENT_RX_MGMT:
+               hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+               break;
+#endif /* NEED_AP_MLME */
+       case EVENT_RX_PROBE_REQ:
+               hostapd_probe_req_rx(hapd, data->rx_probe_req.sa,
+                                    data->rx_probe_req.ie,
+                                    data->rx_probe_req.ie_len);
+               break;
+       case EVENT_NEW_STA:
+               hostapd_event_new_sta(hapd, data->new_sta.addr);
+               break;
+       case EVENT_EAPOL_RX:
+               hostapd_event_eapol_rx(hapd, data->eapol_rx.src,
+                                      data->eapol_rx.data,
+                                      data->eapol_rx.data_len);
+               break;
+       case EVENT_ASSOC:
+               hostapd_notif_assoc(hapd, data->assoc_info.addr,
+                                   data->assoc_info.req_ies,
+                                   data->assoc_info.req_ies_len);
+               break;
+       case EVENT_DISASSOC:
+               if (data)
+                       hostapd_notif_disassoc(hapd, data->disassoc_info.addr);
+               break;
+       case EVENT_DEAUTH:
+               if (data)
+                       hostapd_notif_disassoc(hapd, data->deauth_info.addr);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "Unknown event %d", event);
+               break;
+       }
+}
+
+#endif /* HOSTAPD */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
new file mode 100644 (file)
index 0000000..841f9c5
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius_client.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "authsrv.h"
+#include "sta_info.h"
+#include "accounting.h"
+#include "ap_list.h"
+#include "beacon.h"
+#include "iapp.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "vlan_init.h"
+#include "wpa_auth.h"
+#include "wps_hostapd.h"
+#include "hw_features.h"
+#include "wpa_auth_glue.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+
+
+static int hostapd_flush_old_stations(struct hostapd_data *hapd);
+static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
+
+extern int wpa_debug_level;
+
+
+int hostapd_reload_config(struct hostapd_iface *iface)
+{
+       struct hostapd_data *hapd = iface->bss[0];
+       struct hostapd_config *newconf, *oldconf;
+       size_t j;
+
+       if (iface->config_read_cb == NULL)
+               return -1;
+       newconf = iface->config_read_cb(iface->config_fname);
+       if (newconf == NULL)
+               return -1;
+
+       /*
+        * Deauthenticate all stations since the new configuration may not
+        * allow them to use the BSS anymore.
+        */
+       for (j = 0; j < iface->num_bss; j++)
+               hostapd_flush_old_stations(iface->bss[j]);
+
+#ifndef CONFIG_NO_RADIUS
+       /* TODO: update dynamic data based on changed configuration
+        * items (e.g., open/close sockets, etc.) */
+       radius_client_flush(hapd->radius, 0);
+#endif /* CONFIG_NO_RADIUS */
+
+       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->ieee802_1x || hapd->conf->wpa)
+               hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1);
+       else
+               hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+       if (hapd->conf->wpa && hapd->wpa_auth == NULL)
+               hostapd_setup_wpa(hapd);
+       else if (hapd->conf->wpa) {
+               const u8 *wpa_ie;
+               size_t wpa_ie_len;
+               hostapd_reconfig_wpa(hapd);
+               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.");
+       } 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);
+               hostapd_set_generic_elem(hapd, (u8 *) "", 0);
+       }
+
+       ieee802_11_set_beacon(hapd);
+       hostapd_update_wps(hapd);
+
+       if (hapd->conf->ssid.ssid_set &&
+           hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid,
+                            hapd->conf->ssid.ssid_len)) {
+               wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver");
+               /* try to continue */
+       }
+
+       hostapd_config_free(oldconf);
+
+       wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface);
+
+       return 0;
+}
+
+
+static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd,
+                                             char *ifname)
+{
+       int i;
+
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i,
+                                     i == 0 ? 1 : 0, NULL, 0, NULL, 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 (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL,
+                                             i, i == 0 ? 1 : 0, NULL, 0,
+                                             NULL, 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 &&
+           hapd->drv.set_key(hapd->conf->iface,
+                             hapd, WPA_ALG_WEP, NULL, idx,
+                             idx == ssid->wep.idx,
+                             NULL, 0, ssid->wep.key[idx],
+                             ssid->wep.len[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 (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL,
+                                             idx, idx == key->idx, NULL, 0,
+                                             key->key[idx], key->len[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)
+{
+       if (hapd->iface->ctrl_iface_deinit)
+               hapd->iface->ctrl_iface_deinit(hapd);
+
+       iapp_deinit(hapd->iapp);
+       hapd->iapp = NULL;
+       accounting_deinit(hapd);
+       hostapd_deinit_wpa(hapd);
+       vlan_deinit(hapd);
+       hostapd_acl_deinit(hapd);
+#ifndef CONFIG_NO_RADIUS
+       radius_client_deinit(hapd->radius);
+       hapd->radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+       hostapd_deinit_wps(hapd);
+
+       authsrv_deinit(hapd);
+
+       if (hapd->interface_added &&
+           hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+               wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
+                          hapd->conf->iface);
+       }
+
+       os_free(hapd->probereq_cb);
+       hapd->probereq_cb = NULL;
+}
+
+
+/**
+ * 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) {
+               hostapd_set_privacy(hapd, 1);
+               return 0;
+       }
+
+       for (i = 0; i < 4; i++) {
+               if (hapd->conf->ssid.wep.key[i] &&
+                   hapd->drv.set_key(iface, hapd, WPA_ALG_WEP, NULL, i,
+                                     i == hapd->conf->ssid.wep.idx, NULL, 0,
+                                     hapd->conf->ssid.wep.key[i],
+                                     hapd->conf->ssid.wep.len[i])) {
+                       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) || hapd->drv_priv == NULL)
+               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");
+
+       /* 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) {
+               u8 addr[ETH_ALEN];
+               os_memset(addr, 0xff, ETH_ALEN);
+               hapd->drv.sta_deauth(hapd, addr,
+                                    WLAN_REASON_PREV_AUTH_NOT_VALID);
+       }
+
+       return ret;
+}
+
+
+/**
+ * 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;
+       int auto_addr = 0;
+
+       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) {
+                       if (j)
+                               auto_addr++;
+                       continue;
+               }
+
+               for (i = 0; i < ETH_ALEN; i++) {
+                       mask[i] |=
+                               iface->conf->bss[j].bssid[i] ^
+                               hapd->own_addr[i];
+               }
+       }
+
+       if (!auto_addr)
+               goto skip_mask_ext;
+
+       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) {
+               wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)",
+                          bits);
+               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;
+
+skip_mask_ext:
+       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;
+       }
+
+       if (!auto_addr)
+               return 0;
+
+       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;
+}
+
+
+
+
+/**
+ * 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;
+       char force_ifname[IFNAMSIZ];
+       u8 if_addr[ETH_ALEN];
+
+       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_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
+                                  hapd->conf->iface, hapd->own_addr, hapd,
+                                  &hapd->drv_priv, force_ifname, if_addr)) {
+                       wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
+                                  MACSTR ")", MAC2STR(hapd->own_addr));
+                       return -1;
+               }
+       }
+
+       hostapd_flush_old_stations(hapd);
+       hostapd_set_privacy(hapd, 0);
+
+       hostapd_broadcast_wep_clear(hapd);
+       if (hostapd_setup_encryption(hapd->conf->iface, hapd))
+               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 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;
+#ifndef CONFIG_NO_RADIUS
+       hapd->radius = radius_client_init(hapd, conf->radius);
+       if (hapd->radius == NULL) {
+               wpa_printf(MSG_ERROR, "RADIUS client initialization failed.");
+               return -1;
+       }
+#endif /* CONFIG_NO_RADIUS */
+
+       if (hostapd_acl_init(hapd)) {
+               wpa_printf(MSG_ERROR, "ACL initialization failed.");
+               return -1;
+       }
+       if (hostapd_init_wps(hapd, conf))
+               return -1;
+
+       if (authsrv_init(hapd) < 0)
+               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 (hapd->iface->ctrl_iface_init &&
+           hapd->iface->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;
+       }
+
+       ieee802_11_set_beacon(hapd);
+
+       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 setup_interface(struct hostapd_iface *iface)
+{
+       struct hostapd_data *hapd = iface->bss[0];
+       size_t i;
+       char country[4];
+
+       /*
+        * Make sure that all BSSes get configured with a pointer to the same
+        * driver interface.
+        */
+       for (i = 1; 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;
+
+       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 (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;
+               }
+               ret = hostapd_check_ht_capab(iface);
+               if (ret < 0)
+                       return -1;
+               if (ret == 1) {
+                       wpa_printf(MSG_DEBUG, "Interface initialization will "
+                                  "be completed in a callback");
+                       return 0;
+               }
+       }
+       return hostapd_setup_interface_complete(iface, 0);
+}
+
+
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
+{
+       struct hostapd_data *hapd = iface->bss[0];
+       size_t j;
+       u8 *prev_addr;
+
+       if (err) {
+               wpa_printf(MSG_ERROR, "Interface initialization failed");
+               eloop_terminate();
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "Completing interface initialization");
+       if (hapd->iconf->channel) {
+               iface->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, iface->freq);
+
+               if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+                                    hapd->iconf->channel,
+                                    hapd->iconf->ieee80211n,
+                                    hapd->iconf->secondary_channel)) {
+                       wpa_printf(MSG_ERROR, "Could not set channel for "
+                                  "kernel driver");
+                       return -1;
+               }
+       }
+
+       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;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
+                  iface->bss[0]->conf->iface);
+
+       return 0;
+}
+
+
+/**
+ * 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.
+ */
+int hostapd_setup_interface(struct hostapd_iface *iface)
+{
+       int ret;
+
+       ret = setup_interface(iface);
+       if (ret) {
+               wpa_printf(MSG_ERROR, "%s: Unable to setup interface.",
+                          iface->bss[0]->conf->iface);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * 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.
+ */
+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;
+
+       hostapd_set_driver_ops(&hapd->drv);
+       hapd->new_assoc_sta_cb = hostapd_new_assoc_sta;
+       hapd->iconf = conf;
+       hapd->conf = bss;
+       hapd->iface = hapd_iface;
+       hapd->driver = hapd->iconf->driver;
+
+       return hapd;
+}
+
+
+void hostapd_interface_deinit(struct hostapd_iface *iface)
+{
+       size_t j;
+
+       if (iface == NULL)
+               return;
+
+       hostapd_cleanup_iface_pre(iface);
+       for (j = 0; j < iface->num_bss; j++) {
+               struct hostapd_data *hapd = iface->bss[j];
+               hostapd_free_stas(hapd);
+               hostapd_flush_old_stations(hapd);
+               hostapd_cleanup(hapd);
+       }
+}
+
+
+void hostapd_interface_free(struct hostapd_iface *iface)
+{
+       size_t j;
+       for (j = 0; j < iface->num_bss; j++)
+               os_free(iface->bss[j]);
+       hostapd_cleanup_iface(iface);
+}
+
+
+/**
+ * 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 from ieee802_11.c for drivers that export MLME to hostapd and
+ * from drv_callbacks.c based on driver events 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) {
+               hapd->drv.sta_deauth(hapd, sta->addr,
+                                    WLAN_REASON_MICHAEL_MIC_FAILURE);
+               return;
+       }
+
+       hostapd_prune_associations(hapd, sta->addr);
+
+       /* IEEE 802.11F (IAPP) */
+       if (hapd->conf->ieee802_11f)
+               iapp_new_station(hapd->iapp, sta);
+
+       /* Start accounting here, if IEEE 802.1X and WPA are not used.
+        * IEEE 802.1X/WPA code will start accounting after the station has
+        * been authorized. */
+       if (!hapd->conf->ieee802_1x && !hapd->conf->wpa)
+               accounting_sta_start(hapd, sta);
+
+       /* Start IEEE 802.1X authentication process for new stations */
+       ieee802_1x_new_station(hapd, sta);
+       if (reassoc) {
+               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);
+}
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
new file mode 100644 (file)
index 0000000..d0d67c8
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * 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.
+ */
+
+#ifndef HOSTAPD_H
+#define HOSTAPD_H
+
+#include "common/defs.h"
+
+struct wpa_driver_ops;
+struct wpa_ctrl_dst;
+struct radius_server_data;
+struct upnp_wps_device_sm;
+struct hapd_interfaces;
+struct hostapd_data;
+struct sta_info;
+struct hostap_sta_driver_data;
+struct ieee80211_ht_capabilities;
+struct full_dynamic_vlan;
+
+struct hostapd_probereq_cb {
+       int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len);
+       void *ctx;
+};
+
+#define HOSTAPD_RATE_BASIC 0x00000001
+
+struct hostapd_rate_data {
+       int rate; /* rate in 100 kbps */
+       int flags; /* HOSTAPD_RATE_ flags */
+};
+
+struct hostapd_frame_info {
+       u32 channel;
+       u32 datarate;
+       u32 ssi_signal;
+};
+
+
+struct hostapd_driver_ops {
+       int (*set_ap_wps_ie)(struct hostapd_data *hapd);
+       int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg,
+                              size_t len);
+       int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr,
+                         const u8 *data, size_t data_len, int encrypt);
+       int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta,
+                             int authorized);
+       int (*set_key)(const char *ifname, struct hostapd_data *hapd,
+                      enum wpa_alg alg, const u8 *addr, int key_idx,
+                      int set_tx, const u8 *seq, size_t seq_len,
+                      const u8 *key, size_t key_len);
+       int (*read_sta_data)(struct hostapd_data *hapd,
+                            struct hostap_sta_driver_data *data,
+                            const u8 *addr);
+       int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr);
+       int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta);
+       int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname,
+                                int enabled);
+       int (*set_radius_acl_auth)(struct hostapd_data *hapd,
+                                  const u8 *mac, int accepted,
+                                  u32 session_timeout);
+       int (*set_radius_acl_expire)(struct hostapd_data *hapd,
+                                    const u8 *mac);
+       int (*set_bss_params)(struct hostapd_data *hapd, int use_protection);
+       int (*set_beacon)(struct hostapd_data *hapd,
+                         const u8 *head, size_t head_len,
+                         const u8 *tail, size_t tail_len, int dtim_period,
+                         int beacon_int);
+       int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname);
+       int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname);
+       int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid,
+                          int val);
+       int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd,
+                           const u8 *addr, int vlan_id);
+       int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr);
+       int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr,
+                         int reason);
+       int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr,
+                           int reason);
+       int (*sta_add)(struct hostapd_data *hapd,
+                      const u8 *addr, u16 aid, u16 capability,
+                      const u8 *supp_rates, size_t supp_rates_len,
+                      u16 listen_interval,
+                      const struct ieee80211_ht_capabilities *ht_capab);
+       int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr);
+       int (*set_countermeasures)(struct hostapd_data *hapd, int enabled);
+};
+
+/**
+ * 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 */
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+       struct sta_info *sta_hash[STA_HASH_SIZE];
+
+       /*
+        * Bitfield for indicating which AIDs are allocated. Only AID values
+        * 1-2007 are used and as such, the bit at index 0 corresponds to AID
+        * 1.
+        */
+#define AID_WORDS ((2008 + 31) / 32)
+       u32 sta_aid[AID_WORDS];
+
+       const struct wpa_driver_ops *driver;
+       void *drv_priv;
+       struct hostapd_driver_ops drv;
+
+       void (*new_assoc_sta_cb)(struct hostapd_data *hapd,
+                                struct sta_info *sta, int reassoc);
+
+       void *msg_ctx; /* ctx for wpa_msg() calls */
+
+       struct radius_client_data *radius;
+       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;
+
+       struct wpabuf *wps_beacon_ie;
+       struct wpabuf *wps_probe_resp_ie;
+#ifdef CONFIG_WPS
+       unsigned int ap_pin_failures;
+       struct upnp_wps_device_sm *wps_upnp;
+       unsigned int ap_pin_lockout_time;
+#endif /* CONFIG_WPS */
+
+       struct hostapd_probereq_cb *probereq_cb;
+       size_t num_probereq_cb;
+
+       void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+                                int freq);
+       void *public_action_cb_ctx;
+
+       void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr,
+                                  const u8 *uuid_e);
+       void *wps_reg_success_cb_ctx;
+};
+
+
+/**
+ * struct hostapd_iface - hostapd per-interface data structure
+ */
+struct hostapd_iface {
+       struct hapd_interfaces *interfaces;
+       void *owner;
+       int (*reload_config)(struct hostapd_iface *iface);
+       struct hostapd_config * (*config_read_cb)(const char *config_fname);
+       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;
+       int freq;
+
+       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;
+
+       u16 ht_op_mode;
+       void (*scan_cb)(struct hostapd_iface *iface);
+
+       int (*ctrl_iface_init)(struct hostapd_data *hapd);
+       void (*ctrl_iface_deinit)(struct hostapd_data *hapd);
+
+       int (*for_each_interface)(struct hapd_interfaces *interfaces,
+                                 int (*cb)(struct hostapd_iface *iface,
+                                           void *ctx), void *ctx);
+};
+
+/* hostapd.c */
+int hostapd_reload_config(struct hostapd_iface *iface);
+struct hostapd_data *
+hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
+                      struct hostapd_config *conf,
+                      struct hostapd_bss_config *bss);
+int hostapd_setup_interface(struct hostapd_iface *iface);
+int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
+void hostapd_interface_deinit(struct hostapd_iface *iface);
+void hostapd_interface_free(struct hostapd_iface *iface);
+void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
+                          int reassoc);
+
+/* utils.c */
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+                                int (*cb)(void *ctx, const u8 *sa,
+                                          const u8 *ie, size_t ie_len),
+                                void *ctx);
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr);
+
+/* drv_callbacks.c (TODO: move to somewhere else?) */
+int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
+                       const u8 *ie, size_t ielen);
+void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr);
+
+#endif /* HOSTAPD_H */
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
new file mode 100644 (file)
index 0000000..0159c72
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * hostapd / Hardware feature query and different modes
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2008-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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ap_drv_ops.h"
+#include "hw_features.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_zalloc(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]))
+                       continue;
+
+               rate = &hapd->iface->current_rates[hapd->iface->num_rates];
+               rate->rate = mode->rates[i];
+               if (hostapd_rate_found(basic_rates, rate->rate)) {
+                       rate->flags |= HOSTAPD_RATE_BASIC;
+                       num_basic_rates++;
+               }
+               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 void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
+{
+       if (iface->conf->secondary_channel > 0) {
+               iface->conf->channel += 4;
+               iface->conf->secondary_channel = -1;
+       } else {
+               iface->conf->channel -= 4;
+               iface->conf->secondary_channel = 1;
+       }
+}
+
+
+static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
+                                       int *pri_chan, int *sec_chan)
+{
+       struct ieee80211_ht_operation *oper;
+       struct ieee802_11_elems elems;
+
+       *pri_chan = *sec_chan = 0;
+
+       ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+       if (elems.ht_operation &&
+           elems.ht_operation_len >= sizeof(*oper)) {
+               oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+               *pri_chan = oper->control_chan;
+               if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
+                       if (oper->ht_param &
+                           HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+                               *sec_chan = *pri_chan + 4;
+                       else if (oper->ht_param &
+                                HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+                               *sec_chan = *pri_chan - 4;
+               }
+       }
+}
+
+
+static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
+                                    struct wpa_scan_results *scan_res)
+{
+       int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
+       int bss_pri_chan, bss_sec_chan;
+       size_t i;
+       int match;
+
+       pri_chan = iface->conf->channel;
+       sec_chan = iface->conf->secondary_channel * 4;
+       pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
+       if (iface->conf->secondary_channel > 0)
+               sec_freq = pri_freq + 20;
+       else
+               sec_freq = pri_freq - 20;
+
+       /*
+        * Switch PRI/SEC channels if Beacons were detected on selected SEC
+        * channel, but not on selected PRI channel.
+        */
+       pri_bss = sec_bss = 0;
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               if (bss->freq == pri_freq)
+                       pri_bss++;
+               else if (bss->freq == sec_freq)
+                       sec_bss++;
+       }
+       if (sec_bss && !pri_bss) {
+               wpa_printf(MSG_INFO, "Switch own primary and secondary "
+                          "channel to get secondary channel with no Beacons "
+                          "from other BSSes");
+               ieee80211n_switch_pri_sec(iface);
+       }
+
+       /*
+        * Match PRI/SEC channel with any existing HT40 BSS on the same
+        * channels that we are about to use (if already mixed order in
+        * existing BSSes, use own preference).
+        */
+       match = 0;
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+               if (pri_chan == bss_pri_chan &&
+                   sec_chan == bss_sec_chan) {
+                       match = 1;
+                       break;
+               }
+       }
+       if (!match) {
+               for (i = 0; i < scan_res->num; i++) {
+                       struct wpa_scan_res *bss = scan_res->res[i];
+                       ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
+                                                   &bss_sec_chan);
+                       if (pri_chan == bss_sec_chan &&
+                           sec_chan == bss_pri_chan) {
+                               wpa_printf(MSG_INFO, "Switch own primary and "
+                                          "secondary channel due to BSS "
+                                          "overlap with " MACSTR,
+                                          MAC2STR(bss->bssid));
+                               ieee80211n_switch_pri_sec(iface);
+                               break;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+
+static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
+                                     struct wpa_scan_results *scan_res)
+{
+       int pri_freq, sec_freq;
+       int affected_start, affected_end;
+       size_t i;
+
+       pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+       if (iface->conf->secondary_channel > 0)
+               sec_freq = pri_freq + 20;
+       else
+               sec_freq = pri_freq - 20;
+       affected_start = (pri_freq + sec_freq) / 2 - 25;
+       affected_end = (pri_freq + sec_freq) / 2 + 25;
+       wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+                  affected_start, affected_end);
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               int pri = bss->freq;
+               int sec = pri;
+               int sec_chan, pri_chan;
+
+               ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+               if (sec_chan) {
+                       if (sec_chan < pri_chan)
+                               sec = pri - 20;
+                       else
+                               sec = pri + 20;
+               }
+
+               if ((pri < affected_start || pri > affected_end) &&
+                   (sec < affected_start || sec > affected_end))
+                       continue; /* not within affected channel range */
+
+               wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+                          " freq=%d pri=%d sec=%d",
+                          MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+               if (sec_chan) {
+                       if (pri_freq != pri || sec_freq != sec) {
+                               wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
+                                          "mismatch with BSS " MACSTR
+                                          " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+                                          MAC2STR(bss->bssid),
+                                          pri, sec, pri_chan,
+                                          sec > pri ? '+' : '-',
+                                          pri_freq, sec_freq);
+                               return 0;
+                       }
+               }
+
+               /* TODO: 40 MHz intolerant */
+       }
+
+       return 1;
+}
+
+
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
+
+
+static void ieee80211n_check_scan(struct hostapd_iface *iface)
+{
+       struct wpa_scan_results *scan_res;
+       int oper40;
+
+       /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is
+        * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */
+
+       iface->scan_cb = NULL;
+
+       scan_res = hostapd_driver_get_scan_results(iface->bss[0]);
+       if (scan_res == NULL) {
+               hostapd_setup_interface_complete(iface, 1);
+               return;
+       }
+
+       if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A)
+               oper40 = ieee80211n_check_40mhz_5g(iface, scan_res);
+       else
+               oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
+       wpa_scan_results_free(scan_res);
+
+       if (!oper40) {
+               wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
+                          "channel pri=%d sec=%d based on overlapping BSSes",
+                          iface->conf->channel,
+                          iface->conf->channel +
+                          iface->conf->secondary_channel * 4);
+               iface->conf->secondary_channel = 0;
+               iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+       }
+
+       hostapd_setup_interface_complete(iface, 0);
+}
+
+
+static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
+{
+       struct wpa_driver_scan_params params;
+
+       if (!iface->conf->secondary_channel)
+               return 0; /* HT40 not used */
+
+       wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
+                  "40 MHz channel");
+       os_memset(&params, 0, sizeof(params));
+       /* TODO: scan only the needed frequency */
+       if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+               wpa_printf(MSG_ERROR, "Failed to request a scan of "
+                          "neighboring BSSes");
+               return -1;
+       }
+
+       iface->scan_cb = ieee80211n_check_scan;
+       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 */
+
+
+int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+#ifdef CONFIG_IEEE80211N
+       int ret;
+       ret = ieee80211n_check_40mhz(iface);
+       if (ret)
+               return ret;
+       if (!ieee80211n_allowed_ht40_channel_pair(iface))
+               return -1;
+       if (!ieee80211n_supported_ht_capab(iface))
+               return -1;
+#endif /* CONFIG_IEEE80211N */
+
+       return 0;
+}
+
+
+/**
+ * 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;
+
+       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 == 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 (iface->conf->channel == 0) {
+               /* TODO: could request a scan of neighboring BSSes and select
+                * the channel automatically */
+               wpa_printf(MSG_ERROR, "Channel not configured "
+                          "(hw_mode/channel in hostapd.conf)");
+               return -1;
+       }
+       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;
+       }
+
+       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;
+       }
+
+       return 0;
+}
+
+
+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/src/ap/hw_features.h b/src/ap/hw_features.h
new file mode 100644 (file)
index 0000000..0295549
--- /dev/null
@@ -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
+
+#ifdef NEED_AP_MLME
+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);
+int hostapd_check_ht_capab(struct hostapd_iface *iface);
+#else /* NEED_AP_MLME */
+static inline void
+hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
+                        size_t num_hw_features)
+{
+}
+
+static inline int hostapd_get_hw_features(struct hostapd_iface *iface)
+{
+       return -1;
+}
+
+static inline int hostapd_select_hw_mode(struct hostapd_iface *iface)
+{
+       return -1;
+}
+
+static inline const char * hostapd_hw_mode_txt(int mode)
+{
+       return NULL;
+}
+
+static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
+{
+       return -1;
+}
+
+static inline int hostapd_check_ht_capab(struct hostapd_iface *iface)
+{
+       return 0;
+}
+
+#endif /* NEED_AP_MLME */
+
+#endif /* HW_FEATURES_H */
diff --git a/src/ap/iapp.c b/src/ap/iapp.c
new file mode 100644 (file)
index 0000000..115d91e
--- /dev/null
@@ -0,0 +1,535 @@
+/*
+ * 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 "utils/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 "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "iapp.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");
+       ap_sta_disconnect(iapp->hapd, sta, NULL, 0);
+}
+
+
+/**
+ * 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);
+}
diff --git a/src/ap/iapp.h b/src/ap/iapp.h
new file mode 100644 (file)
index 0000000..5fc01cb
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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);
+
+#else /* CONFIG_IAPP */
+
+static inline void iapp_new_station(struct iapp_data *iapp,
+                                   struct sta_info *sta)
+{
+}
+
+static inline struct iapp_data * iapp_init(struct hostapd_data *hapd,
+                                          const char *iface)
+{
+       return NULL;
+}
+
+static inline void iapp_deinit(struct iapp_data *iapp)
+{
+}
+
+#endif /* CONFIG_IAPP */
+
+#endif /* IAPP_H */
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
new file mode 100644 (file)
index 0000000..3375aa2
--- /dev/null
@@ -0,0 +1,1755 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * Copyright (c) 2002-2010, 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 "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/crypto.h"
+#include "drivers/driver.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "wps/wps.h"
+#include "hostapd.h"
+#include "beacon.h"
+#include "ieee802_11_auth.h"
+#include "sta_info.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "wmm.h"
+#include "ap_list.h"
+#include "accounting.h"
+#include "ap_config.h"
+#include "ap_mlme.h"
+#include "ieee802_11.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;
+}
+
+
+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';
+}
+
+
+static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
+                          u16 auth_transaction, const 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_skip(key, sizeof(key), 0,
+                                sta->challenge, WLAN_AUTH_CHALLENGE_LEN);
+               }
+               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 (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 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,
+                       const 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;
+       const 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,
+                                              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->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 int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       int i, j = 32, aid;
+
+       /* get a unique AID */
+       if (sta->aid > 0) {
+               wpa_printf(MSG_DEBUG, "  old AID %d", sta->aid);
+               return 0;
+       }
+
+       for (i = 0; i < AID_WORDS; i++) {
+               if (hapd->sta_aid[i] == (u32) -1)
+                       continue;
+               for (j = 0; j < 32; j++) {
+                       if (!(hapd->sta_aid[i] & BIT(j)))
+                               break;
+               }
+               if (j < 32)
+                       break;
+       }
+       if (j == 32)
+               return -1;
+       aid = i * 32 + j + 1;
+       if (aid > 2007)
+               return -1;
+
+       sta->aid = aid;
+       hapd->sta_aid[i] |= BIT(j);
+       wpa_printf(MSG_DEBUG, "  new AID %d", sta->aid);
+       return 0;
+}
+
+
+static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
+                     const u8 *ssid_ie, size_t ssid_ie_len)
+{
+       if (ssid_ie == NULL)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+       if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
+           os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
+               char ssid_txt[33];
+               ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len);
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_INFO,
+                              "Station tried to associate with unknown SSID "
+                              "'%s'", ssid_txt);
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
+                    const u8 *wmm_ie, size_t wmm_ie_len)
+{
+       sta->flags &= ~WLAN_STA_WMM;
+       if (wmm_ie && hapd->conf->wmm_enabled) {
+               if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len))
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_WPA,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "invalid WMM element in association "
+                                      "request");
+               else
+                       sta->flags |= WLAN_STA_WMM;
+       }
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
+                          struct ieee802_11_elems *elems)
+{
+       if (!elems->supp_rates) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "No supported rates element in AssocReq");
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       if (elems->supp_rates_len > sizeof(sta->supported_rates)) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "Invalid supported rates element length %d",
+                              elems->supp_rates_len);
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       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, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Invalid supported rates element length"
+                                      " %d+%d", elems->supp_rates_len,
+                                      elems->ext_supp_rates_len);
+                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+               }
+
+               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;
+       }
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
+                          const u8 *ies, size_t ies_len, int reassoc)
+{
+       struct ieee802_11_elems elems;
+       u16 resp;
+       const u8 *wpa_ie;
+       size_t wpa_ie_len;
+
+       if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_INFO, "Station sent an invalid "
+                              "association request");
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len);
+       if (resp != WLAN_STATUS_SUCCESS)
+               return resp;
+       resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
+       if (resp != WLAN_STATUS_SUCCESS)
+               return resp;
+       resp = copy_supp_rates(hapd, sta, &elems);
+       if (resp != WLAN_STATUS_SUCCESS)
+               return resp;
+#ifdef CONFIG_IEEE80211N
+       resp = copy_sta_ht_capab(sta, elems.ht_capabilities,
+                                elems.ht_capabilities_len);
+       if (resp != WLAN_STATUS_SUCCESS)
+               return resp;
+#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 && 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 = ieee802_11_vendor_ie_concat(ies, ies_len,
+                                                         WPS_IE_VENDOR_TYPE);
+               wpa_ie = NULL;
+               wpa_ie_len = 0;
+       } else if (hapd->conf->wps_state && wpa_ie == NULL) {
+               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) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_INFO,
+                              "No WPA/RSN IE in association request");
+               return WLAN_STATUS_INVALID_IE;
+       }
+
+       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) {
+                       wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+                                  "state machine");
+                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+               }
+               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)
+                       return resp;
+#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);
+
+                       return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
+               }
+
+               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));
+                               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       }
+
+                       resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies,
+                                                      ies_len);
+                       if (resp != WLAN_STATUS_SUCCESS)
+                               return resp;
+               }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211N
+               if ((sta->flags & WLAN_STA_HT) &&
+                   wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_INFO,
+                                      "Station tried to use TKIP with HT "
+                                      "association");
+                       return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
+               }
+#endif /* CONFIG_IEEE80211N */
+       } else
+               wpa_auth_sta_no_wpa(sta->wpa_sm);
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
+                       u16 reason_code)
+{
+       int send_len;
+       struct ieee80211_mgmt reply;
+
+       os_memset(&reply, 0, sizeof(reply));
+       reply.frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH);
+       os_memcpy(reply.da, addr, ETH_ALEN);
+       os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN);
+
+       send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth);
+       reply.u.deauth.reason_code = host_to_le16(reason_code);
+
+       if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0)
+               wpa_printf(MSG_INFO, "Failed to send deauth: %s",
+                          strerror(errno));
+}
+
+
+static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
+                           u16 status_code, int reassoc, const u8 *ies,
+                           size_t ies_len)
+{
+       int send_len;
+       u8 buf[sizeof(struct ieee80211_mgmt) + 1024];
+       struct ieee80211_mgmt *reply;
+       u8 *p;
+
+       os_memset(buf, 0, sizeof(buf));
+       reply = (struct ieee80211_mgmt *) buf;
+       reply->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                            (reassoc ? WLAN_FC_STYPE_REASSOC_RESP :
+                             WLAN_FC_STYPE_ASSOC_RESP));
+       os_memcpy(reply->da, sta->addr, ETH_ALEN);
+       os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN);
+
+       send_len = IEEE80211_HDRLEN;
+       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(status_code);
+       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);
+
+#ifdef CONFIG_IEEE80211R
+       if (status_code == WLAN_STATUS_SUCCESS) {
+               /* IEEE 802.11r: Mobility Domain Information, Fast BSS
+                * Transition Information, RSN, [RIC Response] */
+               p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p,
+                                               buf + sizeof(buf) - p,
+                                               sta->auth_alg, ies, ies_len);
+       }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+       if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
+               p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211N
+       p = hostapd_eid_ht_capabilities(hapd, p);
+       p = hostapd_eid_ht_operation(hapd, p);
+#endif /* CONFIG_IEEE80211N */
+
+       if (sta->flags & WLAN_STA_WMM)
+               p = hostapd_eid_wmm(hapd, p);
+
+#ifdef CONFIG_WPS
+       if (sta->flags & WLAN_STA_WPS) {
+               struct wpabuf *wps = wps_build_assoc_resp_ie();
+               if (wps) {
+                       os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps));
+                       p += wpabuf_len(wps);
+                       wpabuf_free(wps);
+               }
+       }
+#endif /* CONFIG_WPS */
+
+       send_len += p - reply->u.assoc_resp.variable;
+
+       if (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0)
+               wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
+                          strerror(errno));
+}
+
+
+static void handle_assoc(struct hostapd_data *hapd,
+                        const struct ieee80211_mgmt *mgmt, size_t len,
+                        int reassoc)
+{
+       u16 capab_info, listen_interval;
+       u16 resp = WLAN_STATUS_SUCCESS;
+       const u8 *pos;
+       int left, i;
+       struct sta_info *sta;
+
+       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) {
+               hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_INFO, "Station tried to "
+                              "associate before authentication "
+                              "(aid=%d flags=0x%x)",
+                              sta ? sta->aid : -1,
+                              sta ? sta->flags : 0);
+               send_deauth(hapd, mgmt->sa,
+                           WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA);
+               return;
+       }
+
+       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;
+       }
+
+       /* followed by SSID and Supported rates; and HT capabilities if 802.11n
+        * is used */
+       resp = check_assoc_ies(hapd, sta, pos, left, reassoc);
+       if (resp != WLAN_STATUS_SUCCESS)
+               goto fail;
+
+       if (hostapd_get_aid(hapd, sta) < 0) {
+               hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_INFO, "No room for more AIDs");
+               resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+               goto fail;
+       }
+
+       sta->capability = capab_info;
+       sta->listen_interval = listen_interval;
+
+       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
+       update_ht_state(hapd, sta);
+#endif /* CONFIG_IEEE80211N */
+
+       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:
+       send_assoc_resp(hapd, sta, resp, reassoc, pos, left);
+}
+
+
+static void handle_disassoc(struct hostapd_data *hapd,
+                           const 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_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+               MAC2STR(sta->addr));
+       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);
+       hapd->drv.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,
+                         const 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_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
+               MAC2STR(sta->addr));
+       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,
+                         const 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;
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
+                  MACSTR, MAC2STR(addr));
+       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+                   trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+
+       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 (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0)
+               perror("ieee802_11_send_sa_query_req: send");
+}
+
+
+static void hostapd_sa_query_request(struct hostapd_data *hapd,
+                                    const struct ieee80211_mgmt *mgmt)
+{
+       struct sta_info *sta;
+       struct ieee80211_mgmt resp;
+       u8 *end;
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
+                  MACSTR, MAC2STR(mgmt->sa));
+       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+                   mgmt->u.action.u.sa_query_resp.trans_id,
+                   WLAN_SA_QUERY_TR_ID_LEN);
+
+       sta = ap_get_sta(hapd, mgmt->sa);
+       if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request "
+                          "from unassociated STA " MACSTR, MAC2STR(mgmt->sa));
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
+                  MACSTR, MAC2STR(mgmt->sa));
+
+       os_memset(&resp, 0, sizeof(resp));
+       resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_ACTION);
+       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.action.category = WLAN_ACTION_SA_QUERY;
+       resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+       os_memcpy(resp.u.action.u.sa_query_req.trans_id,
+                 mgmt->u.action.u.sa_query_req.trans_id,
+                 WLAN_SA_QUERY_TR_ID_LEN);
+       end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
+       if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0)
+               perror("hostapd_sa_query_request: send");
+}
+
+
+static void hostapd_sa_query_action(struct hostapd_data *hapd,
+                                   const struct ieee80211_mgmt *mgmt,
+                                   size_t len)
+{
+       struct sta_info *sta;
+       const 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_REQUEST) {
+               hostapd_sa_query_request(hapd, mgmt);
+               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;
+       }
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from "
+                  MACSTR, MAC2STR(mgmt->sa));
+       wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
+                   mgmt->u.action.u.sa_query_resp.trans_id,
+                   WLAN_SA_QUERY_TR_ID_LEN);
+
+       /* 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);
+}
+
+
+static int robust_action_frame(u8 category)
+{
+       return category != WLAN_ACTION_PUBLIC &&
+               category != WLAN_ACTION_HT;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+static void handle_action(struct hostapd_data *hapd,
+                         const struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct sta_info *sta;
+
+       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;
+       }
+
+       sta = ap_get_sta(hapd, mgmt->sa);
+#ifdef CONFIG_IEEE80211W
+       if (sta && (sta->flags & WLAN_STA_MFP) &&
+           !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
+             robust_action_frame(mgmt->u.action.category))) {
+               hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "Dropped unprotected Robust Action frame from "
+                              "an MFP STA");
+               return;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R
+       case WLAN_ACTION_FT:
+       {
+               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_wmm_action(hapd, mgmt, len);
+               return;
+#ifdef CONFIG_IEEE80211W
+       case WLAN_ACTION_SA_QUERY:
+               hostapd_sa_query_action(hapd, mgmt, len);
+               return;
+#endif /* CONFIG_IEEE80211W */
+       case WLAN_ACTION_PUBLIC:
+               if (hapd->public_action_cb) {
+                       hapd->public_action_cb(hapd->public_action_cb_ctx,
+                                              (u8 *) mgmt, len,
+                                              hapd->iface->freq);
+                       return;
+               }
+               break;
+       }
+
+       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)) {
+               struct ieee80211_mgmt *resp;
+
+               /*
+                * 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");
+               resp = os_malloc(len);
+               if (resp == NULL)
+                       return;
+               os_memcpy(resp, mgmt, len);
+               os_memcpy(resp->da, resp->sa, ETH_ALEN);
+               os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+               os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+               resp->u.action.category |= 0x80;
+
+               hapd->drv.send_mgmt_frame(hapd, resp, len);
+               os_free(resp);
+       }
+}
+
+
+/**
+ * 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
+ * @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, const u8 *buf, size_t len,
+                    struct hostapd_frame_info *fi)
+{
+       struct ieee80211_mgmt *mgmt;
+       int broadcast;
+       u16 fc, stype;
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       fc = le_to_host16(mgmt->frame_control);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       if (stype == WLAN_FC_STYPE_BEACON) {
+               handle_beacon(hapd, mgmt, len, fi);
+               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,
+                          const 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,
+                           const struct ieee80211_mgmt *mgmt,
+                           size_t len, int reassoc, int ok)
+{
+       u16 status;
+       struct sta_info *sta;
+       int new_assoc = 1;
+       struct ieee80211_ht_capabilities ht_cap;
+
+       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 ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+           sta->auth_alg == WLAN_AUTH_FT) {
+               /*
+                * Open, static WEP, or FT protocol; no separate authorization
+                * step.
+                */
+               sta->flags |= WLAN_STA_AUTHORIZED;
+               wpa_msg(hapd->msg_ctx, MSG_INFO,
+                       AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+       }
+
+       if (reassoc)
+               mlme_reassociate_indication(hapd, sta);
+       else
+               mlme_associate_indication(hapd, sta);
+
+#ifdef CONFIG_IEEE80211W
+       sta->sa_query_timed_out = 0;
+#endif /* CONFIG_IEEE80211W */
+
+       /*
+        * Remove the STA entry in order to make sure the STA PS state gets
+        * cleared and configuration gets updated in case of reassociation back
+        * to the same AP.
+        */
+       hapd->drv.sta_remove(hapd, sta->addr);
+
+#ifdef CONFIG_IEEE80211N
+       if (sta->flags & WLAN_STA_HT)
+               hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap);
+#endif /* CONFIG_IEEE80211N */
+
+       if (hapd->drv.sta_add(hapd, sta->addr, sta->aid, sta->capability,
+                             sta->supported_rates, sta->supported_rates_len,
+                             sta->listen_interval,
+                             sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) {
+               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.
+                */
+               if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
+                       goto fail;
+       } else if (sta->vlan_id) {
+               /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
+               if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
+                       goto fail;
+       }
+
+       hapd->drv.set_sta_flags(hapd, sta);
+
+       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);
+       hapd->new_assoc_sta_cb(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, const u8 *buf, size_t len,
+                       u16 stype, int ok)
+{
+       const struct ieee80211_mgmt *mgmt;
+       mgmt = (const 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;
+       }
+}
+
+
+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;
+}
+
+
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+                      const u8 *buf, size_t len, int ack)
+{
+       struct sta_info *sta;
+       struct hostapd_iface *iface = hapd->iface;
+
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL && iface->num_bss > 1) {
+               size_t j;
+               for (j = 0; j < iface->num_bss; j++) {
+                       hapd = iface->bss[j];
+                       sta = ap_get_sta(hapd, addr);
+                       if (sta)
+                               break;
+               }
+       }
+       if (sta == NULL)
+               return;
+       if (sta->flags & WLAN_STA_PENDING_POLL) {
+               wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending "
+                          "activity poll", MAC2STR(sta->addr),
+                          ack ? "ACKed" : "did not ACK");
+               if (ack)
+                       sta->flags &= ~WLAN_STA_PENDING_POLL;
+       }
+
+       ieee802_1x_tx_status(hapd, sta, buf, len, ack);
+}
+
+
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+                               int wds)
+{
+       struct sta_info *sta;
+
+       sta = ap_get_sta(hapd, src);
+       if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+               if (wds && !(sta->flags & WLAN_STA_WDS)) {
+                       wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
+                                  "STA " MACSTR " (aid %u)",
+                                  MAC2STR(sta->addr), sta->aid);
+                       sta->flags |= WLAN_STA_WDS;
+                       hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1);
+               }
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA "
+                  MACSTR, MAC2STR(src));
+       if (sta && (sta->flags & WLAN_STA_AUTH))
+               hapd->drv.sta_disassoc(
+                       hapd, src,
+                       WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+       else
+               hapd->drv.sta_deauth(
+                       hapd, src,
+                       WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA);
+}
+
+
+#endif /* CONFIG_NATIVE_WINDOWS */
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
new file mode 100644 (file)
index 0000000..cfc069c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * hostapd / IEEE 802.11 Management
+ * 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.
+ */
+
+#ifndef IEEE802_11_H
+#define IEEE802_11_H
+
+struct hostapd_iface;
+struct hostapd_data;
+struct sta_info;
+struct hostapd_frame_info;
+struct ieee80211_ht_capabilities;
+
+void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+                    struct hostapd_frame_info *fi);
+void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
+                       u16 stype, int ok);
+void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len);
+#ifdef NEED_AP_MLME
+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);
+#else /* NEED_AP_MLME */
+static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf,
+                                    size_t buflen)
+{
+       return 0;
+}
+
+static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
+                                        struct sta_info *sta,
+                                        char *buf, size_t buflen)
+{
+       return 0;
+}
+#endif /* NEED_AP_MLME */
+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(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);
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+                         struct ieee80211_ht_capabilities *ht_cap,
+                         struct ieee80211_ht_capabilities *neg_ht_cap);
+u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
+                     size_t ht_capab_len);
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
+                      const u8 *buf, size_t len, int ack);
+void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
+                               int wds);
+
+#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
new file mode 100644 (file)
index 0000000..dec56d1
--- /dev/null
@@ -0,0 +1,524 @@
+/*
+ * hostapd / IEEE 802.11 authentication (ACL)
+ * Copyright (c) 2003-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.
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "ieee802_11_auth.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;
+};
+
+
+#ifndef CONFIG_NO_RADIUS
+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;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
+{
+       if (query == NULL)
+               return;
+       os_free(query->auth_msg);
+       os_free(query);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+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);
+       return -1;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * 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) {
+#ifdef CONFIG_NO_RADIUS
+               return HOSTAPD_ACL_REJECT;
+#else /* CONFIG_NO_RADIUS */
+               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;
+#endif /* CONFIG_NO_RADIUS */
+       }
+
+       return HOSTAPD_ACL_REJECT;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+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
+                       hapd->drv.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;
+       struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+       query = hapd->acl_queries;
+       prev = NULL;
+       while (query) {
+               if (query->radius_id == 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 (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+           hdr->code != RADIUS_CODE_ACCESS_REJECT) {
+               wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
+                          "query", 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 (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
+       hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted,
+                                     cache->session_timeout);
+#else /* CONFIG_DRIVER_RADIUS_ACL */
+#ifdef NEED_AP_MLME
+       /* 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, NULL);
+#endif /* NEED_AP_MLME */
+#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;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+/**
+ * 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)
+{
+#ifndef CONFIG_NO_RADIUS
+       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);
+#endif /* CONFIG_NO_RADIUS */
+
+       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;
+
+#ifndef CONFIG_NO_RADIUS
+       eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL);
+
+       hostapd_acl_cache_free(hapd->acl_cache);
+#endif /* CONFIG_NO_RADIUS */
+
+       query = hapd->acl_queries;
+       while (query) {
+               prev = query;
+               query = query->next;
+               hostapd_acl_query_free(prev);
+       }
+}
diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h
new file mode 100644 (file)
index 0000000..b2971e5
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * 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);
+
+#endif /* IEEE802_11_AUTH_H */
diff --git a/src/ap/ieee802_11_ht.c b/src/ap/ieee802_11_ht.c
new file mode 100644 (file)
index 0000000..7541b83
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * hostapd / IEEE 802.11n HT
+ * Copyright (c) 2002-2009, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "sta_info.h"
+#include "beacon.h"
+#include "ieee802_11.h"
+
+
+u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
+{
+       struct ieee80211_ht_capabilities *cap;
+       u8 *pos = eid;
+
+       if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode)
+               return eid;
+
+       *pos++ = WLAN_EID_HT_CAP;
+       *pos++ = sizeof(*cap);
+
+       cap = (struct ieee80211_ht_capabilities *) pos;
+       os_memset(cap, 0, sizeof(*cap));
+       cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab);
+       cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params;
+       os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set,
+                 16);
+
+       /* TODO: ht_extended_capabilities (now fully disabled) */
+       /* TODO: tx_bf_capability_info (now fully disabled) */
+       /* TODO: asel_capabilities (now fully disabled) */
+
+       pos += sizeof(*cap);
+
+       return pos;
+}
+
+
+u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
+{
+       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;
+}
+
+
+/*
+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;
+}
+
+
+u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab,
+                     size_t ht_capab_len)
+{
+       if (!ht_capab ||
+           ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) {
+               sta->flags &= ~WLAN_STA_HT;
+               os_free(sta->ht_capabilities);
+               sta->ht_capabilities = NULL;
+               return WLAN_STATUS_SUCCESS;
+       }
+
+       if (sta->ht_capabilities == NULL) {
+               sta->ht_capabilities =
+                       os_zalloc(sizeof(struct ieee80211_ht_capabilities));
+               if (sta->ht_capabilities == NULL)
+                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       sta->flags |= WLAN_STA_HT;
+       os_memcpy(sta->ht_capabilities, ht_capab,
+                 sizeof(struct ieee80211_ht_capabilities));
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       u16 ht_capab;
+
+       ht_capab = le_to_host16(sta->ht_capabilities->ht_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);
+       }
+}
+
+
+static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       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);
+       }
+}
+
+
+void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities)
+               update_sta_ht(hapd, sta);
+       else
+               update_sta_no_ht(hapd, sta);
+
+       if (hostapd_ht_operation_update(hapd->iface) > 0)
+               ieee802_11_set_beacons(hapd->iface);
+}
+
+
+void hostapd_get_ht_capab(struct hostapd_data *hapd,
+                         struct ieee80211_ht_capabilities *ht_cap,
+                         struct ieee80211_ht_capabilities *neg_ht_cap)
+{
+       u16 cap;
+
+       if (ht_cap == NULL)
+               return;
+       os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap));
+       cap = le_to_host16(neg_ht_cap->ht_capabilities_info);
+       cap &= hapd->iconf->ht_capab;
+       cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED);
+
+       /*
+        * STBC needs to be handled specially
+        * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+        * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+        */
+       if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK))
+               cap &= ~HT_CAP_INFO_TX_STBC;
+       if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC))
+               cap &= ~HT_CAP_INFO_RX_STBC_MASK;
+
+       neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
+}
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
new file mode 100644 (file)
index 0000000..eb160f8
--- /dev/null
@@ -0,0 +1,2025 @@
+/*
+ * hostapd / IEEE 802.1X-2004 Authenticator
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "pmksa_cache_auth.h"
+#include "ap_config.h"
+#include "ieee802_1x.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 {
+               hapd->drv.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) {
+               if (!(sta->flags & WLAN_STA_AUTHORIZED))
+                       wpa_msg(hapd->msg_ctx, MSG_INFO,
+                               AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr));
+               sta->flags |= WLAN_STA_AUTHORIZED;
+               res = hapd->drv.set_authorized(hapd, sta, 1);
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+                              HOSTAPD_LEVEL_DEBUG, "authorizing port");
+       } else {
+               if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) ==
+                   (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC))
+                       wpa_msg(hapd->msg_ctx, MSG_INFO,
+                               AP_STA_DISCONNECTED MACSTR,
+                               MAC2STR(sta->addr));
+               sta->flags &= ~WLAN_STA_AUTHORIZED;
+               res = hapd->drv.set_authorized(hapd, sta, 0);
+               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_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_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);
+}
+
+
+#ifndef CONFIG_NO_VLAN
+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 (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1,
+                             NULL, 0, key->key[key->idx], key->len[key->idx]))
+               printf("Could not set dynamic VLAN WEP encryption key.\n");
+
+       hapd->drv.set_drv_ieee8021x(hapd, ifname, 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];
+}
+#endif /* CONFIG_NO_VLAN */
+
+
+void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       struct eapol_authenticator *eapol = hapd->eapol_auth;
+       struct eapol_state_machine *sm = sta->eapol_sm;
+#ifndef CONFIG_NO_VLAN
+       struct hostapd_wep_keys *key = NULL;
+       int vlan_id;
+#endif /* CONFIG_NO_VLAN */
+
+       if (sm == NULL || !sm->eap_if->eapKeyData)
+               return;
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR,
+                  MAC2STR(sta->addr));
+
+#ifndef CONFIG_NO_VLAN
+       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
+#endif /* CONFIG_NO_VLAN */
+       if (eapol->default_wep_key) {
+               ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
+                                     eapol->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 (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP,
+                                     sta->addr, 0, 1, NULL, 0, ikey,
+                                     hapd->conf->individual_wep_key_len)) {
+                       wpa_printf(MSG_ERROR, "Could not set individual WEP "
+                                  "encryption.");
+               }
+
+               os_free(ikey);
+       }
+}
+
+
+const char *radius_mode_txt(struct hostapd_data *hapd)
+{
+       switch (hapd->iface->conf->hw_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;
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+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 &&
+           radius_msg_get_hdr(sm->last_recv_radius)->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);
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+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_server_get_name(0, 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;
+       }
+}
+
+
+static struct eapol_state_machine *
+ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       int flags = 0;
+       if (sta->flags & WLAN_STA_PREAUTH)
+               flags |= EAPOL_SM_PREAUTH;
+       if (sta->wpa_sm) {
+               flags |= EAPOL_SM_USES_WPA;
+               if (wpa_auth_sta_get_pmksa(sta->wpa_sm))
+                       flags |= EAPOL_SM_FROM_PMKSA_CACHE;
+       }
+       return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
+                               sta->wps_ie, sta);
+}
+
+
+/**
+ * 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 || !(sta->flags & WLAN_STA_ASSOC)) {
+               wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not "
+                          "associated STA");
+               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 = ieee802_1x_alloc_eapol_sm(hapd, 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 = ieee802_1x_alloc_eapol_sm(hapd, 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_station(struct sta_info *sta)
+{
+       struct eapol_state_machine *sm = sta->eapol_sm;
+
+       if (sm == NULL)
+               return;
+
+       sta->eapol_sm = NULL;
+
+#ifndef CONFIG_NO_RADIUS
+       radius_msg_free(sm->last_recv_radius);
+       radius_free_class(&sm->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+       os_free(sm->identity);
+       eapol_auth_free(sm);
+}
+
+
+#ifndef CONFIG_NO_RADIUS
+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_server_get_name(0, eap_type) :
+                           "??",
+                           eap_type);
+               break;
+       case EAP_CODE_RESPONSE:
+               os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
+                           eap_type >= 0 ? eap_server_get_name(0, 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;
+
+       radius_free_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;
+       struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+       sm = ieee802_1x_search_radius_identifier(hapd, 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 (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 (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+           hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+           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));
+
+       radius_msg_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->acct_interim_interval == 0 &&
+           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 (hdr->code) {
+       case RADIUS_CODE_ACCESS_ACCEPT:
+               if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+                       sta->vlan_id = 0;
+#ifndef CONFIG_NO_VLAN
+               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;
+               }
+#endif /* CONFIG_NO_VLAN */
+
+               if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
+                       break;
+
+               /* 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;
+}
+#endif /* CONFIG_NO_RADIUS */
+
+
+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");
+
+#ifndef CONFIG_NO_RADIUS
+       radius_msg_free(sm->last_recv_radius);
+       sm->last_recv_radius = NULL;
+#endif /* CONFIG_NO_RADIUS */
+
+       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;
+               ap_sta_disconnect(hapd, sta, sta->addr,
+                                 WLAN_REASON_PREV_AUTH_NOT_VALID);
+       }
+}
+
+
+static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
+{
+       struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+       if (hapd->conf->default_wep_key_len < 1)
+               return 0;
+
+       os_free(eapol->default_wep_key);
+       eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len);
+       if (eapol->default_wep_key == NULL ||
+           os_get_random(eapol->default_wep_key,
+                         hapd->conf->default_wep_key_len)) {
+               printf("Could not generate random WEP key.\n");
+               os_free(eapol->default_wep_key);
+               eapol->default_wep_key = NULL;
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key",
+                       eapol->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;
+       struct eapol_authenticator *eapol = hapd->eapol_auth;
+
+       if (eapol->default_wep_key_idx >= 3)
+               eapol->default_wep_key_idx =
+                       hapd->conf->individual_wep_key_len > 0 ? 1 : 0;
+       else
+               eapol->default_wep_key_idx++;
+
+       wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d",
+                  eapol->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(eapol->default_wep_key);
+               eapol->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 (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL,
+                             eapol->default_wep_key_idx, 1, NULL, 0,
+                             eapol->default_wep_key,
+                             hapd->conf->default_wep_key_len)) {
+               hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X,
+                              HOSTAPD_LEVEL_WARNING, "failed to configure a "
+                              "new broadcast key");
+               os_free(eapol->default_wep_key);
+               eapol->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)
+{
+#ifdef CONFIG_WPS
+       struct sta_info *sta = sta_ctx;
+
+       if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) ==
+           WLAN_STA_MAYBE_WPS) {
+               const u8 *identity;
+               size_t identity_len;
+               struct eapol_state_machine *sm = sta->eapol_sm;
+
+               identity = eap_get_identity(sm->eap, &identity_len);
+               if (identity &&
+                   ((identity_len == WSC_ID_ENROLLEE_LEN &&
+                     os_memcmp(identity, WSC_ID_ENROLLEE,
+                               WSC_ID_ENROLLEE_LEN) == 0) ||
+                    (identity_len == WSC_ID_REGISTRAR_LEN &&
+                     os_memcmp(identity, WSC_ID_REGISTRAR,
+                               WSC_ID_REGISTRAR_LEN) == 0))) {
+                       wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> "
+                                  "WLAN_STA_WPS");
+                       sta->flags |= WLAN_STA_WPS;
+               }
+       }
+#endif /* CONFIG_WPS */
+
+       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)
+{
+#ifndef CONFIG_NO_RADIUS
+       struct hostapd_data *hapd = ctx;
+       struct sta_info *sta = sta_ctx;
+
+       ieee802_1x_encapsulate_radius(hapd, sta, data, datalen);
+#endif /* CONFIG_NO_RADIUS */
+}
+
+
+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)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+       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);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+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);
+}
+
+
+static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
+                                  enum eapol_event type)
+{
+       /* struct hostapd_data *hapd = ctx; */
+       struct sta_info *sta = sta_ctx;
+       switch (type) {
+       case EAPOL_AUTH_SM_CHANGE:
+               wpa_auth_sm_notify(sta->wpa_sm);
+               break;
+       case EAPOL_AUTH_REAUTHENTICATE:
+               wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL);
+               break;
+       }
+}
+
+
+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.ctx = 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.msg_ctx = hapd->msg_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;
+       cb.eapol_event = ieee802_1x_eapol_event;
+
+       hapd->eapol_auth = eapol_auth_init(&conf, &cb);
+       if (hapd->eapol_auth == NULL)
+               return -1;
+
+       if ((hapd->conf->ieee802_1x || hapd->conf->wpa) &&
+           hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1))
+               return -1;
+
+#ifndef CONFIG_NO_RADIUS
+       if (radius_client_register(hapd->radius, RADIUS_AUTH,
+                                  ieee802_1x_receive_auth, hapd))
+               return -1;
+#endif /* CONFIG_NO_RADIUS */
+
+       if (hapd->conf->default_wep_key_len) {
+               for (i = 0; i < 4; i++)
+                       hapd->drv.set_key(hapd->conf->iface, hapd,
+                                         WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+                                         NULL, 0);
+
+               ieee802_1x_rekey(hapd, NULL);
+
+               if (hapd->eapol_auth->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))
+               hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
+
+       eapol_auth_deinit(hapd->eapol_auth);
+       hapd->eapol_auth = NULL;
+}
+
+
+int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+                        const 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)");
+       }
+
+#ifdef CONFIG_WPS
+       if (!success && (sta->flags & WLAN_STA_WPS)) {
+               /*
+                * Many devices require deauthentication after WPS provisioning
+                * and some may not be be able to do that themselves, so
+                * disconnect the client here.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Force disconnection after "
+                          "EAP-Failure");
+               /* Add a small sleep to increase likelihood of previously
+                * requested EAP-Failure TX getting out before this should the
+                * driver reorder operations.
+                */
+               os_sleep(0, 10000);
+               ap_sta_disconnect(hapd, sta, sta->addr,
+                                 WLAN_REASON_PREV_AUTH_NOT_VALID);
+       }
+#endif /* CONFIG_WPS */
+}
diff --git a/src/ap/ieee802_1x.h b/src/ap/ieee802_1x.h
new file mode 100644 (file)
index 0000000..1a4d2eb
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * 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;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* 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 */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+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_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
+                        const 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);
+
+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/src/ap/peerkey_auth.c b/src/ap/peerkey_auth.c
new file mode 100644 (file)
index 0000000..f68c479
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * hostapd - PeerKey for Direct Link Setup (DLS)
+ * Copyright (c) 2006-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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wpa_auth.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/src/ap/pmksa_cache_auth.c b/src/ap/pmksa_cache_auth.c
new file mode 100644 (file)
index 0000000..22f44b7
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "pmksa_cache_auth.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;
+};
+
+
+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);
+#ifndef CONFIG_NO_RADIUS
+       radius_free_class(&entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+       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);
+               }
+       }
+
+#ifndef CONFIG_NO_RADIUS
+       radius_copy_class(&entry->radius_class, &eapol->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+
+       entry->eap_type_authsrv = eapol->eap_type_authsrv;
+       entry->vlan_id = ((struct sta_info *) 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);
+       }
+
+#ifndef CONFIG_NO_RADIUS
+       radius_free_class(&eapol->radius_class);
+       radius_copy_class(&eapol->radius_class, &entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+       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;
+       ((struct sta_info *) 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_auth_add - Add a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_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_auth_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_auth_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);
+               }
+       }
+#ifndef CONFIG_NO_RADIUS
+       radius_copy_class(&entry->radius_class, &old_entry->radius_class);
+#endif /* CONFIG_NO_RADIUS */
+       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_auth_deinit - Free all entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
+ */
+void pmksa_cache_auth_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_auth_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_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_auth_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_auth_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_auth_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_auth_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/src/ap/pmksa_cache_auth.h b/src/ap/pmksa_cache_auth.h
new file mode 100644 (file)
index 0000000..9628b13
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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
+
+#include "radius/radius.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_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+                                     void *ctx), void *ctx);
+void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_auth_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_auth_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);
+
+#endif /* PMKSA_CACHE_H */
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
new file mode 100644 (file)
index 0000000..8e13315
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * 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 "utils/includes.h"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.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);
+                       os_free(tmp);
+                       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 = host_to_be16(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/src/ap/preauth_auth.h b/src/ap/preauth_auth.h
new file mode 100644 (file)
index 0000000..5348bee
--- /dev/null
@@ -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/src/ap/sta_info.c b/src/ap/sta_info.c
new file mode 100644 (file)
index 0000000..335c9a5
--- /dev/null
@@ -0,0 +1,751 @@
+/*
+ * hostapd / Station table
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "accounting.h"
+#include "ieee802_1x.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "ap_mlme.h"
+#include "vlan_init.h"
+#include "sta_info.h"
+
+static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+                                      struct sta_info *sta);
+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 (sta->flags & WLAN_STA_WDS)
+               hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0);
+
+       if (!(sta->flags & WLAN_STA_PREAUTH))
+               hapd->drv.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) / 32] &=
+                       ~BIT((sta->aid - 1) % 32);
+
+       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++;
+       }
+
+       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 defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N)
+       if (hostapd_ht_operation_update(hapd->iface) > 0)
+               set_beacon++;
+#endif /* NEED_AP_MLME && 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);
+#ifndef CONFIG_NO_RADIUS
+       radius_client_flush_auth(hapd->radius, sta->addr);
+#endif /* CONFIG_NO_RADIUS */
+
+       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->ht_capabilities);
+
+       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 = hapd->drv.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)) {
+#ifndef CONFIG_NATIVE_WINDOWS
+               /* 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;
+
+               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 (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 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) {
+                       hapd->drv.sta_deauth(hapd, sta->addr,
+                                            WLAN_REASON_PREV_AUTH_NOT_VALID);
+               } else {
+                       hapd->drv.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);
+       hapd->drv.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->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;
+       ap_sta_remove_in_other_bss(hapd, sta);
+
+       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 (hapd->drv.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 void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
+                                      struct sta_info *sta)
+{
+       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)
+                       continue;
+
+               ap_sta_disconnect(bss, sta2, sta2->addr,
+                                 WLAN_REASON_PREV_AUTH_NOT_VALID);
+       }
+}
+
+
+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;
+       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);
+       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)
+{
+#ifndef CONFIG_NO_VLAN
+       const char *iface;
+       struct hostapd_vlan *vlan = NULL;
+       int ret;
+
+       /*
+        * 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");
+
+       ret = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
+       if (ret < 0) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
+                              "entry to vlan_id=%d", sta->vlan_id);
+       }
+       return ret;
+#else /* CONFIG_NO_VLAN */
+       return 0;
+#endif /* CONFIG_NO_VLAN */
+}
+
+
+#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);
+
+#ifdef NEED_AP_MLME
+       ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id);
+#endif /* NEED_AP_MLME */
+}
+
+
+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 */
+
+
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+                      const u8 *addr, u16 reason)
+{
+
+       if (sta == NULL && addr)
+               sta = ap_get_sta(hapd, addr);
+
+       if (addr)
+               hapd->drv.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;
+}
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
new file mode 100644 (file)
index 0000000..55faa5a
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * hostapd / Station table
+ * 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.
+ */
+
+#ifndef STA_INFO_H
+#define STA_INFO_H
+
+/* STA flags */
+#define WLAN_STA_AUTH BIT(0)
+#define WLAN_STA_ASSOC BIT(1)
+#define WLAN_STA_PS BIT(2)
+#define WLAN_STA_TIM BIT(3)
+#define WLAN_STA_PERM BIT(4)
+#define WLAN_STA_AUTHORIZED BIT(5)
+#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
+#define WLAN_STA_SHORT_PREAMBLE BIT(7)
+#define WLAN_STA_PREAUTH BIT(8)
+#define WLAN_STA_WMM 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_WDS BIT(14)
+#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; /* Bitfield of WLAN_STA_* */
+       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;
+
+       struct ieee80211_ht_capabilities *ht_capabilities;
+
+#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 */
+};
+
+
+/* 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)
+
+
+struct hostapd_data;
+
+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);
+void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
+                      const u8 *addr, u16 reason);
+
+#endif /* STA_INFO_H */
diff --git a/src/ap/tkip_countermeasures.c b/src/ap/tkip_countermeasures.c
new file mode 100644 (file)
index 0000000..9690348
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * hostapd / TKIP countermeasures
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_mlme.h"
+#include "wpa_auth.h"
+#include "tkip_countermeasures.h"
+
+
+static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx,
+                                               void *timeout_ctx)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       hapd->tkip_countermeasures = 0;
+       hapd->drv.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;
+       hapd->drv.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) {
+               hapd->drv.sta_deauth(hapd, sta->addr,
+                                    WLAN_REASON_MICHAEL_MIC_FAILURE);
+               sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
+                               WLAN_STA_AUTHORIZED);
+               hapd->drv.sta_remove(hapd, sta->addr);
+       }
+}
+
+
+void 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;
+}
diff --git a/src/ap/tkip_countermeasures.h b/src/ap/tkip_countermeasures.h
new file mode 100644 (file)
index 0000000..5a1afce
--- /dev/null
@@ -0,0 +1,20 @@
+/*
+ * hostapd / TKIP countermeasures
+ * 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.
+ */
+
+#ifndef TKIP_COUNTERMEASURES_H
+#define TKIP_COUNTERMEASURES_H
+
+void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local);
+
+#endif /* TKIP_COUNTERMEASURES_H */
diff --git a/src/ap/utils.c b/src/ap/utils.c
new file mode 100644 (file)
index 0000000..0ff48ae
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * AP mode helper functions
+ * Copyright (c) 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 "common.h"
+#include "common/ieee802_11_defs.h"
+#include "sta_info.h"
+#include "hostapd.h"
+
+
+int hostapd_register_probereq_cb(struct hostapd_data *hapd,
+                                int (*cb)(void *ctx, const u8 *sa,
+                                          const u8 *ie, size_t ie_len),
+                                void *ctx)
+{
+       struct hostapd_probereq_cb *n;
+
+       n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) *
+                      sizeof(struct hostapd_probereq_cb));
+       if (n == NULL)
+               return -1;
+
+       hapd->probereq_cb = n;
+       n = &hapd->probereq_cb[hapd->num_probereq_cb];
+       hapd->num_probereq_cb++;
+
+       n->cb = cb;
+       n->ctx = ctx;
+
+       return 0;
+}
+
+
+struct prune_data {
+       struct hostapd_data *hapd;
+       const u8 *addr;
+};
+
+static int prune_associations(struct hostapd_iface *iface, void *ctx)
+{
+       struct prune_data *data = ctx;
+       struct sta_info *osta;
+       struct hostapd_data *ohapd;
+       size_t j;
+
+       for (j = 0; j < iface->num_bss; j++) {
+               ohapd = iface->bss[j];
+               if (ohapd == data->hapd)
+                       continue;
+               osta = ap_get_sta(ohapd, data->addr);
+               if (!osta)
+                       continue;
+
+               ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED);
+       }
+
+       return 0;
+}
+
+/**
+ * hostapd_prune_associations - Remove extraneous associations
+ * @hapd: Pointer to BSS data for the most recent association
+ * @addr: Associated STA address
+ *
+ * This function looks through all radios and BSS's for previous
+ * (stale) associations of STA. If any are found they are removed.
+ */
+void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr)
+{
+       struct prune_data data;
+       data.hapd = hapd;
+       data.addr = addr;
+       if (hapd->iface->for_each_interface)
+               hapd->iface->for_each_interface(hapd->iface->interfaces,
+                                               prune_associations, &data);
+}
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
new file mode 100644 (file)
index 0000000..c9d166a
--- /dev/null
@@ -0,0 +1,903 @@
+/*
+ * hostapd / VLAN initialization
+ * Copyright 2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "ap_config.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 "drivers/priv_netlink.h"
+#include "utils/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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ);
+
+       if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed "
+                          "for interface %s: %s",
+                          __func__, if_name, strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       if (up)
+               ifr.ifr_flags |= IFF_UP;
+       else
+               ifr.ifr_flags &= ~IFF_UP;
+
+       if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed "
+                          "for interface %s (up=%d): %s",
+                          __func__, if_name, up, strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       close(fd);
+       return 0;
+}
+
+
+static int ifconfig_up(const char *if_name)
+{
+       wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name);
+       return ifconfig_helper(if_name, 1);
+}
+
+
+static int ifconfig_down(const char *if_name)
+{
+       wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", 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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name);
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               return -1;
+       }
+
+       if_index = if_nametoindex(if_name);
+
+       if (if_index == 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+                          "interface index for '%s'",
+                          __func__, 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. */
+               wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+                          "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: "
+                          "%s", __func__, br_name, if_name, strerror(errno));
+               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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name);
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               return -1;
+       }
+
+       if_index = if_nametoindex(if_name);
+
+       if (if_index == 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining "
+                          "interface index for '%s'",
+                          __func__, 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;
+               }
+
+               wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE,"
+                          "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: "
+                          "%s", __func__, br_name, if_name, strerror(errno));
+               close(fd);
+               return -1;
+       }
+
+       close(fd);
+       return 0;
+}
+
+
+static int br_delbr(const char *br_name)
+{
+       int fd;
+       unsigned long arg[2];
+
+       wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name);
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               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. */
+               wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for "
+                          "%s: %s", __func__, br_name, strerror(errno));
+               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[4];
+       struct ifreq ifr;
+
+       wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name);
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               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 {
+                       wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE "
+                                  "failed for %s: %s",
+                                  __func__, br_name, strerror(errno));
+                       close(fd);
+                       return -1;
+               }
+       }
+
+       /* Decrease forwarding delay to avoid EAPOL timeouts. */
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
+       arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY;
+       arg[1] = 1;
+       arg[2] = 0;
+       arg[3] = 0;
+       ifr.ifr_data = (char *) &arg;
+       if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: "
+                          "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for "
+                          "%s: %s", __func__, br_name, strerror(errno));
+               /* Continue anyway */
+       }
+
+       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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST "
+                          "failed for %s: %s",
+                          __func__, br_name, strerror(errno));
+               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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name);
+       if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+               wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+                          if_name);
+               return -1;
+       }
+
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: "
+                          "%s", __func__, if_name, strerror(errno));
+               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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)",
+                  if_name, vid);
+       ifconfig_up(if_name);
+
+       if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) {
+               wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'",
+                          if_name);
+               return -1;
+       }
+
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               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);
+                               wpa_printf(MSG_DEBUG, "VLAN: vlan_add: "
+                                          "if_name %s exists already",
+                                          if_request.device1);
+                               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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: "
+                          "%s",
+                          __func__, if_request.device1, strerror(errno));
+               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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)",
+                  name_type);
+       if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) "
+                          "failed: %s", __func__, strerror(errno));
+               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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD "
+                          "name_type=%u failed: %s",
+                          __func__, name_type, strerror(errno));
+               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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
+
+       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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname);
+
+       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 (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
+                               br_delif(br_name, vlan->ifname);
+
+                       if (tagged_interface) {
+                               os_snprintf(vlan_ifname, sizeof(vlan_ifname),
+                                           "vlan%d", vlan->vlan_id);
+                               if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
+                                       br_delif(br_name, vlan_ifname);
+                               ifconfig_down(vlan_ifname);
+
+                               if (vlan->clean & DVLAN_CLEAN_VLAN)
+                                       vlan_rem(vlan_ifname);
+                       }
+
+                       if ((vlan->clean & DVLAN_CLEAN_BR) &&
+                           br_getnumports(br_name) == 0) {
+                               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)
+                       wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s",
+                                  __func__, strerror(errno));
+               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) {
+                       wpa_printf(MSG_DEBUG, "VLAN: 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) {
+               wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of "
+                          "netlink message", __func__, 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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW,"
+                          "NETLINK_ROUTE) failed: %s",
+                          __func__, strerror(errno));
+               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) {
+               wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s",
+                          __func__, strerror(errno));
+               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] &&
+                   hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i,
+                                     i == mssid->wep.idx, NULL, 0,
+                                     mssid->wep.key[i], mssid->wep.len[i])) {
+                       wpa_printf(MSG_ERROR, "VLAN: Could not set WEP "
+                                  "encryption for dynamic VLAN");
+                       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) {
+                       if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) {
+                               if (errno != EEXIST) {
+                                       wpa_printf(MSG_ERROR, "VLAN: Could "
+                                                  "not add VLAN %s: %s",
+                                                  vlan->ifname,
+                                                  strerror(errno));
+                                       return -1;
+                               }
+                       }
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+                       ifconfig_up(vlan->ifname);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+               }
+
+               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 &&
+                   hapd->drv.vlan_if_remove(hapd, vlan->ifname)) {
+                       wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN "
+                                  "iface: %s: %s",
+                                  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)
+{
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+       hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+       if (vlan_dynamic_add(hapd, hapd->conf->vlan))
+               return -1;
+
+        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 */
+}
+
+
+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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)",
+                  __func__, vlan_id, vlan->ifname);
+       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 (hapd->drv.vlan_if_add(hapd, n->ifname)) {
+               os_free(n);
+               return NULL;
+       }
+
+       n->next = hapd->conf->vlan;
+       hapd->conf->vlan = n;
+
+#ifdef CONFIG_FULL_DYNAMIC_VLAN
+       ifconfig_up(n->ifname);
+#endif /* CONFIG_FULL_DYNAMIC_VLAN */
+
+       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;
+
+       wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id);
+
+       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)
+               hapd->drv.vlan_if_remove(hapd, vlan->ifname);
+
+       return 0;
+}
diff --git a/src/ap/vlan_init.h b/src/ap/vlan_init.h
new file mode 100644 (file)
index 0000000..382d5de
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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
+
+#ifndef CONFIG_NO_VLAN
+int vlan_init(struct hostapd_data *hapd);
+void vlan_deinit(struct hostapd_data *hapd);
+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);
+#else /* CONFIG_NO_VLAN */
+static inline int vlan_init(struct hostapd_data *hapd)
+{
+       return 0;
+}
+
+static inline void vlan_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
+                                                    struct hostapd_vlan *vlan,
+                                                    int vlan_id)
+{
+       return NULL;
+}
+
+static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id)
+{
+       return -1;
+}
+
+static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd,
+                                           struct hostapd_ssid *mssid,
+                                           const char *dyn_vlan)
+{
+       return -1;
+}
+#endif /* CONFIG_NO_VLAN */
+
+#endif /* VLAN_INIT_H */
diff --git a/src/ap/wmm.c b/src/ap/wmm.c
new file mode 100644 (file)
index 0000000..3668130
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * hostapd / WMM (Wi-Fi Multimedia)
+ * Copyright 2002-2003, Instant802 Networks, Inc.
+ * Copyright 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "hostapd.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "ap_config.h"
+#include "wmm.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 WMM stations are receiving a certain group */
+
+
+static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci)
+{
+       u8 ret;
+       ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK;
+       if (acm)
+               ret |= WMM_AC_ACM;
+       ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK;
+       return ret;
+}
+
+
+static inline u8 wmm_ecw(int ecwmin, int ecwmax)
+{
+       return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) |
+               ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK);
+}
+
+
+/*
+ * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association
+ * Response frames.
+ */
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+       struct wmm_parameter_element *wmm =
+               (struct wmm_parameter_element *) (pos + 2);
+       int e;
+
+       if (!hapd->conf->wmm_enabled)
+               return eid;
+       eid[0] = WLAN_EID_VENDOR_SPECIFIC;
+       wmm->oui[0] = 0x00;
+       wmm->oui[1] = 0x50;
+       wmm->oui[2] = 0xf2;
+       wmm->oui_type = WMM_OUI_TYPE;
+       wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT;
+       wmm->version = WMM_VERSION;
+       wmm->qos_info = hapd->parameter_set_count & 0xf;
+
+       if (hapd->conf->wmm_uapsd)
+               wmm->qos_info |= 0x80;
+
+       /* fill in a parameter set record for each AC */
+       for (e = 0; e < 4; e++) {
+               struct wmm_ac_parameter *ac = &wmm->ac[e];
+               struct hostapd_wmm_ac_params *acp =
+                       &hapd->iconf->wmm_ac_params[e];
+
+               ac->aci_aifsn = wmm_aci_aifsn(acp->aifs,
+                                             acp->admission_control_mandatory,
+                                             e);
+               ac->cw = wmm_ecw(acp->cwmin, acp->cwmax);
+               ac->txop_limit = host_to_le16(acp->txop_limit);
+       }
+
+       pos = (u8 *) (wmm + 1);
+       eid[1] = pos - eid - 2; /* element length */
+
+       return pos;
+}
+
+
+/* This function is called when a station sends an association request with
+ * WMM info element. The function returns zero on success or non-zero on any
+ * error in WMM element. eid does not include Element ID and Length octets. */
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len)
+{
+       struct wmm_information_element *wmm;
+
+       wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len);
+
+       if (len < sizeof(struct wmm_information_element)) {
+               wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)",
+                          (unsigned long) len);
+               return -1;
+       }
+
+       wmm = (struct wmm_information_element *) eid;
+       wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x  "
+                  "OUI type %d  OUI sub-type %d  version %d  QoS info 0x%x",
+                  wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type,
+                  wmm->oui_subtype, wmm->version, wmm->qos_info);
+       if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT ||
+           wmm->version != WMM_VERSION) {
+               wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
+                           const struct wmm_tspec_element *tspec,
+                           u8 action_code, u8 dialogue_token, u8 status_code)
+{
+       u8 buf[256];
+       struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf;
+       struct wmm_tspec_element *t = (struct wmm_tspec_element *)
+               m->u.action.u.wmm_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.wmm_action.action_code = action_code;
+       m->u.action.u.wmm_action.dialog_token = dialogue_token;
+       m->u.action.u.wmm_action.status_code = status_code;
+       os_memcpy(t, tspec, sizeof(struct wmm_tspec_element));
+       len = ((u8 *) (t + 1)) - buf;
+
+       if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0)
+               perror("wmm_send_action: send");
+}
+
+
+int wmm_process_tspec(struct wmm_tspec_element *tspec)
+{
+       int medium_time, pps, duration;
+       int up, psb, dir, tid;
+       u16 val, surplus;
+
+       up = (tspec->ts_info[1] >> 3) & 0x07;
+       psb = (tspec->ts_info[1] >> 2) & 0x01;
+       dir = (tspec->ts_info[0] >> 5) & 0x03;
+       tid = (tspec->ts_info[0] >> 1) & 0x0f;
+       wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
+                  up, psb, dir, tid);
+       val = le_to_host16(tspec->nominal_msdu_size);
+       wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
+                  val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
+       wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
+                  le_to_host32(tspec->mean_data_rate));
+       wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
+                  le_to_host32(tspec->minimum_phy_rate));
+       val = le_to_host16(tspec->surplus_bandwidth_allowance);
+       wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
+                  val >> 13, 10000 * (val & 0x1fff) / 0x2000);
+
+       val = le_to_host16(tspec->nominal_msdu_size);
+       if (val == 0) {
+               wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)");
+               return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+       }
+       /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */
+       pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val;
+       wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d",
+                  pps);
+
+       if (le_to_host32(tspec->minimum_phy_rate) < 1000000) {
+               wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate");
+               return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+       }
+
+       duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 /
+               (le_to_host32(tspec->minimum_phy_rate) / 1000000) +
+               50 /* FIX: proper SIFS + ACK duration */;
+
+       /* unsigned binary number with an implicit binary point after the
+        * leftmost 3 bits, i.e., 0x2000 = 1.0 */
+       surplus = le_to_host16(tspec->surplus_bandwidth_allowance);
+       if (surplus <= 0x2000) {
+               wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not "
+                          "greater than unity");
+               return WMM_ADDTS_STATUS_INVALID_PARAMETERS;
+       }
+
+       medium_time = surplus * pps * duration / 0x2000;
+       wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time);
+
+       /*
+        * TODO: store list of granted (and still active) TSPECs and check
+        * whether there is available medium time for this request. For now,
+        * just refuse requests that would by themselves take very large
+        * portion of the available bandwidth.
+        */
+       if (medium_time > 750000) {
+               wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over "
+                          "75%% of available bandwidth");
+               return WMM_ADDTS_STATUS_REFUSED;
+       }
+
+       /* Convert to 32 microseconds per second unit */
+       tspec->medium_time = host_to_le16(medium_time / 32);
+
+       return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED;
+}
+
+
+static void wmm_addts_req(struct hostapd_data *hapd,
+                         const struct ieee80211_mgmt *mgmt,
+                         struct wmm_tspec_element *tspec, size_t len)
+{
+       const u8 *end = ((const u8 *) mgmt) + len;
+       int res;
+
+       if ((const u8 *) (tspec + 1) > end) {
+               wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC "
+                  "from " MACSTR,
+                  mgmt->u.action.u.wmm_action.dialog_token,
+                  MAC2STR(mgmt->sa));
+
+       res = wmm_process_tspec(tspec);
+       wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res);
+
+       wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP,
+                       mgmt->u.action.u.wmm_action.dialog_token, res);
+}
+
+
+void hostapd_wmm_action(struct hostapd_data *hapd,
+                       const struct ieee80211_mgmt *mgmt, size_t len)
+{
+       int action_code;
+       int left = len - IEEE80211_HDRLEN - 4;
+       const u8 *pos = ((const 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_WMM)) !=
+           (WLAN_STA_ASSOC | WLAN_STA_WMM)) {
+               hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "wmm action received is not from associated wmm"
+                              " 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_wmm_action - could not parse wmm "
+                              "action");
+               /* TODO: respond with action frame invalid parameters status
+                * code */
+               return;
+       }
+
+       if (!elems.wmm_tspec ||
+           elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) {
+               hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "hostapd_wmm_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.wmm_action.action_code;
+       switch (action_code) {
+       case WMM_ACTION_CODE_ADDTS_REQ:
+               wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *)
+                             (elems.wmm_tspec - 2), len);
+               return;
+#if 0
+       /* TODO: needed for client implementation */
+       case WMM_ACTION_CODE_ADDTS_RESP:
+               wmm_setup_request(hapd, mgmt, len);
+               return;
+       /* TODO: handle station teardown requests */
+       case WMM_ACTION_CODE_DELTS:
+               wmm_teardown(hapd, mgmt, len);
+               return;
+#endif
+       }
+
+       hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_DEBUG,
+                      "hostapd_wmm_action - unknown action code %d",
+                      action_code);
+}
diff --git a/src/ap/wmm.h b/src/ap/wmm.h
new file mode 100644 (file)
index 0000000..96b04e8
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * 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
+
+struct ieee80211_mgmt;
+struct wmm_tspec_element;
+
+u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid);
+int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid,
+                         size_t len);
+void hostapd_wmm_action(struct hostapd_data *hapd,
+                       const struct ieee80211_mgmt *mgmt, size_t len);
+int wmm_process_tspec(struct wmm_tspec_element *tspec);
+
+#endif /* WME_H */
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
new file mode 100644 (file)
index 0000000..36cb0f4
--- /dev/null
@@ -0,0 +1,2651 @@
+/*
+ * hostapd - IEEE 802.11i-2004 / WPA Authenticator
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/state_machine.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.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 int 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 int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
+                         struct wpa_group *group);
+
+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,
+                                  enum wpa_alg 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_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 void wpa_group_set_key_len(struct wpa_group *group, int cipher)
+{
+       switch (cipher) {
+       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;
+       }
+}
+
+
+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;
+
+       wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+
+       /* 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_auth_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_auth_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_auth_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)
+{
+       struct wpa_group *group;
+       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;
+       }
+
+       /*
+        * Reinitialize GTK to make sure it is suitable for the new
+        * configuration.
+        */
+       group = wpa_auth->group;
+       wpa_group_set_key_len(group, wpa_auth->conf.wpa_group);
+       group->GInit = TRUE;
+       wpa_group_sm_step(wpa_auth, group);
+       group->GInit = FALSE;
+       wpa_group_sm_step(wpa_auth, group);
+
+       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;
+}
+
+
+int 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 -1;
+
+#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 0;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       if (sm->started) {
+               os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
+               sm->ReAuthenticationRequest = TRUE;
+               return wpa_sm_step(sm);
+       }
+
+       wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
+                       "start authentication");
+       sm->started = 1;
+
+       sm->Init = TRUE;
+       if (wpa_sm_step(sm) == 1)
+               return 1; /* should not really happen */
+       sm->Init = FALSE;
+       sm->AuthenticationRequest = TRUE;
+       return 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)
+{
+#ifdef CONFIG_IEEE80211R
+       os_free(sm->assoc_resp_ftie);
+#endif /* CONFIG_IEEE80211R */
+       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;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth,
+                              struct wpa_state_machine *sm,
+                              struct wpa_eapol_ie_parse *kde)
+{
+       struct wpa_ie_data ie;
+       struct rsn_mdie *mdie;
+
+       if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 ||
+           ie.num_pmkid != 1 || ie.pmkid == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+                          "FT 4-way handshake message 2/4");
+               return -1;
+       }
+
+       os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant",
+                   sm->sup_pmk_r1_name, PMKID_LEN);
+
+       if (!kde->mdie || !kde->ftie) {
+               wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake "
+                          "message 2/4", kde->mdie ? "FTIE" : "MDIE");
+               return -1;
+       }
+
+       mdie = (struct rsn_mdie *) (kde->mdie + 2);
+       if (kde->mdie[1] < sizeof(struct rsn_mdie) ||
+           os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain,
+                     MOBILITY_DOMAIN_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+               return -1;
+       }
+
+       if (sm->assoc_resp_ftie &&
+           (kde->ftie[1] != sm->assoc_resp_ftie[1] ||
+            os_memcmp(kde->ftie, sm->assoc_resp_ftie,
+                      2 + sm->assoc_resp_ftie[1]) != 0)) {
+               wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+               wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4",
+                           kde->ftie, kde->ftie_len);
+               wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp",
+                           sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]);
+               return -1;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+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;
+       int ft;
+       const u8 *eapol_key_ie;
+       size_t eapol_key_ie_len;
+
+       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;
+       }
+
+       if (sm->wpa == WPA_VERSION_WPA2) {
+               if (key->type != EAPOL_KEY_TYPE_RSN) {
+                       wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+                                  "unexpected type %d in RSN mode",
+                                  key->type);
+                       return;
+               }
+       } else {
+               if (key->type != EAPOL_KEY_TYPE_WPA) {
+                       wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with "
+                                  "unexpected type %d in WPA mode",
+                                  key->type);
+                       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 (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
+                                     &kde) < 0) {
+                       wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+                                        "received EAPOL-Key msg 2/4 with "
+                                        "invalid Key Data contents");
+                       return;
+               }
+               if (kde.rsn_ie) {
+                       eapol_key_ie = kde.rsn_ie;
+                       eapol_key_ie_len = kde.rsn_ie_len;
+               } else {
+                       eapol_key_ie = kde.wpa_ie;
+                       eapol_key_ie_len = kde.wpa_ie_len;
+               }
+               ft = sm->wpa == WPA_VERSION_WPA2 &&
+                       wpa_key_mgmt_ft(sm->wpa_key_mgmt);
+               if (sm->wpa_ie == NULL ||
+                   wpa_compare_rsn_ie(ft,
+                                      sm->wpa_ie, sm->wpa_ie_len,
+                                      eapol_key_ie, eapol_key_ie_len)) {
+                       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",
+                                   eapol_key_ie, eapol_key_ie_len);
+                       /* MLME-DEAUTHENTICATE.request */
+                       wpa_sta_disconnect(wpa_auth, sm->addr);
+                       return;
+               }
+#ifdef CONFIG_IEEE80211R
+               if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
+                       wpa_sta_disconnect(wpa_auth, sm->addr);
+                       return;
+               }
+#endif /* CONFIG_IEEE80211R */
+               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, WPA_ALG_NONE, sm->addr, 0, (u8 *) "",
+                        0);
+       sm->pairwise_set = FALSE;
+       eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+}
+
+
+int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
+{
+       int remove_ptk = 1;
+
+       if (sm == NULL)
+               return -1;
+
+       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->started) {
+                       /*
+                        * When using WPS, we may end up here if the STA
+                        * manages to re-associate without the previous STA
+                        * entry getting removed. Consequently, we need to make
+                        * sure that the WPA state machines gets initialized
+                        * properly at this point.
+                        */
+                       wpa_printf(MSG_DEBUG, "WPA state machine had not been "
+                                  "started - initialize now");
+                       sm->started = 1;
+                       sm->Init = TRUE;
+                       if (wpa_sm_step(sm) == 1)
+                               return 1; /* should not really happen */
+                       sm->Init = FALSE;
+                       sm->AuthenticationRequest = TRUE;
+                       break;
+               }
+               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
+               wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration "
+                          "after association");
+               wpa_ft_install_ptk(sm);
+
+               /* Using FT protocol, not WPA auth state machine */
+               sm->ft_completed = 1;
+               return 0;
+#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);
+       }
+
+       return wpa_sm_step(sm);
+}
+
+
+static enum wpa_alg wpa_alg_enum(int alg)
+{
+       switch (alg) {
+       case WPA_CIPHER_CCMP:
+               return WPA_ALG_CCMP;
+       case WPA_CIPHER_TKIP:
+               return WPA_ALG_TKIP;
+       case WPA_CIPHER_WEP104:
+       case WPA_CIPHER_WEP40:
+               return WPA_ALG_WEP;
+       default:
+               return WPA_ALG_NONE;
+       }
+}
+
+
+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)
+{
+       size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+               return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+#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, ptk_len,
+                      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;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               /*
+                * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
+                * with the value we derived.
+                */
+               if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+                             WPA_PMK_NAME_LEN) != 0) {
+                       wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+                                       "PMKR1Name mismatch in FT 4-way "
+                                       "handshake");
+                       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from "
+                                   "Supplicant",
+                                   sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN);
+                       wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+                                   sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+                       return;
+               }
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       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 (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
+           wpa_auth_get_seqnum(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, [MDIE],
+          GTK[GN], IGTK, [FTIE], [TIE * 2])
+        */
+       os_memset(rsc, 0, WPA_KEY_RSC_LEN);
+       wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc);
+       /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */
+       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;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */
+               kde_len += 300; /* FTIE + 2 * TIE */
+       }
+#endif /* CONFIG_IEEE80211R */
+       kde = os_malloc(kde_len);
+       if (kde == NULL)
+               return;
+
+       pos = kde;
+       os_memcpy(pos, wpa_ie, wpa_ie_len);
+       pos += wpa_ie_len;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name);
+               if (res < 0) {
+                       wpa_printf(MSG_ERROR, "FT: Failed to insert "
+                                  "PMKR1Name into RSN IE in EAPOL-Key data");
+                       os_free(kde);
+                       return;
+               }
+               pos += res;
+       }
+#endif /* CONFIG_IEEE80211R */
+       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);
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+               int res;
+               struct wpa_auth_config *conf;
+
+               conf = &sm->wpa_auth->conf;
+               res = wpa_write_ftie(conf, conf->r0_key_holder,
+                                    conf->r0_key_holder_len,
+                                    NULL, NULL, pos, kde + kde_len - pos,
+                                    NULL, 0);
+               if (res < 0) {
+                       wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE "
+                                  "into EAPOL-Key Key Data");
+                       os_free(kde);
+                       return;
+               }
+               pos += res;
+
+               /* TIE[ReassociationDeadline] (TU) */
+               *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+               *pos++ = 5;
+               *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE;
+               WPA_PUT_LE32(pos, conf->reassociation_deadline);
+               pos += 4;
+
+               /* TIE[KeyLifetime] (seconds) */
+               *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
+               *pos++ = 5;
+               *pos++ = WLAN_TIMEOUT_KEY_LIFETIME;
+               WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60);
+               pos += 4;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       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) {
+               enum wpa_alg alg;
+               int klen;
+               if (sm->pairwise == WPA_CIPHER_TKIP) {
+                       alg = WPA_ALG_TKIP;
+                       klen = 32;
+               } else {
+                       alg = WPA_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 != NO_MGMT_FRAME_PROTECTION) {
+               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_enum(wpa_auth->conf.wpa_group),
+                        NULL, group->GN, group->GTK[group->GN - 1],
+                        group->GTK_len);
+
+#ifdef CONFIG_IEEE80211W
+       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_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 int wpa_sm_step(struct wpa_state_machine *sm)
+{
+       if (sm == NULL)
+               return 0;
+
+       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 0;
+       }
+
+       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);
+               return 1;
+       }
+       return 0;
+}
+
+
+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_auth_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_auth_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;
+}
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
new file mode 100644 (file)
index 0000000..d0136c7
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * 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 "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/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
+
+#define FT_R0KH_R1KH_PULL_DATA_LEN 44
+#define FT_R0KH_R1KH_RESP_DATA_LEN 76
+#define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+
+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];
+       le16 pairwise;
+       u8 pad[2]; /* 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 (88) */
+       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];
+       le16 pairwise;
+       u8 pad[6]; /* 8-octet boundary for AES key wrap */
+       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 wmm_enabled;
+       int wmm_uapsd;
+       int okc;
+#ifdef CONFIG_IEEE80211W
+       enum mfp_options 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, enum wpa_alg 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 (*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);
+int 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);
+int 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,
+                                const u8 *req_ies, size_t req_ies_len);
+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/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
new file mode 100644 (file)
index 0000000..c9871d9
--- /dev/null
@@ -0,0 +1,1776 @@
+/*
+ * hostapd - IEEE 802.11r - Fast BSS Transition
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/aes_wrap.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wmm.h"
+#include "wpa_auth.h"
+#include "wpa_auth_i.h"
+#include "wpa_auth_ie.h"
+
+
+#ifdef CONFIG_IEEE80211R
+
+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;
+       const u8 *ric;
+       size_t ric_len;
+};
+
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+                           struct wpa_ft_ies *parse);
+
+
+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;
+       wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst));
+       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;
+}
+
+
+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];
+       int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+       /* 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];
+       int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
+       /* 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, int pairwise)
+{
+       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->pairwise = pairwise;
+
+       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, int *pairwise)
+{
+       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);
+                       if (pairwise)
+                               *pairwise = r0->pairwise;
+                       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, int pairwise)
+{
+       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->pairwise = pairwise;
+
+       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, int *pairwise)
+{
+       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);
+                       if (pairwise)
+                               *pairwise = r1->pairwise;
+                       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, size_t ptk_len)
+{
+       u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
+       u8 pmk_r1[PMK_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,
+                           sm->pairwise);
+
+       wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr,
+                         pmk_r1, sm->pmk_r1_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+                   WPA_PMK_NAME_LEN);
+       wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
+                           sm->pairwise);
+
+       wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+                         sm->wpa_auth->addr, sm->pmk_r1_name,
+                         (u8 *) ptk, ptk_len, ptk_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
+       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);
+}
+
+
+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[2] | Key Length[1] | RSC[8] |
+        * Key[5..32].
+        */
+       subelem_len = 13 + key_len + 8;
+       subelem = os_zalloc(subelem_len);
+       if (subelem == NULL)
+               return NULL;
+
+       subelem[0] = FTIE_SUBELEM_GTK;
+       subelem[1] = 11 + key_len + 8;
+       /* Key ID in B0-B1 of Key Info */
+       WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
+       subelem[4] = gsm->GTK_len;
+       wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
+       if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
+               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(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 */
+
+
+static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count,
+                               const u8 *ies, size_t ies_len)
+{
+       struct ieee802_11_elems parse;
+       struct rsn_rdie *rdie;
+
+       wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d",
+                  id, descr_count);
+       wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)",
+                   ies, ies_len);
+
+       if (end - pos < (int) sizeof(*rdie)) {
+               wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE");
+               return pos;
+       }
+
+       *pos++ = WLAN_EID_RIC_DATA;
+       *pos++ = sizeof(*rdie);
+       rdie = (struct rsn_rdie *) pos;
+       rdie->id = id;
+       rdie->descr_count = 0;
+       rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+       pos += sizeof(*rdie);
+
+       if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) ==
+           ParseFailed) {
+               wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs");
+               rdie->status_code =
+                       host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+               return pos;
+       }
+
+#ifdef NEED_AP_MLME
+       if (parse.wmm_tspec) {
+               struct wmm_tspec_element *tspec;
+               int res;
+
+               if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) {
+                       wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE "
+                                  "(%d)", (int) parse.wmm_tspec_len);
+                       rdie->status_code =
+                               host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+                       return pos;
+               }
+               if (end - pos < (int) sizeof(*tspec)) {
+                       wpa_printf(MSG_ERROR, "FT: Not enough room for "
+                                  "response TSPEC");
+                       rdie->status_code =
+                               host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+                       return pos;
+               }
+               tspec = (struct wmm_tspec_element *) pos;
+               os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec));
+               res = wmm_process_tspec(tspec);
+               wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res);
+               if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS)
+                       rdie->status_code =
+                               host_to_le16(WLAN_STATUS_INVALID_PARAMETERS);
+               else if (res == WMM_ADDTS_STATUS_REFUSED)
+                       rdie->status_code =
+                               host_to_le16(WLAN_STATUS_REQUEST_DECLINED);
+               else {
+                       /* TSPEC accepted; include updated TSPEC in response */
+                       rdie->descr_count = 1;
+                       pos += sizeof(*tspec);
+               }
+               return pos;
+       }
+#endif /* NEED_AP_MLME */
+
+       wpa_printf(MSG_DEBUG, "FT: No supported resource requested");
+       rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE);
+       return pos;
+}
+
+
+static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len)
+{
+       const u8 *rpos, *start;
+       const struct rsn_rdie *rdie;
+
+       wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len);
+
+       rpos = ric;
+       while (rpos + sizeof(*rdie) < ric + ric_len) {
+               if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) ||
+                   rpos + 2 + rpos[1] > ric + ric_len)
+                       break;
+               rdie = (const struct rsn_rdie *) (rpos + 2);
+               rpos += 2 + rpos[1];
+               start = rpos;
+
+               while (rpos + 2 <= ric + ric_len &&
+                      rpos + 2 + rpos[1] <= ric + ric_len) {
+                       if (rpos[0] == WLAN_EID_RIC_DATA)
+                               break;
+                       rpos += 2 + rpos[1];
+               }
+               pos = wpa_ft_process_rdie(pos, end, rdie->id,
+                                         rdie->descr_count,
+                                         start, rpos - start);
+       }
+
+       return pos;
+}
+
+
+u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
+                                size_t max_len, int auth_alg,
+                                const u8 *req_ies, size_t req_ies_len)
+{
+       u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL;
+       size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0;
+       int res;
+       struct wpa_auth_config *conf;
+       struct rsn_ftie *_ftie;
+       struct wpa_ft_ies parse;
+       u8 *ric_start;
+       u8 *anonce, *snonce;
+
+       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;
+
+       if (auth_alg == WLAN_AUTH_FT) {
+               /*
+                * RSN (only present if this is a Reassociation Response and
+                * part of a fast BSS transition)
+                */
+               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;
+               anonce = sm->ANonce;
+               snonce = sm->SNonce;
+#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;
+               anonce = NULL;
+               snonce = NULL;
+       }
+       res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos,
+                            end - pos, subelem, subelem_len);
+       os_free(subelem);
+       if (res < 0)
+               return pos;
+       ftie = pos;
+       ftie_len = res;
+       pos += res;
+
+       os_free(sm->assoc_resp_ftie);
+       sm->assoc_resp_ftie = os_malloc(ftie_len);
+       if (sm->assoc_resp_ftie)
+               os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len);
+
+       _ftie = (struct rsn_ftie *) (ftie + 2);
+       if (auth_alg == WLAN_AUTH_FT)
+               _ftie->mic_control[1] = 3; /* Information element count */
+
+       ric_start = pos;
+       if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) {
+               pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len);
+               if (auth_alg == WLAN_AUTH_FT)
+                       _ftie->mic_control[1] +=
+                               ieee802_11_ie_count(ric_start,
+                                                   pos - ric_start);
+       }
+       if (ric_start == pos)
+               ric_start = NULL;
+
+       if (auth_alg == WLAN_AUTH_FT &&
+           wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6,
+                      mdie, mdie_len, ftie, ftie_len,
+                      rsnie, rsnie_len,
+                      ric_start, ric_start ? pos - ric_start : 0,
+                      _ftie->mic) < 0)
+               wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+
+       return pos;
+}
+
+
+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;
+       const struct rsn_ftie *ftie;
+       int prot_ie_count = 0;
+
+       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 (pos[1] < sizeof(*ftie))
+                               return -1;
+                       ftie = (const struct rsn_ftie *) (pos + 2);
+                       prot_ie_count = ftie->mic_control[1];
+                       if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+                               return -1;
+                       break;
+               case WLAN_EID_RIC_DATA:
+                       if (parse->ric == NULL)
+                               parse->ric = pos;
+               }
+
+               pos += 2 + pos[1];
+       }
+
+       if (prot_ie_count == 0)
+               return 0; /* no MIC */
+
+       /*
+        * Check that the protected IE count matches with IEs included in the
+        * frame.
+        */
+       if (parse->rsn)
+               prot_ie_count--;
+       if (parse->mdie)
+               prot_ie_count--;
+       if (parse->ftie)
+               prot_ie_count--;
+       if (prot_ie_count < 0) {
+               wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+                          "the protected IE count");
+               return -1;
+       }
+
+       if (prot_ie_count == 0 && parse->ric) {
+               wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+                          "included in protected IE count");
+               return -1;
+       }
+
+       /* Determine the end of the RIC IE(s) */
+       pos = parse->ric;
+       while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+              prot_ie_count) {
+               prot_ie_count--;
+               pos += 2 + pos[1];
+       }
+       parse->ric_len = pos - parse->ric;
+       if (prot_ie_count) {
+               wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+                          "frame", (int) prot_ie_count);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth,
+                                  int vlan_id,
+                                  enum wpa_alg 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);
+}
+
+
+void wpa_ft_install_ptk(struct wpa_state_machine *sm)
+{
+       enum wpa_alg alg;
+       int klen;
+
+       /* MLME-SETKEYS.request(PTK) */
+       if (sm->pairwise == WPA_CIPHER_TKIP) {
+               alg = WPA_ALG_TKIP;
+               klen = 32;
+       } else if (sm->pairwise == WPA_CIPHER_CCMP) {
+               alg = WPA_ALG_CCMP;
+               klen = 16;
+       } else {
+               wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip "
+                          "PTK configuration", sm->pairwise);
+               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. This function will be called
+        * again after association to get the PTK configured, but that could be
+        * optimized by adding the STA entry earlier.
+        */
+       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, ptk_len;
+       int ret;
+       u8 *pos, *end;
+       int pairwise;
+
+       *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,
+                   &pairwise) < 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);
+
+       ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48;
+       wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+                         sm->wpa_auth->addr, pmk_r1_name,
+                         (u8 *) &sm->PTK, ptk_len, ptk_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
+                       (u8 *) &sm->PTK, ptk_len);
+       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+       sm->pairwise = pairwise;
+       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];
+       unsigned int count;
+
+       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;
+       }
+
+       if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+               wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+                           ftie->snonce, WPA_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+                           sm->SNonce, WPA_NONCE_LEN);
+               return -1;
+       }
+
+       if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+               wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+                           ftie->anonce, WPA_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+                           sm->ANonce, WPA_NONCE_LEN);
+               return -1;
+       }
+
+
+       if (parse.r0kh_id == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+               return -1;
+       }
+
+       if (parse.r0kh_id_len != sm->r0kh_id_len ||
+           os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+                          "the current R0KH-ID");
+               wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+                           parse.r0kh_id, parse.r0kh_id_len);
+               wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+                           sm->r0kh_id, sm->r0kh_id_len);
+               return -1;
+       }
+
+       if (parse.r1kh_id == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+               return -1;
+       }
+
+       if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+                     FT_R1KH_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+                          "ReassocReq");
+               wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
+                           parse.r1kh_id, FT_R1KH_ID_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID",
+                           sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+               return -1;
+       }
+
+       if (parse.rsn_pmkid == NULL ||
+           os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+               wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+                          "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+               return -1;
+       }
+
+       count = 3;
+       if (parse.ric)
+               count++;
+       if (ftie->mic_control[1] != count) {
+               wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+                          "Control: received %u expected %u",
+                          ftie->mic_control[1], count);
+               return -1;
+       }
+
+       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,
+                      parse.ric, parse.ric_len,
+                      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];
+       int pairwise;
+
+       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,
+                               &pairwise) < 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);
+       r.pairwise = host_to_le16(pairwise);
+
+       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;
+       int pairwise;
+
+       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 */
+
+       pairwise = le_to_host16(f.pairwise);
+       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 " pairwise=0x%x",
+                  MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+       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,
+                           pairwise);
+       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;
+       int pairwise;
+
+       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;
+       }
+
+       pairwise = le_to_host16(f.pairwise);
+       wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID="
+                  MACSTR " pairwise=0x%x",
+                  MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
+       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,
+                           pairwise);
+       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, int pairwise)
+{
+       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);
+       f.pairwise = host_to_le16(pairwise);
+       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, r0->pairwise);
+               r1kh = r1kh->next;
+       }
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
new file mode 100644 (file)
index 0000000..afa13a6
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "eap_server/eap.h"
+#include "l2_packet/l2_packet.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "preauth_auth.h"
+#include "sta_info.h"
+#include "tkip_countermeasures.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "wpa_auth.h"
+
+
+#ifdef CONFIG_IEEE80211R
+static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+                               size_t len);
+#endif /* CONFIG_IEEE80211R */
+
+
+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->wmm_enabled = conf->wmm_enabled;
+       wconf->wmm_uapsd = conf->wmm_uapsd;
+       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 */
+}
+
+
+static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr,
+                                   logger_level level, const char *txt)
+{
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+       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);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+}
+
+
+static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr,
+                                       u16 reason)
+{
+       struct hostapd_data *hapd = ctx;
+       wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: "
+                  "STA " MACSTR " reason %d",
+                  __func__, MAC2STR(addr), reason);
+       ap_sta_disconnect(hapd, NULL, addr, reason);
+}
+
+
+static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr)
+{
+       struct hostapd_data *hapd = ctx;
+       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, enum wpa_alg 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 hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0,
+                                key, key_len);
+}
+
+
+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_send_eapol(void *ctx, const u8 *addr,
+                                      const u8 *data, size_t data_len,
+                                      int encrypt)
+{
+       struct hostapd_data *hapd = ctx;
+       return hapd->drv.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;
+}
+
+
+struct wpa_auth_iface_iter_data {
+       int (*cb)(struct wpa_authenticator *sm, void *ctx);
+       void *cb_ctx;
+};
+
+static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx)
+{
+       struct wpa_auth_iface_iter_data *data = ctx;
+       size_t i;
+       for (i = 0; i < iface->num_bss; i++) {
+               if (iface->bss[i]->wpa_auth &&
+                   data->cb(iface->bss[i]->wpa_auth, data->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 *hapd = ctx;
+       struct wpa_auth_iface_iter_data data;
+       if (hapd->iface->for_each_interface == NULL)
+               return -1;
+       data.cb = cb;
+       data.cb_ctx = cb_ctx;
+       return hapd->iface->for_each_interface(hapd->iface->interfaces,
+                                              wpa_auth_iface_iter, &data);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+struct wpa_auth_ft_iface_iter_data {
+       struct hostapd_data *src_hapd;
+       const u8 *dst;
+       const u8 *data;
+       size_t data_len;
+};
+
+
+static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx)
+{
+       struct wpa_auth_ft_iface_iter_data *idata = ctx;
+       struct hostapd_data *hapd;
+       size_t j;
+
+       for (j = 0; j < iface->num_bss; j++) {
+               hapd = iface->bss[j];
+               if (hapd == idata->src_hapd)
+                       continue;
+               if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) {
+                       wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to "
+                                  "locally managed BSS " MACSTR "@%s -> "
+                                  MACSTR "@%s",
+                                  MAC2STR(idata->src_hapd->own_addr),
+                                  idata->src_hapd->conf->iface,
+                                  MAC2STR(hapd->own_addr), hapd->conf->iface);
+                       hostapd_rrb_receive(hapd, idata->src_hapd->own_addr,
+                                           idata->data, idata->data_len);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+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;
+
+#ifdef CONFIG_IEEE80211R
+       if (proto == ETH_P_RRB && hapd->iface->for_each_interface) {
+               int res;
+               struct wpa_auth_ft_iface_iter_data idata;
+               idata.src_hapd = hapd;
+               idata.dst = dst;
+               idata.data = data;
+               idata.data_len = data_len;
+               res = hapd->iface->for_each_interface(hapd->iface->interfaces,
+                                                     hostapd_wpa_auth_ft_iter,
+                                                     &idata);
+               if (res == 1)
+                       return data_len;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       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 = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen);
+       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) {
+               sta->auth_alg = WLAN_AUTH_FT;
+               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 */
+
+
+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.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;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (!hostapd_drv_none(hapd)) {
+               hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ?
+                                         hapd->conf->bridge :
+                                         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 */
+
+       return 0;
+
+}
+
+
+void hostapd_reconfig_wpa(struct hostapd_data *hapd)
+{
+       struct wpa_auth_config wpa_auth_conf;
+       hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf);
+       wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf);
+}
+
+
+void hostapd_deinit_wpa(struct hostapd_data *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);
+
+#ifdef CONFIG_IEEE80211R
+       l2_packet_deinit(hapd->l2);
+#endif /* CONFIG_IEEE80211R */
+}
diff --git a/src/ap/wpa_auth_glue.h b/src/ap/wpa_auth_glue.h
new file mode 100644 (file)
index 0000000..79d7e05
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * hostapd / WPA authenticator glue code
+ * 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.
+ */
+
+#ifndef WPA_AUTH_GLUE_H
+#define WPA_AUTH_GLUE_H
+
+int hostapd_setup_wpa(struct hostapd_data *hapd);
+void hostapd_reconfig_wpa(struct hostapd_data *hapd);
+void hostapd_deinit_wpa(struct hostapd_data *hapd);
+
+#endif /* WPA_AUTH_GLUE_H */
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
new file mode 100644 (file)
index 0000000..b69129f
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * 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;
+       u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
+                                              * message 2/4 */
+       u8 *assoc_resp_ftie;
+#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_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);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
+                          struct wpa_ptk *ptk, size_t ptk_len);
+struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
+void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
+void wpa_ft_install_ptk(struct wpa_state_machine *sm);
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* WPA_AUTH_I_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
new file mode 100644 (file)
index 0000000..f8a1804
--- /dev/null
@@ -0,0 +1,868 @@
+/*
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "ap_config.h"
+#include "ieee802_11.h"
+#include "wpa_auth.h"
+#include "pmksa_cache_auth.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->wmm_enabled) {
+               /* 4 PTKSA replay counters when using WMM */
+               capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+       }
+#ifdef CONFIG_IEEE80211W
+       if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               capab |= WPA_CAPABILITY_MFPC;
+               if (conf->ieee80211w == MGMT_FRAME_PROTECTION_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 != NO_MGMT_FRAME_PROTECTION) {
+               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 == MGMT_FRAME_PROTECTION_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 == NO_MGMT_FRAME_PROTECTION ||
+           !(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_auth_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;
+               } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+                       ie->ftie = pos;
+                       ie->ftie_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/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
new file mode 100644 (file)
index 0000000..61d4cb4
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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;
+       const u8 *ftie;
+       size_t ftie_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/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
new file mode 100644 (file)
index 0000000..a6ffd4d
--- /dev/null
@@ -0,0 +1,1020 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "crypto/dh_groups.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+#include "wps/wps_dev_attr.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "beacon.h"
+#include "sta_info.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_probe_req_rx(void *ctx, const u8 *addr,
+                                   const u8 *ie, size_t ie_len);
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+
+
+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, struct wpabuf *beacon_ie,
+                                struct wpabuf *probe_resp_ie)
+{
+       struct hostapd_data *hapd = ctx;
+       wpabuf_free(hapd->wps_beacon_ie);
+       hapd->wps_beacon_ie = beacon_ie;
+       wpabuf_free(hapd->wps_probe_resp_ie);
+       hapd->wps_probe_resp_ie = probe_resp_ie;
+       ieee802_11_set_beacon(hapd);
+       return hapd->drv.set_ap_wps_ie(hapd);
+}
+
+
+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;
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+       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|%s]",
+                         uuid, MAC2STR(dev->mac_addr), dev->device_name,
+                         dev->manufacturer, dev->model_name,
+                         dev->model_number, dev->serial_number,
+                         wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+                                              sizeof(devtype)));
+       if (len > 0 && len < (int) sizeof(txt))
+               wpa_msg(hapd->msg_ctx, 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%s\n",
+                       t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name,
+                       dev->manufacturer, dev->model_name, dev->model_number,
+                       dev->serial_number,
+                       wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+                                            sizeof(devtype)));
+               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_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
+               MAC2STR(mac_addr), uuid);
+       if (hapd->wps_reg_success_cb)
+               hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+                                        mac_addr, uuid_e);
+}
+
+
+static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
+                                        const u8 *uuid_e,
+                                        const u8 *pri_dev_type,
+                                        u16 config_methods,
+                                        u16 dev_password_id, u8 request_type,
+                                        const char *dev_name)
+{
+       struct hostapd_data *hapd = ctx;
+       char uuid[40];
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+       if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+               return;
+       if (dev_name == NULL)
+               dev_name = "";
+       wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR
+                    " %s %s 0x%x %u %u [%s]",
+                    MAC2STR(addr), uuid,
+                    wps_dev_type_bin2str(pri_dev_type, devtype,
+                                         sizeof(devtype)),
+                    config_methods, dev_password_id, request_type, dev_name);
+}
+
+
+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 (iface->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_ctx, MSG_INFO, "%s%s",
+                               WPS_EVENT_NEW_AP_SETTINGS, _buf);
+                       os_free(_buf);
+               }
+       } else
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS);
+
+       if (hapd->conf->wps_cred_processing == 1)
+               return 0;
+
+       os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len);
+       hapd->wps->ssid_len = cred->ssid_len;
+       hapd->wps->encr_types = cred->encr_type;
+       hapd->wps->auth_types = cred->auth_type;
+       if (cred->key_len == 0) {
+               os_free(hapd->wps->network_key);
+               hapd->wps->network_key = NULL;
+               hapd->wps->network_key_len = 0;
+       } else {
+               if (hapd->wps->network_key == NULL ||
+                   hapd->wps->network_key_len < cred->key_len) {
+                       hapd->wps->network_key_len = 0;
+                       os_free(hapd->wps->network_key);
+                       hapd->wps->network_key = os_malloc(cred->key_len);
+                       if (hapd->wps->network_key == NULL)
+                               return -1;
+               }
+               hapd->wps->network_key_len = cred->key_len;
+               os_memcpy(hapd->wps->network_key, cred->key, cred->key_len);
+       }
+       hapd->wps->wps_state = WPS_STATE_CONFIGURED;
+
+       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) {
+                       int key_idx = cred->key_idx;
+                       if (key_idx)
+                               key_idx--;
+                       fprintf(nconf, "wep_default_key=%d\n", key_idx);
+                       fprintf(nconf, "wep_key%d=", key_idx);
+                       if (cred->key_len == 10 || cred->key_len == 26) {
+                               /* WEP key as a hex string */
+                               for (i = 0; i < cred->key_len; i++)
+                                       fputc(cred->key[i], nconf);
+                       } else {
+                               /* Raw WEP key; convert to hex */
+                               for (i = 0; i < cred->key_len; i++)
+                                       fprintf(nconf, "%02x", cred->key[i]);
+                       }
+                       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_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
+{
+       struct hostapd_data *hapd = eloop_data;
+
+       if (hapd->conf->ap_setup_locked)
+               return;
+
+       wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
+       wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+       hapd->wps->ap_setup_locked = 0;
+       wps_registrar_update_ie(hapd->wps->registrar);
+}
+
+
+static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
+                                 struct wps_event_pwd_auth_fail *data)
+{
+       if (!data->enrollee || hapd->conf->ap_pin == NULL)
+               return;
+
+       /*
+        * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
+        * for some time if this happens multiple times to slow down brute
+        * force attacks.
+        */
+       hapd->ap_pin_failures++;
+       wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+                  hapd->ap_pin_failures);
+       if (hapd->ap_pin_failures < 3)
+               return;
+
+       wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
+       hapd->wps->ap_setup_locked = 1;
+
+       wps_registrar_update_ie(hapd->wps->registrar);
+
+       if (!hapd->conf->ap_setup_locked) {
+               if (hapd->ap_pin_lockout_time == 0)
+                       hapd->ap_pin_lockout_time = 60;
+               else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+                        (hapd->ap_pin_failures % 3) == 0)
+                       hapd->ap_pin_lockout_time *= 2;
+
+               wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
+                          hapd->ap_pin_lockout_time);
+               eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+               eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
+                                      hostapd_wps_reenable_ap_pin, hapd,
+                                      NULL);
+       }
+
+       /* TODO: dualband AP may need to update other interfaces */
+}
+
+
+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)
+{
+       wpabuf_free(hapd->wps_beacon_ie);
+       hapd->wps_beacon_ie = NULL;
+
+       wpabuf_free(hapd->wps_probe_resp_ie);
+       hapd->wps_probe_resp_ie = NULL;
+
+       hapd->drv.set_ap_wps_ie(hapd);
+}
+
+
+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;
+       wps->config_methods =
+               wps_config_methods_str2bin(hapd->conf->config_methods);
+       if (hapd->conf->device_type &&
+           wps_dev_type_str2bin(hapd->conf->device_type,
+                                wps->dev.pri_dev_type) < 0) {
+               wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+               os_free(wps);
+               return -1;
+       }
+       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->ssid.wpa_psk) {
+               os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN);
+               wps->psk_set = 1;
+       }
+
+       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.enrollee_seen_cb = hostapd_wps_enrollee_seen_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;
+       if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
+               cfg.static_wep_only = 1;
+
+       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 */
+
+       hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
+
+       hapd->wps = wps;
+
+       return 0;
+}
+
+
+void hostapd_deinit_wps(struct hostapd_data *hapd)
+{
+       eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+       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);
+       wpabuf_free(hapd->wps->dh_pubkey);
+       wpabuf_free(hapd->wps->dh_privkey);
+       wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
+       wpabuf_free(hapd->wps->oob_conf.dev_password);
+       wps_free_pending_msgs(hapd->wps->upnp_msgs);
+       os_free(hapd->wps);
+       hapd->wps = NULL;
+       hostapd_wps_clear_ies(hapd);
+}
+
+
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+       if (hapd->wps == NULL)
+               return;
+       if (hapd->conf->wps_state)
+               wps_registrar_update_ie(hapd->wps->registrar);
+       else
+               hostapd_deinit_wps(hapd);
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
+                       const char *pin, int timeout)
+{
+       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),
+                                    timeout);
+}
+
+
+int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+{
+       if (hapd->wps == NULL)
+               return -1;
+       return wps_registrar_button_pushed(hapd->wps->registrar);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+                         char *path, char *method, char *name)
+{
+       struct wps_context *wps = hapd->wps;
+       struct oob_device_data *oob_dev;
+
+       oob_dev = wps_get_oob_device(device_type);
+       if (oob_dev == NULL)
+               return -1;
+       oob_dev->device_path = path;
+       oob_dev->device_name = name;
+       wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+       if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) {
+               /*
+                * Use pre-configured DH keys in order to be able to write the
+                * key hash into the OOB file.
+                */
+               wpabuf_free(wps->dh_pubkey);
+               wpabuf_free(wps->dh_privkey);
+               wps->dh_privkey = NULL;
+               wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP),
+                                        &wps->dh_privkey);
+               wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+               if (wps->dh_pubkey == NULL) {
+                       wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+                                  "Diffie-Hellman handshake");
+                       return -1;
+               }
+       }
+
+       if (wps_process_oob(wps, oob_dev, 1) < 0)
+               goto error;
+
+       if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+            wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+           hostapd_wps_add_pin(hapd, "any",
+                               wpabuf_head(wps->oob_conf.dev_password), 0) <
+           0)
+               goto error;
+
+       return 0;
+
+error:
+       wpabuf_free(wps->dh_pubkey);
+       wps->dh_pubkey = NULL;
+       wpabuf_free(wps->dh_privkey);
+       wps->dh_privkey = NULL;
+       return -1;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
+                                   const u8 *ie, size_t ie_len)
+{
+       struct hostapd_data *hapd = ctx;
+       struct wpabuf *wps_ie;
+       struct ieee802_11_elems elems;
+
+       if (hapd->wps == NULL)
+               return 0;
+
+       if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+                          MACSTR, MAC2STR(addr));
+               return 0;
+       }
+
+       if (elems.ssid && elems.ssid_len > 0 &&
+           (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+            os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+            0))
+               return 0; /* Not for us */
+
+       wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
+       if (wps_ie == NULL)
+               return 0;
+
+       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);
+
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+
+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(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_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_put_wlan_response = hostapd_rx_req_put_wlan_response;
+       if (hapd->conf->ap_pin)
+               ctx->ap_pin = os_strdup(hapd->conf->ap_pin);
+
+       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 */
+
+
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+                           char *buf, size_t buflen)
+{
+       if (hapd->wps == NULL)
+               return 0;
+       return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
+}
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+       struct hostapd_data *hapd = eloop_data;
+       wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+       hostapd_wps_ap_pin_disable(hapd);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+       hapd->ap_pin_failures = 0;
+       hapd->conf->ap_setup_locked = 0;
+       if (hapd->wps->ap_setup_locked) {
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+               hapd->wps->ap_setup_locked = 0;
+               wps_registrar_update_ie(hapd->wps->registrar);
+       }
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+       if (timeout > 0)
+               eloop_register_timeout(timeout, 0,
+                                      hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = NULL;
+#ifdef CONFIG_WPS_UPNP
+       upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+#endif /* CONFIG_WPS_UPNP */
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+       unsigned int pin;
+       char pin_txt[9];
+
+       pin = wps_generate_pin();
+       os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin);
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = os_strdup(pin_txt);
+#ifdef CONFIG_WPS_UPNP
+       upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt);
+#endif /* CONFIG_WPS_UPNP */
+       hostapd_wps_ap_pin_enable(hapd, timeout);
+       return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+       return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+                          int timeout)
+{
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = os_strdup(pin);
+       if (hapd->conf->ap_pin == NULL)
+               return -1;
+#ifdef CONFIG_WPS_UPNP
+       upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin);
+#endif /* CONFIG_WPS_UPNP */
+       hostapd_wps_ap_pin_enable(hapd, timeout);
+       return 0;
+}
diff --git a/src/ap/wps_hostapd.h b/src/ap/wps_hostapd.h
new file mode 100644 (file)
index 0000000..e978a1c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * hostapd / WPS integration
+ * Copyright (c) 2008-2010, 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);
+void hostapd_update_wps(struct hostapd_data *hapd);
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
+                       const char *pin, int timeout);
+int hostapd_wps_button_pushed(struct hostapd_data *hapd);
+int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
+                         char *path, char *method, char *name);
+int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
+                           char *buf, size_t buflen);
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd);
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout);
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd);
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+                          int timeout);
+
+#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_update_wps(struct hostapd_data *hapd)
+{
+}
+
+static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
+                                         const u8 *addr,
+                                         char *buf, size_t buflen)
+{
+       return 0;
+}
+
+static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd)
+{
+       return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_HOSTAPD_H */
diff --git a/src/common/Makefile b/src/common/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/common/defs.h b/src/common/defs.h
new file mode 100644 (file)
index 0000000..173bbd1
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * WPA Supplicant - Common 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.
+ */
+
+#ifndef DEFS_H
+#define DEFS_H
+
+#ifdef FALSE
+#undef FALSE
+#endif
+#ifdef TRUE
+#undef TRUE
+#endif
+typedef enum { FALSE = 0, TRUE = 1 } Boolean;
+
+
+#define WPA_CIPHER_NONE BIT(0)
+#define WPA_CIPHER_WEP40 BIT(1)
+#define WPA_CIPHER_WEP104 BIT(2)
+#define WPA_CIPHER_TKIP BIT(3)
+#define WPA_CIPHER_CCMP BIT(4)
+#ifdef CONFIG_IEEE80211W
+#define WPA_CIPHER_AES_128_CMAC BIT(5)
+#endif /* CONFIG_IEEE80211W */
+
+#define WPA_KEY_MGMT_IEEE8021X BIT(0)
+#define WPA_KEY_MGMT_PSK BIT(1)
+#define WPA_KEY_MGMT_NONE BIT(2)
+#define WPA_KEY_MGMT_IEEE8021X_NO_WPA BIT(3)
+#define WPA_KEY_MGMT_WPA_NONE BIT(4)
+#define WPA_KEY_MGMT_FT_IEEE8021X BIT(5)
+#define WPA_KEY_MGMT_FT_PSK BIT(6)
+#define WPA_KEY_MGMT_IEEE8021X_SHA256 BIT(7)
+#define WPA_KEY_MGMT_PSK_SHA256 BIT(8)
+#define WPA_KEY_MGMT_WPS BIT(9)
+
+static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
+{
+       return akm == WPA_KEY_MGMT_IEEE8021X ||
+               akm == WPA_KEY_MGMT_FT_IEEE8021X ||
+               akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+}
+
+static inline int wpa_key_mgmt_wpa_psk(int akm)
+{
+       return akm == WPA_KEY_MGMT_PSK ||
+               akm == WPA_KEY_MGMT_FT_PSK ||
+               akm == WPA_KEY_MGMT_PSK_SHA256;
+}
+
+static inline int wpa_key_mgmt_ft(int akm)
+{
+       return akm == WPA_KEY_MGMT_FT_PSK ||
+               akm == WPA_KEY_MGMT_FT_IEEE8021X;
+}
+
+static inline int wpa_key_mgmt_sha256(int akm)
+{
+       return akm == WPA_KEY_MGMT_PSK_SHA256 ||
+               akm == WPA_KEY_MGMT_IEEE8021X_SHA256;
+}
+
+
+#define WPA_PROTO_WPA BIT(0)
+#define WPA_PROTO_RSN BIT(1)
+
+#define WPA_AUTH_ALG_OPEN BIT(0)
+#define WPA_AUTH_ALG_SHARED BIT(1)
+#define WPA_AUTH_ALG_LEAP BIT(2)
+#define WPA_AUTH_ALG_FT BIT(3)
+
+
+enum wpa_alg {
+       WPA_ALG_NONE,
+       WPA_ALG_WEP,
+       WPA_ALG_TKIP,
+       WPA_ALG_CCMP,
+       WPA_ALG_IGTK,
+       WPA_ALG_PMK
+};
+
+/**
+ * enum wpa_cipher - Cipher suites
+ */
+enum wpa_cipher {
+       CIPHER_NONE,
+       CIPHER_WEP40,
+       CIPHER_TKIP,
+       CIPHER_CCMP,
+       CIPHER_WEP104
+};
+
+/**
+ * enum wpa_key_mgmt - Key management suites
+ */
+enum wpa_key_mgmt {
+       KEY_MGMT_802_1X,
+       KEY_MGMT_PSK,
+       KEY_MGMT_NONE,
+       KEY_MGMT_802_1X_NO_WPA,
+       KEY_MGMT_WPA_NONE,
+       KEY_MGMT_FT_802_1X,
+       KEY_MGMT_FT_PSK,
+       KEY_MGMT_802_1X_SHA256,
+       KEY_MGMT_PSK_SHA256,
+       KEY_MGMT_WPS
+};
+
+/**
+ * enum wpa_states - wpa_supplicant state
+ *
+ * These enumeration values are used to indicate the current wpa_supplicant
+ * state (wpa_s->wpa_state). The current state can be retrieved with
+ * wpa_supplicant_get_state() function and the state can be changed by calling
+ * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the
+ * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used
+ * to access the state variable.
+ */
+enum wpa_states {
+       /**
+        * WPA_DISCONNECTED - Disconnected state
+        *
+        * This state indicates that client is not associated, but is likely to
+        * start looking for an access point. This state is entered when a
+        * connection is lost.
+        */
+       WPA_DISCONNECTED,
+
+       /**
+        * WPA_INACTIVE - Inactive state (wpa_supplicant disabled)
+        *
+        * This state is entered if there are no enabled networks in the
+        * configuration. wpa_supplicant is not trying to associate with a new
+        * network and external interaction (e.g., ctrl_iface call to add or
+        * enable a network) is needed to start association.
+        */
+       WPA_INACTIVE,
+
+       /**
+        * WPA_SCANNING - Scanning for a network
+        *
+        * This state is entered when wpa_supplicant starts scanning for a
+        * network.
+        */
+       WPA_SCANNING,
+
+       /**
+        * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID
+        *
+        * This state is entered when wpa_supplicant has found a suitable BSS
+        * to authenticate with and the driver is configured to try to
+        * authenticate with this BSS. This state is used only with drivers
+        * that use wpa_supplicant as the SME.
+        */
+       WPA_AUTHENTICATING,
+
+       /**
+        * WPA_ASSOCIATING - Trying to associate with a BSS/SSID
+        *
+        * This state is entered when wpa_supplicant has found a suitable BSS
+        * to associate with and the driver is configured to try to associate
+        * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this
+        * state is entered when the driver is configured to try to associate
+        * with a network using the configured SSID and security policy.
+        */
+       WPA_ASSOCIATING,
+
+       /**
+        * WPA_ASSOCIATED - Association completed
+        *
+        * This state is entered when the driver reports that association has
+        * been successfully completed with an AP. If IEEE 802.1X is used
+        * (with or without WPA/WPA2), wpa_supplicant remains in this state
+        * until the IEEE 802.1X/EAPOL authentication has been completed.
+        */
+       WPA_ASSOCIATED,
+
+       /**
+        * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress
+        *
+        * This state is entered when WPA/WPA2 4-Way Handshake is started. In
+        * case of WPA-PSK, this happens when receiving the first EAPOL-Key
+        * frame after association. In case of WPA-EAP, this state is entered
+        * when the IEEE 802.1X/EAPOL authentication has been completed.
+        */
+       WPA_4WAY_HANDSHAKE,
+
+       /**
+        * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress
+        *
+        * This state is entered when 4-Way Key Handshake has been completed
+        * (i.e., when the supplicant sends out message 4/4) and when Group
+        * Key rekeying is started by the AP (i.e., when supplicant receives
+        * message 1/2).
+        */
+       WPA_GROUP_HANDSHAKE,
+
+       /**
+        * WPA_COMPLETED - All authentication completed
+        *
+        * This state is entered when the full authentication process is
+        * completed. In case of WPA2, this happens when the 4-Way Handshake is
+        * successfully completed. With WPA, this state is entered after the
+        * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is
+        * completed after dynamic keys are received (or if not used, after
+        * the EAP authentication has been completed). With static WEP keys and
+        * plaintext connections, this state is entered when an association
+        * has been completed.
+        *
+        * This state indicates that the supplicant has completed its
+        * processing for the association phase and that data connection is
+        * fully configured.
+        */
+       WPA_COMPLETED
+};
+
+#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0
+#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1
+#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2
+#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3
+
+#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0
+#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1
+
+
+/**
+ * enum mfp_options - Management frame protection (IEEE 802.11w) options
+ */
+enum mfp_options {
+       NO_MGMT_FRAME_PROTECTION = 0,
+       MGMT_FRAME_PROTECTION_OPTIONAL = 1,
+       MGMT_FRAME_PROTECTION_REQUIRED = 2
+};
+
+/**
+ * enum hostapd_hw_mode - Hardware mode
+ */
+enum hostapd_hw_mode {
+       HOSTAPD_MODE_IEEE80211B,
+       HOSTAPD_MODE_IEEE80211G,
+       HOSTAPD_MODE_IEEE80211A,
+       NUM_HOSTAPD_MODES
+};
+
+#endif /* DEFS_H */
diff --git a/src/common/eapol_common.h b/src/common/eapol_common.h
new file mode 100644 (file)
index 0000000..d70e62d
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * EAPOL definitions shared between hostapd and wpa_supplicant
+ * 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_COMMON_H
+#define EAPOL_COMMON_H
+
+/* IEEE Std 802.1X-2004 */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_hdr {
+       u8 version;
+       u8 type;
+       be16 length;
+       /* followed by length octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAPOL_VERSION 2
+
+enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
+       IEEE802_1X_TYPE_EAPOL_START = 1,
+       IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
+       IEEE802_1X_TYPE_EAPOL_KEY = 3,
+       IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4
+};
+
+enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
+       EAPOL_KEY_TYPE_WPA = 254 };
+
+#endif /* EAPOL_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
new file mode 100644 (file)
index 0000000..96ef5b6
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * IEEE 802.11 Common routines
+ * 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"
+
+#include "common.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+
+
+static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
+                                           struct ieee802_11_elems *elems,
+                                           int show_errors)
+{
+       unsigned int oui;
+
+       /* first 3 bytes in vendor specific information element are the IEEE
+        * OUI of the vendor. The following byte is used a vendor specific
+        * sub-type. */
+       if (elen < 4) {
+               if (show_errors) {
+                       wpa_printf(MSG_MSGDUMP, "short vendor specific "
+                                  "information element ignored (len=%lu)",
+                                  (unsigned long) elen);
+               }
+               return -1;
+       }
+
+       oui = WPA_GET_BE24(pos);
+       switch (oui) {
+       case OUI_MICROSOFT:
+               /* Microsoft/Wi-Fi information elements are further typed and
+                * subtyped */
+               switch (pos[3]) {
+               case 1:
+                       /* Microsoft OUI (00:50:F2) with OUI Type 1:
+                        * real WPA information element */
+                       elems->wpa_ie = pos;
+                       elems->wpa_ie_len = elen;
+                       break;
+               case WMM_OUI_TYPE:
+                       /* WMM information element */
+                       if (elen < 5) {
+                               wpa_printf(MSG_MSGDUMP, "short WMM "
+                                          "information element ignored "
+                                          "(len=%lu)",
+                                          (unsigned long) elen);
+                               return -1;
+                       }
+                       switch (pos[4]) {
+                       case WMM_OUI_SUBTYPE_INFORMATION_ELEMENT:
+                       case WMM_OUI_SUBTYPE_PARAMETER_ELEMENT:
+                               /*
+                                * Share same pointer since only one of these
+                                * is used and they start with same data.
+                                * Length field can be used to distinguish the
+                                * IEs.
+                                */
+                               elems->wmm = pos;
+                               elems->wmm_len = elen;
+                               break;
+                       case WMM_OUI_SUBTYPE_TSPEC_ELEMENT:
+                               elems->wmm_tspec = pos;
+                               elems->wmm_tspec_len = elen;
+                               break;
+                       default:
+                               wpa_printf(MSG_MSGDUMP, "unknown WMM "
+                                          "information element ignored "
+                                          "(subtype=%d len=%lu)",
+                                          pos[4], (unsigned long) elen);
+                               return -1;
+                       }
+                       break;
+               case 4:
+                       /* Wi-Fi Protected Setup (WPS) IE */
+                       elems->wps_ie = pos;
+                       elems->wps_ie_len = elen;
+                       break;
+               default:
+                       wpa_printf(MSG_MSGDUMP, "Unknown Microsoft "
+                                  "information element ignored "
+                                  "(type=%d len=%lu)\n",
+                                  pos[3], (unsigned long) elen);
+                       return -1;
+               }
+               break;
+
+       case OUI_BROADCOM:
+               switch (pos[3]) {
+               case VENDOR_HT_CAPAB_OUI_TYPE:
+                       elems->vendor_ht_cap = pos;
+                       elems->vendor_ht_cap_len = elen;
+                       break;
+               default:
+                       wpa_printf(MSG_MSGDUMP, "Unknown Broadcom "
+                                  "information element ignored "
+                                  "(type=%d len=%lu)\n",
+                                  pos[3], (unsigned long) elen);
+                       return -1;
+               }
+               break;
+
+       default:
+               wpa_printf(MSG_MSGDUMP, "unknown vendor specific information "
+                          "element ignored (vendor OUI %02x:%02x:%02x "
+                          "len=%lu)",
+                          pos[0], pos[1], pos[2], (unsigned long) elen);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_11_parse_elems - Parse information elements in management frames
+ * @start: Pointer to the start of IEs
+ * @len: Length of IE buffer in octets
+ * @elems: Data structure for parsed elements
+ * @show_errors: Whether to show parsing errors in debug log
+ * Returns: Parsing result
+ */
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+                               struct ieee802_11_elems *elems,
+                               int show_errors)
+{
+       size_t left = len;
+       const u8 *pos = start;
+       int unknown = 0;
+
+       os_memset(elems, 0, sizeof(*elems));
+
+       while (left >= 2) {
+               u8 id, elen;
+
+               id = *pos++;
+               elen = *pos++;
+               left -= 2;
+
+               if (elen > left) {
+                       if (show_errors) {
+                               wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
+                                          "parse failed (id=%d elen=%d "
+                                          "left=%lu)",
+                                          id, elen, (unsigned long) left);
+                               wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
+                       }
+                       return ParseFailed;
+               }
+
+               switch (id) {
+               case WLAN_EID_SSID:
+                       elems->ssid = pos;
+                       elems->ssid_len = elen;
+                       break;
+               case WLAN_EID_SUPP_RATES:
+                       elems->supp_rates = pos;
+                       elems->supp_rates_len = elen;
+                       break;
+               case WLAN_EID_FH_PARAMS:
+                       elems->fh_params = pos;
+                       elems->fh_params_len = elen;
+                       break;
+               case WLAN_EID_DS_PARAMS:
+                       elems->ds_params = pos;
+                       elems->ds_params_len = elen;
+                       break;
+               case WLAN_EID_CF_PARAMS:
+                       elems->cf_params = pos;
+                       elems->cf_params_len = elen;
+                       break;
+               case WLAN_EID_TIM:
+                       elems->tim = pos;
+                       elems->tim_len = elen;
+                       break;
+               case WLAN_EID_IBSS_PARAMS:
+                       elems->ibss_params = pos;
+                       elems->ibss_params_len = elen;
+                       break;
+               case WLAN_EID_CHALLENGE:
+                       elems->challenge = pos;
+                       elems->challenge_len = elen;
+                       break;
+               case WLAN_EID_ERP_INFO:
+                       elems->erp_info = pos;
+                       elems->erp_info_len = elen;
+                       break;
+               case WLAN_EID_EXT_SUPP_RATES:
+                       elems->ext_supp_rates = pos;
+                       elems->ext_supp_rates_len = elen;
+                       break;
+               case WLAN_EID_VENDOR_SPECIFIC:
+                       if (ieee802_11_parse_vendor_specific(pos, elen,
+                                                            elems,
+                                                            show_errors))
+                               unknown++;
+                       break;
+               case WLAN_EID_RSN:
+                       elems->rsn_ie = pos;
+                       elems->rsn_ie_len = elen;
+                       break;
+               case WLAN_EID_PWR_CAPABILITY:
+                       elems->power_cap = pos;
+                       elems->power_cap_len = elen;
+                       break;
+               case WLAN_EID_SUPPORTED_CHANNELS:
+                       elems->supp_channels = pos;
+                       elems->supp_channels_len = elen;
+                       break;
+               case WLAN_EID_MOBILITY_DOMAIN:
+                       elems->mdie = pos;
+                       elems->mdie_len = elen;
+                       break;
+               case WLAN_EID_FAST_BSS_TRANSITION:
+                       elems->ftie = pos;
+                       elems->ftie_len = elen;
+                       break;
+               case WLAN_EID_TIMEOUT_INTERVAL:
+                       elems->timeout_int = pos;
+                       elems->timeout_int_len = elen;
+                       break;
+               case WLAN_EID_HT_CAP:
+                       elems->ht_capabilities = pos;
+                       elems->ht_capabilities_len = elen;
+                       break;
+               case WLAN_EID_HT_OPERATION:
+                       elems->ht_operation = pos;
+                       elems->ht_operation_len = elen;
+                       break;
+               default:
+                       unknown++;
+                       if (!show_errors)
+                               break;
+                       wpa_printf(MSG_MSGDUMP, "IEEE 802.11 element parse "
+                                  "ignored unknown element (id=%d elen=%d)",
+                                  id, elen);
+                       break;
+               }
+
+               left -= elen;
+               pos += elen;
+       }
+
+       if (left)
+               return ParseFailed;
+
+       return unknown ? ParseUnknown : ParseOK;
+}
+
+
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
+{
+       int count = 0;
+       const u8 *pos, *end;
+
+       if (ies == NULL)
+               return 0;
+
+       pos = ies;
+       end = ies + ies_len;
+
+       while (pos + 2 <= end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               count++;
+               pos += 2 + pos[1];
+       }
+
+       return count;
+}
+
+
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+                                           u32 oui_type)
+{
+       struct wpabuf *buf;
+       const u8 *end, *pos, *ie;
+
+       pos = ies;
+       end = ies + ies_len;
+       ie = NULL;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       return NULL;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   WPA_GET_BE32(&pos[2]) == oui_type) {
+                       ie = pos;
+                       break;
+               }
+               pos += 2 + pos[1];
+       }
+
+       if (ie == NULL)
+               return NULL; /* No specified vendor IE found */
+
+       buf = wpabuf_alloc(ies_len);
+       if (buf == NULL)
+               return NULL;
+
+       /*
+        * There may be multiple vendor IEs in the message, so need to
+        * concatenate their 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]) == oui_type)
+                       wpabuf_put_data(buf, pos + 6, pos[1] - 4);
+               pos += 2 + pos[1];
+       }
+
+       return buf;
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
new file mode 100644 (file)
index 0000000..4a4f5a7
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * IEEE 802.11 Common routines
+ * 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.
+ */
+
+#ifndef IEEE802_11_COMMON_H
+#define IEEE802_11_COMMON_H
+
+/* Parsed Information Elements */
+struct ieee802_11_elems {
+       const u8 *ssid;
+       const u8 *supp_rates;
+       const u8 *fh_params;
+       const u8 *ds_params;
+       const u8 *cf_params;
+       const u8 *tim;
+       const u8 *ibss_params;
+       const u8 *challenge;
+       const u8 *erp_info;
+       const u8 *ext_supp_rates;
+       const u8 *wpa_ie;
+       const u8 *rsn_ie;
+       const u8 *wmm; /* WMM Information or Parameter Element */
+       const u8 *wmm_tspec;
+       const u8 *wps_ie;
+       const u8 *power_cap;
+       const u8 *supp_channels;
+       const u8 *mdie;
+       const u8 *ftie;
+       const u8 *timeout_int;
+       const u8 *ht_capabilities;
+       const u8 *ht_operation;
+       const u8 *vendor_ht_cap;
+
+       u8 ssid_len;
+       u8 supp_rates_len;
+       u8 fh_params_len;
+       u8 ds_params_len;
+       u8 cf_params_len;
+       u8 tim_len;
+       u8 ibss_params_len;
+       u8 challenge_len;
+       u8 erp_info_len;
+       u8 ext_supp_rates_len;
+       u8 wpa_ie_len;
+       u8 rsn_ie_len;
+       u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
+       u8 wmm_tspec_len;
+       u8 wps_ie_len;
+       u8 power_cap_len;
+       u8 supp_channels_len;
+       u8 mdie_len;
+       u8 ftie_len;
+       u8 timeout_int_len;
+       u8 ht_capabilities_len;
+       u8 ht_operation_len;
+       u8 vendor_ht_cap_len;
+};
+
+typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
+
+ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
+                               struct ieee802_11_elems *elems,
+                               int show_errors);
+int ieee802_11_ie_count(const u8 *ies, size_t ies_len);
+struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
+                                           u32 oui_type);
+
+#endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
new file mode 100644 (file)
index 0000000..4881e39
--- /dev/null
@@ -0,0 +1,607 @@
+/*
+ * IEEE 802.11 Frame type definitions
+ * Copyright (c) 2002-2009, 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_DEFS_H
+#define IEEE802_11_DEFS_H
+
+/* IEEE 802.11 defines */
+
+#define WLAN_FC_PVER           0x0003
+#define WLAN_FC_TODS           0x0100
+#define WLAN_FC_FROMDS         0x0200
+#define WLAN_FC_MOREFRAG       0x0400
+#define WLAN_FC_RETRY          0x0800
+#define WLAN_FC_PWRMGT         0x1000
+#define WLAN_FC_MOREDATA       0x2000
+#define WLAN_FC_ISWEP          0x4000
+#define WLAN_FC_ORDER          0x8000
+
+#define WLAN_FC_GET_TYPE(fc)   (((fc) & 0x000c) >> 2)
+#define WLAN_FC_GET_STYPE(fc)  (((fc) & 0x00f0) >> 4)
+
+#define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
+#define WLAN_GET_SEQ_SEQ(seq) \
+       (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
+
+#define WLAN_FC_TYPE_MGMT              0
+#define WLAN_FC_TYPE_CTRL              1
+#define WLAN_FC_TYPE_DATA              2
+
+/* management */
+#define WLAN_FC_STYPE_ASSOC_REQ                0
+#define WLAN_FC_STYPE_ASSOC_RESP       1
+#define WLAN_FC_STYPE_REASSOC_REQ      2
+#define WLAN_FC_STYPE_REASSOC_RESP     3
+#define WLAN_FC_STYPE_PROBE_REQ                4
+#define WLAN_FC_STYPE_PROBE_RESP       5
+#define WLAN_FC_STYPE_BEACON           8
+#define WLAN_FC_STYPE_ATIM             9
+#define WLAN_FC_STYPE_DISASSOC         10
+#define WLAN_FC_STYPE_AUTH             11
+#define WLAN_FC_STYPE_DEAUTH           12
+#define WLAN_FC_STYPE_ACTION           13
+
+/* control */
+#define WLAN_FC_STYPE_PSPOLL           10
+#define WLAN_FC_STYPE_RTS              11
+#define WLAN_FC_STYPE_CTS              12
+#define WLAN_FC_STYPE_ACK              13
+#define WLAN_FC_STYPE_CFEND            14
+#define WLAN_FC_STYPE_CFENDACK         15
+
+/* data */
+#define WLAN_FC_STYPE_DATA             0
+#define WLAN_FC_STYPE_DATA_CFACK       1
+#define WLAN_FC_STYPE_DATA_CFPOLL      2
+#define WLAN_FC_STYPE_DATA_CFACKPOLL   3
+#define WLAN_FC_STYPE_NULLFUNC         4
+#define WLAN_FC_STYPE_CFACK            5
+#define WLAN_FC_STYPE_CFPOLL           6
+#define WLAN_FC_STYPE_CFACKPOLL                7
+#define WLAN_FC_STYPE_QOS_DATA         8
+
+/* Authentication algorithms */
+#define WLAN_AUTH_OPEN                 0
+#define WLAN_AUTH_SHARED_KEY           1
+#define WLAN_AUTH_FT                   2
+#define WLAN_AUTH_LEAP                 128
+
+#define WLAN_AUTH_CHALLENGE_LEN 128
+
+#define WLAN_CAPABILITY_ESS BIT(0)
+#define WLAN_CAPABILITY_IBSS BIT(1)
+#define WLAN_CAPABILITY_CF_POLLABLE BIT(2)
+#define WLAN_CAPABILITY_CF_POLL_REQUEST BIT(3)
+#define WLAN_CAPABILITY_PRIVACY BIT(4)
+#define WLAN_CAPABILITY_SHORT_PREAMBLE BIT(5)
+#define WLAN_CAPABILITY_PBCC BIT(6)
+#define WLAN_CAPABILITY_CHANNEL_AGILITY BIT(7)
+#define WLAN_CAPABILITY_SPECTRUM_MGMT BIT(8)
+#define WLAN_CAPABILITY_SHORT_SLOT_TIME BIT(10)
+#define WLAN_CAPABILITY_DSSS_OFDM BIT(13)
+
+/* Status codes (IEEE 802.11-2007, 7.3.1.9, Table 7-23) */
+#define WLAN_STATUS_SUCCESS 0
+#define WLAN_STATUS_UNSPECIFIED_FAILURE 1
+#define WLAN_STATUS_CAPS_UNSUPPORTED 10
+#define WLAN_STATUS_REASSOC_NO_ASSOC 11
+#define WLAN_STATUS_ASSOC_DENIED_UNSPEC 12
+#define WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG 13
+#define WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION 14
+#define WLAN_STATUS_CHALLENGE_FAIL 15
+#define WLAN_STATUS_AUTH_TIMEOUT 16
+#define WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA 17
+#define WLAN_STATUS_ASSOC_DENIED_RATES 18
+/* IEEE 802.11b */
+#define WLAN_STATUS_ASSOC_DENIED_NOSHORT 19
+#define WLAN_STATUS_ASSOC_DENIED_NOPBCC 20
+#define WLAN_STATUS_ASSOC_DENIED_NOAGILITY 21
+/* IEEE 802.11h */
+#define WLAN_STATUS_SPEC_MGMT_REQUIRED 22
+#define WLAN_STATUS_PWR_CAPABILITY_NOT_VALID 23
+#define WLAN_STATUS_SUPPORTED_CHANNEL_NOT_VALID 24
+/* IEEE 802.11g */
+#define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25
+#define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26
+#define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27
+#define WLAN_STATUS_R0KH_UNREACHABLE 28
+/* IEEE 802.11w */
+#define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30
+#define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31
+#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32
+#define WLAN_STATUS_REQUEST_DECLINED 37
+#define WLAN_STATUS_INVALID_PARAMETERS 38
+/* IEEE 802.11i */
+#define WLAN_STATUS_INVALID_IE 40
+#define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41
+#define WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID 42
+#define WLAN_STATUS_AKMP_NOT_VALID 43
+#define WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION 44
+#define WLAN_STATUS_INVALID_RSN_IE_CAPAB 45
+#define WLAN_STATUS_CIPHER_REJECTED_PER_POLICY 46
+#define WLAN_STATUS_TS_NOT_CREATED 47
+#define WLAN_STATUS_DIRECT_LINK_NOT_ALLOWED 48
+#define WLAN_STATUS_DEST_STA_NOT_PRESENT 49
+#define WLAN_STATUS_DEST_STA_NOT_QOS_STA 50
+#define WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE 51
+/* IEEE 802.11r */
+#define WLAN_STATUS_INVALID_FT_ACTION_FRAME_COUNT 52
+#define WLAN_STATUS_INVALID_PMKID 53
+#define WLAN_STATUS_INVALID_MDIE 54
+#define WLAN_STATUS_INVALID_FTIE 55
+
+/* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
+#define WLAN_REASON_UNSPECIFIED 1
+#define WLAN_REASON_PREV_AUTH_NOT_VALID 2
+#define WLAN_REASON_DEAUTH_LEAVING 3
+#define WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY 4
+#define WLAN_REASON_DISASSOC_AP_BUSY 5
+#define WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA 6
+#define WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA 7
+#define WLAN_REASON_DISASSOC_STA_HAS_LEFT 8
+#define WLAN_REASON_STA_REQ_ASSOC_WITHOUT_AUTH 9
+/* IEEE 802.11h */
+#define WLAN_REASON_PWR_CAPABILITY_NOT_VALID 10
+#define WLAN_REASON_SUPPORTED_CHANNEL_NOT_VALID 11
+/* IEEE 802.11i */
+#define WLAN_REASON_INVALID_IE 13
+#define WLAN_REASON_MICHAEL_MIC_FAILURE 14
+#define WLAN_REASON_4WAY_HANDSHAKE_TIMEOUT 15
+#define WLAN_REASON_GROUP_KEY_UPDATE_TIMEOUT 16
+#define WLAN_REASON_IE_IN_4WAY_DIFFERS 17
+#define WLAN_REASON_GROUP_CIPHER_NOT_VALID 18
+#define WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID 19
+#define WLAN_REASON_AKMP_NOT_VALID 20
+#define WLAN_REASON_UNSUPPORTED_RSN_IE_VERSION 21
+#define WLAN_REASON_INVALID_RSN_IE_CAPAB 22
+#define WLAN_REASON_IEEE_802_1X_AUTH_FAILED 23
+#define WLAN_REASON_CIPHER_SUITE_REJECTED 24
+
+
+/* Information Element IDs */
+#define WLAN_EID_SSID 0
+#define WLAN_EID_SUPP_RATES 1
+#define WLAN_EID_FH_PARAMS 2
+#define WLAN_EID_DS_PARAMS 3
+#define WLAN_EID_CF_PARAMS 4
+#define WLAN_EID_TIM 5
+#define WLAN_EID_IBSS_PARAMS 6
+#define WLAN_EID_COUNTRY 7
+#define WLAN_EID_CHALLENGE 16
+/* EIDs defined by IEEE 802.11h - START */
+#define WLAN_EID_PWR_CONSTRAINT 32
+#define WLAN_EID_PWR_CAPABILITY 33
+#define WLAN_EID_TPC_REQUEST 34
+#define WLAN_EID_TPC_REPORT 35
+#define WLAN_EID_SUPPORTED_CHANNELS 36
+#define WLAN_EID_CHANNEL_SWITCH 37
+#define WLAN_EID_MEASURE_REQUEST 38
+#define WLAN_EID_MEASURE_REPORT 39
+#define WLAN_EID_QUITE 40
+#define WLAN_EID_IBSS_DFS 41
+/* EIDs defined by IEEE 802.11h - END */
+#define WLAN_EID_ERP_INFO 42
+#define WLAN_EID_HT_CAP 45
+#define WLAN_EID_RSN 48
+#define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_MOBILITY_DOMAIN 54
+#define WLAN_EID_FAST_BSS_TRANSITION 55
+#define WLAN_EID_TIMEOUT_INTERVAL 56
+#define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_HT_OPERATION 61
+#define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
+#define WLAN_EID_20_40_BSS_COEXISTENCE 72
+#define WLAN_EID_20_40_BSS_INTOLERANT 73
+#define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
+#define WLAN_EID_MMIE 76
+#define WLAN_EID_VENDOR_SPECIFIC 221
+
+
+/* Action frame categories (IEEE 802.11-2007, 7.3.1.11, Table 7-24) */
+#define WLAN_ACTION_SPECTRUM_MGMT 0
+#define WLAN_ACTION_QOS 1
+#define WLAN_ACTION_DLS 2
+#define WLAN_ACTION_BLOCK_ACK 3
+#define WLAN_ACTION_PUBLIC 4
+#define WLAN_ACTION_RADIO_MEASUREMENT 5
+#define WLAN_ACTION_FT 6
+#define WLAN_ACTION_HT 7
+#define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
+
+/* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
+#define WLAN_SA_QUERY_REQUEST 0
+#define WLAN_SA_QUERY_RESPONSE 1
+
+#define WLAN_SA_QUERY_TR_ID_LEN 2
+
+/* Timeout Interval Type */
+#define WLAN_TIMEOUT_REASSOC_DEADLINE 1
+#define WLAN_TIMEOUT_KEY_LIFETIME 2
+#define WLAN_TIMEOUT_ASSOC_COMEBACK 3
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+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;
+
+#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))
+
+struct ieee80211_mgmt {
+       le16 frame_control;
+       le16 duration;
+       u8 da[6];
+       u8 sa[6];
+       u8 bssid[6];
+       le16 seq_ctrl;
+       union {
+               struct {
+                       le16 auth_alg;
+                       le16 auth_transaction;
+                       le16 status_code;
+                       /* possibly followed by Challenge text */
+                       u8 variable[0];
+               } STRUCT_PACKED auth;
+               struct {
+                       le16 reason_code;
+               } STRUCT_PACKED deauth;
+               struct {
+                       le16 capab_info;
+                       le16 listen_interval;
+                       /* followed by SSID and Supported rates */
+                       u8 variable[0];
+               } STRUCT_PACKED assoc_req;
+               struct {
+                       le16 capab_info;
+                       le16 status_code;
+                       le16 aid;
+                       /* followed by Supported rates */
+                       u8 variable[0];
+               } STRUCT_PACKED assoc_resp, reassoc_resp;
+               struct {
+                       le16 capab_info;
+                       le16 listen_interval;
+                       u8 current_ap[6];
+                       /* followed by SSID and Supported rates */
+                       u8 variable[0];
+               } STRUCT_PACKED reassoc_req;
+               struct {
+                       le16 reason_code;
+               } STRUCT_PACKED disassoc;
+               struct {
+                       u8 timestamp[8];
+                       le16 beacon_int;
+                       le16 capab_info;
+                       /* followed by some of SSID, Supported rates,
+                        * FH Params, DS Params, CF Params, IBSS Params, TIM */
+                       u8 variable[0];
+               } STRUCT_PACKED beacon;
+               struct {
+                       /* only variable items: SSID, Supported rates */
+                       u8 variable[0];
+               } STRUCT_PACKED probe_req;
+               struct {
+                       u8 timestamp[8];
+                       le16 beacon_int;
+                       le16 capab_info;
+                       /* followed by some of SSID, Supported rates,
+                        * FH Params, DS Params, CF Params, IBSS Params */
+                       u8 variable[0];
+               } STRUCT_PACKED probe_resp;
+               struct {
+                       u8 category;
+                       union {
+                               struct {
+                                       u8 action_code;
+                                       u8 dialog_token;
+                                       u8 status_code;
+                                       u8 variable[0];
+                               } STRUCT_PACKED wmm_action;
+                               struct{
+                                       u8 action_code;
+                                       u8 element_id;
+                                       u8 length;
+                                       u8 switch_mode;
+                                       u8 new_chan;
+                                       u8 switch_count;
+                               } STRUCT_PACKED chan_switch;
+                               struct {
+                                       u8 action;
+                                       u8 sta_addr[ETH_ALEN];
+                                       u8 target_ap_addr[ETH_ALEN];
+                                       u8 variable[0]; /* FT Request */
+                               } STRUCT_PACKED ft_action_req;
+                               struct {
+                                       u8 action;
+                                       u8 sta_addr[ETH_ALEN];
+                                       u8 target_ap_addr[ETH_ALEN];
+                                       le16 status_code;
+                                       u8 variable[0]; /* FT Request */
+                               } STRUCT_PACKED ft_action_resp;
+                               struct {
+                                       u8 action;
+                                       u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+                               } STRUCT_PACKED sa_query_req;
+                               struct {
+                                       u8 action; /* */
+                                       u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+                               } STRUCT_PACKED sa_query_resp;
+                       } u;
+               } STRUCT_PACKED action;
+       } u;
+} STRUCT_PACKED;
+
+
+struct ieee80211_ht_capabilities {
+       le16 ht_capabilities_info;
+       u8 a_mpdu_params;
+       u8 supported_mcs_set[16];
+       le16 ht_extended_capabilities;
+       le32 tx_bf_capability_info;
+       u8 asel_capabilities;
+} STRUCT_PACKED;
+
+
+struct ieee80211_ht_operation {
+       u8 control_chan;
+       u8 ht_param;
+       le16 operation_mode;
+       le16 stbc_param;
+       u8 basic_set[16];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define ERP_INFO_NON_ERP_PRESENT BIT(0)
+#define ERP_INFO_USE_PROTECTION BIT(1)
+#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
+
+
+#define HT_CAP_INFO_LDPC_CODING_CAP            ((u16) BIT(0))
+#define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET     ((u16) BIT(1))
+#define HT_CAP_INFO_SMPS_MASK                  ((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_SMPS_STATIC                        ((u16) 0)
+#define HT_CAP_INFO_SMPS_DYNAMIC               ((u16) BIT(2))
+#define HT_CAP_INFO_SMPS_DISABLED              ((u16) (BIT(2) | BIT(3)))
+#define HT_CAP_INFO_GREEN_FIELD                        ((u16) BIT(4))
+#define HT_CAP_INFO_SHORT_GI20MHZ              ((u16) BIT(5))
+#define HT_CAP_INFO_SHORT_GI40MHZ              ((u16) BIT(6))
+#define HT_CAP_INFO_TX_STBC                    ((u16) BIT(7))
+#define HT_CAP_INFO_RX_STBC_MASK               ((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_RX_STBC_1                  ((u16) BIT(8))
+#define HT_CAP_INFO_RX_STBC_12                 ((u16) BIT(9))
+#define HT_CAP_INFO_RX_STBC_123                        ((u16) (BIT(8) | BIT(9)))
+#define HT_CAP_INFO_DELAYED_BA                 ((u16) BIT(10))
+#define HT_CAP_INFO_MAX_AMSDU_SIZE             ((u16) BIT(11))
+#define HT_CAP_INFO_DSSS_CCK40MHZ              ((u16) BIT(12))
+#define HT_CAP_INFO_PSMP_SUPP                  ((u16) BIT(13))
+#define HT_CAP_INFO_40MHZ_INTOLERANT           ((u16) BIT(14))
+#define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT  ((u16) BIT(15))
+
+
+#define EXT_HT_CAP_INFO_PCO                    ((u16) BIT(0))
+#define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET      1
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET    8
+#define EXT_HT_CAP_INFO_HTC_SUPPORTED          ((u16) BIT(10))
+#define EXT_HT_CAP_INFO_RD_RESPONDER           ((u16) BIT(11))
+
+
+#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
+#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3))
+#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4))
+#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5))
+#define TX_BEAMFORM_CAP_CALIB_OFFSET 6
+#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9))
+#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10))
+#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+
+
+#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6))
+
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK       ((u8) BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE          ((u8) BIT(0))
+#define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW          ((u8) BIT(0) | BIT(1))
+#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH          ((u8) BIT(2))
+#define HT_INFO_HT_PARAM_RIFS_MODE                     ((u8) BIT(3))
+#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY              ((u8) BIT(4))
+#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY      ((u8) BIT(5))
+
+
+#define OP_MODE_PURE                    0
+#define OP_MODE_MAY_BE_LEGACY_STAS      1
+#define OP_MODE_20MHZ_HT_STA_ASSOCED    2
+#define OP_MODE_MIXED                   3
+
+#define HT_INFO_OPERATION_MODE_OP_MODE_MASK    \
+               ((le16) (0x0001 | 0x0002))
+#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET          0
+#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT     ((u8) BIT(2))
+#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT    ((u8) BIT(3))
+#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT      ((u8) BIT(4))
+
+#define HT_INFO_STBC_PARAM_DUAL_BEACON                 ((u16) BIT(6))
+#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT           ((u16) BIT(7))
+#define HT_INFO_STBC_PARAM_SECONDARY_BCN               ((u16) BIT(8))
+#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED   ((u16) BIT(9))
+#define HT_INFO_STBC_PARAM_PCO_ACTIVE                  ((u16) BIT(10))
+#define HT_INFO_STBC_PARAM_PCO_PHASE                   ((u16) BIT(11))
+
+
+#define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
+                               * 00:50:F2 */
+#define WPA_IE_VENDOR_TYPE 0x0050f201
+#define WPS_IE_VENDOR_TYPE 0x0050f204
+
+#define WMM_OUI_TYPE 2
+#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
+#define WMM_OUI_SUBTYPE_PARAMETER_ELEMENT 1
+#define WMM_OUI_SUBTYPE_TSPEC_ELEMENT 2
+#define WMM_VERSION 1
+
+#define WMM_ACTION_CODE_ADDTS_REQ 0
+#define WMM_ACTION_CODE_ADDTS_RESP 1
+#define WMM_ACTION_CODE_DELTS 2
+
+#define WMM_ADDTS_STATUS_ADMISSION_ACCEPTED 0
+#define WMM_ADDTS_STATUS_INVALID_PARAMETERS 1
+/* 2 - Reserved */
+#define WMM_ADDTS_STATUS_REFUSED 3
+/* 4-255 - Reserved */
+
+/* WMM TSPEC Direction Field Values */
+#define WMM_TSPEC_DIRECTION_UPLINK 0
+#define WMM_TSPEC_DIRECTION_DOWNLINK 1
+/* 2 - Reserved */
+#define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3
+
+/*
+ * WMM Information Element (used in (Re)Association Request frames; may also be
+ * used in Beacon frames)
+ */
+struct wmm_information_element {
+       /* Element ID: 221 (0xdd); Length: 7 */
+       /* required fields for WMM version 1 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 0 */
+       u8 version; /* 1 for WMM version 1.0 */
+       u8 qos_info; /* AP/STA specific QoS info */
+
+} STRUCT_PACKED;
+
+#define WMM_AC_AIFSN_MASK 0x0f
+#define WMM_AC_AIFNS_SHIFT 0
+#define WMM_AC_ACM 0x10
+#define WMM_AC_ACI_MASK 0x60
+#define WMM_AC_ACI_SHIFT 5
+
+#define WMM_AC_ECWMIN_MASK 0x0f
+#define WMM_AC_ECWMIN_SHIFT 0
+#define WMM_AC_ECWMAX_MASK 0xf0
+#define WMM_AC_ECWMAX_SHIFT 4
+
+struct wmm_ac_parameter {
+       u8 aci_aifsn; /* AIFSN, ACM, ACI */
+       u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */
+       le16 txop_limit;
+}  STRUCT_PACKED;
+
+/*
+ * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association
+ * Response frmaes)
+ */
+struct wmm_parameter_element {
+       /* Element ID: 221 (0xdd); Length: 24 */
+       /* required fields for WMM version 1 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 1 */
+       u8 version; /* 1 for WMM version 1.0 */
+       u8 qos_info; /* AP/STA specif QoS info */
+       u8 reserved; /* 0 */
+       struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */
+
+} STRUCT_PACKED;
+
+/* WMM TSPEC Element */
+struct wmm_tspec_element {
+       u8 eid; /* 221 = 0xdd */
+       u8 length; /* 6 + 55 = 61 */
+       u8 oui[3]; /* 00:50:f2 */
+       u8 oui_type; /* 2 */
+       u8 oui_subtype; /* 2 */
+       u8 version; /* 1 */
+       /* WMM TSPEC body (55 octets): */
+       u8 ts_info[3];
+       le16 nominal_msdu_size;
+       le16 maximum_msdu_size;
+       le32 minimum_service_interval;
+       le32 maximum_service_interval;
+       le32 inactivity_interval;
+       le32 suspension_interval;
+       le32 service_start_time;
+       le32 minimum_data_rate;
+       le32 mean_data_rate;
+       le32 peak_data_rate;
+       le32 maximum_burst_size;
+       le32 delay_bound;
+       le32 minimum_phy_rate;
+       le16 surplus_bandwidth_allowance;
+       le16 medium_time;
+} STRUCT_PACKED;
+
+
+/* Access Categories / ACI to AC coding */
+enum {
+       WMM_AC_BE = 0 /* Best Effort */,
+       WMM_AC_BK = 1 /* Background */,
+       WMM_AC_VI = 2 /* Video */,
+       WMM_AC_VO = 3 /* Voice */
+};
+
+
+#define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+
+#define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
+
+/* cipher suite selectors */
+#define WLAN_CIPHER_SUITE_USE_GROUP    0x000FAC00
+#define WLAN_CIPHER_SUITE_WEP40                0x000FAC01
+#define WLAN_CIPHER_SUITE_TKIP         0x000FAC02
+/* reserved:                           0x000FAC03 */
+#define WLAN_CIPHER_SUITE_CCMP         0x000FAC04
+#define WLAN_CIPHER_SUITE_WEP104       0x000FAC05
+#define WLAN_CIPHER_SUITE_AES_CMAC     0x000FAC06
+
+/* AKM suite selectors */
+#define WLAN_AKM_SUITE_8021X           0x000FAC01
+#define WLAN_AKM_SUITE_PSK             0x000FAC02
+
+#endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/privsep_commands.h b/src/common/privsep_commands.h
new file mode 100644 (file)
index 0000000..cc900be
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * WPA Supplicant - privilege separation commands
+ * Copyright (c) 2007-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.
+ */
+
+#ifndef PRIVSEP_COMMANDS_H
+#define PRIVSEP_COMMANDS_H
+
+enum privsep_cmd {
+       PRIVSEP_CMD_REGISTER,
+       PRIVSEP_CMD_UNREGISTER,
+       PRIVSEP_CMD_SCAN,
+       PRIVSEP_CMD_GET_SCAN_RESULTS,
+       PRIVSEP_CMD_ASSOCIATE,
+       PRIVSEP_CMD_GET_BSSID,
+       PRIVSEP_CMD_GET_SSID,
+       PRIVSEP_CMD_SET_KEY,
+       PRIVSEP_CMD_GET_CAPA,
+       PRIVSEP_CMD_L2_REGISTER,
+       PRIVSEP_CMD_L2_UNREGISTER,
+       PRIVSEP_CMD_L2_NOTIFY_AUTH_START,
+       PRIVSEP_CMD_L2_SEND,
+       PRIVSEP_CMD_SET_COUNTRY,
+};
+
+struct privsep_cmd_associate
+{
+       u8 bssid[ETH_ALEN];
+       u8 ssid[32];
+       size_t ssid_len;
+       int freq;
+       int pairwise_suite;
+       int group_suite;
+       int key_mgmt_suite;
+       int auth_alg;
+       int mode;
+       size_t wpa_ie_len;
+       /* followed by wpa_ie_len bytes of wpa_ie */
+};
+
+struct privsep_cmd_set_key
+{
+       int alg;
+       u8 addr[ETH_ALEN];
+       int key_idx;
+       int set_tx;
+       u8 seq[8];
+       size_t seq_len;
+       u8 key[32];
+       size_t key_len;
+};
+
+enum privsep_event {
+       PRIVSEP_EVENT_SCAN_RESULTS,
+       PRIVSEP_EVENT_ASSOC,
+       PRIVSEP_EVENT_DISASSOC,
+       PRIVSEP_EVENT_ASSOCINFO,
+       PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
+       PRIVSEP_EVENT_INTERFACE_STATUS,
+       PRIVSEP_EVENT_PMKID_CANDIDATE,
+       PRIVSEP_EVENT_STKSTART,
+       PRIVSEP_EVENT_FT_RESPONSE,
+       PRIVSEP_EVENT_RX_EAPOL,
+};
+
+#endif /* PRIVSEP_COMMANDS_H */
diff --git a/src/common/version.h b/src/common/version.h
new file mode 100644 (file)
index 0000000..02f34be
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef VERSION_H
+#define VERSION_H
+
+#define VERSION_STR "0.7.3"
+
+#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
new file mode 100644 (file)
index 0000000..b295f31
--- /dev/null
@@ -0,0 +1,787 @@
+/*
+ * WPA/RSN - Shared functions for supplicant and 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 "common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_11_defs.h"
+#include "defs.h"
+#include "wpa_common.h"
+
+
+/**
+ * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
+ * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
+ * @buf: Pointer to the beginning of the EAPOL header (version field)
+ * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
+ * @mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ * Returns: 0 on success, -1 on failure
+ *
+ * Calculate EAPOL-Key MIC for an EAPOL-Key packet. The EAPOL-Key MIC field has
+ * to be cleared (all zeroes) when calling this function.
+ *
+ * Note: 'IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames' has an error in the
+ * description of the Key MIC calculation. It includes packet data from the
+ * beginning of the EAPOL-Key header, not EAPOL header. This incorrect change
+ * happened during final editing of the standard and the correct behavior is
+ * defined in the last draft (IEEE 802.11i/D10).
+ */
+int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
+                     u8 *mic)
+{
+       u8 hash[SHA1_MAC_LEN];
+
+       switch (ver) {
+       case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+               return hmac_md5(key, 16, buf, len, mic);
+       case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+               if (hmac_sha1(key, 16, buf, len, hash))
+                       return -1;
+               os_memcpy(mic, hash, MD5_MAC_LEN);
+               break;
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+       case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+               return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_pmk_to_ptk - Calculate PTK from PMK, addresses, and nonces
+ * @pmk: Pairwise master key
+ * @pmk_len: Length of PMK
+ * @label: Label to use in derivation
+ * @addr1: AA or SA
+ * @addr2: SA or AA
+ * @nonce1: ANonce or SNonce
+ * @nonce2: SNonce or ANonce
+ * @ptk: Buffer for pairwise transient key
+ * @ptk_len: Length of PTK
+ * @use_sha256: Whether to use SHA256-based KDF
+ *
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ * PTK = PRF-X(PMK, "Pairwise key expansion",
+ *             Min(AA, SA) || Max(AA, SA) ||
+ *             Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ *
+ * STK = PRF-X(SMK, "Peer key expansion",
+ *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
+ *             Min(INonce, PNonce) || Max(INonce, PNonce))
+ */
+void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+                   const u8 *addr1, const u8 *addr2,
+                   const u8 *nonce1, const u8 *nonce2,
+                   u8 *ptk, size_t ptk_len, int use_sha256)
+{
+       u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+
+       if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
+               os_memcpy(data, addr1, ETH_ALEN);
+               os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
+       } else {
+               os_memcpy(data, addr2, ETH_ALEN);
+               os_memcpy(data + ETH_ALEN, addr1, ETH_ALEN);
+       }
+
+       if (os_memcmp(nonce1, nonce2, WPA_NONCE_LEN) < 0) {
+               os_memcpy(data + 2 * ETH_ALEN, nonce1, WPA_NONCE_LEN);
+               os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce2,
+                         WPA_NONCE_LEN);
+       } else {
+               os_memcpy(data + 2 * ETH_ALEN, nonce2, WPA_NONCE_LEN);
+               os_memcpy(data + 2 * ETH_ALEN + WPA_NONCE_LEN, nonce1,
+                         WPA_NONCE_LEN);
+       }
+
+#ifdef CONFIG_IEEE80211W
+       if (use_sha256)
+               sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+                          ptk, ptk_len);
+       else
+#endif /* CONFIG_IEEE80211W */
+               sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk,
+                        ptk_len);
+
+       wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
+                  MAC2STR(addr1), MAC2STR(addr2));
+       wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
+              u8 transaction_seqnum, const u8 *mdie, size_t mdie_len,
+              const u8 *ftie, size_t ftie_len,
+              const u8 *rsnie, size_t rsnie_len,
+              const u8 *ric, size_t ric_len, u8 *mic)
+{
+       u8 *buf, *pos;
+       size_t buf_len;
+
+       buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len;
+       buf = os_malloc(buf_len);
+       if (buf == NULL)
+               return -1;
+
+       pos = buf;
+       os_memcpy(pos, sta_addr, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, ap_addr, ETH_ALEN);
+       pos += ETH_ALEN;
+       *pos++ = transaction_seqnum;
+       if (rsnie) {
+               os_memcpy(pos, rsnie, rsnie_len);
+               pos += rsnie_len;
+       }
+       if (mdie) {
+               os_memcpy(pos, mdie, mdie_len);
+               pos += mdie_len;
+       }
+       if (ftie) {
+               struct rsn_ftie *_ftie;
+               os_memcpy(pos, ftie, ftie_len);
+               if (ftie_len < 2 + sizeof(*_ftie)) {
+                       os_free(buf);
+                       return -1;
+               }
+               _ftie = (struct rsn_ftie *) (pos + 2);
+               os_memset(_ftie->mic, 0, sizeof(_ftie->mic));
+               pos += ftie_len;
+       }
+       if (ric) {
+               os_memcpy(pos, ric, ric_len);
+               pos += ric_len;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "FT: MIC data", buf, pos - buf);
+       if (omac1_aes_128(kck, buf, pos - buf, mic)) {
+               os_free(buf);
+               return -1;
+       }
+
+       os_free(buf);
+
+       return 0;
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifndef CONFIG_NO_WPA2
+static int rsn_selector_to_bitfield(const u8 *s)
+{
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NONE)
+               return WPA_CIPHER_NONE;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP40)
+               return WPA_CIPHER_WEP40;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_TKIP)
+               return WPA_CIPHER_TKIP;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP)
+               return WPA_CIPHER_CCMP;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_WEP104)
+               return WPA_CIPHER_WEP104;
+#ifdef CONFIG_IEEE80211W
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_AES_128_CMAC)
+               return WPA_CIPHER_AES_128_CMAC;
+#endif /* CONFIG_IEEE80211W */
+       return 0;
+}
+
+
+static int rsn_key_mgmt_to_bitfield(const u8 *s)
+{
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_UNSPEC_802_1X)
+               return WPA_KEY_MGMT_IEEE8021X;
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X)
+               return WPA_KEY_MGMT_PSK;
+#ifdef CONFIG_IEEE80211R
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_802_1X)
+               return WPA_KEY_MGMT_FT_IEEE8021X;
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_PSK)
+               return WPA_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SHA256)
+               return WPA_KEY_MGMT_IEEE8021X_SHA256;
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_PSK_SHA256)
+               return WPA_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+       return 0;
+}
+#endif /* CONFIG_NO_WPA2 */
+
+
+/**
+ * wpa_parse_wpa_ie_rsn - Parse RSN IE
+ * @rsn_ie: Buffer containing RSN IE
+ * @rsn_ie_len: RSN IE buffer length (including IE number and length octets)
+ * @data: Pointer to structure that will be filled in with parsed data
+ * Returns: 0 on success, <0 on failure
+ */
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+                        struct wpa_ie_data *data)
+{
+#ifndef CONFIG_NO_WPA2
+       const struct rsn_ie_hdr *hdr;
+       const u8 *pos;
+       int left;
+       int i, count;
+
+       os_memset(data, 0, sizeof(*data));
+       data->proto = WPA_PROTO_RSN;
+       data->pairwise_cipher = WPA_CIPHER_CCMP;
+       data->group_cipher = WPA_CIPHER_CCMP;
+       data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+       data->capabilities = 0;
+       data->pmkid = NULL;
+       data->num_pmkid = 0;
+#ifdef CONFIG_IEEE80211W
+       data->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+#else /* CONFIG_IEEE80211W */
+       data->mgmt_group_cipher = 0;
+#endif /* CONFIG_IEEE80211W */
+
+       if (rsn_ie_len == 0) {
+               /* No RSN IE - fail silently */
+               return -1;
+       }
+
+       if (rsn_ie_len < sizeof(struct rsn_ie_hdr)) {
+               wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+                          __func__, (unsigned long) rsn_ie_len);
+               return -1;
+       }
+
+       hdr = (const struct rsn_ie_hdr *) rsn_ie;
+
+       if (hdr->elem_id != WLAN_EID_RSN ||
+           hdr->len != rsn_ie_len - 2 ||
+           WPA_GET_LE16(hdr->version) != RSN_VERSION) {
+               wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+                          __func__);
+               return -2;
+       }
+
+       pos = (const u8 *) (hdr + 1);
+       left = rsn_ie_len - sizeof(*hdr);
+
+       if (left >= RSN_SELECTOR_LEN) {
+               data->group_cipher = rsn_selector_to_bitfield(pos);
+#ifdef CONFIG_IEEE80211W
+               if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) {
+                       wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group "
+                                  "cipher", __func__);
+                       return -1;
+               }
+#endif /* CONFIG_IEEE80211W */
+               pos += RSN_SELECTOR_LEN;
+               left -= RSN_SELECTOR_LEN;
+       } else if (left > 0) {
+               wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+                          __func__, left);
+               return -3;
+       }
+
+       if (left >= 2) {
+               data->pairwise_cipher = 0;
+               count = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+               if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+                       wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+                                  "count %u left %u", __func__, count, left);
+                       return -4;
+               }
+               for (i = 0; i < count; i++) {
+                       data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
+                       pos += RSN_SELECTOR_LEN;
+                       left -= RSN_SELECTOR_LEN;
+               }
+#ifdef CONFIG_IEEE80211W
+               if (data->pairwise_cipher & WPA_CIPHER_AES_128_CMAC) {
+                       wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as "
+                                  "pairwise cipher", __func__);
+                       return -1;
+               }
+#endif /* CONFIG_IEEE80211W */
+       } else if (left == 1) {
+               wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+                          __func__);
+               return -5;
+       }
+
+       if (left >= 2) {
+               data->key_mgmt = 0;
+               count = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+               if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+                       wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+                                  "count %u left %u", __func__, count, left);
+                       return -6;
+               }
+               for (i = 0; i < count; i++) {
+                       data->key_mgmt |= rsn_key_mgmt_to_bitfield(pos);
+                       pos += RSN_SELECTOR_LEN;
+                       left -= RSN_SELECTOR_LEN;
+               }
+       } else if (left == 1) {
+               wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+                          __func__);
+               return -7;
+       }
+
+       if (left >= 2) {
+               data->capabilities = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+       }
+
+       if (left >= 2) {
+               data->num_pmkid = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+               if (left < (int) data->num_pmkid * PMKID_LEN) {
+                       wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
+                                  "(num_pmkid=%lu left=%d)",
+                                  __func__, (unsigned long) data->num_pmkid,
+                                  left);
+                       data->num_pmkid = 0;
+                       return -9;
+               } else {
+                       data->pmkid = pos;
+                       pos += data->num_pmkid * PMKID_LEN;
+                       left -= data->num_pmkid * PMKID_LEN;
+               }
+       }
+
+#ifdef CONFIG_IEEE80211W
+       if (left >= 4) {
+               data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
+               if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+                       wpa_printf(MSG_DEBUG, "%s: Unsupported management "
+                                  "group cipher 0x%x", __func__,
+                                  data->mgmt_group_cipher);
+                       return -10;
+               }
+               pos += RSN_SELECTOR_LEN;
+               left -= RSN_SELECTOR_LEN;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       if (left > 0) {
+               wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+                          __func__, left);
+       }
+
+       return 0;
+#else /* CONFIG_NO_WPA2 */
+       return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+/**
+ * wpa_derive_pmk_r0 - Derive PMK-R0 and PMKR0Name
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.3
+ */
+void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+                      const u8 *ssid, size_t ssid_len,
+                      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+                      const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name)
+{
+       u8 buf[1 + WPA_MAX_SSID_LEN + MOBILITY_DOMAIN_ID_LEN + 1 +
+              FT_R0KH_ID_MAX_LEN + ETH_ALEN];
+       u8 *pos, r0_key_data[48], hash[32];
+       const u8 *addr[2];
+       size_t len[2];
+
+       /*
+        * R0-Key-Data = KDF-384(XXKey, "FT-R0",
+        *                       SSIDlength || SSID || MDID || R0KHlength ||
+        *                       R0KH-ID || S0KH-ID)
+        * XXKey is either the second 256 bits of MSK or PSK.
+        * PMK-R0 = L(R0-Key-Data, 0, 256)
+        * PMK-R0Name-Salt = L(R0-Key-Data, 256, 128)
+        */
+       if (ssid_len > WPA_MAX_SSID_LEN || r0kh_id_len > FT_R0KH_ID_MAX_LEN)
+               return;
+       pos = buf;
+       *pos++ = ssid_len;
+       os_memcpy(pos, ssid, ssid_len);
+       pos += ssid_len;
+       os_memcpy(pos, mdid, MOBILITY_DOMAIN_ID_LEN);
+       pos += MOBILITY_DOMAIN_ID_LEN;
+       *pos++ = r0kh_id_len;
+       os_memcpy(pos, r0kh_id, r0kh_id_len);
+       pos += r0kh_id_len;
+       os_memcpy(pos, s0kh_id, ETH_ALEN);
+       pos += ETH_ALEN;
+
+       sha256_prf(xxkey, xxkey_len, "FT-R0", buf, pos - buf,
+                  r0_key_data, sizeof(r0_key_data));
+       os_memcpy(pmk_r0, r0_key_data, PMK_LEN);
+
+       /*
+        * PMKR0Name = Truncate-128(SHA-256("FT-R0N" || PMK-R0Name-Salt)
+        */
+       addr[0] = (const u8 *) "FT-R0N";
+       len[0] = 6;
+       addr[1] = r0_key_data + PMK_LEN;
+       len[1] = 16;
+
+       sha256_vector(2, addr, len, hash);
+       os_memcpy(pmk_r0_name, hash, WPA_PMK_NAME_LEN);
+}
+
+
+/**
+ * wpa_derive_pmk_r1_name - Derive PMKR1Name
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+                           const u8 *s1kh_id, u8 *pmk_r1_name)
+{
+       u8 hash[32];
+       const u8 *addr[4];
+       size_t len[4];
+
+       /*
+        * PMKR1Name = Truncate-128(SHA-256("FT-R1N" || PMKR0Name ||
+        *                                  R1KH-ID || S1KH-ID))
+        */
+       addr[0] = (const u8 *) "FT-R1N";
+       len[0] = 6;
+       addr[1] = pmk_r0_name;
+       len[1] = WPA_PMK_NAME_LEN;
+       addr[2] = r1kh_id;
+       len[2] = FT_R1KH_ID_LEN;
+       addr[3] = s1kh_id;
+       len[3] = ETH_ALEN;
+
+       sha256_vector(4, addr, len, hash);
+       os_memcpy(pmk_r1_name, hash, WPA_PMK_NAME_LEN);
+}
+
+
+/**
+ * wpa_derive_pmk_r1 - Derive PMK-R1 and PMKR1Name from PMK-R0
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.4
+ */
+void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+                      const u8 *r1kh_id, const u8 *s1kh_id,
+                      u8 *pmk_r1, u8 *pmk_r1_name)
+{
+       u8 buf[FT_R1KH_ID_LEN + ETH_ALEN];
+       u8 *pos;
+
+       /* PMK-R1 = KDF-256(PMK-R0, "FT-R1", R1KH-ID || S1KH-ID) */
+       pos = buf;
+       os_memcpy(pos, r1kh_id, FT_R1KH_ID_LEN);
+       pos += FT_R1KH_ID_LEN;
+       os_memcpy(pos, s1kh_id, ETH_ALEN);
+       pos += ETH_ALEN;
+
+       sha256_prf(pmk_r0, PMK_LEN, "FT-R1", buf, pos - buf, pmk_r1, PMK_LEN);
+
+       wpa_derive_pmk_r1_name(pmk_r0_name, r1kh_id, s1kh_id, pmk_r1_name);
+}
+
+
+/**
+ * wpa_pmk_r1_to_ptk - Derive PTK and PTKName from PMK-R1
+ *
+ * IEEE Std 802.11r-2008 - 8.5.1.5.5
+ */
+void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+                      const u8 *sta_addr, const u8 *bssid,
+                      const u8 *pmk_r1_name,
+                      u8 *ptk, size_t ptk_len, u8 *ptk_name)
+{
+       u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
+       u8 *pos, hash[32];
+       const u8 *addr[6];
+       size_t len[6];
+
+       /*
+        * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
+        *                  BSSID || STA-ADDR)
+        */
+       pos = buf;
+       os_memcpy(pos, snonce, WPA_NONCE_LEN);
+       pos += WPA_NONCE_LEN;
+       os_memcpy(pos, anonce, WPA_NONCE_LEN);
+       pos += WPA_NONCE_LEN;
+       os_memcpy(pos, bssid, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, sta_addr, ETH_ALEN);
+       pos += ETH_ALEN;
+
+       sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len);
+
+       /*
+        * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
+        *                                ANonce || BSSID || STA-ADDR))
+        */
+       addr[0] = pmk_r1_name;
+       len[0] = WPA_PMK_NAME_LEN;
+       addr[1] = (const u8 *) "FT-PTKN";
+       len[1] = 7;
+       addr[2] = snonce;
+       len[2] = WPA_NONCE_LEN;
+       addr[3] = anonce;
+       len[3] = WPA_NONCE_LEN;
+       addr[4] = bssid;
+       len[4] = ETH_ALEN;
+       addr[5] = sta_addr;
+       len[5] = ETH_ALEN;
+
+       sha256_vector(6, addr, len, hash);
+       os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+/**
+ * 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);
+}
+
+
+/**
+ * wpa_cipher_txt - Convert cipher suite to a text string
+ * @cipher: Cipher suite (WPA_CIPHER_* enum)
+ * Returns: Pointer to a text string of the cipher suite name
+ */
+const char * wpa_cipher_txt(int cipher)
+{
+       switch (cipher) {
+       case WPA_CIPHER_NONE:
+               return "NONE";
+       case WPA_CIPHER_WEP40:
+               return "WEP-40";
+       case WPA_CIPHER_WEP104:
+               return "WEP-104";
+       case WPA_CIPHER_TKIP:
+               return "TKIP";
+       case WPA_CIPHER_CCMP:
+               return "CCMP";
+       case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP:
+               return "CCMP+TKIP";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+/**
+ * wpa_key_mgmt_txt - Convert key management suite to a text string
+ * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum)
+ * @proto: WPA/WPA2 version (WPA_PROTO_*)
+ * Returns: Pointer to a text string of the key management suite name
+ */
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
+{
+       switch (key_mgmt) {
+       case WPA_KEY_MGMT_IEEE8021X:
+               if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+                       return "WPA2+WPA/IEEE 802.1X/EAP";
+               return proto == WPA_PROTO_RSN ?
+                       "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP";
+       case WPA_KEY_MGMT_PSK:
+               if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA))
+                       return "WPA2-PSK+WPA-PSK";
+               return proto == WPA_PROTO_RSN ?
+                       "WPA2-PSK" : "WPA-PSK";
+       case WPA_KEY_MGMT_NONE:
+               return "NONE";
+       case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+               return "IEEE 802.1X (no WPA)";
+#ifdef CONFIG_IEEE80211R
+       case WPA_KEY_MGMT_FT_IEEE8021X:
+               return "FT-EAP";
+       case WPA_KEY_MGMT_FT_PSK:
+               return "FT-PSK";
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       case WPA_KEY_MGMT_IEEE8021X_SHA256:
+               return "WPA2-EAP-SHA256";
+       case WPA_KEY_MGMT_PSK_SHA256:
+               return "WPA2-PSK-SHA256";
+#endif /* CONFIG_IEEE80211W */
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+int wpa_compare_rsn_ie(int ft_initial_assoc,
+                      const u8 *ie1, size_t ie1len,
+                      const u8 *ie2, size_t ie2len)
+{
+       if (ie1 == NULL || ie2 == NULL)
+               return -1;
+
+       if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0)
+               return 0; /* identical IEs */
+
+#ifdef CONFIG_IEEE80211R
+       if (ft_initial_assoc) {
+               struct wpa_ie_data ie1d, ie2d;
+               /*
+                * The PMKID-List in RSN IE is different between Beacon/Probe
+                * Response/(Re)Association Request frames and EAPOL-Key
+                * messages in FT initial mobility domain association. Allow
+                * for this, but verify that other parts of the RSN IEs are
+                * identical.
+                */
+               if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 ||
+                   wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0)
+                       return -1;
+               if (ie1d.proto == ie2d.proto &&
+                   ie1d.pairwise_cipher == ie2d.pairwise_cipher &&
+                   ie1d.group_cipher == ie2d.group_cipher &&
+                   ie1d.key_mgmt == ie2d.key_mgmt &&
+                   ie1d.capabilities == ie2d.capabilities &&
+                   ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher)
+                       return 0;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       return -1;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
+{
+       u8 *start, *end, *rpos, *rend;
+       int added = 0;
+
+       start = ies;
+       end = ies + ies_len;
+
+       while (start < end) {
+               if (*start == WLAN_EID_RSN)
+                       break;
+               start += 2 + start[1];
+       }
+       if (start >= end) {
+               wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in "
+                          "IEs data");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification",
+                   start, 2 + start[1]);
+
+       /* Find start of PMKID-Count */
+       rpos = start + 2;
+       rend = rpos + start[1];
+
+       /* Skip Version and Group Data Cipher Suite */
+       rpos += 2 + 4;
+       /* Skip Pairwise Cipher Suite Count and List */
+       rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+       /* Skip AKM Suite Count and List */
+       rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN;
+
+       if (rpos == rend) {
+               /* Add RSN Capabilities */
+               os_memmove(rpos + 2, rpos, end - rpos);
+               *rpos++ = 0;
+               *rpos++ = 0;
+       } else {
+               /* Skip RSN Capabilities */
+               rpos += 2;
+               if (rpos > rend) {
+                       wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in "
+                                  "IEs data");
+                       return -1;
+               }
+       }
+
+       if (rpos == rend) {
+               /* No PMKID-Count field included; add it */
+               os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos);
+               WPA_PUT_LE16(rpos, 1);
+               rpos += 2;
+               os_memcpy(rpos, pmkid, PMKID_LEN);
+               added += 2 + PMKID_LEN;
+               start[1] += 2 + PMKID_LEN;
+       } else {
+               /* PMKID-Count was included; use it */
+               if (WPA_GET_LE16(rpos) != 0) {
+                       wpa_printf(MSG_ERROR, "FT: Unexpected PMKID "
+                                  "in RSN IE in EAPOL-Key data");
+                       return -1;
+               }
+               WPA_PUT_LE16(rpos, 1);
+               rpos += 2;
+               os_memmove(rpos + PMKID_LEN, rpos, end - rpos);
+               os_memcpy(rpos, pmkid, PMKID_LEN);
+               added += PMKID_LEN;
+               start[1] += PMKID_LEN;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification "
+                   "(PMKID inserted)", start, 2 + start[1]);
+
+       return added;
+}
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
new file mode 100644 (file)
index 0000000..fd8a79f
--- /dev/null
@@ -0,0 +1,351 @@
+/*
+ * WPA definitions shared between hostapd and wpa_supplicant
+ * 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 WPA_COMMON_H
+#define WPA_COMMON_H
+
+#define WPA_MAX_SSID_LEN 32
+
+/* IEEE 802.11i */
+#define PMKID_LEN 16
+#define PMK_LEN 32
+#define WPA_REPLAY_COUNTER_LEN 8
+#define WPA_NONCE_LEN 32
+#define WPA_KEY_RSC_LEN 8
+#define WPA_GMK_LEN 32
+#define WPA_GTK_MAX_LEN 32
+
+#define WPA_SELECTOR_LEN 4
+#define WPA_VERSION 1
+#define RSN_SELECTOR_LEN 4
+#define RSN_VERSION 1
+
+#define RSN_SELECTOR(a, b, c, d) \
+       ((((u32) (a)) << 24) | (((u32) (b)) << 16) | (((u32) (c)) << 8) | \
+        (u32) (d))
+
+#define WPA_AUTH_KEY_MGMT_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+#define WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#define WPA_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x50, 0xf2, 0)
+#define WPA_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+#define WPA_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x50, 0xf2, 2)
+#if 0
+#define WPA_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x50, 0xf2, 3)
+#endif
+#define WPA_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x50, 0xf2, 4)
+#define WPA_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x50, 0xf2, 5)
+
+
+#define RSN_AUTH_KEY_MGMT_UNSPEC_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#define RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#ifdef CONFIG_IEEE80211R
+#define RSN_AUTH_KEY_MGMT_FT_802_1X RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_AUTH_KEY_MGMT_FT_PSK RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#endif /* CONFIG_IEEE80211R */
+#define RSN_AUTH_KEY_MGMT_802_1X_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_AUTH_KEY_MGMT_PSK_SHA256 RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+
+#define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
+#define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#define RSN_CIPHER_SUITE_TKIP RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#if 0
+#define RSN_CIPHER_SUITE_WRAP RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#endif
+#define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#ifdef CONFIG_IEEE80211W
+#define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#endif /* CONFIG_IEEE80211W */
+
+/* EAPOL-Key Key Data Encapsulation
+ * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
+ */
+#define RSN_KEY_DATA_GROUPKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
+#if 0
+#define RSN_KEY_DATA_STAKEY RSN_SELECTOR(0x00, 0x0f, 0xac, 2)
+#endif
+#define RSN_KEY_DATA_MAC_ADDR RSN_SELECTOR(0x00, 0x0f, 0xac, 3)
+#define RSN_KEY_DATA_PMKID RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
+#ifdef CONFIG_PEERKEY
+#define RSN_KEY_DATA_SMK RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
+#define RSN_KEY_DATA_NONCE RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
+#define RSN_KEY_DATA_LIFETIME RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
+#define RSN_KEY_DATA_ERROR RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211W
+#define RSN_KEY_DATA_IGTK RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#endif /* CONFIG_IEEE80211W */
+
+#define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
+
+#define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
+#define RSN_SELECTOR_GET(a) WPA_GET_BE32((const u8 *) (a))
+
+#define RSN_NUM_REPLAY_COUNTERS_1 0
+#define RSN_NUM_REPLAY_COUNTERS_2 1
+#define RSN_NUM_REPLAY_COUNTERS_4 2
+#define RSN_NUM_REPLAY_COUNTERS_16 3
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_LEN 16
+#endif /* CONFIG_IEEE80211W */
+
+
+/* IEEE 802.11, 7.3.2.25.3 RSN Capabilities */
+#define WPA_CAPABILITY_PREAUTH BIT(0)
+#define WPA_CAPABILITY_NO_PAIRWISE BIT(1)
+/* B2-B3: PTKSA Replay Counter */
+/* B4-B5: GTKSA Replay Counter */
+#define WPA_CAPABILITY_MFPR BIT(6)
+#define WPA_CAPABILITY_MFPC BIT(7)
+#define WPA_CAPABILITY_PEERKEY_ENABLED BIT(9)
+
+
+/* IEEE 802.11r */
+#define MOBILITY_DOMAIN_ID_LEN 2
+#define FT_R0KH_ID_MAX_LEN 48
+#define FT_R1KH_ID_LEN 6
+#define WPA_PMK_NAME_LEN 16
+
+
+/* IEEE 802.11, 8.5.2 EAPOL-Key frames */
+#define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
+#define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
+#define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
+#define WPA_KEY_INFO_TYPE_AES_128_CMAC 3
+#define WPA_KEY_INFO_KEY_TYPE BIT(3) /* 1 = Pairwise, 0 = Group key */
+/* bit4..5 is used in WPA, but is reserved in IEEE 802.11i/RSN */
+#define WPA_KEY_INFO_KEY_INDEX_MASK (BIT(4) | BIT(5))
+#define WPA_KEY_INFO_KEY_INDEX_SHIFT 4
+#define WPA_KEY_INFO_INSTALL BIT(6) /* pairwise */
+#define WPA_KEY_INFO_TXRX BIT(6) /* group */
+#define WPA_KEY_INFO_ACK BIT(7)
+#define WPA_KEY_INFO_MIC BIT(8)
+#define WPA_KEY_INFO_SECURE BIT(9)
+#define WPA_KEY_INFO_ERROR BIT(10)
+#define WPA_KEY_INFO_REQUEST BIT(11)
+#define WPA_KEY_INFO_ENCR_KEY_DATA BIT(12) /* IEEE 802.11i/RSN only */
+#define WPA_KEY_INFO_SMK_MESSAGE BIT(13)
+
+
+struct wpa_eapol_key {
+       u8 type;
+       /* Note: key_info, key_length, and key_data_length are unaligned */
+       u8 key_info[2]; /* big endian */
+       u8 key_length[2]; /* big endian */
+       u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+       u8 key_nonce[WPA_NONCE_LEN];
+       u8 key_iv[16];
+       u8 key_rsc[WPA_KEY_RSC_LEN];
+       u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+       u8 key_mic[16];
+       u8 key_data_length[2]; /* big endian */
+       /* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+/**
+ * struct wpa_ptk - WPA Pairwise Transient Key
+ * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
+ */
+struct wpa_ptk {
+       u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */
+       u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */
+       u8 tk1[16]; /* Temporal Key 1 (TK1) */
+       union {
+               u8 tk2[16]; /* Temporal Key 2 (TK2) */
+               struct {
+                       u8 tx_mic_key[8];
+                       u8 rx_mic_key[8];
+               } auth;
+       } u;
+} STRUCT_PACKED;
+
+
+/* WPA IE version 1
+ * 00-50-f2:1 (OUI:OUI type)
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: TKIP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: TKIP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1X)
+ * WPA Capabilities (2 octets, little endian) (default: 0)
+ */
+
+struct wpa_ie_hdr {
+       u8 elem_id;
+       u8 len;
+       u8 oui[4]; /* 24-bit OUI followed by 8-bit OUI type */
+       u8 version[2]; /* little endian */
+} STRUCT_PACKED;
+
+
+/* 1/4: PMKID
+ * 2/4: RSN IE
+ * 3/4: one or two RSN IEs + GTK IE (encrypted)
+ * 4/4: empty
+ * 1/2: GTK IE (encrypted)
+ * 2/2: empty
+ */
+
+/* RSN IE version 1
+ * 0x01 0x00 (version; little endian)
+ * (all following fields are optional:)
+ * Group Suite Selector (4 octets) (default: CCMP)
+ * Pairwise Suite Count (2 octets, little endian) (default: 1)
+ * Pairwise Suite List (4 * n octets) (default: CCMP)
+ * Authenticated Key Management Suite Count (2 octets, little endian)
+ *    (default: 1)
+ * Authenticated Key Management Suite List (4 * n octets)
+ *    (default: unspec 802.1X)
+ * RSN Capabilities (2 octets, little endian) (default: 0)
+ * PMKID Count (2 octets) (default: 0)
+ * PMKID List (16 * n octets)
+ * Management Group Cipher Suite (4 octets) (default: AES-128-CMAC)
+ */
+
+struct rsn_ie_hdr {
+       u8 elem_id; /* WLAN_EID_RSN */
+       u8 len;
+       u8 version[2]; /* little endian */
+} STRUCT_PACKED;
+
+
+#ifdef CONFIG_PEERKEY
+enum {
+       STK_MUI_4WAY_STA_AP = 1,
+       STK_MUI_4WAY_STAT_STA = 2,
+       STK_MUI_GTK = 3,
+       STK_MUI_SMK = 4
+};
+
+enum {
+       STK_ERR_STA_NR = 1,
+       STK_ERR_STA_NRSN = 2,
+       STK_ERR_CPHR_NS = 3,
+       STK_ERR_NO_STSL = 4
+};
+#endif /* CONFIG_PEERKEY */
+
+struct rsn_error_kde {
+       be16 mui;
+       be16 error_type;
+} STRUCT_PACKED;
+
+#ifdef CONFIG_IEEE80211W
+struct wpa_igtk_kde {
+       u8 keyid[2];
+       u8 pn[6];
+       u8 igtk[WPA_IGTK_LEN];
+} STRUCT_PACKED;
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_IEEE80211R
+struct rsn_mdie {
+       u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+       u8 ft_capab;
+} STRUCT_PACKED;
+
+#define RSN_FT_CAPAB_FT_OVER_DS BIT(0)
+#define RSN_FT_CAPAB_FT_RESOURCE_REQ_SUPP BIT(1)
+
+struct rsn_ftie {
+       u8 mic_control[2];
+       u8 mic[16];
+       u8 anonce[WPA_NONCE_LEN];
+       u8 snonce[WPA_NONCE_LEN];
+       /* followed by optional parameters */
+} STRUCT_PACKED;
+
+#define FTIE_SUBELEM_R1KH_ID 1
+#define FTIE_SUBELEM_GTK 2
+#define FTIE_SUBELEM_R0KH_ID 3
+#define FTIE_SUBELEM_IGTK 4
+
+struct rsn_rdie {
+       u8 id;
+       u8 descr_count;
+       le16 status_code;
+} STRUCT_PACKED;
+
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
+                     u8 *mic);
+void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+                   const u8 *addr1, const u8 *addr2,
+                   const u8 *nonce1, const u8 *nonce2,
+                   u8 *ptk, size_t ptk_len, int use_sha256);
+
+#ifdef CONFIG_IEEE80211R
+int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
+              u8 transaction_seqnum, const u8 *mdie, size_t mdie_len,
+              const u8 *ftie, size_t ftie_len,
+              const u8 *rsnie, size_t rsnie_len,
+              const u8 *ric, size_t ric_len, u8 *mic);
+void wpa_derive_pmk_r0(const u8 *xxkey, size_t xxkey_len,
+                      const u8 *ssid, size_t ssid_len,
+                      const u8 *mdid, const u8 *r0kh_id, size_t r0kh_id_len,
+                      const u8 *s0kh_id, u8 *pmk_r0, u8 *pmk_r0_name);
+void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
+                           const u8 *s1kh_id, u8 *pmk_r1_name);
+void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
+                      const u8 *r1kh_id, const u8 *s1kh_id,
+                      u8 *pmk_r1, u8 *pmk_r1_name);
+void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+                      const u8 *sta_addr, const u8 *bssid,
+                      const u8 *pmk_r1_name,
+                      u8 *ptk, size_t ptk_len, u8 *ptk_name);
+#endif /* CONFIG_IEEE80211R */
+
+struct wpa_ie_data {
+       int proto;
+       int pairwise_cipher;
+       int group_cipher;
+       int key_mgmt;
+       int capabilities;
+       size_t num_pmkid;
+       const u8 *pmkid;
+       int mgmt_group_cipher;
+};
+
+
+int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
+                        struct wpa_ie_data *data);
+
+void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
+              u8 *pmkid, int use_sha256);
+
+const char * wpa_cipher_txt(int cipher);
+const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
+int wpa_compare_rsn_ie(int ft_initial_assoc,
+                      const u8 *ie1, size_t ie1len,
+                      const u8 *ie2, size_t ie2len);
+int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid);
+
+#endif /* WPA_COMMON_H */
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
new file mode 100644 (file)
index 0000000..2b4e3aa
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * 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_CTRL_IFACE
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+#include <sys/un.h>
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+#include "wpa_ctrl.h"
+#include "common.h"
+
+
+#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
+#define CTRL_IFACE_SOCKET
+#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */
+
+
+/**
+ * struct wpa_ctrl - Internal structure for control interface library
+ *
+ * This structure is used by the wpa_supplicant/hostapd control interface
+ * library to store internal data. Programs using the library should not touch
+ * this data directly. They can only use the pointer to the data structure as
+ * an identifier for the control interface connection and use this as one of
+ * the arguments for most of the control interface library functions.
+ */
+struct wpa_ctrl {
+#ifdef CONFIG_CTRL_IFACE_UDP
+       int s;
+       struct sockaddr_in local;
+       struct sockaddr_in dest;
+       char *cookie;
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+       int s;
+       struct sockaddr_un local;
+       struct sockaddr_un dest;
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+       HANDLE pipe;
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+};
+
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+       struct wpa_ctrl *ctrl;
+       static int counter = 0;
+       int ret;
+       size_t res;
+       int tries = 0;
+
+       ctrl = os_malloc(sizeof(*ctrl));
+       if (ctrl == NULL)
+               return NULL;
+       os_memset(ctrl, 0, sizeof(*ctrl));
+
+       ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (ctrl->s < 0) {
+               os_free(ctrl);
+               return NULL;
+       }
+
+       ctrl->local.sun_family = AF_UNIX;
+       counter++;
+try_again:
+       ret = os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path),
+                         "/tmp/wpa_ctrl_%d-%d", getpid(), counter);
+       if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
+               close(ctrl->s);
+               os_free(ctrl);
+               return NULL;
+       }
+       tries++;
+       if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+                   sizeof(ctrl->local)) < 0) {
+               if (errno == EADDRINUSE && tries < 2) {
+                       /*
+                        * getpid() returns unique identifier for this instance
+                        * of wpa_ctrl, so the existing socket file must have
+                        * been left by unclean termination of an earlier run.
+                        * Remove the file and try again.
+                        */
+                       unlink(ctrl->local.sun_path);
+                       goto try_again;
+               }
+               close(ctrl->s);
+               os_free(ctrl);
+               return NULL;
+       }
+
+       ctrl->dest.sun_family = AF_UNIX;
+       res = os_strlcpy(ctrl->dest.sun_path, ctrl_path,
+                        sizeof(ctrl->dest.sun_path));
+       if (res >= sizeof(ctrl->dest.sun_path)) {
+               close(ctrl->s);
+               os_free(ctrl);
+               return NULL;
+       }
+       if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+                   sizeof(ctrl->dest)) < 0) {
+               close(ctrl->s);
+               unlink(ctrl->local.sun_path);
+               os_free(ctrl);
+               return NULL;
+       }
+
+       return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+       unlink(ctrl->local.sun_path);
+       close(ctrl->s);
+       os_free(ctrl);
+}
+
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+       struct wpa_ctrl *ctrl;
+       char buf[128];
+       size_t len;
+
+       ctrl = os_malloc(sizeof(*ctrl));
+       if (ctrl == NULL)
+               return NULL;
+       os_memset(ctrl, 0, sizeof(*ctrl));
+
+       ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (ctrl->s < 0) {
+               perror("socket");
+               os_free(ctrl);
+               return NULL;
+       }
+
+       ctrl->local.sin_family = AF_INET;
+       ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
+       if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
+                sizeof(ctrl->local)) < 0) {
+               close(ctrl->s);
+               os_free(ctrl);
+               return NULL;
+       }
+
+       ctrl->dest.sin_family = AF_INET;
+       ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
+       ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
+       if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
+                   sizeof(ctrl->dest)) < 0) {
+               perror("connect");
+               close(ctrl->s);
+               os_free(ctrl);
+               return NULL;
+       }
+
+       len = sizeof(buf) - 1;
+       if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) {
+               buf[len] = '\0';
+               ctrl->cookie = os_strdup(buf);
+       }
+
+       return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+       close(ctrl->s);
+       os_free(ctrl->cookie);
+       os_free(ctrl);
+}
+
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef CTRL_IFACE_SOCKET
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+                    char *reply, size_t *reply_len,
+                    void (*msg_cb)(char *msg, size_t len))
+{
+       struct timeval tv;
+       int res;
+       fd_set rfds;
+       const char *_cmd;
+       char *cmd_buf = NULL;
+       size_t _cmd_len;
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+       if (ctrl->cookie) {
+               char *pos;
+               _cmd_len = os_strlen(ctrl->cookie) + 1 + cmd_len;
+               cmd_buf = os_malloc(_cmd_len);
+               if (cmd_buf == NULL)
+                       return -1;
+               _cmd = cmd_buf;
+               pos = cmd_buf;
+               os_strlcpy(pos, ctrl->cookie, _cmd_len);
+               pos += os_strlen(ctrl->cookie);
+               *pos++ = ' ';
+               os_memcpy(pos, cmd, cmd_len);
+       } else
+#endif /* CONFIG_CTRL_IFACE_UDP */
+       {
+               _cmd = cmd;
+               _cmd_len = cmd_len;
+       }
+
+       if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) {
+               os_free(cmd_buf);
+               return -1;
+       }
+       os_free(cmd_buf);
+
+       for (;;) {
+               tv.tv_sec = 2;
+               tv.tv_usec = 0;
+               FD_ZERO(&rfds);
+               FD_SET(ctrl->s, &rfds);
+               res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+               if (FD_ISSET(ctrl->s, &rfds)) {
+                       res = recv(ctrl->s, reply, *reply_len, 0);
+                       if (res < 0)
+                               return res;
+                       if (res > 0 && reply[0] == '<') {
+                               /* This is an unsolicited message from
+                                * wpa_supplicant, not the reply to the
+                                * request. Use msg_cb to report this to the
+                                * caller. */
+                               if (msg_cb) {
+                                       /* Make sure the message is nul
+                                        * terminated. */
+                                       if ((size_t) res == *reply_len)
+                                               res = (*reply_len) - 1;
+                                       reply[res] = '\0';
+                                       msg_cb(reply, res);
+                               }
+                               continue;
+                       }
+                       *reply_len = res;
+                       break;
+               } else {
+                       return -2;
+               }
+       }
+       return 0;
+}
+#endif /* CTRL_IFACE_SOCKET */
+
+
+static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach)
+{
+       char buf[10];
+       int ret;
+       size_t len = 10;
+
+       ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6,
+                              buf, &len, NULL);
+       if (ret < 0)
+               return ret;
+       if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0)
+               return 0;
+       return -1;
+}
+
+
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl)
+{
+       return wpa_ctrl_attach_helper(ctrl, 1);
+}
+
+
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl)
+{
+       return wpa_ctrl_attach_helper(ctrl, 0);
+}
+
+
+#ifdef CTRL_IFACE_SOCKET
+
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
+{
+       int res;
+
+       res = recv(ctrl->s, reply, *reply_len, 0);
+       if (res < 0)
+               return res;
+       *reply_len = res;
+       return 0;
+}
+
+
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
+{
+       struct timeval tv;
+       fd_set rfds;
+       tv.tv_sec = 0;
+       tv.tv_usec = 0;
+       FD_ZERO(&rfds);
+       FD_SET(ctrl->s, &rfds);
+       select(ctrl->s + 1, &rfds, NULL, NULL, &tv);
+       return FD_ISSET(ctrl->s, &rfds);
+}
+
+
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+{
+       return ctrl->s;
+}
+
+#endif /* CTRL_IFACE_SOCKET */
+
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+
+#ifndef WPA_SUPPLICANT_NAMED_PIPE
+#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
+#endif
+#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
+
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
+{
+       struct wpa_ctrl *ctrl;
+       DWORD mode;
+       TCHAR name[256];
+       int i, ret;
+
+       ctrl = os_malloc(sizeof(*ctrl));
+       if (ctrl == NULL)
+               return NULL;
+       os_memset(ctrl, 0, sizeof(*ctrl));
+
+#ifdef UNICODE
+       if (ctrl_path == NULL)
+               ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX);
+       else
+               ret = _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
+                                ctrl_path);
+#else /* UNICODE */
+       if (ctrl_path == NULL)
+               ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX);
+       else
+               ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
+                                 ctrl_path);
+#endif /* UNICODE */
+       if (ret < 0 || ret >= 256) {
+               os_free(ctrl);
+               return NULL;
+       }
+
+       for (i = 0; i < 10; i++) {
+               ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0,
+                                       NULL, OPEN_EXISTING, 0, NULL);
+               /*
+                * Current named pipe server side in wpa_supplicant is
+                * re-opening the pipe for new clients only after the previous
+                * one is taken into use. This leaves a small window for race
+                * conditions when two connections are being opened at almost
+                * the same time. Retry if that was the case.
+                */
+               if (ctrl->pipe != INVALID_HANDLE_VALUE ||
+                   GetLastError() != ERROR_PIPE_BUSY)
+                       break;
+               WaitNamedPipe(name, 1000);
+       }
+       if (ctrl->pipe == INVALID_HANDLE_VALUE) {
+               os_free(ctrl);
+               return NULL;
+       }
+
+       mode = PIPE_READMODE_MESSAGE;
+       if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) {
+               CloseHandle(ctrl->pipe);
+               os_free(ctrl);
+               return NULL;
+       }
+
+       return ctrl;
+}
+
+
+void wpa_ctrl_close(struct wpa_ctrl *ctrl)
+{
+       CloseHandle(ctrl->pipe);
+       os_free(ctrl);
+}
+
+
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+                    char *reply, size_t *reply_len,
+                    void (*msg_cb)(char *msg, size_t len))
+{
+       DWORD written;
+       DWORD readlen = *reply_len;
+
+       if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL))
+               return -1;
+
+       if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL))
+               return -1;
+       *reply_len = readlen;
+
+       return 0;
+}
+
+
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len)
+{
+       DWORD len = *reply_len;
+       if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL))
+               return -1;
+       *reply_len = len;
+       return 0;
+}
+
+
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl)
+{
+       DWORD left;
+
+       if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL))
+               return -1;
+       return left ? 1 : 0;
+}
+
+
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl)
+{
+       return -1;
+}
+
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+#endif /* CONFIG_CTRL_IFACE */
diff --git a/src/common/wpa_ctrl.h b/src/common/wpa_ctrl.h
new file mode 100644 (file)
index 0000000..d770fd4
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * wpa_supplicant/hostapd control interface library
+ * Copyright (c) 2004-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 WPA_CTRL_H
+#define WPA_CTRL_H
+
+#ifdef  __cplusplus
+extern "C" {
+#endif
+
+/* wpa_supplicant control interface - fixed message prefixes */
+
+/** Interactive request for identity/password/pin */
+#define WPA_CTRL_REQ "CTRL-REQ-"
+
+/** Response to identity/password/pin request */
+#define WPA_CTRL_RSP "CTRL-RSP-"
+
+/* Event messages with fixed prefix */
+/** Authentication completed successfully and data connection enabled */
+#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED "
+/** Disconnected, data connection is not available */
+#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED "
+/** wpa_supplicant is exiting */
+#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING "
+/** Password change was completed successfully */
+#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED "
+/** EAP-Request/Notification received */
+#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION "
+/** EAP authentication started (EAP-Request/Identity received) */
+#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED "
+/** EAP method proposed by the server */
+#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD "
+/** EAP method selected */
+#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
+/** EAP peer certificate from TLS */
+#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP TLS certificate chain validation error */
+#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
+/** EAP authentication completed successfully */
+#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS "
+/** EAP authentication failed (EAP-Failure received) */
+#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE "
+/** New scan results available */
+#define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** A new BSS entry was added (followed by BSS entry id and BSSID) */
+#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
+/** A BSS entry was removed (followed by BSS entry id and BSSID) */
+#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
+
+/** WPS overlap detected in PBC mode */
+#define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
+/** Available WPS AP with active PBC found in scan results */
+#define WPS_EVENT_AP_AVAILABLE_PBC "WPS-AP-AVAILABLE-PBC "
+/** Available WPS AP with recently selected PIN registrar found in scan results
+ */
+#define WPS_EVENT_AP_AVAILABLE_PIN "WPS-AP-AVAILABLE-PIN "
+/** Available WPS AP found in scan results */
+#define WPS_EVENT_AP_AVAILABLE "WPS-AP-AVAILABLE "
+/** A new credential received */
+#define WPS_EVENT_CRED_RECEIVED "WPS-CRED-RECEIVED "
+/** M2D received */
+#define WPS_EVENT_M2D "WPS-M2D "
+/** WPS registration failed after M2/M2D */
+#define WPS_EVENT_FAIL "WPS-FAIL "
+/** WPS registration completed successfully */
+#define WPS_EVENT_SUCCESS "WPS-SUCCESS "
+/** WPS enrollment attempt timed out and was terminated */
+#define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
+
+#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
+
+/* WPS ER events */
+#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD "
+#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE "
+#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD "
+#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE "
+
+/* hostapd control interface - fixed message prefixes */
+#define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
+#define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS "
+#define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS "
+#define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED "
+#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED "
+#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED "
+#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED "
+#define AP_STA_CONNECTED "AP-STA-CONNECTED "
+#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED "
+
+
+/* wpa_supplicant/hostapd control interface access */
+
+/**
+ * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd
+ * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used.
+ * Returns: Pointer to abstract control interface data or %NULL on failure
+ *
+ * This function is used to open a control interface to wpa_supplicant/hostapd.
+ * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path
+ * is configured in wpa_supplicant/hostapd and other programs using the control
+ * interface need to use matching path configuration.
+ */
+struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path);
+
+
+/**
+ * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ *
+ * This function is used to close a control interface.
+ */
+void wpa_ctrl_close(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @cmd: Command; usually, ASCII text, e.g., "PING"
+ * @cmd_len: Length of the cmd in bytes
+ * @reply: Buffer for the response
+ * @reply_len: Reply buffer length
+ * @msg_cb: Callback function for unsolicited messages or %NULL if not used
+ * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout
+ *
+ * This function is used to send commands to wpa_supplicant/hostapd. Received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply. This function will block for up to two seconds while waiting
+ * for the reply. If unsolicited messages are received, the blocking time may
+ * be longer.
+ *
+ * msg_cb can be used to register a callback function that will be called for
+ * unsolicited messages received while waiting for the command response. These
+ * messages may be received if wpa_ctrl_request() is called at the same time as
+ * wpa_supplicant/hostapd is sending such a message. This can happen only if
+ * the program has used wpa_ctrl_attach() to register itself as a monitor for
+ * event messages. Alternatively to msg_cb, programs can register two control
+ * interface connections and use one of them for commands and the other one for
+ * receiving event messages, in other words, call wpa_ctrl_attach() only for
+ * the control interface connection that will be used for event messages.
+ */
+int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
+                    char *reply, size_t *reply_len,
+                    void (*msg_cb)(char *msg, size_t len));
+
+
+/**
+ * wpa_ctrl_attach - Register as an event monitor for the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function registers the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the
+ * control interface connection starts receiving event messages that can be
+ * read with wpa_ctrl_recv().
+ */
+int wpa_ctrl_attach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_detach - Unregister event monitor from the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 0 on success, -1 on failure, -2 on timeout
+ *
+ * This function unregisters the control interface connection as a monitor for
+ * wpa_supplicant/hostapd events, i.e., cancels the registration done with
+ * wpa_ctrl_attach().
+ */
+int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_recv - Receive a pending control interface message
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * @reply: Buffer for the message data
+ * @reply_len: Length of the reply buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function will receive a pending control interface message. This
+ * function will block if no messages are available. The received response will
+ * be written to reply and reply_len is set to the actual length of the reply.
+ * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
+ * must have been used to register the control interface as an event monitor.
+ */
+int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len);
+
+
+/**
+ * wpa_ctrl_pending - Check whether there are pending event messages
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: 1 if there are pending messages, 0 if no, or -1 on error
+ *
+ * This function will check whether there are any pending control interface
+ * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is
+ * only used for event messages, i.e., wpa_ctrl_attach() must have been used to
+ * register the control interface as an event monitor.
+ */
+int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
+
+
+/**
+ * wpa_ctrl_get_fd - Get file descriptor used by the control interface
+ * @ctrl: Control interface data from wpa_ctrl_open()
+ * Returns: File descriptor used for the connection
+ *
+ * This function can be used to get the file descriptor that is used for the
+ * control interface connection. The returned value can be used, e.g., with
+ * select() while waiting for multiple events.
+ *
+ * The returned file descriptor must not be used directly for sending or
+ * receiving packets; instead, the library functions wpa_ctrl_request() and
+ * wpa_ctrl_recv() must be used for this.
+ */
+int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
+
+#ifdef CONFIG_CTRL_IFACE_UDP
+#define WPA_CTRL_IFACE_PORT 9877
+#define WPA_GLOBAL_CTRL_IFACE_PORT 9878
+#endif /* CONFIG_CTRL_IFACE_UDP */
+
+
+#ifdef  __cplusplus
+}
+#endif
+
+#endif /* WPA_CTRL_H */
diff --git a/src/crypto/.gitignore b/src/crypto/.gitignore
new file mode 100644 (file)
index 0000000..ee60604
--- /dev/null
@@ -0,0 +1 @@
+libcrypto.a
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
new file mode 100644 (file)
index 0000000..69aa16a
--- /dev/null
@@ -0,0 +1,56 @@
+all: libcrypto.a
+
+clean:
+       rm -f *~ *.o *.d libcrypto.a
+
+install:
+       @echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
+#CFLAGS += -DALL_DH_GROUPS
+
+LIB_OBJS= \
+       aes-cbc.o \
+       aes-ctr.o \
+       aes-eax.o \
+       aes-encblock.o \
+       aes-internal.o \
+       aes-internal-dec.o \
+       aes-internal-enc.o \
+       aes-omac1.o \
+       aes-unwrap.o \
+       aes-wrap.o \
+       des-internal.o \
+       dh_group5.o \
+       dh_groups.o \
+       md4-internal.o \
+       md5.o \
+       md5-internal.o \
+       md5-non-fips.o \
+       milenage.o \
+       ms_funcs.o \
+       rc4.o \
+       sha1.o \
+       sha1-internal.o \
+       sha1-pbkdf2.o \
+       sha1-tlsprf.o \
+       sha1-tprf.o \
+       sha256.o \
+       sha256-internal.o
+
+LIB_OBJS += crypto_internal.o
+LIB_OBJS += crypto_internal-cipher.o
+LIB_OBJS += crypto_internal-modexp.o
+LIB_OBJS += crypto_internal-rsa.o
+LIB_OBJS += tls_internal.o
+LIB_OBJS += fips_prf_internal.o
+
+
+libcrypto.a: $(LIB_OBJS)
+       $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/crypto/aes-cbc.c b/src/crypto/aes-cbc.c
new file mode 100644 (file)
index 0000000..bd74769
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * AES-128 CBC
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_cbc_encrypt - AES-128 CBC encryption
+ * @key: Encryption key
+ * @iv: Encryption IV for CBC mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+       void *ctx;
+       u8 cbc[AES_BLOCK_SIZE];
+       u8 *pos = data;
+       int i, j, blocks;
+
+       ctx = aes_encrypt_init(key, 16);
+       if (ctx == NULL)
+               return -1;
+       os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+       blocks = data_len / AES_BLOCK_SIZE;
+       for (i = 0; i < blocks; i++) {
+               for (j = 0; j < AES_BLOCK_SIZE; j++)
+                       cbc[j] ^= pos[j];
+               aes_encrypt(ctx, cbc, cbc);
+               os_memcpy(pos, cbc, AES_BLOCK_SIZE);
+               pos += AES_BLOCK_SIZE;
+       }
+       aes_encrypt_deinit(ctx);
+       return 0;
+}
+
+
+/**
+ * aes_128_cbc_decrypt - AES-128 CBC decryption
+ * @key: Decryption key
+ * @iv: Decryption IV for CBC mode (16 bytes)
+ * @data: Data to decrypt in-place
+ * @data_len: Length of data in bytes (must be divisible by 16)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len)
+{
+       void *ctx;
+       u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE];
+       u8 *pos = data;
+       int i, j, blocks;
+
+       ctx = aes_decrypt_init(key, 16);
+       if (ctx == NULL)
+               return -1;
+       os_memcpy(cbc, iv, AES_BLOCK_SIZE);
+
+       blocks = data_len / AES_BLOCK_SIZE;
+       for (i = 0; i < blocks; i++) {
+               os_memcpy(tmp, pos, AES_BLOCK_SIZE);
+               aes_decrypt(ctx, pos, pos);
+               for (j = 0; j < AES_BLOCK_SIZE; j++)
+                       pos[j] ^= cbc[j];
+               os_memcpy(cbc, tmp, AES_BLOCK_SIZE);
+               pos += AES_BLOCK_SIZE;
+       }
+       aes_decrypt_deinit(ctx);
+       return 0;
+}
diff --git a/src/crypto/aes-ctr.c b/src/crypto/aes-ctr.c
new file mode 100644 (file)
index 0000000..468f877
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * AES-128 CTR
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_ctr_encrypt - AES-128 CTR mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode (16 bytes)
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+                       u8 *data, size_t data_len)
+{
+       void *ctx;
+       size_t j, len, left = data_len;
+       int i;
+       u8 *pos = data;
+       u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE];
+
+       ctx = aes_encrypt_init(key, 16);
+       if (ctx == NULL)
+               return -1;
+       os_memcpy(counter, nonce, AES_BLOCK_SIZE);
+
+       while (left > 0) {
+               aes_encrypt(ctx, counter, buf);
+
+               len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE;
+               for (j = 0; j < len; j++)
+                       pos[j] ^= buf[j];
+               pos += len;
+               left -= len;
+
+               for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) {
+                       counter[i]++;
+                       if (counter[i])
+                               break;
+               }
+       }
+       aes_encrypt_deinit(ctx);
+       return 0;
+}
diff --git a/src/crypto/aes-eax.c b/src/crypto/aes-eax.c
new file mode 100644 (file)
index 0000000..d5c3971
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * AES-128 EAX
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_eax_encrypt - AES-128 EAX mode encryption
+ * @key: Key for encryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+                       const u8 *hdr, size_t hdr_len,
+                       u8 *data, size_t data_len, u8 *tag)
+{
+       u8 *buf;
+       size_t buf_len;
+       u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+               data_mac[AES_BLOCK_SIZE];
+       int i, ret = -1;
+
+       if (nonce_len > data_len)
+               buf_len = nonce_len;
+       else
+               buf_len = data_len;
+       if (hdr_len > buf_len)
+               buf_len = hdr_len;
+       buf_len += 16;
+
+       buf = os_malloc(buf_len);
+       if (buf == NULL)
+               return -1;
+
+       os_memset(buf, 0, 15);
+
+       buf[15] = 0;
+       os_memcpy(buf + 16, nonce, nonce_len);
+       if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac))
+               goto fail;
+
+       buf[15] = 1;
+       os_memcpy(buf + 16, hdr, hdr_len);
+       if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac))
+               goto fail;
+
+       if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len))
+               goto fail;
+       buf[15] = 2;
+       os_memcpy(buf + 16, data, data_len);
+       if (omac1_aes_128(key, buf, 16 + data_len, data_mac))
+               goto fail;
+
+       for (i = 0; i < AES_BLOCK_SIZE; i++)
+               tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i];
+
+       ret = 0;
+fail:
+       os_free(buf);
+
+       return ret;
+}
+
+
+/**
+ * aes_128_eax_decrypt - AES-128 EAX mode decryption
+ * @key: Key for decryption (16 bytes)
+ * @nonce: Nonce for counter mode
+ * @nonce_len: Nonce length in bytes
+ * @hdr: Header data to be authenticity protected
+ * @hdr_len: Length of the header data bytes
+ * @data: Data to encrypt in-place
+ * @data_len: Length of data in bytes
+ * @tag: 16-byte tag value
+ * Returns: 0 on success, -1 on failure, -2 if tag does not match
+ */
+int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
+                       const u8 *hdr, size_t hdr_len,
+                       u8 *data, size_t data_len, const u8 *tag)
+{
+       u8 *buf;
+       size_t buf_len;
+       u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE],
+               data_mac[AES_BLOCK_SIZE];
+       int i;
+
+       if (nonce_len > data_len)
+               buf_len = nonce_len;
+       else
+               buf_len = data_len;
+       if (hdr_len > buf_len)
+               buf_len = hdr_len;
+       buf_len += 16;
+
+       buf = os_malloc(buf_len);
+       if (buf == NULL)
+               return -1;
+
+       os_memset(buf, 0, 15);
+
+       buf[15] = 0;
+       os_memcpy(buf + 16, nonce, nonce_len);
+       if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) {
+               os_free(buf);
+               return -1;
+       }
+
+       buf[15] = 1;
+       os_memcpy(buf + 16, hdr, hdr_len);
+       if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) {
+               os_free(buf);
+               return -1;
+       }
+
+       buf[15] = 2;
+       os_memcpy(buf + 16, data, data_len);
+       if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) {
+               os_free(buf);
+               return -1;
+       }
+
+       os_free(buf);
+
+       for (i = 0; i < AES_BLOCK_SIZE; i++) {
+               if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]))
+                       return -2;
+       }
+
+       return aes_128_ctr_encrypt(key, nonce_mac, data, data_len);
+}
diff --git a/src/crypto/aes-encblock.c b/src/crypto/aes-encblock.c
new file mode 100644 (file)
index 0000000..8f35caa
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * AES encrypt_block
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_128_encrypt_block - Perform one AES 128-bit block operation
+ * @key: Key for AES
+ * @in: Input data (16 bytes)
+ * @out: Output of the AES block operation (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out)
+{
+       void *ctx;
+       ctx = aes_encrypt_init(key, 16);
+       if (ctx == NULL)
+               return -1;
+       aes_encrypt(ctx, in, out);
+       aes_encrypt_deinit(ctx);
+       return 0;
+}
diff --git a/src/crypto/aes-internal-dec.c b/src/crypto/aes-internal-dec.c
new file mode 100644 (file)
index 0000000..2d32c03
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * AES (Rijndael) cipher - decrypt
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <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 "crypto.h"
+#include "aes_i.h"
+
+/**
+ * Expand the cipher key into the decryption key schedule.
+ *
+ * @return     the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[])
+{
+       int Nr = 10, i, j;
+       u32 temp;
+
+       /* expand the cipher key: */
+       rijndaelKeySetupEnc(rk, cipherKey);
+       /* invert the order of the round keys: */
+       for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) {
+               temp = rk[i    ]; rk[i    ] = rk[j    ]; rk[j    ] = temp;
+               temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;
+               temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;
+               temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;
+       }
+       /* apply the inverse MixColumn transform to all round keys but the
+        * first and the last: */
+       for (i = 1; i < Nr; i++) {
+               rk += 4;
+               for (j = 0; j < 4; j++) {
+                       rk[j] = TD0_(TE4((rk[j] >> 24)       )) ^
+                               TD1_(TE4((rk[j] >> 16) & 0xff)) ^
+                               TD2_(TE4((rk[j] >>  8) & 0xff)) ^
+                               TD3_(TE4((rk[j]      ) & 0xff));
+               }
+       }
+}
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       u32 *rk;
+       if (len != 16)
+               return NULL;
+       rk = os_malloc(AES_PRIV_SIZE);
+       if (rk == NULL)
+               return NULL;
+       rijndaelKeySetupDec(rk, key);
+       return rk;
+}
+
+static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16])
+{
+       u32 s0, s1, s2, s3, t0, t1, t2, t3;
+       const int Nr = 10;
+#ifndef FULL_UNROLL
+       int r;
+#endif /* ?FULL_UNROLL */
+
+       /*
+        * map byte array block to cipher state
+        * and add initial round key:
+        */
+       s0 = GETU32(ct     ) ^ rk[0];
+       s1 = GETU32(ct +  4) ^ rk[1];
+       s2 = GETU32(ct +  8) ^ rk[2];
+       s3 = GETU32(ct + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \
+d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \
+d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \
+d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+       ROUND(1,t,s);
+       ROUND(2,s,t);
+       ROUND(3,t,s);
+       ROUND(4,s,t);
+       ROUND(5,t,s);
+       ROUND(6,s,t);
+       ROUND(7,t,s);
+       ROUND(8,s,t);
+       ROUND(9,t,s);
+
+       rk += Nr << 2;
+
+#else  /* !FULL_UNROLL */
+
+       /* Nr - 1 full rounds: */
+       r = Nr >> 1;
+       for (;;) {
+               ROUND(1,t,s);
+               rk += 8;
+               if (--r == 0)
+                       break;
+               ROUND(0,s,t);
+       }
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+       /*
+        * apply last round and
+        * map cipher state to byte array block:
+        */
+       s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0];
+       PUTU32(pt     , s0);
+       s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1];
+       PUTU32(pt +  4, s1);
+       s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2];
+       PUTU32(pt +  8, s2);
+       s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3];
+       PUTU32(pt + 12, s3);
+}
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+       rijndaelDecrypt(ctx, crypt, plain);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+       os_memset(ctx, 0, AES_PRIV_SIZE);
+       os_free(ctx);
+}
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
new file mode 100644 (file)
index 0000000..2f19826
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * AES (Rijndael) cipher - encrypt
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <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 "crypto.h"
+#include "aes_i.h"
+
+void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16])
+{
+       u32 s0, s1, s2, s3, t0, t1, t2, t3;
+       const int Nr = 10;
+#ifndef FULL_UNROLL
+       int r;
+#endif /* ?FULL_UNROLL */
+
+       /*
+        * map byte array block to cipher state
+        * and add initial round key:
+        */
+       s0 = GETU32(pt     ) ^ rk[0];
+       s1 = GETU32(pt +  4) ^ rk[1];
+       s2 = GETU32(pt +  8) ^ rk[2];
+       s3 = GETU32(pt + 12) ^ rk[3];
+
+#define ROUND(i,d,s) \
+d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \
+d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \
+d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \
+d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3]
+
+#ifdef FULL_UNROLL
+
+       ROUND(1,t,s);
+       ROUND(2,s,t);
+       ROUND(3,t,s);
+       ROUND(4,s,t);
+       ROUND(5,t,s);
+       ROUND(6,s,t);
+       ROUND(7,t,s);
+       ROUND(8,s,t);
+       ROUND(9,t,s);
+
+       rk += Nr << 2;
+
+#else  /* !FULL_UNROLL */
+
+       /* Nr - 1 full rounds: */
+       r = Nr >> 1;
+       for (;;) {
+               ROUND(1,t,s);
+               rk += 8;
+               if (--r == 0)
+                       break;
+               ROUND(0,s,t);
+       }
+
+#endif /* ?FULL_UNROLL */
+
+#undef ROUND
+
+       /*
+        * apply last round and
+        * map cipher state to byte array block:
+        */
+       s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0];
+       PUTU32(ct     , s0);
+       s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1];
+       PUTU32(ct +  4, s1);
+       s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2];
+       PUTU32(ct +  8, s2);
+       s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3];
+       PUTU32(ct + 12, s3);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       u32 *rk;
+       if (len != 16)
+               return NULL;
+       rk = os_malloc(AES_PRIV_SIZE);
+       if (rk == NULL)
+               return NULL;
+       rijndaelKeySetupEnc(rk, key);
+       return rk;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+       rijndaelEncrypt(ctx, plain, crypt);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+       os_memset(ctx, 0, AES_PRIV_SIZE);
+       os_free(ctx);
+}
diff --git a/src/crypto/aes-internal.c b/src/crypto/aes-internal.c
new file mode 100644 (file)
index 0000000..4161220
--- /dev/null
@@ -0,0 +1,805 @@
+/*
+ * AES (Rijndael) cipher
+ *
+ * Modifications to public domain implementation:
+ * - support only 128-bit keys
+ * - cleanup
+ * - use C pre-processor to make it easier to change S table access
+ * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at
+ *   cost of reduced throughput (quite small difference on Pentium 4,
+ *   10-25% when using -O1 or -O2 optimization)
+ *
+ * Copyright (c) 2003-2005, Jouni Malinen <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 "crypto.h"
+#include "aes_i.h"
+
+/*
+ * rijndael-alg-fst.c
+ *
+ * @version 3.0 (December 2000)
+ *
+ * Optimised ANSI C code for the Rijndael cipher (now AES)
+ *
+ * @author Vincent Rijmen <vincent.rijmen@esat.kuleuven.ac.be>
+ * @author Antoon Bosselaers <antoon.bosselaers@esat.kuleuven.ac.be>
+ * @author Paulo Barreto <paulo.barreto@terra.com.br>
+ *
+ * This code is hereby placed in the public domain.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*
+Te0[x] = S [x].[02, 01, 01, 03];
+Te1[x] = S [x].[03, 02, 01, 01];
+Te2[x] = S [x].[01, 03, 02, 01];
+Te3[x] = S [x].[01, 01, 03, 02];
+Te4[x] = S [x].[01, 01, 01, 01];
+
+Td0[x] = Si[x].[0e, 09, 0d, 0b];
+Td1[x] = Si[x].[0b, 0e, 09, 0d];
+Td2[x] = Si[x].[0d, 0b, 0e, 09];
+Td3[x] = Si[x].[09, 0d, 0b, 0e];
+Td4[x] = Si[x].[01, 01, 01, 01];
+*/
+
+const u32 Te0[256] = {
+    0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,
+    0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,
+    0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,
+    0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,
+    0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,
+    0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,
+    0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,
+    0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,
+    0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,
+    0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,
+    0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,
+    0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,
+    0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,
+    0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,
+    0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,
+    0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,
+    0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,
+    0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,
+    0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,
+    0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,
+    0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,
+    0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,
+    0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,
+    0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,
+    0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,
+    0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,
+    0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,
+    0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,
+    0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,
+    0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,
+    0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,
+    0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,
+    0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,
+    0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,
+    0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,
+    0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,
+    0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,
+    0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,
+    0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,
+    0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,
+    0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,
+    0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,
+    0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,
+    0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,
+    0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,
+    0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,
+    0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,
+    0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,
+    0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,
+    0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,
+    0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,
+    0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,
+    0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,
+    0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,
+    0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,
+    0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,
+    0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,
+    0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,
+    0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,
+    0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,
+    0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,
+    0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,
+    0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,
+    0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Te1[256] = {
+    0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,
+    0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,
+    0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,
+    0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,
+    0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,
+    0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,
+    0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,
+    0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,
+    0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,
+    0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,
+    0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,
+    0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,
+    0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,
+    0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,
+    0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,
+    0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,
+    0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,
+    0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,
+    0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,
+    0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,
+    0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,
+    0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,
+    0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,
+    0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,
+    0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,
+    0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,
+    0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,
+    0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,
+    0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,
+    0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,
+    0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,
+    0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,
+    0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,
+    0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,
+    0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,
+    0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,
+    0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,
+    0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,
+    0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,
+    0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,
+    0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,
+    0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,
+    0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,
+    0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,
+    0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,
+    0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,
+    0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,
+    0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,
+    0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,
+    0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,
+    0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,
+    0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,
+    0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,
+    0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,
+    0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,
+    0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,
+    0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,
+    0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,
+    0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,
+    0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,
+    0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,
+    0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,
+    0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,
+    0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,
+};
+const u32 Te2[256] = {
+    0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,
+    0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,
+    0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,
+    0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,
+    0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,
+    0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,
+    0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,
+    0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,
+    0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,
+    0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,
+    0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,
+    0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,
+    0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,
+    0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,
+    0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,
+    0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,
+    0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,
+    0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,
+    0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,
+    0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,
+    0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,
+    0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,
+    0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,
+    0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,
+    0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,
+    0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,
+    0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,
+    0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,
+    0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,
+    0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,
+    0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,
+    0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,
+    0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,
+    0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,
+    0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,
+    0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,
+    0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,
+    0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,
+    0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,
+    0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,
+    0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,
+    0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,
+    0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,
+    0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,
+    0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,
+    0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,
+    0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,
+    0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,
+    0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,
+    0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,
+    0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,
+    0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,
+    0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,
+    0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,
+    0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,
+    0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,
+    0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,
+    0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,
+    0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,
+    0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,
+    0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,
+    0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,
+    0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,
+    0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,
+};
+const u32 Te3[256] = {
+
+    0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,
+    0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,
+    0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,
+    0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,
+    0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,
+    0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,
+    0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,
+    0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,
+    0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,
+    0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,
+    0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,
+    0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,
+    0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,
+    0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,
+    0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,
+    0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,
+    0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,
+    0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,
+    0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,
+    0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,
+    0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,
+    0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,
+    0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,
+    0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,
+    0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,
+    0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,
+    0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,
+    0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,
+    0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,
+    0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,
+    0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,
+    0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,
+    0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,
+    0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,
+    0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,
+    0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,
+    0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,
+    0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,
+    0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,
+    0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,
+    0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,
+    0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,
+    0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,
+    0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,
+    0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,
+    0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,
+    0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,
+    0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,
+    0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,
+    0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,
+    0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,
+    0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,
+    0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,
+    0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,
+    0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,
+    0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,
+    0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,
+    0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,
+    0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,
+    0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,
+    0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,
+    0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,
+    0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,
+    0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,
+};
+const u32 Te4[256] = {
+    0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,
+    0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,
+    0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,
+    0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,
+    0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,
+    0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,
+    0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,
+    0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,
+    0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,
+    0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,
+    0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,
+    0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,
+    0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,
+    0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,
+    0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,
+    0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,
+    0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,
+    0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,
+    0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,
+    0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,
+    0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,
+    0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,
+    0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,
+    0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,
+    0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,
+    0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,
+    0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,
+    0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,
+    0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,
+    0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,
+    0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,
+    0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,
+    0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,
+    0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,
+    0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,
+    0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,
+    0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,
+    0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,
+    0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,
+    0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,
+    0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,
+    0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,
+    0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,
+    0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,
+    0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,
+    0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,
+    0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,
+    0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,
+    0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,
+    0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,
+    0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,
+    0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,
+    0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,
+    0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,
+    0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,
+    0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,
+    0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,
+    0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,
+    0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,
+    0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,
+    0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,
+    0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,
+    0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,
+    0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,
+};
+#endif /* AES_SMALL_TABLES */
+const u32 Td0[256] = {
+    0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,
+    0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,
+    0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,
+    0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,
+    0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,
+    0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,
+    0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,
+    0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,
+    0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,
+    0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,
+    0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,
+    0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,
+    0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,
+    0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,
+    0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,
+    0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,
+    0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,
+    0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,
+    0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,
+    0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,
+    0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,
+    0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,
+    0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,
+    0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,
+    0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,
+    0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,
+    0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,
+    0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,
+    0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,
+    0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,
+    0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,
+    0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,
+    0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,
+    0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,
+    0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,
+    0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,
+    0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,
+    0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,
+    0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,
+    0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,
+    0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,
+    0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,
+    0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,
+    0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,
+    0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,
+    0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,
+    0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,
+    0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,
+    0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,
+    0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,
+    0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,
+    0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,
+    0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,
+    0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,
+    0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,
+    0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,
+    0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,
+    0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,
+    0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,
+    0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,
+    0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,
+    0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,
+    0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,
+    0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,
+};
+#ifndef AES_SMALL_TABLES
+const u32 Td1[256] = {
+    0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,
+    0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,
+    0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,
+    0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,
+    0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,
+    0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,
+    0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,
+    0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,
+    0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,
+    0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,
+    0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,
+    0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,
+    0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,
+    0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,
+    0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,
+    0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,
+    0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,
+    0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,
+    0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,
+    0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,
+    0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,
+    0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,
+    0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,
+    0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,
+    0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,
+    0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,
+    0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,
+    0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,
+    0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,
+    0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,
+    0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,
+    0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,
+    0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,
+    0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,
+    0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,
+    0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,
+    0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,
+    0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,
+    0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,
+    0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,
+    0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,
+    0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,
+    0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,
+    0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,
+    0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,
+    0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,
+    0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,
+    0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,
+    0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,
+    0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,
+    0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,
+    0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,
+    0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,
+    0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,
+    0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,
+    0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,
+    0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,
+    0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,
+    0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,
+    0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,
+    0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,
+    0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,
+    0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,
+    0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,
+};
+const u32 Td2[256] = {
+    0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,
+    0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,
+    0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,
+    0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,
+    0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,
+    0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,
+    0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,
+    0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,
+    0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,
+    0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,
+    0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,
+    0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,
+    0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,
+    0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,
+    0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,
+    0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,
+    0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,
+    0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,
+    0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,
+    0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,
+
+    0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,
+    0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,
+    0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,
+    0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,
+    0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,
+    0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,
+    0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,
+    0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,
+    0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,
+    0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,
+    0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,
+    0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,
+    0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,
+    0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,
+    0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,
+    0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,
+    0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,
+    0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,
+    0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,
+    0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,
+    0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,
+    0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,
+    0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,
+    0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,
+    0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,
+    0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,
+    0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,
+    0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,
+    0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,
+    0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,
+    0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,
+    0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,
+    0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,
+    0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,
+    0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,
+    0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,
+    0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,
+    0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,
+    0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,
+    0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,
+    0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,
+    0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,
+    0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,
+    0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,
+};
+const u32 Td3[256] = {
+    0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,
+    0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,
+    0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,
+    0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,
+    0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,
+    0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,
+    0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,
+    0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,
+    0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,
+    0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,
+    0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,
+    0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,
+    0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,
+    0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,
+    0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,
+    0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,
+    0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,
+    0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,
+    0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,
+    0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,
+    0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,
+    0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,
+    0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,
+    0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,
+    0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,
+    0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,
+    0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,
+    0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,
+    0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,
+    0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,
+    0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,
+    0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,
+    0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,
+    0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,
+    0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,
+    0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,
+    0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,
+    0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,
+    0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,
+    0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,
+    0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,
+    0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,
+    0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,
+    0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,
+    0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,
+    0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,
+    0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,
+    0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,
+    0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,
+    0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,
+    0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,
+    0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,
+    0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,
+    0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,
+    0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,
+    0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,
+    0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,
+    0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,
+    0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,
+    0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,
+    0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,
+    0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,
+    0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,
+    0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,
+};
+const u32 Td4[256] = {
+    0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,
+    0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,
+    0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,
+    0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,
+    0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,
+    0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,
+    0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,
+    0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,
+    0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,
+    0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,
+    0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,
+    0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,
+    0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,
+    0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,
+    0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,
+    0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,
+    0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,
+    0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,
+    0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,
+    0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,
+    0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,
+    0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,
+    0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,
+    0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,
+    0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,
+    0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,
+    0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,
+    0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,
+    0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,
+    0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,
+    0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,
+    0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,
+    0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,
+    0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,
+    0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,
+    0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,
+    0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,
+    0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,
+    0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,
+    0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,
+    0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,
+    0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,
+    0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,
+    0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,
+    0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,
+    0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,
+    0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,
+    0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,
+    0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,
+    0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,
+    0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,
+    0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,
+    0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,
+    0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,
+    0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,
+    0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,
+    0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,
+    0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,
+    0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,
+    0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,
+    0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,
+    0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,
+    0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,
+    0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,
+};
+const u32 rcon[] = {
+       0x01000000, 0x02000000, 0x04000000, 0x08000000,
+       0x10000000, 0x20000000, 0x40000000, 0x80000000,
+       0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#else /* AES_SMALL_TABLES */
+const u8 Td4s[256] = {
+    0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U,
+    0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU,
+    0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U,
+    0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU,
+    0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU,
+    0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU,
+    0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U,
+    0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U,
+    0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U,
+    0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U,
+    0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU,
+    0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U,
+    0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU,
+    0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U,
+    0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U,
+    0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU,
+    0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU,
+    0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U,
+    0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U,
+    0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU,
+    0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U,
+    0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU,
+    0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U,
+    0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U,
+    0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U,
+    0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU,
+    0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU,
+    0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU,
+    0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U,
+    0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U,
+    0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U,
+    0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU,
+};
+const u8 rcons[] = {
+       0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36
+       /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */
+};
+#endif /* AES_SMALL_TABLES */
+/**
+ * Expand the cipher key into the encryption key schedule.
+ *
+ * @return     the number of rounds for the given cipher key size.
+ */
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[])
+{
+       int i;
+       u32 temp;
+
+       rk[0] = GETU32(cipherKey     );
+       rk[1] = GETU32(cipherKey +  4);
+       rk[2] = GETU32(cipherKey +  8);
+       rk[3] = GETU32(cipherKey + 12);
+       for (i = 0; i < 10; i++) {
+               temp  = rk[3];
+               rk[4] = rk[0] ^
+                       TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^
+                       RCON(i);
+               rk[5] = rk[1] ^ rk[4];
+               rk[6] = rk[2] ^ rk[5];
+               rk[7] = rk[3] ^ rk[6];
+               rk += 4;
+       }
+}
diff --git a/src/crypto/aes-omac1.c b/src/crypto/aes-omac1.c
new file mode 100644 (file)
index 0000000..f775296
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * One-key CBC MAC (OMAC1) hash with AES-128
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+static void gf_mulx(u8 *pad)
+{
+       int i, carry;
+
+       carry = pad[0] & 0x80;
+       for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+               pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+       pad[AES_BLOCK_SIZE - 1] <<= 1;
+       if (carry)
+               pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+/**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+                        const u8 *addr[], const size_t *len, u8 *mac)
+{
+       void *ctx;
+       u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
+       const u8 *pos, *end;
+       size_t i, e, left, total_len;
+
+       ctx = aes_encrypt_init(key, 16);
+       if (ctx == NULL)
+               return -1;
+       os_memset(cbc, 0, AES_BLOCK_SIZE);
+
+       total_len = 0;
+       for (e = 0; e < num_elem; e++)
+               total_len += len[e];
+       left = total_len;
+
+       e = 0;
+       pos = addr[0];
+       end = pos + len[0];
+
+       while (left >= AES_BLOCK_SIZE) {
+               for (i = 0; i < AES_BLOCK_SIZE; i++) {
+                       cbc[i] ^= *pos++;
+                       if (pos >= end) {
+                               e++;
+                               pos = addr[e];
+                               end = pos + len[e];
+                       }
+               }
+               if (left > AES_BLOCK_SIZE)
+                       aes_encrypt(ctx, cbc, cbc);
+               left -= AES_BLOCK_SIZE;
+       }
+
+       os_memset(pad, 0, AES_BLOCK_SIZE);
+       aes_encrypt(ctx, pad, pad);
+       gf_mulx(pad);
+
+       if (left || total_len == 0) {
+               for (i = 0; i < left; i++) {
+                       cbc[i] ^= *pos++;
+                       if (pos >= end) {
+                               e++;
+                               pos = addr[e];
+                               end = pos + len[e];
+                       }
+               }
+               cbc[left] ^= 0x80;
+               gf_mulx(pad);
+       }
+
+       for (i = 0; i < AES_BLOCK_SIZE; i++)
+               pad[i] ^= cbc[i];
+       aes_encrypt(ctx, pad, mac);
+       aes_encrypt_deinit(ctx);
+       return 0;
+}
+
+
+/**
+ * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
+ * @key: 128-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+       return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/aes-unwrap.c b/src/crypto/aes-unwrap.c
new file mode 100644 (file)
index 0000000..f233ffa
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * AES key unwrap (128-bit KEK, RFC3394)
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits
+ * @plain: Plaintext key, n * 64 bits
+ * Returns: 0 on success, -1 on failure (e.g., integrity verification failed)
+ */
+int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain)
+{
+       u8 a[8], *r, b[16];
+       int i, j;
+       void *ctx;
+
+       /* 1) Initialize variables. */
+       os_memcpy(a, cipher, 8);
+       r = plain;
+       os_memcpy(r, cipher + 8, 8 * n);
+
+       ctx = aes_decrypt_init(kek, 16);
+       if (ctx == NULL)
+               return -1;
+
+       /* 2) Compute intermediate values.
+        * For j = 5 to 0
+        *     For i = n to 1
+        *         B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
+        *         A = MSB(64, B)
+        *         R[i] = LSB(64, B)
+        */
+       for (j = 5; j >= 0; j--) {
+               r = plain + (n - 1) * 8;
+               for (i = n; i >= 1; i--) {
+                       os_memcpy(b, a, 8);
+                       b[7] ^= n * j + i;
+
+                       os_memcpy(b + 8, r, 8);
+                       aes_decrypt(ctx, b, b);
+                       os_memcpy(a, b, 8);
+                       os_memcpy(r, b + 8, 8);
+                       r -= 8;
+               }
+       }
+       aes_decrypt_deinit(ctx);
+
+       /* 3) Output results.
+        *
+        * These are already in @plain due to the location of temporary
+        * variables. Just verify that the IV matches with the expected value.
+        */
+       for (i = 0; i < 8; i++) {
+               if (a[i] != 0xa6)
+                       return -1;
+       }
+
+       return 0;
+}
diff --git a/src/crypto/aes-wrap.c b/src/crypto/aes-wrap.c
new file mode 100644 (file)
index 0000000..28d0c89
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ *
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+
+/**
+ * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * @kek: 16-octet Key encryption key (KEK)
+ * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
+ * bytes
+ * @plain: Plaintext key to be wrapped, n * 64 bits
+ * @cipher: Wrapped key, (n + 1) * 64 bits
+ * Returns: 0 on success, -1 on failure
+ */
+int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher)
+{
+       u8 *a, *r, b[16];
+       int i, j;
+       void *ctx;
+
+       a = cipher;
+       r = cipher + 8;
+
+       /* 1) Initialize variables. */
+       os_memset(a, 0xa6, 8);
+       os_memcpy(r, plain, 8 * n);
+
+       ctx = aes_encrypt_init(kek, 16);
+       if (ctx == NULL)
+               return -1;
+
+       /* 2) Calculate intermediate values.
+        * For j = 0 to 5
+        *     For i=1 to n
+        *         B = AES(K, A | R[i])
+        *         A = MSB(64, B) ^ t where t = (n*j)+i
+        *         R[i] = LSB(64, B)
+        */
+       for (j = 0; j <= 5; j++) {
+               r = cipher + 8;
+               for (i = 1; i <= n; i++) {
+                       os_memcpy(b, a, 8);
+                       os_memcpy(b + 8, r, 8);
+                       aes_encrypt(ctx, b, b);
+                       os_memcpy(a, b, 8);
+                       a[7] ^= n * j + i;
+                       os_memcpy(r, b + 8, 8);
+                       r += 8;
+               }
+       }
+       aes_encrypt_deinit(ctx);
+
+       /* 3) Output the results.
+        *
+        * These are already in @cipher due to the location of temporary
+        * variables.
+        */
+
+       return 0;
+}
diff --git a/src/crypto/aes.h b/src/crypto/aes.h
new file mode 100644 (file)
index 0000000..ba384a9
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * AES functions
+ * Copyright (c) 2003-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 AES_H
+#define AES_H
+
+#define AES_BLOCK_SIZE 16
+
+void * aes_encrypt_init(const u8 *key, size_t len);
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+void aes_encrypt_deinit(void *ctx);
+void * aes_decrypt_init(const u8 *key, size_t len);
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+void aes_decrypt_deinit(void *ctx);
+
+#endif /* AES_H */
diff --git a/src/crypto/aes_i.h b/src/crypto/aes_i.h
new file mode 100644 (file)
index 0000000..6b40bc7
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * AES (Rijndael) cipher
+ * 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 AES_I_H
+#define AES_I_H
+
+#include "aes.h"
+
+/* #define FULL_UNROLL */
+#define AES_SMALL_TABLES
+
+extern const u32 Te0[256];
+extern const u32 Te1[256];
+extern const u32 Te2[256];
+extern const u32 Te3[256];
+extern const u32 Te4[256];
+extern const u32 Td0[256];
+extern const u32 Td1[256];
+extern const u32 Td2[256];
+extern const u32 Td3[256];
+extern const u32 Td4[256];
+extern const u32 rcon[10];
+extern const u8 Td4s[256];
+extern const u8 rcons[10];
+
+#ifndef AES_SMALL_TABLES
+
+#define RCON(i) rcon[(i)]
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) Te1[((i) >> 16) & 0xff]
+#define TE2(i) Te2[((i) >> 8) & 0xff]
+#define TE3(i) Te3[(i) & 0xff]
+#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000)
+#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff)
+#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000)
+#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff)
+#define TE4(i) (Te4[(i)] & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) Td1[((i) >> 16) & 0xff]
+#define TD2(i) Td2[((i) >> 8) & 0xff]
+#define TD3(i) Td3[(i) & 0xff]
+#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000)
+#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff)
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) Td1[(i) & 0xff]
+#define TD2_(i) Td2[(i) & 0xff]
+#define TD3_(i) Td3[(i) & 0xff]
+
+#else /* AES_SMALL_TABLES */
+
+#define RCON(i) (rcons[(i)] << 24)
+
+static inline u32 rotr(u32 val, int bits)
+{
+       return (val >> bits) | (val << (32 - bits));
+}
+
+#define TE0(i) Te0[((i) >> 24) & 0xff]
+#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8)
+#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16)
+#define TE3(i) rotr(Te0[(i) & 0xff], 24)
+#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000)
+#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000)
+#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00)
+#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff)
+#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000)
+#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000)
+#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00)
+#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff)
+#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff)
+
+#define TD0(i) Td0[((i) >> 24) & 0xff]
+#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8)
+#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16)
+#define TD3(i) rotr(Td0[(i) & 0xff], 24)
+#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24)
+#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16)
+#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8)
+#define TD44(i) (Td4s[(i) & 0xff])
+#define TD0_(i) Td0[(i) & 0xff]
+#define TD1_(i) rotr(Td0[(i) & 0xff], 8)
+#define TD2_(i) rotr(Td0[(i) & 0xff], 16)
+#define TD3_(i) rotr(Td0[(i) & 0xff], 24)
+
+#endif /* AES_SMALL_TABLES */
+
+#ifdef _MSC_VER
+#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)
+#define GETU32(p) SWAP(*((u32 *)(p)))
+#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }
+#else
+#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \
+((u32)(pt)[2] <<  8) ^ ((u32)(pt)[3]))
+#define PUTU32(ct, st) { \
+(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \
+(ct)[2] = (u8)((st) >>  8); (ct)[3] = (u8)(st); }
+#endif
+
+#define AES_PRIV_SIZE (4 * 44)
+
+void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]);
+
+#endif /* AES_I_H */
diff --git a/src/crypto/aes_wrap.h b/src/crypto/aes_wrap.h
new file mode 100644 (file)
index 0000000..4b1c7b0
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * AES-based functions
+ *
+ * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128
+ * - AES-128 CTR mode encryption
+ * - AES-128 EAX mode encryption/decryption
+ * - AES-128 CBC
+ *
+ * Copyright (c) 2003-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 AES_WRAP_H
+#define AES_WRAP_H
+
+int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
+                                     const u8 *addr[], const size_t *len,
+                                     u8 *mac);
+int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
+                              u8 *mac);
+int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
+int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
+                                    u8 *data, size_t data_len);
+int __must_check aes_128_eax_encrypt(const u8 *key,
+                                    const u8 *nonce, size_t nonce_len,
+                                    const u8 *hdr, size_t hdr_len,
+                                    u8 *data, size_t data_len, u8 *tag);
+int __must_check aes_128_eax_decrypt(const u8 *key,
+                                    const u8 *nonce, size_t nonce_len,
+                                    const u8 *hdr, size_t hdr_len,
+                                    u8 *data, size_t data_len, const u8 *tag);
+int __must_check aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data,
+                                    size_t data_len);
+int __must_check aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data,
+                                    size_t data_len);
+
+#endif /* AES_WRAP_H */
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
new file mode 100644 (file)
index 0000000..587b5a9
--- /dev/null
@@ -0,0 +1,469 @@
+/*
+ * WPA Supplicant / wrapper functions for crypto libraries
+ * 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.
+ *
+ * This file defines the cryptographic functions that need to be implemented
+ * for wpa_supplicant and hostapd. When TLS is not used, internal
+ * implementation of MD5, SHA1, and AES is used and no external libraries are
+ * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the
+ * crypto library used by the TLS implementation is expected to be used for
+ * non-TLS needs, too, in order to save space by not implementing these
+ * functions twice.
+ *
+ * Wrapper code for using each crypto library is in its own file (crypto*.c)
+ * and one of these files is build and linked in to provide the functions
+ * defined here.
+ */
+
+#ifndef CRYPTO_H
+#define CRYPTO_H
+
+/**
+ * md4_vector - MD4 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac);
+
+#ifdef CONFIG_FIPS
+/**
+ * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed)
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
+                             const size_t *len, u8 *mac);
+#else /* CONFIG_FIPS */
+#define md5_vector_non_fips_allow md5_vector
+#endif /* CONFIG_FIPS */
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+               u8 *mac);
+
+/**
+ * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF
+ * @seed: Seed/key for the PRF
+ * @seed_len: Seed length in bytes
+ * @x: Buffer for PRF output
+ * @xlen: Output length in bytes
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function implements random number generation specified in NIST FIPS
+ * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to
+ * SHA-1, but has different message padding.
+ */
+int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x,
+                              size_t xlen);
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 on failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac);
+
+/**
+ * des_encrypt - Encrypt one block with DES
+ * @clear: 8 octets (in)
+ * @key: 7 octets (in) (no parity bits included)
+ * @cypher: 8 octets (out)
+ */
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher);
+
+/**
+ * aes_encrypt_init - Initialize AES for encryption
+ * @key: Encryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_encrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_encrypt - Encrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @plain: Plaintext data to be encrypted (16 bytes)
+ * @crypt: Buffer for the encrypted data (16 bytes)
+ */
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt);
+
+/**
+ * aes_encrypt_deinit - Deinitialize AES encryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_encrypt_deinit(void *ctx);
+
+/**
+ * aes_decrypt_init - Initialize AES for decryption
+ * @key: Decryption key
+ * @len: Key length in bytes (usually 16, i.e., 128 bits)
+ * Returns: Pointer to context data or %NULL on failure
+ */
+void * aes_decrypt_init(const u8 *key, size_t len);
+
+/**
+ * aes_decrypt - Decrypt one AES block
+ * @ctx: Context pointer from aes_encrypt_init()
+ * @crypt: Encrypted data (16 bytes)
+ * @plain: Buffer for the decrypted data (16 bytes)
+ */
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain);
+
+/**
+ * aes_decrypt_deinit - Deinitialize AES decryption
+ * @ctx: Context pointer from aes_encrypt_init()
+ */
+void aes_decrypt_deinit(void *ctx);
+
+
+enum crypto_hash_alg {
+       CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1,
+       CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1
+};
+
+struct crypto_hash;
+
+/**
+ * crypto_hash_init - Initialize hash/HMAC function
+ * @alg: Hash algorithm
+ * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed
+ * @key_len: Length of the key in bytes
+ * Returns: Pointer to hash context to use with other hash functions or %NULL
+ * on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+                                     size_t key_len);
+
+/**
+ * crypto_hash_update - Add data to hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @data: Data buffer to add
+ * @len: Length of the buffer
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len);
+
+/**
+ * crypto_hash_finish - Complete hash calculation
+ * @ctx: Context pointer from crypto_hash_init()
+ * @hash: Buffer for hash value or %NULL if caller is just freeing the hash
+ * context
+ * @len: Pointer to length of the buffer or %NULL if caller is just freeing the
+ * hash context; on return, this is set to the actual length of the hash value
+ * Returns: 0 on success, -1 if buffer is too small (len set to needed length),
+ * or -2 on other failures (including failed crypto_hash_update() operations)
+ *
+ * This function calculates the hash value and frees the context buffer that
+ * was used for hash calculation.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len);
+
+
+enum crypto_cipher_alg {
+       CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES,
+       CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4
+};
+
+struct crypto_cipher;
+
+/**
+ * crypto_cipher_init - Initialize block/stream cipher function
+ * @alg: Cipher algorithm
+ * @iv: Initialization vector for block ciphers or %NULL for stream ciphers
+ * @key: Cipher key
+ * @key_len: Length of key in bytes
+ * Returns: Pointer to cipher context to use with other cipher functions or
+ * %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len);
+
+/**
+ * crypto_cipher_encrypt - Cipher encrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @plain: Plaintext to cipher
+ * @crypt: Resulting ciphertext
+ * @len: Length of the plaintext
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_encrypt(struct crypto_cipher *ctx,
+                                      const u8 *plain, u8 *crypt, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Cipher decrypt
+ * @ctx: Context pointer from crypto_cipher_init()
+ * @crypt: Ciphertext to decrypt
+ * @plain: Resulting plaintext
+ * @len: Length of the cipher text
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_cipher_decrypt(struct crypto_cipher *ctx,
+                                      const u8 *crypt, u8 *plain, size_t len);
+
+/**
+ * crypto_cipher_decrypt - Free cipher context
+ * @ctx: Context pointer from crypto_cipher_init()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_cipher_deinit(struct crypto_cipher *ctx);
+
+
+struct crypto_public_key;
+struct crypto_private_key;
+
+/**
+ * crypto_public_key_import - Import an RSA public key
+ * @key: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library supports X.509
+ * parsing. In that case, crypto_public_key_from_cert() is used to import the
+ * public key from a certificate.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
+
+/**
+ * crypto_private_key_import - Import an RSA private key
+ * @key: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * @passwd: Key encryption password or %NULL if key is not encrypted
+ * Returns: Pointer to the private key or %NULL on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+                                                     size_t len,
+                                                     const char *passwd);
+
+/**
+ * crypto_public_key_from_cert - Import an RSA public key from a certificate
+ * @buf: DER encoded X.509 certificate
+ * @len: Certificate buffer length in bytes
+ * Returns: Pointer to public key or %NULL on failure
+ *
+ * This function can just return %NULL if the crypto library does not support
+ * X.509 parsing. In that case, internal code will be used to parse the
+ * certificate and public key is imported using crypto_public_key_import().
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+                                                      size_t len);
+
+/**
+ * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5)
+ * @key: Public key
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_public_key_encrypt_pkcs1_v15(
+       struct crypto_public_key *key, const u8 *in, size_t inlen,
+       u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_decrypt_pkcs1_v15 - Private key decryption (PKCS #1 v1.5)
+ * @key: Private key
+ * @in: Encrypted buffer
+ * @inlen: Length of encrypted buffer in bytes
+ * @out: Output buffer for encrypted data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_decrypt_pkcs1_v15(
+       struct crypto_private_key *key, const u8 *in, size_t inlen,
+       u8 *out, size_t *outlen);
+
+/**
+ * crypto_private_key_sign_pkcs1 - Sign with private key (PKCS #1)
+ * @key: Private key from crypto_private_key_import()
+ * @in: Plaintext buffer
+ * @inlen: Length of plaintext buffer in bytes
+ * @out: Output buffer for encrypted (signed) data
+ * @outlen: Length of output buffer in bytes; set to used length on success
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+                                              const u8 *in, size_t inlen,
+                                              u8 *out, size_t *outlen);
+
+/**
+ * crypto_public_key_free - Free public key
+ * @key: Public key
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_public_key_free(struct crypto_public_key *key);
+
+/**
+ * crypto_private_key_free - Free private key
+ * @key: Private key from crypto_private_key_import()
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_private_key_free(struct crypto_private_key *key);
+
+/**
+ * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature
+ * @key: Public key
+ * @crypt: Encrypted signature data (using the private key)
+ * @crypt_len: Encrypted signature data length
+ * @plain: Buffer for plaintext (at least crypt_len bytes)
+ * @plain_len: Plaintext length (max buffer size on input, real len on output);
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check crypto_public_key_decrypt_pkcs1(
+       struct crypto_public_key *key, const u8 *crypt, size_t crypt_len,
+       u8 *plain, size_t *plain_len);
+
+/**
+ * crypto_global_init - Initialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_global_init(void);
+
+/**
+ * crypto_global_deinit - Deinitialize crypto wrapper
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+void crypto_global_deinit(void);
+
+/**
+ * crypto_mod_exp - Modular exponentiation of large integers
+ * @base: Base integer (big endian byte array)
+ * @base_len: Length of base integer in bytes
+ * @power: Power integer (big endian byte array)
+ * @power_len: Length of power integer in bytes
+ * @modulus: Modulus integer (big endian byte array)
+ * @modulus_len: Length of modulus integer in bytes
+ * @result: Buffer for the result
+ * @result_len: Result length (max buffer size on input, real len on output)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function calculates result = base ^ power mod modulus. modules_len is
+ * used as the maximum size of modulus buffer. It is set to the used size on
+ * success.
+ *
+ * This function is only used with internal TLSv1 implementation
+ * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need
+ * to implement this.
+ */
+int __must_check crypto_mod_exp(const u8 *base, size_t base_len,
+                               const u8 *power, size_t power_len,
+                               const u8 *modulus, size_t modulus_len,
+                               u8 *result, size_t *result_len);
+
+/**
+ * rc4_skip - XOR RC4 stream to given data with skip-stream-start
+ * @key: RC4 key
+ * @keylen: RC4 key length
+ * @skip: number of bytes to skip from the beginning of the RC4 stream
+ * @data: data to be XOR'ed with RC4 stream
+ * @data_len: buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Generate RC4 pseudo random stream for the given key, skip beginning of the
+ * stream, and XOR the end result with the data buffer to perform RC4
+ * encryption/decryption.
+ */
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+            u8 *data, size_t data_len);
+
+#endif /* CRYPTO_H */
diff --git a/src/crypto/crypto_cryptoapi.c b/src/crypto/crypto_cryptoapi.c
new file mode 100644 (file)
index 0000000..2a8d200
--- /dev/null
@@ -0,0 +1,789 @@
+/*
+ * Crypto wrapper for Microsoft CryptoAPI
+ * Copyright (c) 2005-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 <windows.h>
+#include <wincrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+#ifndef MS_ENH_RSA_AES_PROV
+#ifdef UNICODE
+#define MS_ENH_RSA_AES_PROV \
+L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
+#else
+#define MS_ENH_RSA_AES_PROV \
+"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)"
+#endif
+#endif /* MS_ENH_RSA_AES_PROV */
+
+#ifndef CALG_HMAC
+#define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC)
+#endif
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+
+static BOOL WINAPI
+(*CryptImportPublicKeyInfo)(HCRYPTPROV hCryptProv, DWORD dwCertEncodingType,
+                           PCERT_PUBLIC_KEY_INFO pInfo, HCRYPTKEY *phKey)
+= NULL; /* to be loaded from crypt32.dll */
+
+
+static int mingw_load_crypto_func(void)
+{
+       HINSTANCE dll;
+
+       /* MinGW does not yet have full CryptoAPI support, so load the needed
+        * function here. */
+
+       if (CryptImportPublicKeyInfo)
+               return 0;
+
+       dll = LoadLibrary("crypt32");
+       if (dll == NULL) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 "
+                          "library");
+               return -1;
+       }
+
+       CryptImportPublicKeyInfo = GetProcAddress(
+               dll, "CryptImportPublicKeyInfo");
+       if (CryptImportPublicKeyInfo == NULL) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get "
+                          "CryptImportPublicKeyInfo() address from "
+                          "crypt32 library");
+               return -1;
+       }
+
+       return 0;
+}
+
+#else /* __MINGW32_VERSION */
+
+static int mingw_load_crypto_func(void)
+{
+       return 0;
+}
+
+#endif /* __MINGW32_VERSION */
+
+
+static void cryptoapi_report_error(const char *msg)
+{
+       char *s, *pos;
+       DWORD err = GetLastError();
+
+       if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_FROM_SYSTEM,
+                         NULL, err, 0, (LPTSTR) &s, 0, NULL) == 0) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d", msg, (int) err);
+       }
+
+       pos = s;
+       while (*pos) {
+               if (*pos == '\n' || *pos == '\r') {
+                       *pos = '\0';
+                       break;
+               }
+               pos++;
+       }
+
+       wpa_printf(MSG_DEBUG, "CryptoAPI: %s: %d: (%s)", msg, (int) err, s);
+       LocalFree(s);
+}
+
+
+int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem,
+                         const u8 *addr[], const size_t *len, u8 *mac)
+{
+       HCRYPTPROV prov;
+       HCRYPTHASH hash;
+       size_t i;
+       DWORD hlen;
+       int ret = 0;
+
+       if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, 0)) {
+               cryptoapi_report_error("CryptAcquireContext");
+               return -1;
+       }
+
+       if (!CryptCreateHash(prov, alg, 0, 0, &hash)) {
+               cryptoapi_report_error("CryptCreateHash");
+               CryptReleaseContext(prov, 0);
+               return -1;
+       }
+
+       for (i = 0; i < num_elem; i++) {
+               if (!CryptHashData(hash, (BYTE *) addr[i], len[i], 0)) {
+                       cryptoapi_report_error("CryptHashData");
+                       CryptDestroyHash(hash);
+                       CryptReleaseContext(prov, 0);
+               }
+       }
+
+       hlen = hash_len;
+       if (!CryptGetHashParam(hash, HP_HASHVAL, mac, &hlen, 0)) {
+               cryptoapi_report_error("CryptGetHashParam");
+               ret = -1;
+       }
+
+       CryptDestroyHash(hash);
+       CryptReleaseContext(prov, 0);
+
+       return ret;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac);
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       u8 next, tmp;
+       int i;
+       HCRYPTPROV prov;
+       HCRYPTKEY ckey;
+       DWORD dlen;
+       struct {
+               BLOBHEADER hdr;
+               DWORD len;
+               BYTE key[8];
+       } key_blob;
+       DWORD mode = CRYPT_MODE_ECB;
+
+       key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+       key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+       key_blob.hdr.reserved = 0;
+       key_blob.hdr.aiKeyAlg = CALG_DES;
+       key_blob.len = 8;
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               key_blob.key[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       key_blob.key[i] = next | 1;
+
+       if (!CryptAcquireContext(&prov, NULL, MS_ENHANCED_PROV, PROV_RSA_FULL,
+                                CRYPT_VERIFYCONTEXT)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: "
+                          "%d", (int) GetLastError());
+               return;
+       }
+
+       if (!CryptImportKey(prov, (BYTE *) &key_blob, sizeof(key_blob), 0, 0,
+                           &ckey)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d",
+                          (int) GetLastError());
+               CryptReleaseContext(prov, 0);
+               return;
+       }
+
+       if (!CryptSetKeyParam(ckey, KP_MODE, (BYTE *) &mode, 0)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) "
+                          "failed: %d", (int) GetLastError());
+               CryptDestroyKey(ckey);
+               CryptReleaseContext(prov, 0);
+               return;
+       }
+
+       os_memcpy(cypher, clear, 8);
+       dlen = 8;
+       if (!CryptEncrypt(ckey, 0, FALSE, 0, cypher, &dlen, 8)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d",
+                          (int) GetLastError());
+               os_memset(cypher, 0, 8);
+       }
+
+       CryptDestroyKey(ckey);
+       CryptReleaseContext(prov, 0);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac);
+}
+
+
+struct aes_context {
+       HCRYPTPROV prov;
+       HCRYPTKEY ckey;
+};
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       struct aes_context *akey;
+       struct {
+               BLOBHEADER hdr;
+               DWORD len;
+               BYTE key[16];
+       } key_blob;
+       DWORD mode = CRYPT_MODE_ECB;
+
+       if (len != 16)
+               return NULL;
+
+       key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+       key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+       key_blob.hdr.reserved = 0;
+       key_blob.hdr.aiKeyAlg = CALG_AES_128;
+       key_blob.len = len;
+       os_memcpy(key_blob.key, key, len);
+
+       akey = os_zalloc(sizeof(*akey));
+       if (akey == NULL)
+               return NULL;
+
+       if (!CryptAcquireContext(&akey->prov, NULL,
+                                MS_ENH_RSA_AES_PROV, PROV_RSA_AES,
+                                CRYPT_VERIFYCONTEXT)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptAcquireContext failed: "
+                          "%d", (int) GetLastError());
+               os_free(akey);
+               return NULL;
+       }
+
+       if (!CryptImportKey(akey->prov, (BYTE *) &key_blob, sizeof(key_blob),
+                           0, 0, &akey->ckey)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptImportKey failed: %d",
+                          (int) GetLastError());
+               CryptReleaseContext(akey->prov, 0);
+               os_free(akey);
+               return NULL;
+       }
+
+       if (!CryptSetKeyParam(akey->ckey, KP_MODE, (BYTE *) &mode, 0)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptSetKeyParam(KP_MODE) "
+                          "failed: %d", (int) GetLastError());
+               CryptDestroyKey(akey->ckey);
+               CryptReleaseContext(akey->prov, 0);
+               os_free(akey);
+               return NULL;
+       }
+
+       return akey;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+       struct aes_context *akey = ctx;
+       DWORD dlen;
+
+       os_memcpy(crypt, plain, 16);
+       dlen = 16;
+       if (!CryptEncrypt(akey->ckey, 0, FALSE, 0, crypt, &dlen, 16)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptEncrypt failed: %d",
+                          (int) GetLastError());
+               os_memset(crypt, 0, 16);
+       }
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+       struct aes_context *akey = ctx;
+       if (akey) {
+               CryptDestroyKey(akey->ckey);
+               CryptReleaseContext(akey->prov, 0);
+               os_free(akey);
+       }
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       return aes_encrypt_init(key, len);
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+       struct aes_context *akey = ctx;
+       DWORD dlen;
+
+       os_memcpy(plain, crypt, 16);
+       dlen = 16;
+
+       if (!CryptDecrypt(akey->ckey, 0, FALSE, 0, plain, &dlen)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: CryptDecrypt failed: %d",
+                          (int) GetLastError());
+       }
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+       aes_encrypt_deinit(ctx);
+}
+
+
+struct crypto_hash {
+       enum crypto_hash_alg alg;
+       int error;
+       HCRYPTPROV prov;
+       HCRYPTHASH hash;
+       HCRYPTKEY key;
+};
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+                                     size_t key_len)
+{
+       struct crypto_hash *ctx;
+       ALG_ID calg;
+       struct {
+               BLOBHEADER hdr;
+               DWORD len;
+               BYTE key[32];
+       } key_blob;
+
+       os_memset(&key_blob, 0, sizeof(key_blob));
+       switch (alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               calg = CALG_MD5;
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               calg = CALG_SHA;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               calg = CALG_HMAC;
+               key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+               key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+               key_blob.hdr.reserved = 0;
+               /*
+                * Note: RC2 is not really used, but that can be used to
+                * import HMAC keys of up to 16 byte long.
+                * CRYPT_IPSEC_HMAC_KEY flag for CryptImportKey() is needed to
+                * be able to import longer keys (HMAC-SHA1 uses 20-byte key).
+                */
+               key_blob.hdr.aiKeyAlg = CALG_RC2;
+               key_blob.len = key_len;
+               if (key_len > sizeof(key_blob.key))
+                       return NULL;
+               os_memcpy(key_blob.key, key, key_len);
+               break;
+       default:
+               return NULL;
+       }
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       ctx->alg = alg;
+
+       if (!CryptAcquireContext(&ctx->prov, NULL, NULL, PROV_RSA_FULL, 0)) {
+               cryptoapi_report_error("CryptAcquireContext");
+               os_free(ctx);
+               return NULL;
+       }
+
+       if (calg == CALG_HMAC) {
+#ifndef CRYPT_IPSEC_HMAC_KEY
+#define CRYPT_IPSEC_HMAC_KEY 0x00000100
+#endif
+               if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob,
+                                   sizeof(key_blob), 0, CRYPT_IPSEC_HMAC_KEY,
+                                   &ctx->key)) {
+                       cryptoapi_report_error("CryptImportKey");
+                       CryptReleaseContext(ctx->prov, 0);
+                       os_free(ctx);
+                       return NULL;
+               }
+       }
+
+       if (!CryptCreateHash(ctx->prov, calg, ctx->key, 0, &ctx->hash)) {
+               cryptoapi_report_error("CryptCreateHash");
+               CryptReleaseContext(ctx->prov, 0);
+               os_free(ctx);
+               return NULL;
+       }
+
+       if (calg == CALG_HMAC) {
+               HMAC_INFO info;
+               os_memset(&info, 0, sizeof(info));
+               switch (alg) {
+               case CRYPTO_HASH_ALG_HMAC_MD5:
+                       info.HashAlgid = CALG_MD5;
+                       break;
+               case CRYPTO_HASH_ALG_HMAC_SHA1:
+                       info.HashAlgid = CALG_SHA;
+                       break;
+               default:
+                       /* unreachable */
+                       break;
+               }
+
+               if (!CryptSetHashParam(ctx->hash, HP_HMAC_INFO, (BYTE *) &info,
+                                      0)) {
+                       cryptoapi_report_error("CryptSetHashParam");
+                       CryptDestroyHash(ctx->hash);
+                       CryptReleaseContext(ctx->prov, 0);
+                       os_free(ctx);
+                       return NULL;
+               }
+       }
+
+       return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+       if (ctx == NULL || ctx->error)
+               return;
+
+       if (!CryptHashData(ctx->hash, (BYTE *) data, len, 0)) {
+               cryptoapi_report_error("CryptHashData");
+               ctx->error = 1;
+       }
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+       int ret = 0;
+       DWORD hlen;
+
+       if (ctx == NULL)
+               return -2;
+
+       if (mac == NULL || len == NULL)
+               goto done;
+
+       if (ctx->error) {
+               ret = -2;
+               goto done;
+       }
+
+       hlen = *len;
+       if (!CryptGetHashParam(ctx->hash, HP_HASHVAL, mac, &hlen, 0)) {
+               cryptoapi_report_error("CryptGetHashParam");
+               ret = -2;
+       }
+       *len = hlen;
+
+done:
+       if (ctx->alg == CRYPTO_HASH_ALG_HMAC_SHA1 ||
+           ctx->alg == CRYPTO_HASH_ALG_HMAC_MD5)
+               CryptDestroyKey(ctx->key);
+
+       os_free(ctx);
+
+       return ret;
+}
+
+
+struct crypto_cipher {
+       HCRYPTPROV prov;
+       HCRYPTKEY key;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{      
+       struct crypto_cipher *ctx;
+       struct {
+               BLOBHEADER hdr;
+               DWORD len;
+               BYTE key[32];
+       } key_blob;
+       DWORD mode = CRYPT_MODE_CBC;
+
+       key_blob.hdr.bType = PLAINTEXTKEYBLOB;
+       key_blob.hdr.bVersion = CUR_BLOB_VERSION;
+       key_blob.hdr.reserved = 0;
+       key_blob.len = key_len;
+       if (key_len > sizeof(key_blob.key))
+               return NULL;
+       os_memcpy(key_blob.key, key, key_len);
+
+       switch (alg) {
+       case CRYPTO_CIPHER_ALG_AES:
+               if (key_len == 32)
+                       key_blob.hdr.aiKeyAlg = CALG_AES_256;
+               else if (key_len == 24)
+                       key_blob.hdr.aiKeyAlg = CALG_AES_192;
+               else
+                       key_blob.hdr.aiKeyAlg = CALG_AES_128;
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               key_blob.hdr.aiKeyAlg = CALG_3DES;
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               key_blob.hdr.aiKeyAlg = CALG_DES;
+               break;
+       case CRYPTO_CIPHER_ALG_RC2:
+               key_blob.hdr.aiKeyAlg = CALG_RC2;
+               break;
+       case CRYPTO_CIPHER_ALG_RC4:
+               key_blob.hdr.aiKeyAlg = CALG_RC4;
+               break;
+       default:
+               return NULL;
+       }
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       if (!CryptAcquireContext(&ctx->prov, NULL, MS_ENH_RSA_AES_PROV,
+                                PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
+               cryptoapi_report_error("CryptAcquireContext");
+               goto fail1;
+       }
+
+       if (!CryptImportKey(ctx->prov, (BYTE *) &key_blob,
+                           sizeof(key_blob), 0, 0, &ctx->key)) {
+               cryptoapi_report_error("CryptImportKey");
+               goto fail2;
+       }
+
+       if (!CryptSetKeyParam(ctx->key, KP_MODE, (BYTE *) &mode, 0)) {
+               cryptoapi_report_error("CryptSetKeyParam(KP_MODE)");
+               goto fail3;
+       }
+
+       if (iv && !CryptSetKeyParam(ctx->key, KP_IV, (BYTE *) iv, 0)) {
+               cryptoapi_report_error("CryptSetKeyParam(KP_IV)");
+               goto fail3;
+       }
+
+       return ctx;
+
+fail3:
+       CryptDestroyKey(ctx->key);
+fail2:
+       CryptReleaseContext(ctx->prov, 0);
+fail1:
+       os_free(ctx);
+       return NULL;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       DWORD dlen;
+
+       os_memcpy(crypt, plain, len);
+       dlen = len;
+       if (!CryptEncrypt(ctx->key, 0, FALSE, 0, crypt, &dlen, len)) {
+               cryptoapi_report_error("CryptEncrypt");
+               os_memset(crypt, 0, len);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       DWORD dlen;
+
+       os_memcpy(plain, crypt, len);
+       dlen = len;
+       if (!CryptDecrypt(ctx->key, 0, FALSE, 0, plain, &dlen)) {
+               cryptoapi_report_error("CryptDecrypt");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+       CryptDestroyKey(ctx->key);
+       CryptReleaseContext(ctx->prov, 0);
+       os_free(ctx);
+}
+
+
+struct crypto_public_key {
+       HCRYPTPROV prov;
+       HCRYPTKEY rsa;
+};
+
+struct crypto_private_key {
+       HCRYPTPROV prov;
+       HCRYPTKEY rsa;
+};
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+       /* Use crypto_public_key_from_cert() instead. */
+       return NULL;
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+                                                     size_t len,
+                                                     const char *passwd)
+{
+       /* TODO */
+       return NULL;
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+                                                      size_t len)
+{
+       struct crypto_public_key *pk;
+       PCCERT_CONTEXT cc;
+
+       pk = os_zalloc(sizeof(*pk));
+       if (pk == NULL)
+               return NULL;
+
+       cc = CertCreateCertificateContext(X509_ASN_ENCODING |
+                                         PKCS_7_ASN_ENCODING, buf, len);
+       if (!cc) {
+               cryptoapi_report_error("CryptCreateCertificateContext");
+               os_free(pk);
+               return NULL;
+       }
+
+       if (!CryptAcquireContext(&pk->prov, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+                                0)) {
+               cryptoapi_report_error("CryptAcquireContext");
+               os_free(pk);
+               CertFreeCertificateContext(cc);
+               return NULL;
+       }
+
+       if (!CryptImportPublicKeyInfo(pk->prov, X509_ASN_ENCODING |
+                                     PKCS_7_ASN_ENCODING,
+                                     &cc->pCertInfo->SubjectPublicKeyInfo,
+                                     &pk->rsa)) {
+               cryptoapi_report_error("CryptImportPublicKeyInfo");
+               CryptReleaseContext(pk->prov, 0);
+               os_free(pk);
+               CertFreeCertificateContext(cc);
+               return NULL;
+       }
+
+       CertFreeCertificateContext(cc);
+
+       return pk;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+                                       const u8 *in, size_t inlen,
+                                       u8 *out, size_t *outlen)
+{
+       DWORD clen;
+       u8 *tmp;
+       size_t i;
+
+       if (*outlen < inlen)
+               return -1;
+       tmp = malloc(*outlen);
+       if (tmp == NULL)
+               return -1;
+
+       os_memcpy(tmp, in, inlen);
+       clen = inlen;
+       if (!CryptEncrypt(key->rsa, 0, TRUE, 0, tmp, &clen, *outlen)) {
+               wpa_printf(MSG_DEBUG, "CryptoAPI: Failed to encrypt using "
+                          "public key: %d", (int) GetLastError());
+               os_free(tmp);
+               return -1;
+       }
+
+       *outlen = clen;
+
+       /* Reverse the output */
+       for (i = 0; i < *outlen; i++)
+               out[i] = tmp[*outlen - 1 - i];
+
+       os_free(tmp);
+
+       return 0;
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+                                 const u8 *in, size_t inlen,
+                                 u8 *out, size_t *outlen)
+{
+       /* TODO */
+       return -1;
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+       if (key) {
+               CryptDestroyKey(key->rsa);
+               CryptReleaseContext(key->prov, 0);
+               os_free(key);
+       }
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+       if (key) {
+               CryptDestroyKey(key->rsa);
+               CryptReleaseContext(key->prov, 0);
+               os_free(key);
+       }
+}
+
+
+int crypto_global_init(void)
+{
+       return mingw_load_crypto_func();
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+                  const u8 *power, size_t power_len,
+                  const u8 *modulus, size_t modulus_len,
+                  u8 *result, size_t *result_len)
+{
+       /* TODO */
+       return -1;
+}
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
new file mode 100644 (file)
index 0000000..0998cca
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * WPA Supplicant / wrapper functions for libgcrypt
+ * 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 <gcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       gcry_md_hd_t hd;
+       unsigned char *p;
+       size_t i;
+
+       if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR)
+               return -1;
+       for (i = 0; i < num_elem; i++)
+               gcry_md_write(hd, addr[i], len[i]);
+       p = gcry_md_read(hd, GCRY_MD_MD4);
+       if (p)
+               memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4));
+       gcry_md_close(hd);
+       return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       gcry_cipher_hd_t hd;
+       u8 pkey[8], next, tmp;
+       int i;
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               pkey[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       pkey[i] = next | 1;
+
+       gcry_cipher_open(&hd, GCRY_CIPHER_DES, GCRY_CIPHER_MODE_ECB, 0);
+       gcry_err_code(gcry_cipher_setkey(hd, pkey, 8));
+       gcry_cipher_encrypt(hd, cypher, 8, clear, 8);
+       gcry_cipher_close(hd);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       gcry_md_hd_t hd;
+       unsigned char *p;
+       size_t i;
+
+       if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR)
+               return -1;
+       for (i = 0; i < num_elem; i++)
+               gcry_md_write(hd, addr[i], len[i]);
+       p = gcry_md_read(hd, GCRY_MD_MD5);
+       if (p)
+               memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5));
+       gcry_md_close(hd);
+       return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       gcry_md_hd_t hd;
+       unsigned char *p;
+       size_t i;
+
+       if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR)
+               return -1;
+       for (i = 0; i < num_elem; i++)
+               gcry_md_write(hd, addr[i], len[i]);
+       p = gcry_md_read(hd, GCRY_MD_SHA1);
+       if (p)
+               memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1));
+       gcry_md_close(hd);
+       return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       gcry_cipher_hd_t hd;
+
+       if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+           GPG_ERR_NO_ERROR) {
+               printf("cipher open failed\n");
+               return NULL;
+       }
+       if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+               printf("setkey failed\n");
+               gcry_cipher_close(hd);
+               return NULL;
+       }
+
+       return hd;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+       gcry_cipher_hd_t hd = ctx;
+       gcry_cipher_encrypt(hd, crypt, 16, plain, 16);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+       gcry_cipher_hd_t hd = ctx;
+       gcry_cipher_close(hd);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       gcry_cipher_hd_t hd;
+
+       if (gcry_cipher_open(&hd, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 0) !=
+           GPG_ERR_NO_ERROR)
+               return NULL;
+       if (gcry_cipher_setkey(hd, key, len) != GPG_ERR_NO_ERROR) {
+               gcry_cipher_close(hd);
+               return NULL;
+       }
+
+       return hd;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+       gcry_cipher_hd_t hd = ctx;
+       gcry_cipher_decrypt(hd, plain, 16, crypt, 16);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+       gcry_cipher_hd_t hd = ctx;
+       gcry_cipher_close(hd);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+                  const u8 *power, size_t power_len,
+                  const u8 *modulus, size_t modulus_len,
+                  u8 *result, size_t *result_len)
+{
+       gcry_mpi_t bn_base = NULL, bn_exp = NULL, bn_modulus = NULL,
+               bn_result = NULL;
+       int ret = -1;
+
+       if (gcry_mpi_scan(&bn_base, GCRYMPI_FMT_USG, base, base_len, NULL) !=
+           GPG_ERR_NO_ERROR ||
+           gcry_mpi_scan(&bn_exp, GCRYMPI_FMT_USG, power, power_len, NULL) !=
+           GPG_ERR_NO_ERROR ||
+           gcry_mpi_scan(&bn_modulus, GCRYMPI_FMT_USG, modulus, modulus_len,
+                         NULL) != GPG_ERR_NO_ERROR)
+               goto error;
+       bn_result = gcry_mpi_new(modulus_len * 8);
+
+       gcry_mpi_powm(bn_result, bn_base, bn_exp, bn_modulus);
+
+       if (gcry_mpi_print(GCRYMPI_FMT_USG, result, *result_len, result_len,
+                          bn_result) != GPG_ERR_NO_ERROR)
+               goto error;
+
+       ret = 0;
+
+error:
+       gcry_mpi_release(bn_base);
+       gcry_mpi_release(bn_exp);
+       gcry_mpi_release(bn_modulus);
+       gcry_mpi_release(bn_result);
+       return ret;
+}
+
+
+struct crypto_cipher {
+       gcry_cipher_hd_t enc;
+       gcry_cipher_hd_t dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{
+       struct crypto_cipher *ctx;
+       gcry_error_t res;
+       enum gcry_cipher_algos a;
+       int ivlen;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       switch (alg) {
+       case CRYPTO_CIPHER_ALG_RC4:
+               a = GCRY_CIPHER_ARCFOUR;
+               res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_STREAM,
+                                      0);
+               gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_STREAM, 0);
+               break;
+       case CRYPTO_CIPHER_ALG_AES:
+               if (key_len == 24)
+                       a = GCRY_CIPHER_AES192;
+               else if (key_len == 32)
+                       a = GCRY_CIPHER_AES256;
+               else
+                       a = GCRY_CIPHER_AES;
+               res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+               gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               a = GCRY_CIPHER_3DES;
+               res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+               gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               a = GCRY_CIPHER_DES;
+               res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+               gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+               break;
+       case CRYPTO_CIPHER_ALG_RC2:
+               if (key_len == 5)
+                       a = GCRY_CIPHER_RFC2268_40;
+               else
+                       a = GCRY_CIPHER_RFC2268_128;
+               res = gcry_cipher_open(&ctx->enc, a, GCRY_CIPHER_MODE_CBC, 0);
+               gcry_cipher_open(&ctx->dec, a, GCRY_CIPHER_MODE_CBC, 0);
+               break;
+       default:
+               os_free(ctx);
+               return NULL;
+       }
+
+       if (res != GPG_ERR_NO_ERROR) {
+               os_free(ctx);
+               return NULL;
+       }
+
+       if (gcry_cipher_setkey(ctx->enc, key, key_len) != GPG_ERR_NO_ERROR ||
+           gcry_cipher_setkey(ctx->dec, key, key_len) != GPG_ERR_NO_ERROR) {
+               gcry_cipher_close(ctx->enc);
+               gcry_cipher_close(ctx->dec);
+               os_free(ctx);
+               return NULL;
+       }
+
+       ivlen = gcry_cipher_get_algo_blklen(a);
+       if (gcry_cipher_setiv(ctx->enc, iv, ivlen) != GPG_ERR_NO_ERROR ||
+           gcry_cipher_setiv(ctx->dec, iv, ivlen) != GPG_ERR_NO_ERROR) {
+               gcry_cipher_close(ctx->enc);
+               gcry_cipher_close(ctx->dec);
+               os_free(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       if (gcry_cipher_encrypt(ctx->enc, crypt, len, plain, len) !=
+           GPG_ERR_NO_ERROR)
+               return -1;
+       return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       if (gcry_cipher_decrypt(ctx->dec, plain, len, crypt, len) !=
+           GPG_ERR_NO_ERROR)
+               return -1;
+       return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+       gcry_cipher_close(ctx->enc);
+       gcry_cipher_close(ctx->dec);
+       os_free(ctx);
+}
diff --git a/src/crypto/crypto_internal-cipher.c b/src/crypto/crypto_internal-cipher.c
new file mode 100644 (file)
index 0000000..75134f0
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Crypto wrapper for internal crypto implementation - Cipher wrappers
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto.h"
+#include "aes.h"
+#include "des_i.h"
+
+
+struct crypto_cipher {
+       enum crypto_cipher_alg alg;
+       union {
+               struct {
+                       size_t used_bytes;
+                       u8 key[16];
+                       size_t keylen;
+               } rc4;
+               struct {
+                       u8 cbc[32];
+                       size_t block_size;
+                       void *ctx_enc;
+                       void *ctx_dec;
+               } aes;
+               struct {
+                       struct des3_key_s key;
+                       u8 cbc[8];
+               } des3;
+               struct {
+                       u32 ek[32];
+                       u32 dk[32];
+                       u8 cbc[8];
+               } des;
+       } u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{
+       struct crypto_cipher *ctx;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       ctx->alg = alg;
+
+       switch (alg) {
+       case CRYPTO_CIPHER_ALG_RC4:
+               if (key_len > sizeof(ctx->u.rc4.key)) {
+                       os_free(ctx);
+                       return NULL;
+               }
+               ctx->u.rc4.keylen = key_len;
+               os_memcpy(ctx->u.rc4.key, key, key_len);
+               break;
+       case CRYPTO_CIPHER_ALG_AES:
+               if (key_len > sizeof(ctx->u.aes.cbc)) {
+                       os_free(ctx);
+                       return NULL;
+               }
+               ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len);
+               if (ctx->u.aes.ctx_enc == NULL) {
+                       os_free(ctx);
+                       return NULL;
+               }
+               ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len);
+               if (ctx->u.aes.ctx_dec == NULL) {
+                       aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+                       os_free(ctx);
+                       return NULL;
+               }
+               ctx->u.aes.block_size = key_len;
+               os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size);
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               if (key_len != 24) {
+                       os_free(ctx);
+                       return NULL;
+               }
+               des3_key_setup(key, &ctx->u.des3.key);
+               os_memcpy(ctx->u.des3.cbc, iv, 8);
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               if (key_len != 8) {
+                       os_free(ctx);
+                       return NULL;
+               }
+               des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk);
+               os_memcpy(ctx->u.des.cbc, iv, 8);
+               break;
+       default:
+               os_free(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       size_t i, j, blocks;
+
+       switch (ctx->alg) {
+       case CRYPTO_CIPHER_ALG_RC4:
+               if (plain != crypt)
+                       os_memcpy(crypt, plain, len);
+               rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+                        ctx->u.rc4.used_bytes, crypt, len);
+               ctx->u.rc4.used_bytes += len;
+               break;
+       case CRYPTO_CIPHER_ALG_AES:
+               if (len % ctx->u.aes.block_size)
+                       return -1;
+               blocks = len / ctx->u.aes.block_size;
+               for (i = 0; i < blocks; i++) {
+                       for (j = 0; j < ctx->u.aes.block_size; j++)
+                               ctx->u.aes.cbc[j] ^= plain[j];
+                       aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc,
+                                   ctx->u.aes.cbc);
+                       os_memcpy(crypt, ctx->u.aes.cbc,
+                                 ctx->u.aes.block_size);
+                       plain += ctx->u.aes.block_size;
+                       crypt += ctx->u.aes.block_size;
+               }
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               if (len % 8)
+                       return -1;
+               blocks = len / 8;
+               for (i = 0; i < blocks; i++) {
+                       for (j = 0; j < 8; j++)
+                               ctx->u.des3.cbc[j] ^= plain[j];
+                       des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key,
+                                    ctx->u.des3.cbc);
+                       os_memcpy(crypt, ctx->u.des3.cbc, 8);
+                       plain += 8;
+                       crypt += 8;
+               }
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               if (len % 8)
+                       return -1;
+               blocks = len / 8;
+               for (i = 0; i < blocks; i++) {
+                       for (j = 0; j < 8; j++)
+                               ctx->u.des3.cbc[j] ^= plain[j];
+                       des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek,
+                                         ctx->u.des.cbc);
+                       os_memcpy(crypt, ctx->u.des.cbc, 8);
+                       plain += 8;
+                       crypt += 8;
+               }
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       size_t i, j, blocks;
+       u8 tmp[32];
+
+       switch (ctx->alg) {
+       case CRYPTO_CIPHER_ALG_RC4:
+               if (plain != crypt)
+                       os_memcpy(plain, crypt, len);
+               rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+                        ctx->u.rc4.used_bytes, plain, len);
+               ctx->u.rc4.used_bytes += len;
+               break;
+       case CRYPTO_CIPHER_ALG_AES:
+               if (len % ctx->u.aes.block_size)
+                       return -1;
+               blocks = len / ctx->u.aes.block_size;
+               for (i = 0; i < blocks; i++) {
+                       os_memcpy(tmp, crypt, ctx->u.aes.block_size);
+                       aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain);
+                       for (j = 0; j < ctx->u.aes.block_size; j++)
+                               plain[j] ^= ctx->u.aes.cbc[j];
+                       os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size);
+                       plain += ctx->u.aes.block_size;
+                       crypt += ctx->u.aes.block_size;
+               }
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               if (len % 8)
+                       return -1;
+               blocks = len / 8;
+               for (i = 0; i < blocks; i++) {
+                       os_memcpy(tmp, crypt, 8);
+                       des3_decrypt(crypt, &ctx->u.des3.key, plain);
+                       for (j = 0; j < 8; j++)
+                               plain[j] ^= ctx->u.des3.cbc[j];
+                       os_memcpy(ctx->u.des3.cbc, tmp, 8);
+                       plain += 8;
+                       crypt += 8;
+               }
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               if (len % 8)
+                       return -1;
+               blocks = len / 8;
+               for (i = 0; i < blocks; i++) {
+                       os_memcpy(tmp, crypt, 8);
+                       des_block_decrypt(crypt, ctx->u.des.dk, plain);
+                       for (j = 0; j < 8; j++)
+                               plain[j] ^= ctx->u.des.cbc[j];
+                       os_memcpy(ctx->u.des.cbc, tmp, 8);
+                       plain += 8;
+                       crypt += 8;
+               }
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+       switch (ctx->alg) {
+       case CRYPTO_CIPHER_ALG_AES:
+               aes_encrypt_deinit(ctx->u.aes.ctx_enc);
+               aes_decrypt_deinit(ctx->u.aes.ctx_dec);
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               break;
+       default:
+               break;
+       }
+       os_free(ctx);
+}
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
new file mode 100644 (file)
index 0000000..3124742
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Crypto wrapper for internal crypto implementation - modexp
+ * Copyright (c) 2006-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 "common.h"
+#include "tls/bignum.h"
+#include "crypto.h"
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+                  const u8 *power, size_t power_len,
+                  const u8 *modulus, size_t modulus_len,
+                  u8 *result, size_t *result_len)
+{
+       struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result;
+       int ret = -1;
+
+       bn_base = bignum_init();
+       bn_exp = bignum_init();
+       bn_modulus = bignum_init();
+       bn_result = bignum_init();
+
+       if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+           bn_result == NULL)
+               goto error;
+
+       if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 ||
+           bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 ||
+           bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0)
+               goto error;
+
+       if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0)
+               goto error;
+
+       ret = bignum_get_unsigned_bin(bn_result, result, result_len);
+
+error:
+       bignum_deinit(bn_base);
+       bignum_deinit(bn_exp);
+       bignum_deinit(bn_modulus);
+       bignum_deinit(bn_result);
+       return ret;
+}
diff --git a/src/crypto/crypto_internal-rsa.c b/src/crypto/crypto_internal-rsa.c
new file mode 100644 (file)
index 0000000..205042c
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Crypto wrapper for internal crypto implementation - RSA parts
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto.h"
+#include "tls/rsa.h"
+#include "tls/bignum.h"
+#include "tls/pkcs1.h"
+#include "tls/pkcs8.h"
+
+/* Dummy structures; these are just typecast to struct crypto_rsa_key */
+struct crypto_public_key;
+struct crypto_private_key;
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+       return (struct crypto_public_key *)
+               crypto_rsa_import_public_key(key, len);
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+                                                     size_t len,
+                                                     const char *passwd)
+{
+       struct crypto_private_key *res;
+
+       /* First, check for possible PKCS #8 encoding */
+       res = pkcs8_key_import(key, len);
+       if (res)
+               return res;
+
+       if (passwd) {
+               /* Try to parse as encrypted PKCS #8 */
+               res = pkcs8_enc_key_import(key, len, passwd);
+               if (res)
+                       return res;
+       }
+
+       /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */
+       wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private "
+                  "key");
+       return (struct crypto_private_key *)
+               crypto_rsa_import_private_key(key, len);
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+                                                      size_t len)
+{
+       /* No X.509 support in crypto_internal.c */
+       return NULL;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+                                       const u8 *in, size_t inlen,
+                                       u8 *out, size_t *outlen)
+{
+       return pkcs1_encrypt(2, (struct crypto_rsa_key *) key,
+                            0, in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key,
+                                        const u8 *in, size_t inlen,
+                                        u8 *out, size_t *outlen)
+{
+       return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key,
+                                            in, inlen, out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+                                 const u8 *in, size_t inlen,
+                                 u8 *out, size_t *outlen)
+{
+       return pkcs1_encrypt(1, (struct crypto_rsa_key *) key,
+                            1, in, inlen, out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+       crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+       crypto_rsa_free((struct crypto_rsa_key *) key);
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+                                   const u8 *crypt, size_t crypt_len,
+                                   u8 *plain, size_t *plain_len)
+{
+       return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key,
+                                       crypt, crypt_len, plain, plain_len);
+}
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
new file mode 100644 (file)
index 0000000..8fdba65
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Crypto wrapper for internal crypto implementation
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto.h"
+#include "sha1_i.h"
+#include "md5_i.h"
+
+struct crypto_hash {
+       enum crypto_hash_alg alg;
+       union {
+               struct MD5Context md5;
+               struct SHA1Context sha1;
+       } u;
+       u8 key[64];
+       size_t key_len;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+                                     size_t key_len)
+{
+       struct crypto_hash *ctx;
+       u8 k_pad[64];
+       u8 tk[20];
+       size_t i;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       ctx->alg = alg;
+
+       switch (alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               MD5Init(&ctx->u.md5);
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               SHA1Init(&ctx->u.sha1);
+               break;
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+               if (key_len > sizeof(k_pad)) {
+                       MD5Init(&ctx->u.md5);
+                       MD5Update(&ctx->u.md5, key, key_len);
+                       MD5Final(tk, &ctx->u.md5);
+                       key = tk;
+                       key_len = 16;
+               }
+               os_memcpy(ctx->key, key, key_len);
+               ctx->key_len = key_len;
+
+               os_memcpy(k_pad, key, key_len);
+               os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+               for (i = 0; i < sizeof(k_pad); i++)
+                       k_pad[i] ^= 0x36;
+               MD5Init(&ctx->u.md5);
+               MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+               break;
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               if (key_len > sizeof(k_pad)) {
+                       SHA1Init(&ctx->u.sha1);
+                       SHA1Update(&ctx->u.sha1, key, key_len);
+                       SHA1Final(tk, &ctx->u.sha1);
+                       key = tk;
+                       key_len = 20;
+               }
+               os_memcpy(ctx->key, key, key_len);
+               ctx->key_len = key_len;
+
+               os_memcpy(k_pad, key, key_len);
+               os_memset(k_pad + key_len, 0, sizeof(k_pad) - key_len);
+               for (i = 0; i < sizeof(k_pad); i++)
+                       k_pad[i] ^= 0x36;
+               SHA1Init(&ctx->u.sha1);
+               SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+               break;
+       default:
+               os_free(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+       if (ctx == NULL)
+               return;
+
+       switch (ctx->alg) {
+       case CRYPTO_HASH_ALG_MD5:
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+               MD5Update(&ctx->u.md5, data, len);
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               SHA1Update(&ctx->u.sha1, data, len);
+               break;
+       }
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+       u8 k_pad[64];
+       size_t i;
+
+       if (ctx == NULL)
+               return -2;
+
+       if (mac == NULL || len == NULL) {
+               os_free(ctx);
+               return 0;
+       }
+
+       switch (ctx->alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               if (*len < 16) {
+                       *len = 16;
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = 16;
+               MD5Final(mac, &ctx->u.md5);
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               if (*len < 20) {
+                       *len = 20;
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = 20;
+               SHA1Final(mac, &ctx->u.sha1);
+               break;
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+               if (*len < 16) {
+                       *len = 16;
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = 16;
+
+               MD5Final(mac, &ctx->u.md5);
+
+               os_memcpy(k_pad, ctx->key, ctx->key_len);
+               os_memset(k_pad + ctx->key_len, 0,
+                         sizeof(k_pad) - ctx->key_len);
+               for (i = 0; i < sizeof(k_pad); i++)
+                       k_pad[i] ^= 0x5c;
+               MD5Init(&ctx->u.md5);
+               MD5Update(&ctx->u.md5, k_pad, sizeof(k_pad));
+               MD5Update(&ctx->u.md5, mac, 16);
+               MD5Final(mac, &ctx->u.md5);
+               break;
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               if (*len < 20) {
+                       *len = 20;
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = 20;
+
+               SHA1Final(mac, &ctx->u.sha1);
+
+               os_memcpy(k_pad, ctx->key, ctx->key_len);
+               os_memset(k_pad + ctx->key_len, 0,
+                         sizeof(k_pad) - ctx->key_len);
+               for (i = 0; i < sizeof(k_pad); i++)
+                       k_pad[i] ^= 0x5c;
+               SHA1Init(&ctx->u.sha1);
+               SHA1Update(&ctx->u.sha1, k_pad, sizeof(k_pad));
+               SHA1Update(&ctx->u.sha1, mac, 20);
+               SHA1Final(mac, &ctx->u.sha1);
+               break;
+       }
+
+       os_free(ctx);
+
+       return 0;
+}
+
+
+int crypto_global_init(void)
+{
+       return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
new file mode 100644 (file)
index 0000000..52b67a7
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+ * WPA Supplicant / Crypto wrapper for LibTomCrypt (for internal TLSv1)
+ * Copyright (c) 2005-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.
+ */
+
+#include "includes.h"
+#include <tomcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+#ifndef mp_init_multi
+#define mp_init_multi                ltc_init_multi
+#define mp_clear_multi               ltc_deinit_multi
+#define mp_unsigned_bin_size(a)      ltc_mp.unsigned_size(a)
+#define mp_to_unsigned_bin(a, b)     ltc_mp.unsigned_write(a, b)
+#define mp_read_unsigned_bin(a, b, c) ltc_mp.unsigned_read(a, b, c)
+#define mp_exptmod(a,b,c,d)          ltc_mp.exptmod(a,b,c,d)
+#endif
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       hash_state md;
+       size_t i;
+
+       md4_init(&md);
+       for (i = 0; i < num_elem; i++)
+               md4_process(&md, addr[i], len[i]);
+       md4_done(&md, mac);
+       return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       u8 pkey[8], next, tmp;
+       int i;
+       symmetric_key skey;
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               pkey[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       pkey[i] = next | 1;
+
+       des_setup(pkey, 8, 0, &skey);
+       des_ecb_encrypt(clear, cypher, &skey);
+       des_done(&skey);
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       hash_state md;
+       size_t i;
+
+       md5_init(&md);
+       for (i = 0; i < num_elem; i++)
+               md5_process(&md, addr[i], len[i]);
+       md5_done(&md, mac);
+       return 0;
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       hash_state md;
+       size_t i;
+
+       sha1_init(&md);
+       for (i = 0; i < num_elem; i++)
+               sha1_process(&md, addr[i], len[i]);
+       sha1_done(&md, mac);
+       return 0;
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       symmetric_key *skey;
+       skey = os_malloc(sizeof(*skey));
+       if (skey == NULL)
+               return NULL;
+       if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+               os_free(skey);
+               return NULL;
+       }
+       return skey;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+       symmetric_key *skey = ctx;
+       aes_ecb_encrypt(plain, crypt, skey);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+       symmetric_key *skey = ctx;
+       aes_done(skey);
+       os_free(skey);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       symmetric_key *skey;
+       skey = os_malloc(sizeof(*skey));
+       if (skey == NULL)
+               return NULL;
+       if (aes_setup(key, len, 0, skey) != CRYPT_OK) {
+               os_free(skey);
+               return NULL;
+       }
+       return skey;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+       symmetric_key *skey = ctx;
+       aes_ecb_encrypt(plain, (u8 *) crypt, skey);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+       symmetric_key *skey = ctx;
+       aes_done(skey);
+       os_free(skey);
+}
+
+
+struct crypto_hash {
+       enum crypto_hash_alg alg;
+       int error;
+       union {
+               hash_state md;
+               hmac_state hmac;
+       } u;
+};
+
+
+struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
+                                     size_t key_len)
+{
+       struct crypto_hash *ctx;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       ctx->alg = alg;
+
+       switch (alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               if (md5_init(&ctx->u.md) != CRYPT_OK)
+                       goto fail;
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               if (sha1_init(&ctx->u.md) != CRYPT_OK)
+                       goto fail;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+               if (hmac_init(&ctx->u.hmac, find_hash("md5"), key, key_len) !=
+                   CRYPT_OK)
+                       goto fail;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               if (hmac_init(&ctx->u.hmac, find_hash("sha1"), key, key_len) !=
+                   CRYPT_OK)
+                       goto fail;
+               break;
+       default:
+               goto fail;
+       }
+
+       return ctx;
+
+fail:
+       os_free(ctx);
+       return NULL;
+}
+
+void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len)
+{
+       if (ctx == NULL || ctx->error)
+               return;
+
+       switch (ctx->alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               ctx->error = md5_process(&ctx->u.md, data, len) != CRYPT_OK;
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               ctx->error = sha1_process(&ctx->u.md, data, len) != CRYPT_OK;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               ctx->error = hmac_process(&ctx->u.hmac, data, len) != CRYPT_OK;
+               break;
+       }
+}
+
+
+int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
+{
+       int ret = 0;
+       unsigned long clen;
+
+       if (ctx == NULL)
+               return -2;
+
+       if (mac == NULL || len == NULL) {
+               os_free(ctx);
+               return 0;
+       }
+
+       if (ctx->error) {
+               os_free(ctx);
+               return -2;
+       }
+
+       switch (ctx->alg) {
+       case CRYPTO_HASH_ALG_MD5:
+               if (*len < 16) {
+                       *len = 16;
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = 16;
+               if (md5_done(&ctx->u.md, mac) != CRYPT_OK)
+                       ret = -2;
+               break;
+       case CRYPTO_HASH_ALG_SHA1:
+               if (*len < 20) {
+                       *len = 20;
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = 20;
+               if (sha1_done(&ctx->u.md, mac) != CRYPT_OK)
+                       ret = -2;
+               break;
+       case CRYPTO_HASH_ALG_HMAC_SHA1:
+               if (*len < 20) {
+                       *len = 20;
+                       os_free(ctx);
+                       return -1;
+               }
+               /* continue */
+       case CRYPTO_HASH_ALG_HMAC_MD5:
+               if (*len < 16) {
+                       *len = 16;
+                       os_free(ctx);
+                       return -1;
+               }
+               clen = *len;
+               if (hmac_done(&ctx->u.hmac, mac, &clen) != CRYPT_OK) {
+                       os_free(ctx);
+                       return -1;
+               }
+               *len = clen;
+               break;
+       default:
+               ret = -2;
+               break;
+       }
+
+       os_free(ctx);
+
+       return ret;
+}
+
+
+struct crypto_cipher {
+       int rc4;
+       union {
+               symmetric_CBC cbc;
+               struct {
+                       size_t used_bytes;
+                       u8 key[16];
+                       size_t keylen;
+               } rc4;
+       } u;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{      
+       struct crypto_cipher *ctx;
+       int idx, res, rc4 = 0;
+
+       switch (alg) {
+       case CRYPTO_CIPHER_ALG_AES:
+               idx = find_cipher("aes");
+               break;
+       case CRYPTO_CIPHER_ALG_3DES:
+               idx = find_cipher("3des");
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               idx = find_cipher("des");
+               break;
+       case CRYPTO_CIPHER_ALG_RC2:
+               idx = find_cipher("rc2");
+               break;
+       case CRYPTO_CIPHER_ALG_RC4:
+               idx = -1;
+               rc4 = 1;
+               break;
+       default:
+               return NULL;
+       }
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       if (rc4) {
+               ctx->rc4 = 1;
+               if (key_len > sizeof(ctx->u.rc4.key)) {
+                       os_free(ctx);
+                       return NULL;
+               }
+               ctx->u.rc4.keylen = key_len;
+               os_memcpy(ctx->u.rc4.key, key, key_len);
+       } else {
+               res = cbc_start(idx, iv, key, key_len, 0, &ctx->u.cbc);
+               if (res != CRYPT_OK) {
+                       wpa_printf(MSG_DEBUG, "LibTomCrypt: Cipher start "
+                                  "failed: %s", error_to_string(res));
+                       os_free(ctx);
+                       return NULL;
+               }
+       }
+
+       return ctx;
+}
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       int res;
+
+       if (ctx->rc4) {
+               if (plain != crypt)
+                       os_memcpy(crypt, plain, len);
+               rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+                        ctx->u.rc4.used_bytes, crypt, len);
+               ctx->u.rc4.used_bytes += len;
+               return 0;
+       }
+
+       res = cbc_encrypt(plain, crypt, len, &ctx->u.cbc);
+       if (res != CRYPT_OK) {
+               wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC encryption "
+                          "failed: %s", error_to_string(res));
+               return -1;
+       }
+       return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       int res;
+
+       if (ctx->rc4) {
+               if (plain != crypt)
+                       os_memcpy(plain, crypt, len);
+               rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen,
+                        ctx->u.rc4.used_bytes, plain, len);
+               ctx->u.rc4.used_bytes += len;
+               return 0;
+       }
+
+       res = cbc_decrypt(crypt, plain, len, &ctx->u.cbc);
+       if (res != CRYPT_OK) {
+               wpa_printf(MSG_DEBUG, "LibTomCrypt: CBC decryption "
+                          "failed: %s", error_to_string(res));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+       if (!ctx->rc4)
+               cbc_done(&ctx->u.cbc);
+       os_free(ctx);
+}
+
+
+struct crypto_public_key {
+       rsa_key rsa;
+};
+
+struct crypto_private_key {
+       rsa_key rsa;
+};
+
+
+struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
+{
+       int res;
+       struct crypto_public_key *pk;
+
+       pk = os_zalloc(sizeof(*pk));
+       if (pk == NULL)
+               return NULL;
+
+       res = rsa_import(key, len, &pk->rsa);
+       if (res != CRYPT_OK) {
+               wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+                          "public key (res=%d '%s')",
+                          res, error_to_string(res));
+               os_free(pk);
+               return NULL;
+       }
+
+       if (pk->rsa.type != PK_PUBLIC) {
+               wpa_printf(MSG_ERROR, "LibTomCrypt: Public key was not of "
+                          "correct type");
+               rsa_free(&pk->rsa);
+               os_free(pk);
+               return NULL;
+       }
+
+       return pk;
+}
+
+
+struct crypto_private_key * crypto_private_key_import(const u8 *key,
+                                                     size_t len,
+                                                     const char *passwd)
+{
+       int res;
+       struct crypto_private_key *pk;
+
+       pk = os_zalloc(sizeof(*pk));
+       if (pk == NULL)
+               return NULL;
+
+       res = rsa_import(key, len, &pk->rsa);
+       if (res != CRYPT_OK) {
+               wpa_printf(MSG_ERROR, "LibTomCrypt: Failed to import "
+                          "private key (res=%d '%s')",
+                          res, error_to_string(res));
+               os_free(pk);
+               return NULL;
+       }
+
+       if (pk->rsa.type != PK_PRIVATE) {
+               wpa_printf(MSG_ERROR, "LibTomCrypt: Private key was not of "
+                          "correct type");
+               rsa_free(&pk->rsa);
+               os_free(pk);
+               return NULL;
+       }
+
+       return pk;
+}
+
+
+struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf,
+                                                      size_t len)
+{
+       /* No X.509 support in LibTomCrypt */
+       return NULL;
+}
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+                                          const u8 *in, size_t inlen,
+                                          u8 *out, size_t *outlen)
+{
+       size_t ps_len;
+       u8 *pos;
+
+       /*
+        * PKCS #1 v1.5, 8.1:
+        *
+        * EB = 00 || BT || PS || 00 || D
+        * BT = 00 or 01 for private-key operation; 02 for public-key operation
+        * PS = k-3-||D||; at least eight octets
+        * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+        * k = length of modulus in octets (modlen)
+        */
+
+       if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+               wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+                          "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+                          __func__, (unsigned long) modlen,
+                          (unsigned long) *outlen,
+                          (unsigned long) inlen);
+               return -1;
+       }
+
+       pos = out;
+       *pos++ = 0x00;
+       *pos++ = block_type; /* BT */
+       ps_len = modlen - inlen - 3;
+       switch (block_type) {
+       case 0:
+               os_memset(pos, 0x00, ps_len);
+               pos += ps_len;
+               break;
+       case 1:
+               os_memset(pos, 0xff, ps_len);
+               pos += ps_len;
+               break;
+       case 2:
+               if (os_get_random(pos, ps_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+                                  "random data for PS", __func__);
+                       return -1;
+               }
+               while (ps_len--) {
+                       if (*pos == 0x00)
+                               *pos = 0x01;
+                       pos++;
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+                          "%d", __func__, block_type);
+               return -1;
+       }
+       *pos++ = 0x00;
+       os_memcpy(pos, in, inlen); /* D */
+
+       return 0;
+}
+
+
+static int crypto_rsa_encrypt_pkcs1(int block_type, rsa_key *key, int key_type,
+                                   const u8 *in, size_t inlen,
+                                   u8 *out, size_t *outlen)
+{
+       unsigned long len, modlen;
+       int res;
+
+       modlen = mp_unsigned_bin_size(key->N);
+
+       if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+                                           out, outlen) < 0)
+               return -1;
+
+       len = *outlen;
+       res = rsa_exptmod(out, modlen, out, &len, key_type, key);
+       if (res != CRYPT_OK) {
+               wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+                          error_to_string(res));
+               return -1;
+       }
+       *outlen = len;
+
+       return 0;
+}
+
+
+int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key,
+                                       const u8 *in, size_t inlen,
+                                       u8 *out, size_t *outlen)
+{
+       return crypto_rsa_encrypt_pkcs1(2, &key->rsa, PK_PUBLIC, in, inlen,
+                                       out, outlen);
+}
+
+
+int crypto_private_key_sign_pkcs1(struct crypto_private_key *key,
+                                 const u8 *in, size_t inlen,
+                                 u8 *out, size_t *outlen)
+{
+       return crypto_rsa_encrypt_pkcs1(1, &key->rsa, PK_PRIVATE, in, inlen,
+                                       out, outlen);
+}
+
+
+void crypto_public_key_free(struct crypto_public_key *key)
+{
+       if (key) {
+               rsa_free(&key->rsa);
+               os_free(key);
+       }
+}
+
+
+void crypto_private_key_free(struct crypto_private_key *key)
+{
+       if (key) {
+               rsa_free(&key->rsa);
+               os_free(key);
+       }
+}
+
+
+int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key,
+                                   const u8 *crypt, size_t crypt_len,
+                                   u8 *plain, size_t *plain_len)
+{
+       int res;
+       unsigned long len;
+       u8 *pos;
+
+       len = *plain_len;
+       res = rsa_exptmod(crypt, crypt_len, plain, &len, PK_PUBLIC,
+                         &key->rsa);
+       if (res != CRYPT_OK) {
+               wpa_printf(MSG_DEBUG, "LibTomCrypt: rsa_exptmod failed: %s",
+                          error_to_string(res));
+               return -1;
+       }
+
+       /*
+        * PKCS #1 v1.5, 8.1:
+        *
+        * EB = 00 || BT || PS || 00 || D
+        * BT = 01
+        * PS = k-3-||D|| times FF
+        * k = length of modulus in octets
+        */
+
+       if (len < 3 + 8 + 16 /* min hash len */ ||
+           plain[0] != 0x00 || plain[1] != 0x01 || plain[2] != 0xff) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+                          "structure");
+               return -1;
+       }
+
+       pos = plain + 3;
+       while (pos < plain + len && *pos == 0xff)
+               pos++;
+       if (pos - plain - 2 < 8) {
+               /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+               wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+                          "padding");
+               return -1;
+       }
+
+       if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+                          "structure (2)");
+               return -1;
+       }
+       pos++;
+       len -= pos - plain;
+
+       /* Strip PKCS #1 header */
+       os_memmove(plain, pos, len);
+       *plain_len = len;
+
+       return 0;
+}
+
+
+int crypto_global_init(void)
+{
+       ltc_mp = tfm_desc;
+       /* TODO: only register algorithms that are really needed */
+       if (register_hash(&md4_desc) < 0 ||
+           register_hash(&md5_desc) < 0 ||
+           register_hash(&sha1_desc) < 0 ||
+           register_cipher(&aes_desc) < 0 ||
+           register_cipher(&des_desc) < 0 ||
+           register_cipher(&des3_desc) < 0) {
+               wpa_printf(MSG_ERROR, "TLSv1: Failed to register "
+                          "hash/cipher functions");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void crypto_global_deinit(void)
+{
+}
+
+
+#ifdef CONFIG_MODEXP
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+                  const u8 *power, size_t power_len,
+                  const u8 *modulus, size_t modulus_len,
+                  u8 *result, size_t *result_len)
+{
+       void *b, *p, *m, *r;
+
+       if (mp_init_multi(&b, &p, &m, &r, NULL) != CRYPT_OK)
+               return -1;
+
+       if (mp_read_unsigned_bin(b, (u8 *) base, base_len) != CRYPT_OK ||
+           mp_read_unsigned_bin(p, (u8 *) power, power_len) != CRYPT_OK ||
+           mp_read_unsigned_bin(m, (u8 *) modulus, modulus_len) != CRYPT_OK)
+               goto fail;
+
+       if (mp_exptmod(b, p, m, r) != CRYPT_OK)
+               goto fail;
+
+       *result_len = mp_unsigned_bin_size(r);
+       if (mp_to_unsigned_bin(r, result) != CRYPT_OK)
+               goto fail;
+
+       mp_clear_multi(b, p, m, r, NULL);
+       return 0;
+
+fail:
+       mp_clear_multi(b, p, m, r, NULL);
+       return -1;
+}
+
+#endif /* CONFIG_MODEXP */
diff --git a/src/crypto/crypto_none.c b/src/crypto/crypto_none.c
new file mode 100644 (file)
index 0000000..9f43775
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * WPA Supplicant / Empty template functions for crypto wrapper
+ * 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 "crypto.h"
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+}
diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c
new file mode 100644 (file)
index 0000000..fee4195
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Crypto wrapper functions for NSS
+ * Copyright (c) 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 <nspr/prtypes.h>
+#include <nspr/plarenas.h>
+#include <nspr/plhash.h>
+#include <nspr/prtime.h>
+#include <nspr/prinrval.h>
+#include <nspr/prclist.h>
+#include <nspr/prlock.h>
+#include <nss/sechash.h>
+#include <nss/pk11pub.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static int nss_hash(HASH_HashType type, unsigned int max_res_len,
+                   size_t num_elem, const u8 *addr[], const size_t *len,
+                   u8 *mac)
+{
+       HASHContext *ctx;
+       size_t i;
+       unsigned int reslen;
+
+       ctx = HASH_Create(type);
+       if (ctx == NULL)
+               return -1;
+
+       HASH_Begin(ctx);
+       for (i = 0; i < num_elem; i++)
+               HASH_Update(ctx, addr[i], len[i]);
+       HASH_End(ctx, mac, &reslen, max_res_len);
+       HASH_Destroy(ctx);
+
+       return 0;
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       PK11Context *ctx = NULL;
+       PK11SlotInfo *slot;
+       SECItem *param = NULL;
+       PK11SymKey *symkey = NULL;
+       SECItem item;
+       int olen;
+       u8 pkey[8], next, tmp;
+       int i;
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               pkey[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       pkey[i] = next | 1;
+
+       slot = PK11_GetBestSlot(CKM_DES_ECB, NULL);
+       if (slot == NULL) {
+               wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed");
+               goto out;
+       }
+
+       item.type = siBuffer;
+       item.data = pkey;
+       item.len = 8;
+       symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive,
+                                  CKA_ENCRYPT, &item, NULL);
+       if (symkey == NULL) {
+               wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed");
+               goto out;
+       }
+
+       param = PK11_GenerateNewParam(CKM_DES_ECB, symkey);
+       if (param == NULL) {
+               wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed");
+               goto out;
+       }
+
+       ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT,
+                                        symkey, param);
+       if (ctx == NULL) {
+               wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey("
+                          "CKM_DES_ECB) failed");
+               goto out;
+       }
+
+       if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) !=
+           SECSuccess) {
+               wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed");
+               goto out;
+       }
+
+out:
+       if (ctx)
+               PK11_DestroyContext(ctx, PR_TRUE);
+       if (symkey)
+               PK11_FreeSymKey(symkey);
+       if (param)
+               SECITEM_FreeItem(param, PR_TRUE);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+            u8 *data, size_t data_len)
+{
+       return -1;
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac);
+}
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac);
+}
+
+
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac)
+{
+       return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac);
+}
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       return NULL;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       return NULL;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+                  const u8 *power, size_t power_len,
+                  const u8 *modulus, size_t modulus_len,
+                  u8 *result, size_t *result_len)
+{
+       return -1;
+}
+
+
+struct crypto_cipher {
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{
+       return NULL;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       return -1;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       return -1;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
new file mode 100644 (file)
index 0000000..08c98af
--- /dev/null
@@ -0,0 +1,505 @@
+/*
+ * WPA Supplicant / wrapper functions for libcrypto
+ * 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 <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/des.h>
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/dh.h>
+
+#include "common.h"
+#include "wpabuf.h"
+#include "dh_group5.h"
+#include "crypto.h"
+
+#if OPENSSL_VERSION_NUMBER < 0x00907000
+#define DES_key_schedule des_key_schedule
+#define DES_cblock des_cblock
+#define DES_set_key(key, schedule) des_set_key((key), *(schedule))
+#define DES_ecb_encrypt(input, output, ks, enc) \
+       des_ecb_encrypt((input), (output), *(ks), (enc))
+#endif /* openssl < 0.9.7 */
+
+static BIGNUM * get_group5_prime(void)
+{
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+       static const unsigned char RFC3526_PRIME_1536[] = {
+               0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
+               0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
+               0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6,
+               0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD,
+               0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D,
+               0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45,
+               0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9,
+               0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED,
+               0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11,
+               0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D,
+               0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36,
+               0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F,
+               0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56,
+               0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D,
+               0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08,
+               0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+       };
+        return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
+#else /* openssl < 0.9.8 */
+       return get_rfc3526_prime_1536(NULL);
+#endif /* openssl < 0.9.8 */
+}
+
+#if OPENSSL_VERSION_NUMBER < 0x00908000
+#ifndef OPENSSL_NO_SHA256
+#ifndef OPENSSL_FIPS
+#define NO_SHA256_WRAPPER
+#endif
+#endif
+
+#endif /* openssl < 0.9.8 */
+
+#ifdef OPENSSL_NO_SHA256
+#define NO_SHA256_WRAPPER
+#endif
+
+static int openssl_digest_vector(const EVP_MD *type, int non_fips,
+                                size_t num_elem, const u8 *addr[],
+                                const size_t *len, u8 *mac)
+{
+       EVP_MD_CTX ctx;
+       size_t i;
+       unsigned int mac_len;
+
+       EVP_MD_CTX_init(&ctx);
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+       if (non_fips)
+               EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW);
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+       if (!EVP_DigestInit_ex(&ctx, type, NULL)) {
+               wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s",
+                          ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+       for (i = 0; i < num_elem; i++) {
+               if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) {
+                       wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate "
+                                  "failed: %s",
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       return -1;
+               }
+       }
+       if (!EVP_DigestFinal(&ctx, mac, &mac_len)) {
+               wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s",
+                          ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac);
+}
+
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       u8 pkey[8], next, tmp;
+       int i;
+       DES_key_schedule ks;
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               pkey[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       pkey[i] = next | 1;
+
+       DES_set_key(&pkey, &ks);
+       DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
+                       DES_ENCRYPT);
+}
+
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+            u8 *data, size_t data_len)
+{
+#ifdef OPENSSL_NO_RC4
+       return -1;
+#else /* OPENSSL_NO_RC4 */
+       EVP_CIPHER_CTX ctx;
+       int outl;
+       int res = -1;
+       unsigned char skip_buf[16];
+
+       EVP_CIPHER_CTX_init(&ctx);
+       if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) ||
+           !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) ||
+           !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) ||
+           !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1))
+               goto out;
+
+       while (skip >= sizeof(skip_buf)) {
+               size_t len = skip;
+               if (len > sizeof(skip_buf))
+                       len = sizeof(skip_buf);
+               if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len))
+                       goto out;
+               skip -= len;
+       }
+
+       if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len))
+               res = 0;
+
+out:
+       EVP_CIPHER_CTX_cleanup(&ctx);
+       return res;
+#endif /* OPENSSL_NO_RC4 */
+}
+
+
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac);
+}
+
+
+#ifdef CONFIG_FIPS
+int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[],
+                             const size_t *len, u8 *mac)
+{
+       return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac);
+}
+#endif /* CONFIG_FIPS */
+
+
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac);
+}
+
+
+#ifndef NO_SHA256_WRAPPER
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac)
+{
+       return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len,
+                                    mac);
+}
+#endif /* NO_SHA256_WRAPPER */
+
+
+void * aes_encrypt_init(const u8 *key, size_t len)
+{
+       AES_KEY *ak;
+       ak = os_malloc(sizeof(*ak));
+       if (ak == NULL)
+               return NULL;
+       if (AES_set_encrypt_key(key, 8 * len, ak) < 0) {
+               os_free(ak);
+               return NULL;
+       }
+       return ak;
+}
+
+
+void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
+{
+       AES_encrypt(plain, crypt, ctx);
+}
+
+
+void aes_encrypt_deinit(void *ctx)
+{
+       os_free(ctx);
+}
+
+
+void * aes_decrypt_init(const u8 *key, size_t len)
+{
+       AES_KEY *ak;
+       ak = os_malloc(sizeof(*ak));
+       if (ak == NULL)
+               return NULL;
+       if (AES_set_decrypt_key(key, 8 * len, ak) < 0) {
+               os_free(ak);
+               return NULL;
+       }
+       return ak;
+}
+
+
+void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
+{
+       AES_decrypt(crypt, plain, ctx);
+}
+
+
+void aes_decrypt_deinit(void *ctx)
+{
+       os_free(ctx);
+}
+
+
+int crypto_mod_exp(const u8 *base, size_t base_len,
+                  const u8 *power, size_t power_len,
+                  const u8 *modulus, size_t modulus_len,
+                  u8 *result, size_t *result_len)
+{
+       BIGNUM *bn_base, *bn_exp, *bn_modulus, *bn_result;
+       int ret = -1;
+       BN_CTX *ctx;
+
+       ctx = BN_CTX_new();
+       if (ctx == NULL)
+               return -1;
+
+       bn_base = BN_bin2bn(base, base_len, NULL);
+       bn_exp = BN_bin2bn(power, power_len, NULL);
+       bn_modulus = BN_bin2bn(modulus, modulus_len, NULL);
+       bn_result = BN_new();
+
+       if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL ||
+           bn_result == NULL)
+               goto error;
+
+       if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+               goto error;
+
+       *result_len = BN_bn2bin(bn_result, result);
+       ret = 0;
+
+error:
+       BN_free(bn_base);
+       BN_free(bn_exp);
+       BN_free(bn_modulus);
+       BN_free(bn_result);
+       BN_CTX_free(ctx);
+       return ret;
+}
+
+
+struct crypto_cipher {
+       EVP_CIPHER_CTX enc;
+       EVP_CIPHER_CTX dec;
+};
+
+
+struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
+                                         const u8 *iv, const u8 *key,
+                                         size_t key_len)
+{
+       struct crypto_cipher *ctx;
+       const EVP_CIPHER *cipher;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+
+       switch (alg) {
+#ifndef OPENSSL_NO_RC4
+       case CRYPTO_CIPHER_ALG_RC4:
+               cipher = EVP_rc4();
+               break;
+#endif /* OPENSSL_NO_RC4 */
+#ifndef OPENSSL_NO_AES
+       case CRYPTO_CIPHER_ALG_AES:
+               switch (key_len) {
+               case 16:
+                       cipher = EVP_aes_128_cbc();
+                       break;
+               case 24:
+                       cipher = EVP_aes_192_cbc();
+                       break;
+               case 32:
+                       cipher = EVP_aes_256_cbc();
+                       break;
+               default:
+                       os_free(ctx);
+                       return NULL;
+               }
+               break;
+#endif /* OPENSSL_NO_AES */
+#ifndef OPENSSL_NO_DES
+       case CRYPTO_CIPHER_ALG_3DES:
+               cipher = EVP_des_ede3_cbc();
+               break;
+       case CRYPTO_CIPHER_ALG_DES:
+               cipher = EVP_des_cbc();
+               break;
+#endif /* OPENSSL_NO_DES */
+#ifndef OPENSSL_NO_RC2
+       case CRYPTO_CIPHER_ALG_RC2:
+               cipher = EVP_rc2_ecb();
+               break;
+#endif /* OPENSSL_NO_RC2 */
+       default:
+               os_free(ctx);
+               return NULL;
+       }
+
+       EVP_CIPHER_CTX_init(&ctx->enc);
+       EVP_CIPHER_CTX_set_padding(&ctx->enc, 0);
+       if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) ||
+           !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) ||
+           !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) {
+               EVP_CIPHER_CTX_cleanup(&ctx->enc);
+               os_free(ctx);
+               return NULL;
+       }
+
+       EVP_CIPHER_CTX_init(&ctx->dec);
+       EVP_CIPHER_CTX_set_padding(&ctx->dec, 0);
+       if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) ||
+           !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) ||
+           !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) {
+               EVP_CIPHER_CTX_cleanup(&ctx->enc);
+               EVP_CIPHER_CTX_cleanup(&ctx->dec);
+               os_free(ctx);
+               return NULL;
+       }
+
+       return ctx;
+}
+
+
+int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
+                         u8 *crypt, size_t len)
+{
+       int outl;
+       if (!EVP_EncryptUpdate(&ctx->enc, crypt, &outl, plain, len))
+               return -1;
+       return 0;
+}
+
+
+int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
+                         u8 *plain, size_t len)
+{
+       int outl;
+       outl = len;
+       if (!EVP_DecryptUpdate(&ctx->dec, plain, &outl, crypt, len))
+               return -1;
+       return 0;
+}
+
+
+void crypto_cipher_deinit(struct crypto_cipher *ctx)
+{
+       EVP_CIPHER_CTX_cleanup(&ctx->enc);
+       EVP_CIPHER_CTX_cleanup(&ctx->dec);
+       os_free(ctx);
+}
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+       DH *dh;
+       struct wpabuf *pubkey = NULL, *privkey = NULL;
+       size_t publen, privlen;
+
+       *priv = NULL;
+       *publ = NULL;
+
+       dh = DH_new();
+       if (dh == NULL)
+               return NULL;
+
+       dh->g = BN_new();
+       if (dh->g == NULL || BN_set_word(dh->g, 2) != 1)
+               goto err;
+
+       dh->p = get_group5_prime();
+       if (dh->p == NULL)
+               goto err;
+
+       if (DH_generate_key(dh) != 1)
+               goto err;
+
+       publen = BN_num_bytes(dh->pub_key);
+       pubkey = wpabuf_alloc(publen);
+       if (pubkey == NULL)
+               goto err;
+       privlen = BN_num_bytes(dh->priv_key);
+       privkey = wpabuf_alloc(privlen);
+       if (privkey == NULL)
+               goto err;
+
+       BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen));
+       BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen));
+
+       *priv = privkey;
+       *publ = pubkey;
+       return dh;
+
+err:
+       wpabuf_free(pubkey);
+       wpabuf_free(privkey);
+       DH_free(dh);
+       return NULL;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+                                 const struct wpabuf *own_private)
+{
+       BIGNUM *pub_key;
+       struct wpabuf *res = NULL;
+       size_t rlen;
+       DH *dh = ctx;
+       int keylen;
+
+       if (ctx == NULL)
+               return NULL;
+
+       pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public),
+                           NULL);
+       if (pub_key == NULL)
+               return NULL;
+
+       rlen = DH_size(dh);
+       res = wpabuf_alloc(rlen);
+       if (res == NULL)
+               goto err;
+
+       keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh);
+       if (keylen < 0)
+               goto err;
+       wpabuf_put(res, keylen);
+       BN_free(pub_key);
+
+       return res;
+
+err:
+       BN_free(pub_key);
+       wpabuf_free(res);
+       return NULL;
+}
+
+
+void dh5_free(void *ctx)
+{
+       DH *dh;
+       if (ctx == NULL)
+               return;
+       dh = ctx;
+       DH_free(dh);
+}
diff --git a/src/crypto/des-internal.c b/src/crypto/des-internal.c
new file mode 100644 (file)
index 0000000..ccea950
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * DES and 3DES-EDE ciphers
+ *
+ * Modifications to LibTomCrypt implementation:
+ * Copyright (c) 2006-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 "common.h"
+#include "crypto.h"
+#include "des_i.h"
+
+/*
+ * This implementation is based on a DES implementation included in
+ * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd
+ * coding style.
+ */
+
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ *
+ * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com
+ */
+
+/**
+  DES code submitted by Dobes Vandermeer
+*/
+
+#define ROLc(x, y) \
+       ((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \
+         (((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+          (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define RORc(x, y) \
+       (((((unsigned long) (x) & 0xFFFFFFFFUL) >> \
+          (unsigned long) ((y) & 31)) | \
+         ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \
+        0xFFFFFFFFUL)
+
+
+static const u32 bytebit[8] =
+{
+       0200, 0100, 040, 020, 010, 04, 02, 01 
+};
+
+static const u32 bigbyte[24] =
+{
+       0x800000UL,  0x400000UL,  0x200000UL,  0x100000UL,
+       0x80000UL,   0x40000UL,   0x20000UL,   0x10000UL,
+       0x8000UL,    0x4000UL,    0x2000UL,    0x1000UL,
+       0x800UL,     0x400UL,     0x200UL,     0x100UL,
+       0x80UL,      0x40UL,      0x20UL,      0x10UL,
+       0x8UL,       0x4UL,       0x2UL,       0x1L 
+};
+
+/* Use the key schedule specific in the standard (ANSI X3.92-1981) */
+
+static const u8 pc1[56] = {
+       56, 48, 40, 32, 24, 16,  8,  0, 57, 49, 41, 33, 25, 17,  
+        9,  1, 58, 50, 42, 34, 26, 18, 10,  2, 59, 51, 43, 35, 
+       62, 54, 46, 38, 30, 22, 14,  6, 61, 53, 45, 37, 29, 21,
+       13,  5, 60, 52, 44, 36, 28, 20, 12,  4, 27, 19, 11,  3 
+};
+
+static const u8 totrot[16] = {
+       1,   2,  4,  6,
+       8,  10, 12, 14, 
+       15, 17, 19, 21, 
+       23, 25, 27, 28
+};
+
+static const u8 pc2[48] = {
+       13, 16, 10, 23,  0,  4,      2, 27, 14,  5, 20,  9,
+       22, 18, 11,  3, 25,  7,     15,  6, 26, 19, 12,  1,
+       40, 51, 30, 36, 46, 54,     29, 39, 50, 44, 32, 47,
+       43, 48, 38, 55, 33, 52,     45, 41, 49, 35, 28, 31
+};
+
+
+static const u32 SP1[64] =
+{
+       0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL,
+       0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL,
+       0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL,
+       0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL,
+       0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL,
+       0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL,
+       0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL,
+       0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL,
+       0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL,
+       0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL,
+       0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL,
+       0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL,
+       0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL,
+       0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL,
+       0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL,
+       0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL
+};
+
+static const u32 SP2[64] =
+{
+       0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL,
+       0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL,
+       0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL,
+       0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL,
+       0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL,
+       0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL,
+       0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL,
+       0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL,
+       0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL,
+       0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL,
+       0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL,
+       0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL,
+       0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL,
+       0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL,
+       0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL,
+       0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL
+};
+
+static const u32 SP3[64] =
+{
+       0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL,
+       0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL,
+       0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL,
+       0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL,
+       0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL,
+       0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL,
+       0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL,
+       0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL,
+       0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL,
+       0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL,
+       0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL,
+       0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL,
+       0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL,
+       0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL,
+       0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL,
+       0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL
+};
+
+static const u32 SP4[64] =
+{
+       0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+       0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL,
+       0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL,
+       0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL,
+       0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL,
+       0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL,
+       0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL,
+       0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL,
+       0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL,
+       0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL,
+       0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL,
+       0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL,
+       0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL,
+       0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL,
+       0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL,
+       0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL
+};
+
+static const u32 SP5[64] =
+{
+       0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL,
+       0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL,
+       0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL,
+       0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL,
+       0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL,
+       0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL,
+       0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL,
+       0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL,
+       0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL,
+       0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL,
+       0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL,
+       0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL,
+       0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL,
+       0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL,
+       0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL,
+       0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL
+};
+
+static const u32 SP6[64] =
+{
+       0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL,
+       0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL,
+       0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL,
+       0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+       0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL,
+       0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL,
+       0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL,
+       0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL,
+       0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL,
+       0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL,
+       0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL,
+       0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL,
+       0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL,
+       0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL,
+       0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL,
+       0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL
+};
+
+static const u32 SP7[64] =
+{
+       0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL,
+       0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL,
+       0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL,
+       0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL,
+       0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL,
+       0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL,
+       0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL,
+       0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL,
+       0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL,
+       0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL,
+       0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL,
+       0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL,
+       0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL,
+       0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL,
+       0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL,
+       0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL
+};
+
+static const u32 SP8[64] =
+{
+       0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL,
+       0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL,
+       0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL,
+       0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL,
+       0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL,
+       0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL,
+       0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL,
+       0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL,
+       0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL,
+       0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL,
+       0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL,
+       0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL,
+       0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL,
+       0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL,
+       0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL,
+       0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL
+};
+
+
+static void cookey(const u32 *raw1, u32 *keyout)
+{
+       u32 *cook;
+       const u32 *raw0;
+       u32 dough[32];
+       int i;
+
+       cook = dough;
+       for (i = 0; i < 16; i++, raw1++) {
+               raw0 = raw1++;
+               *cook    = (*raw0 & 0x00fc0000L) << 6;
+               *cook   |= (*raw0 & 0x00000fc0L) << 10;
+               *cook   |= (*raw1 & 0x00fc0000L) >> 10;
+               *cook++ |= (*raw1 & 0x00000fc0L) >> 6;
+               *cook    = (*raw0 & 0x0003f000L) << 12;
+               *cook   |= (*raw0 & 0x0000003fL) << 16;
+               *cook   |= (*raw1 & 0x0003f000L) >> 4;
+               *cook++ |= (*raw1 & 0x0000003fL);
+       }
+
+       os_memcpy(keyout, dough, sizeof(dough));
+}
+
+
+static void deskey(const u8 *key, int decrypt, u32 *keyout)
+{
+       u32 i, j, l, m, n, kn[32];
+       u8 pc1m[56], pcr[56];
+
+       for (j = 0; j < 56; j++) {
+               l = (u32) pc1[j];
+               m = l & 7;
+               pc1m[j] = (u8)
+                       ((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0);
+       }
+
+       for (i = 0; i < 16; i++) {
+               if (decrypt)
+                       m = (15 - i) << 1;
+               else
+                       m = i << 1;
+               n = m + 1;
+               kn[m] = kn[n] = 0L;
+               for (j = 0; j < 28; j++) {
+                       l = j + (u32) totrot[i];
+                       if (l < 28)
+                               pcr[j] = pc1m[l];
+                       else
+                               pcr[j] = pc1m[l - 28];
+               }
+               for (/* j = 28 */; j < 56; j++) {
+                       l = j + (u32) totrot[i];
+                       if (l < 56)
+                               pcr[j] = pc1m[l];
+                       else
+                               pcr[j] = pc1m[l - 28];
+               }
+               for (j = 0; j < 24; j++) {
+                       if ((int) pcr[(int) pc2[j]] != 0)
+                               kn[m] |= bigbyte[j];
+                       if ((int) pcr[(int) pc2[j + 24]] != 0)
+                               kn[n] |= bigbyte[j];
+               }
+       }
+
+       cookey(kn, keyout);
+}
+
+
+static void desfunc(u32 *block, const u32 *keys)
+{
+       u32 work, right, leftt;
+       int cur_round;
+
+       leftt = block[0];
+       right = block[1];
+
+       work = ((leftt >> 4)  ^ right) & 0x0f0f0f0fL;
+       right ^= work;
+       leftt ^= (work << 4);
+
+       work = ((leftt >> 16) ^ right) & 0x0000ffffL;
+       right ^= work;
+       leftt ^= (work << 16);
+
+       work = ((right >> 2)  ^ leftt) & 0x33333333L;
+       leftt ^= work;
+       right ^= (work << 2);
+
+       work = ((right >> 8)  ^ leftt) & 0x00ff00ffL;
+       leftt ^= work;
+       right ^= (work << 8);
+
+       right = ROLc(right, 1);
+       work = (leftt ^ right) & 0xaaaaaaaaL;
+
+       leftt ^= work;
+       right ^= work;
+       leftt = ROLc(leftt, 1);
+
+       for (cur_round = 0; cur_round < 8; cur_round++) {
+               work  = RORc(right, 4) ^ *keys++;
+               leftt ^= SP7[work        & 0x3fL]
+                       ^ SP5[(work >>  8) & 0x3fL]
+                       ^ SP3[(work >> 16) & 0x3fL]
+                       ^ SP1[(work >> 24) & 0x3fL];
+               work  = right ^ *keys++;
+               leftt ^= SP8[ work        & 0x3fL]
+                       ^  SP6[(work >>  8) & 0x3fL]
+                       ^  SP4[(work >> 16) & 0x3fL]
+                       ^  SP2[(work >> 24) & 0x3fL];
+
+               work = RORc(leftt, 4) ^ *keys++;
+               right ^= SP7[ work        & 0x3fL]
+                       ^  SP5[(work >>  8) & 0x3fL]
+                       ^  SP3[(work >> 16) & 0x3fL]
+                       ^  SP1[(work >> 24) & 0x3fL];
+               work  = leftt ^ *keys++;
+               right ^= SP8[ work        & 0x3fL]
+                       ^  SP6[(work >>  8) & 0x3fL]
+                       ^  SP4[(work >> 16) & 0x3fL]
+                       ^  SP2[(work >> 24) & 0x3fL];
+       }
+
+       right = RORc(right, 1);
+       work = (leftt ^ right) & 0xaaaaaaaaL;
+       leftt ^= work;
+       right ^= work;
+       leftt = RORc(leftt, 1);
+       work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
+       right ^= work;
+       leftt ^= (work << 8);
+       /* -- */
+       work = ((leftt >> 2) ^ right) & 0x33333333L;
+       right ^= work;
+       leftt ^= (work << 2);
+       work = ((right >> 16) ^ leftt) & 0x0000ffffL;
+       leftt ^= work;
+       right ^= (work << 16);
+       work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
+       leftt ^= work;
+       right ^= (work << 4);
+
+       block[0] = right;
+       block[1] = leftt;
+}
+
+
+/* wpa_supplicant/hostapd specific wrapper */
+
+void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
+{
+       u8 pkey[8], next, tmp;
+       int i;
+       u32 ek[32], work[2];
+
+       /* Add parity bits to the key */
+       next = 0;
+       for (i = 0; i < 7; i++) {
+               tmp = key[i];
+               pkey[i] = (tmp >> i) | next | 1;
+               next = tmp << (7 - i);
+       }
+       pkey[i] = next | 1;
+
+       deskey(pkey, 0, ek);
+
+       work[0] = WPA_GET_BE32(clear);
+       work[1] = WPA_GET_BE32(clear + 4);
+       desfunc(work, ek);
+       WPA_PUT_BE32(cypher, work[0]);
+       WPA_PUT_BE32(cypher + 4, work[1]);
+
+       os_memset(pkey, 0, sizeof(pkey));
+       os_memset(ek, 0, sizeof(ek));
+}
+
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk)
+{
+       deskey(key, 0, ek);
+       deskey(key, 1, dk);
+}
+
+
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt)
+{
+       u32 work[2];
+       work[0] = WPA_GET_BE32(plain);
+       work[1] = WPA_GET_BE32(plain + 4);
+       desfunc(work, ek);
+       WPA_PUT_BE32(crypt, work[0]);
+       WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain)
+{
+       u32 work[2];
+       work[0] = WPA_GET_BE32(crypt);
+       work[1] = WPA_GET_BE32(crypt + 4);
+       desfunc(work, dk);
+       WPA_PUT_BE32(plain, work[0]);
+       WPA_PUT_BE32(plain + 4, work[1]);
+}
+
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey)
+{
+       deskey(key, 0, dkey->ek[0]);
+       deskey(key + 8, 1, dkey->ek[1]);
+       deskey(key + 16, 0, dkey->ek[2]);
+
+       deskey(key, 1, dkey->dk[2]);
+       deskey(key + 8, 0, dkey->dk[1]);
+       deskey(key + 16, 1, dkey->dk[0]);
+}
+
+
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt)
+{
+       u32 work[2];
+
+       work[0] = WPA_GET_BE32(plain);
+       work[1] = WPA_GET_BE32(plain + 4);
+       desfunc(work, key->ek[0]);
+       desfunc(work, key->ek[1]);
+       desfunc(work, key->ek[2]);
+       WPA_PUT_BE32(crypt, work[0]);
+       WPA_PUT_BE32(crypt + 4, work[1]);
+}
+
+
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain)
+{
+       u32 work[2];
+
+       work[0] = WPA_GET_BE32(crypt);
+       work[1] = WPA_GET_BE32(crypt + 4);
+       desfunc(work, key->dk[0]);
+       desfunc(work, key->dk[1]);
+       desfunc(work, key->dk[2]);
+       WPA_PUT_BE32(plain, work[0]);
+       WPA_PUT_BE32(plain + 4, work[1]);
+}
diff --git a/src/crypto/des_i.h b/src/crypto/des_i.h
new file mode 100644 (file)
index 0000000..6f27414
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * DES and 3DES-EDE ciphers
+ * Copyright (c) 2006-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.
+ */
+
+#ifndef DES_I_H
+#define DES_I_H
+
+struct des3_key_s {
+       u32 ek[3][32];
+       u32 dk[3][32];
+};
+
+void des_key_setup(const u8 *key, u32 *ek, u32 *dk);
+void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt);
+void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain);
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
+
+#endif /* DES_I_H */
diff --git a/src/crypto/dh_group5.c b/src/crypto/dh_group5.c
new file mode 100644 (file)
index 0000000..8c475bf
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 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 "common.h"
+#include "dh_groups.h"
+#include "dh_group5.h"
+
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
+{
+       *publ = dh_init(dh_groups_get(5), priv);
+       if (*publ == 0)
+               return NULL;
+       return (void *) 1;
+}
+
+
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+                                 const struct wpabuf *own_private)
+{
+       return dh_derive_shared(peer_public, own_private, dh_groups_get(5));
+}
+
+
+void dh5_free(void *ctx)
+{
+}
diff --git a/src/crypto/dh_group5.h b/src/crypto/dh_group5.h
new file mode 100644 (file)
index 0000000..595f111
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Diffie-Hellman group 5 operations
+ * Copyright (c) 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.
+ */
+
+#ifndef DH_GROUP5_H
+#define DH_GROUP5_H
+
+void * dh5_init(struct wpabuf **priv, struct wpabuf **publ);
+struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
+                                 const struct wpabuf *own_private);
+void dh5_free(void *ctx);
+
+#endif /* DH_GROUP5_H */
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
new file mode 100644 (file)
index 0000000..7bd2fb7
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Diffie-Hellman groups
+ * 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"
+
+#include "common.h"
+#include "crypto.h"
+#include "dh_groups.h"
+
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 4306, B.1. Group 1 - 768 Bit MODP
+ * Generator: 2
+ * Prime: 2^768 - 2 ^704 - 1 + 2^64 * { [2^638 pi] + 149686 }
+ */
+static const u8 dh_group1_generator[1] = { 0x02 };
+static const u8 dh_group1_prime[96] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x3A, 0x36, 0x20,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 4306, B.2. Group 2 - 1024 Bit MODP
+ * Generator: 2
+ * Prime: 2^1024 - 2^960 - 1 + 2^64 * { [2^894 pi] + 129093 }
+ */
+static const u8 dh_group2_generator[1] = { 0x02 };
+static const u8 dh_group2_prime[128] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE6, 0x53, 0x81,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif /* ALL_DH_GROUPS */
+
+/* RFC 3526, 2. Group 5 - 1536 Bit MODP
+ * Generator: 2
+ * Prime: 2^1536 - 2^1472 - 1 + 2^64 * { [2^1406 pi] + 741804 }
+ */
+static const u8 dh_group5_generator[1] = { 0x02 };
+static const u8 dh_group5_prime[192] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+       0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+       0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+       0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+       0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+       0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+       0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+       0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+       0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x23, 0x73, 0x27,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#ifdef ALL_DH_GROUPS
+
+/* RFC 3526, 3. Group 14 - 2048 Bit MODP
+ * Generator: 2
+ * Prime: 2^2048 - 2^1984 - 1 + 2^64 * { [2^1918 pi] + 124476 }
+ */
+static const u8 dh_group14_generator[1] = { 0x02 };
+static const u8 dh_group14_prime[256] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+       0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+       0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+       0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+       0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+       0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+       0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+       0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+       0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+       0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+       0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+       0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+       0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+       0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+       0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+       0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+       0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAC, 0xAA, 0x68,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 4. Group 15 - 3072 Bit MODP
+ * Generator: 2
+ * Prime: 2^3072 - 2^3008 - 1 + 2^64 * { [2^2942 pi] + 1690314 }
+ */
+static const u8 dh_group15_generator[1] = { 0x02 };
+static const u8 dh_group15_prime[384] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+       0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+       0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+       0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+       0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+       0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+       0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+       0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+       0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+       0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+       0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+       0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+       0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+       0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+       0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+       0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+       0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+       0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+       0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+       0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+       0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+       0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+       0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+       0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+       0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+       0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+       0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+       0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+       0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+       0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+       0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+       0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+       0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x3A, 0xD2, 0xCA,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 5. Group 16 - 4096 Bit MODP
+ * Generator: 2
+ * Prime: 2^4096 - 2^4032 - 1 + 2^64 * { [2^3966 pi] + 240904 }
+ */
+static const u8 dh_group16_generator[1] = { 0x02 };
+static const u8 dh_group16_prime[512] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+       0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+       0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+       0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+       0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+       0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+       0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+       0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+       0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+       0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+       0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+       0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+       0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+       0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+       0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+       0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+       0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+       0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+       0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+       0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+       0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+       0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+       0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+       0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+       0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+       0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+       0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+       0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+       0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+       0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+       0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+       0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+       0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+       0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+       0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+       0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+       0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+       0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+       0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+       0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+       0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+       0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+       0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+       0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+       0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+       0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+       0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+       0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+       0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x06, 0x31, 0x99,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 6. Group 17 - 6144 Bit MODP
+ * Generator: 2
+ * Prime: 2^6144 - 2^6080 - 1 + 2^64 * { [2^6014 pi] + 929484 }
+ */
+static const u8 dh_group17_generator[1] = { 0x02 };
+static const u8 dh_group17_prime[768] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+       0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+       0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+       0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+       0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+       0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+       0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+       0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+       0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+       0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+       0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+       0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+       0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+       0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+       0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+       0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+       0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+       0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+       0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+       0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+       0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+       0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+       0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+       0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+       0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+       0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+       0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+       0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+       0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+       0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+       0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+       0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+       0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+       0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+       0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+       0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+       0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+       0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+       0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+       0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+       0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+       0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+       0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+       0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+       0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+       0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+       0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+       0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+       0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+       0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+       0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+       0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+       0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+       0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+       0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+       0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+       0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+       0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+       0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+       0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+       0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+       0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+       0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+       0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+       0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+       0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+       0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+       0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+       0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+       0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+       0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+       0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+       0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+       0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+       0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+       0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+       0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+       0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+       0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+       0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+       0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xCC, 0x40, 0x24,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+/* RFC 3526, 7. Group 18 - 8192 Bit MODP
+ * Generator: 2
+ * Prime: 2^8192 - 2^8128 - 1 + 2^64 * { [2^8062 pi] + 4743158 }
+ */
+static const u8 dh_group18_generator[1] = { 0x02 };
+static const u8 dh_group18_prime[1024] = {
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+       0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34,
+       0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1,
+       0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74,
+       0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x13, 0x9B, 0x22,
+       0x51, 0x4A, 0x08, 0x79, 0x8E, 0x34, 0x04, 0xDD,
+       0xEF, 0x95, 0x19, 0xB3, 0xCD, 0x3A, 0x43, 0x1B,
+       0x30, 0x2B, 0x0A, 0x6D, 0xF2, 0x5F, 0x14, 0x37,
+       0x4F, 0xE1, 0x35, 0x6D, 0x6D, 0x51, 0xC2, 0x45,
+       0xE4, 0x85, 0xB5, 0x76, 0x62, 0x5E, 0x7E, 0xC6,
+       0xF4, 0x4C, 0x42, 0xE9, 0xA6, 0x37, 0xED, 0x6B,
+       0x0B, 0xFF, 0x5C, 0xB6, 0xF4, 0x06, 0xB7, 0xED,
+       0xEE, 0x38, 0x6B, 0xFB, 0x5A, 0x89, 0x9F, 0xA5,
+       0xAE, 0x9F, 0x24, 0x11, 0x7C, 0x4B, 0x1F, 0xE6,
+       0x49, 0x28, 0x66, 0x51, 0xEC, 0xE4, 0x5B, 0x3D,
+       0xC2, 0x00, 0x7C, 0xB8, 0xA1, 0x63, 0xBF, 0x05,
+       0x98, 0xDA, 0x48, 0x36, 0x1C, 0x55, 0xD3, 0x9A,
+       0x69, 0x16, 0x3F, 0xA8, 0xFD, 0x24, 0xCF, 0x5F,
+       0x83, 0x65, 0x5D, 0x23, 0xDC, 0xA3, 0xAD, 0x96,
+       0x1C, 0x62, 0xF3, 0x56, 0x20, 0x85, 0x52, 0xBB,
+       0x9E, 0xD5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6D,
+       0x67, 0x0C, 0x35, 0x4E, 0x4A, 0xBC, 0x98, 0x04,
+       0xF1, 0x74, 0x6C, 0x08, 0xCA, 0x18, 0x21, 0x7C,
+       0x32, 0x90, 0x5E, 0x46, 0x2E, 0x36, 0xCE, 0x3B,
+       0xE3, 0x9E, 0x77, 0x2C, 0x18, 0x0E, 0x86, 0x03,
+       0x9B, 0x27, 0x83, 0xA2, 0xEC, 0x07, 0xA2, 0x8F,
+       0xB5, 0xC5, 0x5D, 0xF0, 0x6F, 0x4C, 0x52, 0xC9,
+       0xDE, 0x2B, 0xCB, 0xF6, 0x95, 0x58, 0x17, 0x18,
+       0x39, 0x95, 0x49, 0x7C, 0xEA, 0x95, 0x6A, 0xE5,
+       0x15, 0xD2, 0x26, 0x18, 0x98, 0xFA, 0x05, 0x10,
+       0x15, 0x72, 0x8E, 0x5A, 0x8A, 0xAA, 0xC4, 0x2D,
+       0xAD, 0x33, 0x17, 0x0D, 0x04, 0x50, 0x7A, 0x33,
+       0xA8, 0x55, 0x21, 0xAB, 0xDF, 0x1C, 0xBA, 0x64,
+       0xEC, 0xFB, 0x85, 0x04, 0x58, 0xDB, 0xEF, 0x0A,
+       0x8A, 0xEA, 0x71, 0x57, 0x5D, 0x06, 0x0C, 0x7D,
+       0xB3, 0x97, 0x0F, 0x85, 0xA6, 0xE1, 0xE4, 0xC7,
+       0xAB, 0xF5, 0xAE, 0x8C, 0xDB, 0x09, 0x33, 0xD7,
+       0x1E, 0x8C, 0x94, 0xE0, 0x4A, 0x25, 0x61, 0x9D,
+       0xCE, 0xE3, 0xD2, 0x26, 0x1A, 0xD2, 0xEE, 0x6B,
+       0xF1, 0x2F, 0xFA, 0x06, 0xD9, 0x8A, 0x08, 0x64,
+       0xD8, 0x76, 0x02, 0x73, 0x3E, 0xC8, 0x6A, 0x64,
+       0x52, 0x1F, 0x2B, 0x18, 0x17, 0x7B, 0x20, 0x0C,
+       0xBB, 0xE1, 0x17, 0x57, 0x7A, 0x61, 0x5D, 0x6C,
+       0x77, 0x09, 0x88, 0xC0, 0xBA, 0xD9, 0x46, 0xE2,
+       0x08, 0xE2, 0x4F, 0xA0, 0x74, 0xE5, 0xAB, 0x31,
+       0x43, 0xDB, 0x5B, 0xFC, 0xE0, 0xFD, 0x10, 0x8E,
+       0x4B, 0x82, 0xD1, 0x20, 0xA9, 0x21, 0x08, 0x01,
+       0x1A, 0x72, 0x3C, 0x12, 0xA7, 0x87, 0xE6, 0xD7,
+       0x88, 0x71, 0x9A, 0x10, 0xBD, 0xBA, 0x5B, 0x26,
+       0x99, 0xC3, 0x27, 0x18, 0x6A, 0xF4, 0xE2, 0x3C,
+       0x1A, 0x94, 0x68, 0x34, 0xB6, 0x15, 0x0B, 0xDA,
+       0x25, 0x83, 0xE9, 0xCA, 0x2A, 0xD4, 0x4C, 0xE8,
+       0xDB, 0xBB, 0xC2, 0xDB, 0x04, 0xDE, 0x8E, 0xF9,
+       0x2E, 0x8E, 0xFC, 0x14, 0x1F, 0xBE, 0xCA, 0xA6,
+       0x28, 0x7C, 0x59, 0x47, 0x4E, 0x6B, 0xC0, 0x5D,
+       0x99, 0xB2, 0x96, 0x4F, 0xA0, 0x90, 0xC3, 0xA2,
+       0x23, 0x3B, 0xA1, 0x86, 0x51, 0x5B, 0xE7, 0xED,
+       0x1F, 0x61, 0x29, 0x70, 0xCE, 0xE2, 0xD7, 0xAF,
+       0xB8, 0x1B, 0xDD, 0x76, 0x21, 0x70, 0x48, 0x1C,
+       0xD0, 0x06, 0x91, 0x27, 0xD5, 0xB0, 0x5A, 0xA9,
+       0x93, 0xB4, 0xEA, 0x98, 0x8D, 0x8F, 0xDD, 0xC1,
+       0x86, 0xFF, 0xB7, 0xDC, 0x90, 0xA6, 0xC0, 0x8F,
+       0x4D, 0xF4, 0x35, 0xC9, 0x34, 0x02, 0x84, 0x92,
+       0x36, 0xC3, 0xFA, 0xB4, 0xD2, 0x7C, 0x70, 0x26,
+       0xC1, 0xD4, 0xDC, 0xB2, 0x60, 0x26, 0x46, 0xDE,
+       0xC9, 0x75, 0x1E, 0x76, 0x3D, 0xBA, 0x37, 0xBD,
+       0xF8, 0xFF, 0x94, 0x06, 0xAD, 0x9E, 0x53, 0x0E,
+       0xE5, 0xDB, 0x38, 0x2F, 0x41, 0x30, 0x01, 0xAE,
+       0xB0, 0x6A, 0x53, 0xED, 0x90, 0x27, 0xD8, 0x31,
+       0x17, 0x97, 0x27, 0xB0, 0x86, 0x5A, 0x89, 0x18,
+       0xDA, 0x3E, 0xDB, 0xEB, 0xCF, 0x9B, 0x14, 0xED,
+       0x44, 0xCE, 0x6C, 0xBA, 0xCE, 0xD4, 0xBB, 0x1B,
+       0xDB, 0x7F, 0x14, 0x47, 0xE6, 0xCC, 0x25, 0x4B,
+       0x33, 0x20, 0x51, 0x51, 0x2B, 0xD7, 0xAF, 0x42,
+       0x6F, 0xB8, 0xF4, 0x01, 0x37, 0x8C, 0xD2, 0xBF,
+       0x59, 0x83, 0xCA, 0x01, 0xC6, 0x4B, 0x92, 0xEC,
+       0xF0, 0x32, 0xEA, 0x15, 0xD1, 0x72, 0x1D, 0x03,
+       0xF4, 0x82, 0xD7, 0xCE, 0x6E, 0x74, 0xFE, 0xF6,
+       0xD5, 0x5E, 0x70, 0x2F, 0x46, 0x98, 0x0C, 0x82,
+       0xB5, 0xA8, 0x40, 0x31, 0x90, 0x0B, 0x1C, 0x9E,
+       0x59, 0xE7, 0xC9, 0x7F, 0xBE, 0xC7, 0xE8, 0xF3,
+       0x23, 0xA9, 0x7A, 0x7E, 0x36, 0xCC, 0x88, 0xBE,
+       0x0F, 0x1D, 0x45, 0xB7, 0xFF, 0x58, 0x5A, 0xC5,
+       0x4B, 0xD4, 0x07, 0xB2, 0x2B, 0x41, 0x54, 0xAA,
+       0xCC, 0x8F, 0x6D, 0x7E, 0xBF, 0x48, 0xE1, 0xD8,
+       0x14, 0xCC, 0x5E, 0xD2, 0x0F, 0x80, 0x37, 0xE0,
+       0xA7, 0x97, 0x15, 0xEE, 0xF2, 0x9B, 0xE3, 0x28,
+       0x06, 0xA1, 0xD5, 0x8B, 0xB7, 0xC5, 0xDA, 0x76,
+       0xF5, 0x50, 0xAA, 0x3D, 0x8A, 0x1F, 0xBF, 0xF0,
+       0xEB, 0x19, 0xCC, 0xB1, 0xA3, 0x13, 0xD5, 0x5C,
+       0xDA, 0x56, 0xC9, 0xEC, 0x2E, 0xF2, 0x96, 0x32,
+       0x38, 0x7F, 0xE8, 0xD7, 0x6E, 0x3C, 0x04, 0x68,
+       0x04, 0x3E, 0x8F, 0x66, 0x3F, 0x48, 0x60, 0xEE,
+       0x12, 0xBF, 0x2D, 0x5B, 0x0B, 0x74, 0x74, 0xD6,
+       0xE6, 0x94, 0xF9, 0x1E, 0x6D, 0xBE, 0x11, 0x59,
+       0x74, 0xA3, 0x92, 0x6F, 0x12, 0xFE, 0xE5, 0xE4,
+       0x38, 0x77, 0x7C, 0xB6, 0xA9, 0x32, 0xDF, 0x8C,
+       0xD8, 0xBE, 0xC4, 0xD0, 0x73, 0xB9, 0x31, 0xBA,
+       0x3B, 0xC8, 0x32, 0xB6, 0x8D, 0x9D, 0xD3, 0x00,
+       0x74, 0x1F, 0xA7, 0xBF, 0x8A, 0xFC, 0x47, 0xED,
+       0x25, 0x76, 0xF6, 0x93, 0x6B, 0xA4, 0x24, 0x66,
+       0x3A, 0xAB, 0x63, 0x9C, 0x5A, 0xE4, 0xF5, 0x68,
+       0x34, 0x23, 0xB4, 0x74, 0x2B, 0xF1, 0xC9, 0x78,
+       0x23, 0x8F, 0x16, 0xCB, 0xE3, 0x9D, 0x65, 0x2D,
+       0xE3, 0xFD, 0xB8, 0xBE, 0xFC, 0x84, 0x8A, 0xD9,
+       0x22, 0x22, 0x2E, 0x04, 0xA4, 0x03, 0x7C, 0x07,
+       0x13, 0xEB, 0x57, 0xA8, 0x1A, 0x23, 0xF0, 0xC7,
+       0x34, 0x73, 0xFC, 0x64, 0x6C, 0xEA, 0x30, 0x6B,
+       0x4B, 0xCB, 0xC8, 0x86, 0x2F, 0x83, 0x85, 0xDD,
+       0xFA, 0x9D, 0x4B, 0x7F, 0xA2, 0xC0, 0x87, 0xE8,
+       0x79, 0x68, 0x33, 0x03, 0xED, 0x5B, 0xDD, 0x3A,
+       0x06, 0x2B, 0x3C, 0xF5, 0xB3, 0xA2, 0x78, 0xA6,
+       0x6D, 0x2A, 0x13, 0xF8, 0x3F, 0x44, 0xF8, 0x2D,
+       0xDF, 0x31, 0x0E, 0xE0, 0x74, 0xAB, 0x6A, 0x36,
+       0x45, 0x97, 0xE8, 0x99, 0xA0, 0x25, 0x5D, 0xC1,
+       0x64, 0xF3, 0x1C, 0xC5, 0x08, 0x46, 0x85, 0x1D,
+       0xF9, 0xAB, 0x48, 0x19, 0x5D, 0xED, 0x7E, 0xA1,
+       0xB1, 0xD5, 0x10, 0xBD, 0x7E, 0xE7, 0x4D, 0x73,
+       0xFA, 0xF3, 0x6B, 0xC3, 0x1E, 0xCF, 0xA2, 0x68,
+       0x35, 0x90, 0x46, 0xF4, 0xEB, 0x87, 0x9F, 0x92,
+       0x40, 0x09, 0x43, 0x8B, 0x48, 0x1C, 0x6C, 0xD7,
+       0x88, 0x9A, 0x00, 0x2E, 0xD5, 0xEE, 0x38, 0x2B,
+       0xC9, 0x19, 0x0D, 0xA6, 0xFC, 0x02, 0x6E, 0x47,
+       0x95, 0x58, 0xE4, 0x47, 0x56, 0x77, 0xE9, 0xAA,
+       0x9E, 0x30, 0x50, 0xE2, 0x76, 0x56, 0x94, 0xDF,
+       0xC8, 0x1F, 0x56, 0xE8, 0x80, 0xB9, 0x6E, 0x71,
+       0x60, 0xC9, 0x80, 0xDD, 0x98, 0xED, 0xD3, 0xDF,
+       0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
+};
+
+#endif /* ALL_DH_GROUPS */
+
+
+#define DH_GROUP(id) \
+{ id, dh_group ## id ## _generator, sizeof(dh_group ## id ## _generator), \
+dh_group ## id ## _prime, sizeof(dh_group ## id ## _prime) }
+               
+
+static struct dh_group dh_groups[] = {
+       DH_GROUP(5),
+#ifdef ALL_DH_GROUPS
+       DH_GROUP(1),
+       DH_GROUP(2),
+       DH_GROUP(14),
+       DH_GROUP(15),
+       DH_GROUP(16),
+       DH_GROUP(17),
+       DH_GROUP(18)
+#endif /* ALL_DH_GROUPS */
+};
+
+#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0]))
+
+
+const struct dh_group * dh_groups_get(int id)
+{
+       size_t i;
+
+       for (i = 0; i < NUM_DH_GROUPS; i++) {
+               if (dh_groups[i].id == id)
+                       return &dh_groups[i];
+       }
+       return NULL;
+}
+
+
+/**
+ * dh_init - Initialize Diffie-Hellman handshake
+ * @dh: Selected Diffie-Hellman group
+ * @priv: Pointer for returning Diffie-Hellman private key
+ * Returns: Diffie-Hellman public value
+ */
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
+{
+       struct wpabuf *pv;
+       size_t pv_len;
+
+       if (dh == NULL)
+               return NULL;
+
+       wpabuf_free(*priv);
+       *priv = wpabuf_alloc(dh->prime_len);
+       if (*priv == NULL)
+               return NULL;
+
+       if (os_get_random(wpabuf_put(*priv, dh->prime_len), dh->prime_len)) {
+               wpabuf_free(*priv);
+               *priv = NULL;
+               return NULL;
+       }
+
+       if (os_memcmp(wpabuf_head(*priv), dh->prime, dh->prime_len) > 0) {
+               /* Make sure private value is smaller than prime */
+               *(wpabuf_mhead_u8(*priv)) = 0;
+       }
+       wpa_hexdump_buf_key(MSG_DEBUG, "DH: private value", *priv);
+
+       pv_len = dh->prime_len;
+       pv = wpabuf_alloc(pv_len);
+       if (pv == NULL)
+               return NULL;
+       if (crypto_mod_exp(dh->generator, dh->generator_len,
+                          wpabuf_head(*priv), wpabuf_len(*priv),
+                          dh->prime, dh->prime_len, wpabuf_mhead(pv),
+                          &pv_len) < 0) {
+               wpabuf_free(pv);
+               wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+               return NULL;
+       }
+       wpabuf_put(pv, pv_len);
+       wpa_hexdump_buf(MSG_DEBUG, "DH: public value", pv);
+
+       return pv;
+}
+
+
+/**
+ * dh_derive_shared - Derive shared Diffie-Hellman key
+ * @peer_public: Diffie-Hellman public value from peer
+ * @own_private: Diffie-Hellman private key from dh_init()
+ * @dh: Selected Diffie-Hellman group
+ * Returns: Diffie-Hellman shared key
+ */
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+                                const struct wpabuf *own_private,
+                                const struct dh_group *dh)
+{
+       struct wpabuf *shared;
+       size_t shared_len;
+
+       if (dh == NULL || peer_public == NULL || own_private == NULL)
+               return NULL;
+
+       shared_len = dh->prime_len;
+       shared = wpabuf_alloc(shared_len);
+       if (shared == NULL)
+               return NULL;
+       if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public),
+                          wpabuf_head(own_private), wpabuf_len(own_private),
+                          dh->prime, dh->prime_len,
+                          wpabuf_mhead(shared), &shared_len) < 0) {
+               wpabuf_free(shared);
+               wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
+               return NULL;
+       }
+       wpabuf_put(shared, shared_len);
+       wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared);
+
+       return shared;
+}
diff --git a/src/crypto/dh_groups.h b/src/crypto/dh_groups.h
new file mode 100644 (file)
index 0000000..5c61539
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Diffie-Hellman groups
+ * 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.
+ */
+
+#ifndef DH_GROUPS_H
+#define DH_GROUPS_H
+
+struct dh_group {
+       int id;
+       const u8 *generator;
+       size_t generator_len;
+       const u8 *prime;
+       size_t prime_len;
+};
+
+const struct dh_group * dh_groups_get(int id);
+struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv);
+struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
+                                const struct wpabuf *own_private,
+                                const struct dh_group *dh);
+
+#endif /* DH_GROUPS_H */
diff --git a/src/crypto/fips_prf_cryptoapi.c b/src/crypto/fips_prf_cryptoapi.c
new file mode 100644 (file)
index 0000000..17d3116
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * FIPS 186-2 PRF for Microsoft CryptoAPI
+ * Copyright (c) 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 "common.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+       /* FIX: how to do this with CryptoAPI? */
+       return -1;
+}
diff --git a/src/crypto/fips_prf_gnutls.c b/src/crypto/fips_prf_gnutls.c
new file mode 100644 (file)
index 0000000..f742e98
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * FIPS 186-2 PRF for libgcrypt
+ * 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 <gcrypt.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+       /* FIX: how to do this with libgcrypt? */
+       return -1;
+}
diff --git a/src/crypto/fips_prf_internal.c b/src/crypto/fips_prf_internal.c
new file mode 100644 (file)
index 0000000..a85cb14
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * FIPS 186-2 PRF for internal crypto implementation
+ * Copyright (c) 2006-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 "sha1.h"
+#include "sha1_i.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+       u8 xkey[64];
+       u32 t[5], _t[5];
+       int i, j, m, k;
+       u8 *xpos = x;
+       u32 carry;
+
+       if (seed_len > sizeof(xkey))
+               seed_len = sizeof(xkey);
+
+       /* FIPS 186-2 + change notice 1 */
+
+       os_memcpy(xkey, seed, seed_len);
+       os_memset(xkey + seed_len, 0, 64 - seed_len);
+       t[0] = 0x67452301;
+       t[1] = 0xEFCDAB89;
+       t[2] = 0x98BADCFE;
+       t[3] = 0x10325476;
+       t[4] = 0xC3D2E1F0;
+
+       m = xlen / 40;
+       for (j = 0; j < m; j++) {
+               /* XSEED_j = 0 */
+               for (i = 0; i < 2; i++) {
+                       /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+                       /* w_i = G(t, XVAL) */
+                       os_memcpy(_t, t, 20);
+                       SHA1Transform(_t, xkey);
+                       _t[0] = host_to_be32(_t[0]);
+                       _t[1] = host_to_be32(_t[1]);
+                       _t[2] = host_to_be32(_t[2]);
+                       _t[3] = host_to_be32(_t[3]);
+                       _t[4] = host_to_be32(_t[4]);
+                       os_memcpy(xpos, _t, 20);
+
+                       /* XKEY = (1 + XKEY + w_i) mod 2^b */
+                       carry = 1;
+                       for (k = 19; k >= 0; k--) {
+                               carry += xkey[k] + xpos[k];
+                               xkey[k] = carry & 0xff;
+                               carry >>= 8;
+                       }
+
+                       xpos += SHA1_MAC_LEN;
+               }
+               /* x_j = w_0|w_1 */
+       }
+
+       return 0;
+}
diff --git a/src/crypto/fips_prf_nss.c b/src/crypto/fips_prf_nss.c
new file mode 100644 (file)
index 0000000..f941983
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * FIPS 186-2 PRF for NSS
+ * Copyright (c) 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 <openssl/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+       return -1;
+}
diff --git a/src/crypto/fips_prf_openssl.c b/src/crypto/fips_prf_openssl.c
new file mode 100644 (file)
index 0000000..d0af983
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * FIPS 186-2 PRF for libcrypto
+ * 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.
+ */
+
+#include "includes.h"
+#include <openssl/sha.h>
+
+#include "common.h"
+#include "crypto.h"
+
+
+static void sha1_transform(u8 *state, const u8 data[64])
+{
+       SHA_CTX context;
+       os_memset(&context, 0, sizeof(context));
+       os_memcpy(&context.h0, state, 5 * 4);
+       SHA1_Transform(&context, data);
+       os_memcpy(state, &context.h0, 5 * 4);
+}
+
+
+int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen)
+{
+       u8 xkey[64];
+       u32 t[5], _t[5];
+       int i, j, m, k;
+       u8 *xpos = x;
+       u32 carry;
+
+       if (seed_len > sizeof(xkey))
+               seed_len = sizeof(xkey);
+
+       /* FIPS 186-2 + change notice 1 */
+
+       os_memcpy(xkey, seed, seed_len);
+       os_memset(xkey + seed_len, 0, 64 - seed_len);
+       t[0] = 0x67452301;
+       t[1] = 0xEFCDAB89;
+       t[2] = 0x98BADCFE;
+       t[3] = 0x10325476;
+       t[4] = 0xC3D2E1F0;
+
+       m = xlen / 40;
+       for (j = 0; j < m; j++) {
+               /* XSEED_j = 0 */
+               for (i = 0; i < 2; i++) {
+                       /* XVAL = (XKEY + XSEED_j) mod 2^b */
+
+                       /* w_i = G(t, XVAL) */
+                       os_memcpy(_t, t, 20);
+                       sha1_transform((u8 *) _t, xkey);
+                       _t[0] = host_to_be32(_t[0]);
+                       _t[1] = host_to_be32(_t[1]);
+                       _t[2] = host_to_be32(_t[2]);
+                       _t[3] = host_to_be32(_t[3]);
+                       _t[4] = host_to_be32(_t[4]);
+                       os_memcpy(xpos, _t, 20);
+
+                       /* XKEY = (1 + XKEY + w_i) mod 2^b */
+                       carry = 1;
+                       for (k = 19; k >= 0; k--) {
+                               carry += xkey[k] + xpos[k];
+                               xkey[k] = carry & 0xff;
+                               carry >>= 8;
+                       }
+
+                       xpos += 20;
+               }
+               /* x_j = w_0|w_1 */
+       }
+
+       return 0;
+}
diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c
new file mode 100644 (file)
index 0000000..d9f499f
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * MD4 hash implementation
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define        MD4_BLOCK_LENGTH                64
+#define        MD4_DIGEST_LENGTH               16
+
+typedef struct MD4Context {
+       u32 state[4];                   /* state */
+       u64 count;                      /* number of bits, mod 2^64 */
+       u8 buffer[MD4_BLOCK_LENGTH];    /* input buffer */
+} MD4_CTX;
+
+
+static void MD4Init(MD4_CTX *ctx);
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len);
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx);
+
+
+int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       MD4_CTX ctx;
+       size_t i;
+
+       MD4Init(&ctx);
+       for (i = 0; i < num_elem; i++)
+               MD4Update(&ctx, addr[i], len[i]);
+       MD4Final(mac, &ctx);
+       return 0;
+}
+
+
+/* ===== start - public domain MD4 implementation ===== */
+/*     $OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $   */
+
+/*
+ * This code implements the MD4 message-digest algorithm.
+ * The algorithm is due to Ron Rivest. This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD4Context structure, pass it to MD4Init, call MD4Update as
+ * needed on buffers full of bytes, and then call MD4Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#define        MD4_DIGEST_STRING_LENGTH        (MD4_DIGEST_LENGTH * 2 + 1)
+
+
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]);
+
+#define PUT_64BIT_LE(cp, value) do {                                   \
+       (cp)[7] = (value) >> 56;                                        \
+       (cp)[6] = (value) >> 48;                                        \
+       (cp)[5] = (value) >> 40;                                        \
+       (cp)[4] = (value) >> 32;                                        \
+       (cp)[3] = (value) >> 24;                                        \
+       (cp)[2] = (value) >> 16;                                        \
+       (cp)[1] = (value) >> 8;                                         \
+       (cp)[0] = (value); } while (0)
+
+#define PUT_32BIT_LE(cp, value) do {                                   \
+       (cp)[3] = (value) >> 24;                                        \
+       (cp)[2] = (value) >> 16;                                        \
+       (cp)[1] = (value) >> 8;                                         \
+       (cp)[0] = (value); } while (0)
+
+static u8 PADDING[MD4_BLOCK_LENGTH] = {
+       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/*
+ * Start MD4 accumulation.
+ * Set bit count to 0 and buffer to mysterious initialization constants.
+ */
+static void MD4Init(MD4_CTX *ctx)
+{
+       ctx->count = 0;
+       ctx->state[0] = 0x67452301;
+       ctx->state[1] = 0xefcdab89;
+       ctx->state[2] = 0x98badcfe;
+       ctx->state[3] = 0x10325476;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len)
+{
+       size_t have, need;
+
+       /* Check how many bytes we already have and how many more we need. */
+       have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+       need = MD4_BLOCK_LENGTH - have;
+
+       /* Update bitcount */
+       ctx->count += (u64)len << 3;
+
+       if (len >= need) {
+               if (have != 0) {
+                       os_memcpy(ctx->buffer + have, input, need);
+                       MD4Transform(ctx->state, ctx->buffer);
+                       input += need;
+                       len -= need;
+                       have = 0;
+               }
+
+               /* Process data in MD4_BLOCK_LENGTH-byte chunks. */
+               while (len >= MD4_BLOCK_LENGTH) {
+                       MD4Transform(ctx->state, input);
+                       input += MD4_BLOCK_LENGTH;
+                       len -= MD4_BLOCK_LENGTH;
+               }
+       }
+
+       /* Handle any remaining bytes of data. */
+       if (len != 0)
+               os_memcpy(ctx->buffer + have, input, len);
+}
+
+/*
+ * Pad pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+static void MD4Pad(MD4_CTX *ctx)
+{
+       u8 count[8];
+       size_t padlen;
+
+       /* Convert count to 8 bytes in little endian order. */
+       PUT_64BIT_LE(count, ctx->count);
+
+       /* Pad out to 56 mod 64. */
+       padlen = MD4_BLOCK_LENGTH -
+           ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1));
+       if (padlen < 1 + 8)
+               padlen += MD4_BLOCK_LENGTH;
+       MD4Update(ctx, PADDING, padlen - 8);            /* padlen - 8 <= 64 */
+       MD4Update(ctx, count, 8);
+}
+
+/*
+ * Final wrapup--call MD4Pad, fill in digest and zero out ctx.
+ */
+static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx)
+{
+       int i;
+
+       MD4Pad(ctx);
+       if (digest != NULL) {
+               for (i = 0; i < 4; i++)
+                       PUT_32BIT_LE(digest + i * 4, ctx->state[i]);
+               os_memset(ctx, 0, sizeof(*ctx));
+       }
+}
+
+
+/* The three core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) ((x & y) | (x & z) | (y & z))
+#define F3(x, y, z) (x ^ y ^ z)
+
+/* This is the central step in the MD4 algorithm. */
+#define MD4STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s) )
+
+/*
+ * The core of the MD4 algorithm, this alters an existing MD4 hash to
+ * reflect the addition of 16 longwords of new data.  MD4Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void
+MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH])
+{
+       u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4];
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+       os_memcpy(in, block, sizeof(in));
+#else
+       for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) {
+               in[a] = (u32)(
+                   (u32)(block[a * 4 + 0]) |
+                   (u32)(block[a * 4 + 1]) <<  8 |
+                   (u32)(block[a * 4 + 2]) << 16 |
+                   (u32)(block[a * 4 + 3]) << 24);
+       }
+#endif
+
+       a = state[0];
+       b = state[1];
+       c = state[2];
+       d = state[3];
+
+       MD4STEP(F1, a, b, c, d, in[ 0],  3);
+       MD4STEP(F1, d, a, b, c, in[ 1],  7);
+       MD4STEP(F1, c, d, a, b, in[ 2], 11);
+       MD4STEP(F1, b, c, d, a, in[ 3], 19);
+       MD4STEP(F1, a, b, c, d, in[ 4],  3);
+       MD4STEP(F1, d, a, b, c, in[ 5],  7);
+       MD4STEP(F1, c, d, a, b, in[ 6], 11);
+       MD4STEP(F1, b, c, d, a, in[ 7], 19);
+       MD4STEP(F1, a, b, c, d, in[ 8],  3);
+       MD4STEP(F1, d, a, b, c, in[ 9],  7);
+       MD4STEP(F1, c, d, a, b, in[10], 11);
+       MD4STEP(F1, b, c, d, a, in[11], 19);
+       MD4STEP(F1, a, b, c, d, in[12],  3);
+       MD4STEP(F1, d, a, b, c, in[13],  7);
+       MD4STEP(F1, c, d, a, b, in[14], 11);
+       MD4STEP(F1, b, c, d, a, in[15], 19);
+
+       MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999,  3);
+       MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999,  5);
+       MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999,  9);
+       MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13);
+       MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999,  3);
+       MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999,  5);
+       MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999,  9);
+       MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13);
+       MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999,  3);
+       MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999,  5);
+       MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999,  9);
+       MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13);
+       MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999,  3);
+       MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999,  5);
+       MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999,  9);
+       MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13);
+
+       MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1,  3);
+       MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1,  9);
+       MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11);
+       MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15);
+       MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1,  3);
+       MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1,  9);
+       MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11);
+       MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15);
+       MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1,  3);
+       MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1,  9);
+       MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11);
+       MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15);
+       MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1,  3);
+       MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1,  9);
+       MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11);
+       MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15);
+
+       state[0] += a;
+       state[1] += b;
+       state[2] += c;
+       state[3] += d;
+}
+/* ===== end - public domain MD4 implementation ===== */
diff --git a/src/crypto/md5-internal.c b/src/crypto/md5-internal.c
new file mode 100644 (file)
index 0000000..f8692a9
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * MD5 hash implementation and interface functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "md5_i.h"
+#include "crypto.h"
+
+
+static void MD5Transform(u32 buf[4], u32 const in[16]);
+
+
+typedef struct MD5Context MD5_CTX;
+
+
+/**
+ * md5_vector - MD5 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       MD5_CTX ctx;
+       size_t i;
+
+       MD5Init(&ctx);
+       for (i = 0; i < num_elem; i++)
+               MD5Update(&ctx, addr[i], len[i]);
+       MD5Final(mac, &ctx);
+       return 0;
+}
+
+
+/* ===== start - public domain MD5 implementation ===== */
+/*
+ * This code implements the MD5 message-digest algorithm.
+ * The algorithm is due to Ron Rivest.  This code was
+ * written by Colin Plumb in 1993, no copyright is claimed.
+ * This code is in the public domain; do with it what you wish.
+ *
+ * Equivalent code is available from RSA Data Security, Inc.
+ * This code has been tested against that, and is equivalent,
+ * except that you don't need to include two pages of legalese
+ * with every copy.
+ *
+ * To compute the message digest of a chunk of bytes, declare an
+ * MD5Context structure, pass it to MD5Init, call MD5Update as
+ * needed on buffers full of bytes, and then call MD5Final, which
+ * will fill a supplied 16-byte array with the digest.
+ */
+
+#ifndef WORDS_BIGENDIAN
+#define byteReverse(buf, len)  /* Nothing */
+#else
+/*
+ * Note: this code is harmless on little-endian machines.
+ */
+static void byteReverse(unsigned char *buf, unsigned longs)
+{
+    u32 t;
+    do {
+       t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 |
+           ((unsigned) buf[1] << 8 | buf[0]);
+       *(u32 *) buf = t;
+       buf += 4;
+    } while (--longs);
+}
+#endif
+
+/*
+ * Start MD5 accumulation.  Set bit count to 0 and buffer to mysterious
+ * initialization constants.
+ */
+void MD5Init(struct MD5Context *ctx)
+{
+    ctx->buf[0] = 0x67452301;
+    ctx->buf[1] = 0xefcdab89;
+    ctx->buf[2] = 0x98badcfe;
+    ctx->buf[3] = 0x10325476;
+
+    ctx->bits[0] = 0;
+    ctx->bits[1] = 0;
+}
+
+/*
+ * Update context to reflect the concatenation of another buffer full
+ * of bytes.
+ */
+void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len)
+{
+    u32 t;
+
+    /* Update bitcount */
+
+    t = ctx->bits[0];
+    if ((ctx->bits[0] = t + ((u32) len << 3)) < t)
+       ctx->bits[1]++;         /* Carry from low to high */
+    ctx->bits[1] += len >> 29;
+
+    t = (t >> 3) & 0x3f;       /* Bytes already in shsInfo->data */
+
+    /* Handle any leading odd-sized chunks */
+
+    if (t) {
+       unsigned char *p = (unsigned char *) ctx->in + t;
+
+       t = 64 - t;
+       if (len < t) {
+           os_memcpy(p, buf, len);
+           return;
+       }
+       os_memcpy(p, buf, t);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (u32 *) ctx->in);
+       buf += t;
+       len -= t;
+    }
+    /* Process data in 64-byte chunks */
+
+    while (len >= 64) {
+       os_memcpy(ctx->in, buf, 64);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (u32 *) ctx->in);
+       buf += 64;
+       len -= 64;
+    }
+
+    /* Handle any remaining bytes of data. */
+
+    os_memcpy(ctx->in, buf, len);
+}
+
+/*
+ * Final wrapup - pad to 64-byte boundary with the bit pattern
+ * 1 0* (64-bit count of bits processed, MSB-first)
+ */
+void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
+{
+    unsigned count;
+    unsigned char *p;
+
+    /* Compute number of bytes mod 64 */
+    count = (ctx->bits[0] >> 3) & 0x3F;
+
+    /* Set the first char of padding to 0x80.  This is safe since there is
+       always at least one byte free */
+    p = ctx->in + count;
+    *p++ = 0x80;
+
+    /* Bytes of padding needed to make 64 bytes */
+    count = 64 - 1 - count;
+
+    /* Pad out to 56 mod 64 */
+    if (count < 8) {
+       /* Two lots of padding:  Pad the first block to 64 bytes */
+       os_memset(p, 0, count);
+       byteReverse(ctx->in, 16);
+       MD5Transform(ctx->buf, (u32 *) ctx->in);
+
+       /* Now fill the next block with 56 bytes */
+       os_memset(ctx->in, 0, 56);
+    } else {
+       /* Pad block to 56 bytes */
+       os_memset(p, 0, count - 8);
+    }
+    byteReverse(ctx->in, 14);
+
+    /* Append length in bits and transform */
+    ((u32 *) ctx->in)[14] = ctx->bits[0];
+    ((u32 *) ctx->in)[15] = ctx->bits[1];
+
+    MD5Transform(ctx->buf, (u32 *) ctx->in);
+    byteReverse((unsigned char *) ctx->buf, 4);
+    os_memcpy(digest, ctx->buf, 16);
+    os_memset(ctx, 0, sizeof(ctx));    /* In case it's sensitive */
+}
+
+/* The four core functions - F1 is optimized somewhat */
+
+/* #define F1(x, y, z) (x & y | ~x & z) */
+#define F1(x, y, z) (z ^ (x & (y ^ z)))
+#define F2(x, y, z) F1(z, x, y)
+#define F3(x, y, z) (x ^ y ^ z)
+#define F4(x, y, z) (y ^ (x | ~z))
+
+/* This is the central step in the MD5 algorithm. */
+#define MD5STEP(f, w, x, y, z, data, s) \
+       ( w += f(x, y, z) + data,  w = w<<s | w>>(32-s),  w += x )
+
+/*
+ * The core of the MD5 algorithm, this alters an existing MD5 hash to
+ * reflect the addition of 16 longwords of new data.  MD5Update blocks
+ * the data and converts bytes into longwords for this routine.
+ */
+static void MD5Transform(u32 buf[4], u32 const in[16])
+{
+    register u32 a, b, c, d;
+
+    a = buf[0];
+    b = buf[1];
+    c = buf[2];
+    d = buf[3];
+
+    MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7);
+    MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12);
+    MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17);
+    MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22);
+    MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7);
+    MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12);
+    MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17);
+    MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22);
+    MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7);
+    MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12);
+    MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17);
+    MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22);
+    MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7);
+    MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12);
+    MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17);
+    MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22);
+
+    MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5);
+    MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9);
+    MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14);
+    MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20);
+    MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5);
+    MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9);
+    MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14);
+    MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20);
+    MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5);
+    MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9);
+    MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14);
+    MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20);
+    MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5);
+    MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9);
+    MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14);
+    MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20);
+
+    MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4);
+    MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11);
+    MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16);
+    MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23);
+    MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4);
+    MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11);
+    MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16);
+    MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23);
+    MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4);
+    MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11);
+    MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16);
+    MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23);
+    MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4);
+    MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11);
+    MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16);
+    MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23);
+
+    MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6);
+    MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10);
+    MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15);
+    MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21);
+    MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6);
+    MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10);
+    MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15);
+    MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21);
+    MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6);
+    MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10);
+    MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15);
+    MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21);
+    MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6);
+    MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10);
+    MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15);
+    MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21);
+
+    buf[0] += a;
+    buf[1] += b;
+    buf[2] += c;
+    buf[3] += d;
+}
+/* ===== end - public domain MD5 implementation ===== */
diff --git a/src/crypto/md5-non-fips.c b/src/crypto/md5-non-fips.c
new file mode 100644 (file)
index 0000000..6f29201
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * MD5 hash implementation and interface functions (non-FIPS allowed cases)
+ * Copyright (c) 2003-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 "common.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_md5_vector_non_fips_allow - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
+                                  size_t num_elem, const u8 *addr[],
+                                  const size_t *len, u8 *mac)
+{
+       u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+       u8 tk[16];
+       const u8 *_addr[6];
+       size_t i, _len[6];
+
+       if (num_elem > 5) {
+               /*
+                * Fixed limit on the number of fragments to avoid having to
+                * allocate memory (which could fail).
+                */
+               return -1;
+       }
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+               if (md5_vector_non_fips_allow(1, &key, &key_len, tk))
+                       return -1;
+               key = tk;
+               key_len = 16;
+        }
+
+       /* the HMAC_MD5 transform looks like:
+        *
+        * MD5(K XOR opad, MD5(K XOR ipad, text))
+        *
+        * where K is an n byte key
+        * ipad is the byte 0x36 repeated 64 times
+        * opad is the byte 0x5c repeated 64 times
+        * and text is the data being protected */
+
+       /* start out by storing key in ipad */
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+
+       /* XOR key with ipad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x36;
+
+       /* perform inner MD5 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       for (i = 0; i < num_elem; i++) {
+               _addr[i + 1] = addr[i];
+               _len[i + 1] = len[i];
+       }
+       if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac))
+               return -1;
+
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+       /* XOR key with opad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x5c;
+
+       /* perform outer MD5 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       _addr[1] = mac;
+       _len[1] = MD5_MAC_LEN;
+       return md5_vector_non_fips_allow(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_md5_non_fips_allow - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
+                           size_t data_len, u8 *mac)
+{
+       return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data,
+                                             &data_len, mac);
+}
diff --git a/src/crypto/md5.c b/src/crypto/md5.c
new file mode 100644 (file)
index 0000000..7f14e9b
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * MD5 hash implementation and interface functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+                   const u8 *addr[], const size_t *len, u8 *mac)
+{
+       u8 k_pad[64]; /* padding - key XORd with ipad/opad */
+       u8 tk[16];
+       const u8 *_addr[6];
+       size_t i, _len[6];
+
+       if (num_elem > 5) {
+               /*
+                * Fixed limit on the number of fragments to avoid having to
+                * allocate memory (which could fail).
+                */
+               return -1;
+       }
+
+        /* if key is longer than 64 bytes reset it to key = MD5(key) */
+        if (key_len > 64) {
+               if (md5_vector(1, &key, &key_len, tk))
+                       return -1;
+               key = tk;
+               key_len = 16;
+        }
+
+       /* the HMAC_MD5 transform looks like:
+        *
+        * MD5(K XOR opad, MD5(K XOR ipad, text))
+        *
+        * where K is an n byte key
+        * ipad is the byte 0x36 repeated 64 times
+        * opad is the byte 0x5c repeated 64 times
+        * and text is the data being protected */
+
+       /* start out by storing key in ipad */
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+
+       /* XOR key with ipad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x36;
+
+       /* perform inner MD5 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       for (i = 0; i < num_elem; i++) {
+               _addr[i + 1] = addr[i];
+               _len[i + 1] = len[i];
+       }
+       if (md5_vector(1 + num_elem, _addr, _len, mac))
+               return -1;
+
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+       /* XOR key with opad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x5c;
+
+       /* perform outer MD5 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       _addr[1] = mac;
+       _len[1] = MD5_MAC_LEN;
+       return md5_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (16 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+             u8 *mac)
+{
+       return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/md5.h b/src/crypto/md5.h
new file mode 100644 (file)
index 0000000..8952590
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * MD5 hash implementation and interface functions
+ * Copyright (c) 2003-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.
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+#define MD5_MAC_LEN 16
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+                   const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+            u8 *mac);
+#ifdef CONFIG_FIPS
+int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len,
+                                  size_t num_elem, const u8 *addr[],
+                                  const size_t *len, u8 *mac);
+int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data,
+                           size_t data_len, u8 *mac);
+#else /* CONFIG_FIPS */
+#define hmac_md5_vector_non_fips_allow hmac_md5_vector
+#define hmac_md5_non_fips_allow hmac_md5
+#endif /* CONFIG_FIPS */
+
+#endif /* MD5_H */
diff --git a/src/crypto/md5_i.h b/src/crypto/md5_i.h
new file mode 100644 (file)
index 0000000..b7f6596
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * MD5 internal definitions
+ * 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 MD5_I_H
+#define MD5_I_H
+
+struct MD5Context {
+       u32 buf[4];
+       u32 bits[2];
+       u8 in[64];
+};
+
+void MD5Init(struct MD5Context *context);
+void MD5Update(struct MD5Context *context, unsigned char const *buf,
+              unsigned len);
+void MD5Final(unsigned char digest[16], struct MD5Context *context);
+
+#endif /* MD5_I_H */
diff --git a/src/crypto/milenage.c b/src/crypto/milenage.c
new file mode 100644 (file)
index 0000000..cf0c60e
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <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.
+ *
+ * This file implements an example authentication algorithm defined for 3GPP
+ * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow
+ * EAP-AKA to be tested properly with real USIM cards.
+ *
+ * This implementations assumes that the r1..r5 and c1..c5 constants defined in
+ * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00,
+ * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to
+ * be AES (Rijndael).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "milenage.h"
+
+
+/**
+ * milenage_f1 - Milenage f1 and f1* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sqn: SQN = 48-bit sequence number
+ * @amf: AMF = 16-bit authentication management field
+ * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL
+ * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+               const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s)
+{
+       u8 tmp1[16], tmp2[16], tmp3[16];
+       int i;
+
+       /* tmp1 = TEMP = E_K(RAND XOR OP_C) */
+       for (i = 0; i < 16; i++)
+               tmp1[i] = _rand[i] ^ opc[i];
+       if (aes_128_encrypt_block(k, tmp1, tmp1))
+               return -1;
+
+       /* tmp2 = IN1 = SQN || AMF || SQN || AMF */
+       os_memcpy(tmp2, sqn, 6);
+       os_memcpy(tmp2 + 6, amf, 2);
+       os_memcpy(tmp2 + 8, tmp2, 8);
+
+       /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */
+
+       /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */
+       for (i = 0; i < 16; i++)
+               tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i];
+       /* XOR with TEMP = E_K(RAND XOR OP_C) */
+       for (i = 0; i < 16; i++)
+               tmp3[i] ^= tmp1[i];
+       /* XOR with c1 (= ..00, i.e., NOP) */
+
+       /* f1 || f1* = E_K(tmp3) XOR OP_c */
+       if (aes_128_encrypt_block(k, tmp3, tmp1))
+               return -1;
+       for (i = 0; i < 16; i++)
+               tmp1[i] ^= opc[i];
+       if (mac_a)
+               os_memcpy(mac_a, tmp1, 8); /* f1 */
+       if (mac_s)
+               os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */
+       return 0;
+}
+
+
+/**
+ * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms
+ * @opc: OPc = 128-bit value derived from OP and K
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL
+ * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL
+ * Returns: 0 on success, -1 on failure
+ */
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+                  u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar)
+{
+       u8 tmp1[16], tmp2[16], tmp3[16];
+       int i;
+
+       /* tmp2 = TEMP = E_K(RAND XOR OP_C) */
+       for (i = 0; i < 16; i++)
+               tmp1[i] = _rand[i] ^ opc[i];
+       if (aes_128_encrypt_block(k, tmp1, tmp2))
+               return -1;
+
+       /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */
+       /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */
+       /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */
+       /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */
+
+       /* f2 and f5 */
+       /* rotate by r2 (= 0, i.e., NOP) */
+       for (i = 0; i < 16; i++)
+               tmp1[i] = tmp2[i] ^ opc[i];
+       tmp1[15] ^= 1; /* XOR c2 (= ..01) */
+       /* f5 || f2 = E_K(tmp1) XOR OP_c */
+       if (aes_128_encrypt_block(k, tmp1, tmp3))
+               return -1;
+       for (i = 0; i < 16; i++)
+               tmp3[i] ^= opc[i];
+       if (res)
+               os_memcpy(res, tmp3 + 8, 8); /* f2 */
+       if (ak)
+               os_memcpy(ak, tmp3, 6); /* f5 */
+
+       /* f3 */
+       if (ck) {
+               /* rotate by r3 = 0x20 = 4 bytes */
+               for (i = 0; i < 16; i++)
+                       tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i];
+               tmp1[15] ^= 2; /* XOR c3 (= ..02) */
+               if (aes_128_encrypt_block(k, tmp1, ck))
+                       return -1;
+               for (i = 0; i < 16; i++)
+                       ck[i] ^= opc[i];
+       }
+
+       /* f4 */
+       if (ik) {
+               /* rotate by r4 = 0x40 = 8 bytes */
+               for (i = 0; i < 16; i++)
+                       tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i];
+               tmp1[15] ^= 4; /* XOR c4 (= ..04) */
+               if (aes_128_encrypt_block(k, tmp1, ik))
+                       return -1;
+               for (i = 0; i < 16; i++)
+                       ik[i] ^= opc[i];
+       }
+
+       /* f5* */
+       if (akstar) {
+               /* rotate by r5 = 0x60 = 12 bytes */
+               for (i = 0; i < 16; i++)
+                       tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i];
+               tmp1[15] ^= 8; /* XOR c5 (= ..08) */
+               if (aes_128_encrypt_block(k, tmp1, tmp1))
+                       return -1;
+               for (i = 0; i < 6; i++)
+                       akstar[i] = tmp1[i] ^ opc[i];
+       }
+
+       return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @amf: AMF = 16-bit authentication management field
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: Buffer for AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Max length for res; set to used length or 0 on failure
+ */
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+                      const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+                      u8 *ck, u8 *res, size_t *res_len)
+{
+       int i;
+       u8 mac_a[8], ak[6];
+
+       if (*res_len < 8) {
+               *res_len = 0;
+               return;
+       }
+       if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) ||
+           milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) {
+               *res_len = 0;
+               return;
+       }
+       *res_len = 8;
+
+       /* AUTN = (SQN ^ AK) || AMF || MAC */
+       for (i = 0; i < 6; i++)
+               autn[i] = sqn[i] ^ ak[i];
+       os_memcpy(autn + 6, amf, 2);
+       os_memcpy(autn + 8, mac_a, 8);
+}
+
+
+/**
+ * milenage_auts - Milenage AUTS validation
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @auts: AUTS = 112-bit authentication token from client
+ * @sqn: Buffer for SQN = 48-bit sequence number
+ * Returns: 0 = success (sqn filled), -1 on failure
+ */
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+                 u8 *sqn)
+{
+       u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+       u8 ak[6], mac_s[8];
+       int i;
+
+       if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+               return -1;
+       for (i = 0; i < 6; i++)
+               sqn[i] = auts[i] ^ ak[i];
+       if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
+           memcmp(mac_s, auts + 6, 8) != 0)
+               return -1;
+       return 0;
+}
+
+
+/**
+ * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @_rand: RAND = 128-bit random challenge
+ * @sres: Buffer for SRES = 32-bit SRES
+ * @kc: Buffer for Kc = 64-bit Kc
+ * Returns: 0 on success, -1 on failure
+ */
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc)
+{
+       u8 res[8], ck[16], ik[16];
+       int i;
+
+       if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL))
+               return -1;
+
+       for (i = 0; i < 8; i++)
+               kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8];
+
+#ifdef GSM_MILENAGE_ALT_SRES
+       os_memcpy(sres, res, 4);
+#else /* GSM_MILENAGE_ALT_SRES */
+       for (i = 0; i < 4; i++)
+               sres[i] = res[i] ^ res[i + 4];
+#endif /* GSM_MILENAGE_ALT_SRES */
+       return 0;
+}
+
+
+/**
+ * milenage_generate - Generate AKA AUTN,IK,CK,RES
+ * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.)
+ * @k: K = 128-bit subscriber key
+ * @sqn: SQN = 48-bit sequence number
+ * @_rand: RAND = 128-bit random challenge
+ * @autn: AUTN = 128-bit authentication token
+ * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL
+ * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL
+ * @res: Buffer for RES = 64-bit signed response (f2), or %NULL
+ * @res_len: Variable that will be set to RES length
+ * @auts: 112-bit buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 on synchronization failure
+ */
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+                  const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+                  u8 *auts)
+{
+       int i;
+       u8 mac_a[8], ak[6], rx_sqn[6];
+       const u8 *amf;
+
+       wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16);
+       wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16);
+
+       if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+               return -1;
+
+       *res_len = 8;
+       wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len);
+       wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16);
+       wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16);
+       wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6);
+
+       /* AUTN = (SQN ^ AK) || AMF || MAC */
+       for (i = 0; i < 6; i++)
+               rx_sqn[i] = autn[i] ^ ak[i];
+       wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6);
+
+       if (os_memcmp(rx_sqn, sqn, 6) <= 0) {
+               u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+               if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+                       return -1;
+               wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6);
+               for (i = 0; i < 6; i++)
+                       auts[i] = sqn[i] ^ ak[i];
+               if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+                       return -1;
+               wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14);
+               return -2;
+       }
+
+       amf = autn + 6;
+       wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2);
+       if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL))
+               return -1;
+
+       wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
+
+       if (os_memcmp(mac_a, autn + 8, 8) != 0) {
+               wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
+               wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
+                           autn + 8, 8);
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/src/crypto/milenage.h b/src/crypto/milenage.h
new file mode 100644 (file)
index 0000000..d5054d6
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208)
+ * Copyright (c) 2006-2007 <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 MILENAGE_H
+#define MILENAGE_H
+
+void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k,
+                      const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik,
+                      u8 *ck, u8 *res, size_t *res_len);
+int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
+                 u8 *sqn);
+int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres,
+                u8 *kc);
+int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
+                  const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len,
+                  u8 *auts);
+int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand,
+               const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s);
+int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
+                  u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
+
+#endif /* MILENAGE_H */
diff --git a/src/crypto/ms_funcs.c b/src/crypto/ms_funcs.c
new file mode 100644 (file)
index 0000000..dae15ab
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * 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 "common.h"
+#include "sha1.h"
+#include "ms_funcs.h"
+#include "crypto.h"
+
+
+/**
+ * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @challenge: 8-octet Challenge (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge,
+                         const u8 *username, size_t username_len,
+                         u8 *challenge)
+{
+       u8 hash[SHA1_MAC_LEN];
+       const unsigned char *addr[3];
+       size_t len[3];
+
+       addr[0] = peer_challenge;
+       len[0] = 16;
+       addr[1] = auth_challenge;
+       len[1] = 16;
+       addr[2] = username;
+       len[2] = username_len;
+
+       if (sha1_vector(3, addr, len, hash))
+               return -1;
+       os_memcpy(challenge, hash, 8);
+       return 0;
+}
+
+
+/**
+ * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_password_hash(const u8 *password, size_t password_len,
+                     u8 *password_hash)
+{
+       u8 buf[512], *pos;
+       size_t i, len;
+
+       if (password_len > 256)
+               password_len = 256;
+
+       /* Convert password into unicode */
+       for (i = 0; i < password_len; i++) {
+               buf[2 * i] = password[i];
+               buf[2 * i + 1] = 0;
+       }
+
+       len = password_len * 2;
+       pos = buf;
+       return md4_vector(1, (const u8 **) &pos, &len, password_hash);
+}
+
+
+/**
+ * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @password_hash_hash: 16-octet PasswordHashHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash)
+{
+       size_t len = 16;
+       return md4_vector(1, &password_hash, &len, password_hash_hash);
+}
+
+
+/**
+ * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ */
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+                       u8 *response)
+{
+       u8 zpwd[7];
+       des_encrypt(challenge, password_hash, response);
+       des_encrypt(challenge, password_hash + 7, response + 8);
+       zpwd[0] = password_hash[14];
+       zpwd[1] = password_hash[15];
+       os_memset(zpwd + 2, 0, 5);
+       des_encrypt(challenge, zpwd, response + 16);
+}
+
+
+/**
+ * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+                        const u8 *username, size_t username_len,
+                        const u8 *password, size_t password_len,
+                        u8 *response)
+{
+       u8 challenge[8];
+       u8 password_hash[16];
+
+       challenge_hash(peer_challenge, auth_challenge, username, username_len,
+                      challenge);
+       if (nt_password_hash(password, password_len, password_hash))
+               return -1;
+       challenge_response(challenge, password_hash, response);
+       return 0;
+}
+
+
+/**
+ * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+                               const u8 *peer_challenge,
+                               const u8 *username, size_t username_len,
+                               const u8 *password_hash,
+                               u8 *response)
+{
+       u8 challenge[8];
+
+       if (challenge_hash(peer_challenge, auth_challenge,
+                          username, username_len,
+                          challenge))
+               return -1;
+       challenge_response(challenge, password_hash, response);
+       return 0;
+}
+
+
+/**
+ * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response_pwhash(
+       const u8 *password_hash,
+       const u8 *peer_challenge, const u8 *auth_challenge,
+       const u8 *username, size_t username_len,
+       const u8 *nt_response, u8 *response)
+{
+       static const u8 magic1[39] = {
+               0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+               0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+               0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+               0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74
+       };
+       static const u8 magic2[41] = {
+               0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+               0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+               0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+               0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+               0x6E
+       };
+
+       u8 password_hash_hash[16], challenge[8];
+       const unsigned char *addr1[3];
+       const size_t len1[3] = { 16, 24, sizeof(magic1) };
+       const unsigned char *addr2[3];
+       const size_t len2[3] = { SHA1_MAC_LEN, 8, sizeof(magic2) };
+
+       addr1[0] = password_hash_hash;
+       addr1[1] = nt_response;
+       addr1[2] = magic1;
+
+       addr2[0] = response;
+       addr2[1] = challenge;
+       addr2[2] = magic2;
+
+       if (hash_nt_password_hash(password_hash, password_hash_hash))
+               return -1;
+       if (sha1_vector(3, addr1, len1, response))
+               return -1;
+
+       challenge_hash(peer_challenge, auth_challenge, username, username_len,
+                      challenge);
+       return sha1_vector(3, addr2, len2, response);
+}
+
+
+/**
+ * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @nt_response: 24-octet NT-Response (IN)
+ * @peer_challenge: 16-octet PeerChallenge (IN)
+ * @auth_challenge: 16-octet AuthenticatorChallenge (IN)
+ * @username: 0-to-256-char UserName (IN)
+ * @username_len: Length of username
+ * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually
+ * encoded as a 42-octet ASCII string (S=hexdump_of_response)
+ * Returns: 0 on success, -1 on failure
+ */
+int generate_authenticator_response(const u8 *password, size_t password_len,
+                                   const u8 *peer_challenge,
+                                   const u8 *auth_challenge,
+                                   const u8 *username, size_t username_len,
+                                   const u8 *nt_response, u8 *response)
+{
+       u8 password_hash[16];
+       if (nt_password_hash(password, password_len, password_hash))
+               return -1;
+       return generate_authenticator_response_pwhash(
+               password_hash, peer_challenge, auth_challenge,
+               username, username_len, nt_response, response);
+}
+
+
+/**
+ * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5
+ * @challenge: 8-octet Challenge (IN)
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @response: 24-octet Response (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+                         size_t password_len, u8 *response)
+{
+       u8 password_hash[16];
+       if (nt_password_hash(password, password_len, password_hash))
+               return -1;
+       challenge_response(challenge, password_hash, response);
+       return 0;
+}
+
+
+/**
+ * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4
+ * @password_hash_hash: 16-octet PasswordHashHash (IN)
+ * @nt_response: 24-octet NTResponse (IN)
+ * @master_key: 16-octet MasterKey (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+                  u8 *master_key)
+{
+       static const u8 magic1[27] = {
+               0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+               0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+               0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79
+       };
+       const unsigned char *addr[3];
+       const size_t len[3] = { 16, 24, sizeof(magic1) };
+       u8 hash[SHA1_MAC_LEN];
+
+       addr[0] = password_hash_hash;
+       addr[1] = nt_response;
+       addr[2] = magic1;
+
+       if (sha1_vector(3, addr, len, hash))
+               return -1;
+       os_memcpy(master_key, hash, 16);
+       return 0;
+}
+
+
+/**
+ * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4
+ * @master_key: 16-octet MasterKey (IN)
+ * @session_key: 8-to-16 octet SessionKey (OUT)
+ * @session_key_len: SessionKeyLength (Length of session_key) (IN)
+ * @is_send: IsSend (IN, BOOLEAN)
+ * @is_server: IsServer (IN, BOOLEAN)
+ * Returns: 0 on success, -1 on failure
+ */
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+                           size_t session_key_len, int is_send,
+                           int is_server)
+{
+       static const u8 magic2[84] = {
+               0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+               0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+               0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+               0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+               0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+               0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+               0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+               0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+               0x6b, 0x65, 0x79, 0x2e
+       };
+       static const u8 magic3[84] = {
+               0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+               0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+               0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+               0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+               0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+               0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+               0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+               0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+               0x6b, 0x65, 0x79, 0x2e
+       };
+       static const u8 shs_pad1[40] = {
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+       };
+
+       static const u8 shs_pad2[40] = {
+               0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+               0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+               0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
+               0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2
+       };
+       u8 digest[SHA1_MAC_LEN];
+       const unsigned char *addr[4];
+       const size_t len[4] = { 16, 40, 84, 40 };
+
+       addr[0] = master_key;
+       addr[1] = shs_pad1;
+       if (is_send) {
+               addr[2] = is_server ? magic3 : magic2;
+       } else {
+               addr[2] = is_server ? magic2 : magic3;
+       }
+       addr[3] = shs_pad2;
+
+       if (sha1_vector(4, addr, len, digest))
+               return -1;
+
+       if (session_key_len > SHA1_MAC_LEN)
+               session_key_len = SHA1_MAC_LEN;
+       os_memcpy(session_key, digest, session_key_len);
+       return 0;
+}
+
+
+#define PWBLOCK_LEN 516
+
+/**
+ * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10
+ * @password: 0-to-256-unicode-char Password (IN; ASCII)
+ * @password_len: Length of password
+ * @password_hash: 16-octet PasswordHash (IN)
+ * @pw_block: 516-byte PwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int encrypt_pw_block_with_password_hash(
+       const u8 *password, size_t password_len,
+       const u8 *password_hash, u8 *pw_block)
+{
+       size_t i, offset;
+       u8 *pos;
+
+       if (password_len > 256)
+               return -1;
+
+       os_memset(pw_block, 0, PWBLOCK_LEN);
+       offset = (256 - password_len) * 2;
+       if (os_get_random(pw_block, offset) < 0)
+               return -1;
+       for (i = 0; i < password_len; i++)
+               pw_block[offset + i * 2] = password[i];
+       /*
+        * PasswordLength is 4 octets, but since the maximum password length is
+        * 256, only first two (in little endian byte order) can be non-zero.
+        */
+       pos = &pw_block[2 * 256];
+       WPA_PUT_LE16(pos, password_len * 2);
+       rc4_skip(password_hash, 16, 0, pw_block, PWBLOCK_LEN);
+       return 0;
+}
+
+
+/**
+ * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password_len: Length of old_password
+ * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int new_password_encrypted_with_old_nt_password_hash(
+       const u8 *new_password, size_t new_password_len,
+       const u8 *old_password, size_t old_password_len,
+       u8 *encrypted_pw_block)
+{
+       u8 password_hash[16];
+
+       if (nt_password_hash(old_password, old_password_len, password_hash))
+               return -1;
+       if (encrypt_pw_block_with_password_hash(new_password, new_password_len,
+                                               password_hash,
+                                               encrypted_pw_block))
+               return -1;
+       return 0;
+}
+
+
+/**
+ * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13
+ * @password_hash: 16-octer PasswordHash (IN)
+ * @block: 16-octet Block (IN)
+ * @cypher: 16-octer Cypher (OUT)
+ */
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+                                          const u8 *block, u8 *cypher)
+{
+       des_encrypt(password_hash, block, cypher);
+       des_encrypt(password_hash + 8, block + 7, cypher + 8);
+}
+
+
+/**
+ * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12
+ * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII)
+ * @new_password_len: Length of new_password
+ * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII)
+ * @old_password_len: Length of old_password
+ * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT)
+ * Returns: 0 on success, -1 on failure
+ */
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+       const u8 *new_password, size_t new_password_len,
+       const u8 *old_password, size_t old_password_len,
+       u8 *encrypted_password_hash)
+{
+       u8 old_password_hash[16], new_password_hash[16];
+
+       if (nt_password_hash(old_password, old_password_len,
+                            old_password_hash) ||
+           nt_password_hash(new_password, new_password_len,
+                            new_password_hash))
+               return -1;
+       nt_password_hash_encrypted_with_block(old_password_hash,
+                                             new_password_hash,
+                                             encrypted_password_hash);
+       return 0;
+}
diff --git a/src/crypto/ms_funcs.h b/src/crypto/ms_funcs.h
new file mode 100644 (file)
index 0000000..298dbcf
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759
+ * 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.
+ */
+
+#ifndef MS_FUNCS_H
+#define MS_FUNCS_H
+
+int generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge,
+                        const u8 *username, size_t username_len,
+                        const u8 *password, size_t password_len,
+                        u8 *response);
+int generate_nt_response_pwhash(const u8 *auth_challenge,
+                               const u8 *peer_challenge,
+                               const u8 *username, size_t username_len,
+                               const u8 *password_hash,
+                               u8 *response);
+int generate_authenticator_response(const u8 *password, size_t password_len,
+                                   const u8 *peer_challenge,
+                                   const u8 *auth_challenge,
+                                   const u8 *username, size_t username_len,
+                                   const u8 *nt_response, u8 *response);
+int generate_authenticator_response_pwhash(
+       const u8 *password_hash,
+       const u8 *peer_challenge, const u8 *auth_challenge,
+       const u8 *username, size_t username_len,
+       const u8 *nt_response, u8 *response);
+int nt_challenge_response(const u8 *challenge, const u8 *password,
+                         size_t password_len, u8 *response);
+
+void challenge_response(const u8 *challenge, const u8 *password_hash,
+                       u8 *response);
+int nt_password_hash(const u8 *password, size_t password_len,
+                    u8 *password_hash);
+int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash);
+int get_master_key(const u8 *password_hash_hash, const u8 *nt_response,
+                  u8 *master_key);
+int get_asymetric_start_key(const u8 *master_key, u8 *session_key,
+                           size_t session_key_len, int is_send,
+                           int is_server);
+int __must_check encrypt_pw_block_with_password_hash(
+       const u8 *password, size_t password_len,
+       const u8 *password_hash, u8 *pw_block);
+int __must_check new_password_encrypted_with_old_nt_password_hash(
+       const u8 *new_password, size_t new_password_len,
+       const u8 *old_password, size_t old_password_len,
+       u8 *encrypted_pw_block);
+void nt_password_hash_encrypted_with_block(const u8 *password_hash,
+                                          const u8 *block, u8 *cypher);
+int old_nt_password_hash_encrypted_with_new_nt_password_hash(
+       const u8 *new_password, size_t new_password_len,
+       const u8 *old_password, size_t old_password_len,
+       u8 *encrypted_password_hash);
+
+#endif /* MS_FUNCS_H */
diff --git a/src/crypto/rc4.c b/src/crypto/rc4.c
new file mode 100644 (file)
index 0000000..5ab1be1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * RC4 stream cipher
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto.h"
+
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+
+int rc4_skip(const u8 *key, size_t keylen, size_t skip,
+            u8 *data, size_t data_len)
+{
+       u32 i, j, k;
+       u8 S[256], *pos;
+       size_t kpos;
+
+       /* Setup RC4 state */
+       for (i = 0; i < 256; i++)
+               S[i] = i;
+       j = 0;
+       kpos = 0;
+       for (i = 0; i < 256; i++) {
+               j = (j + S[i] + key[kpos]) & 0xff;
+               kpos++;
+               if (kpos >= keylen)
+                       kpos = 0;
+               S_SWAP(i, j);
+       }
+
+       /* Skip the start of the stream */
+       i = j = 0;
+       for (k = 0; k < skip; k++) {
+               i = (i + 1) & 0xff;
+               j = (j + S[i]) & 0xff;
+               S_SWAP(i, j);
+       }
+
+       /* Apply RC4 to data */
+       pos = data;
+       for (k = 0; k < data_len; k++) {
+               i = (i + 1) & 0xff;
+               j = (j + S[i]) & 0xff;
+               S_SWAP(i, j);
+               *pos++ ^= S[(S[i] + S[j]) & 0xff];
+       }
+
+       return 0;
+}
diff --git a/src/crypto/sha1-internal.c b/src/crypto/sha1-internal.c
new file mode 100644 (file)
index 0000000..3f05ca1
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "sha1_i.h"
+#include "md5.h"
+#include "crypto.h"
+
+typedef struct SHA1Context SHA1_CTX;
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+
+/**
+ * sha1_vector - SHA-1 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
+{
+       SHA1_CTX ctx;
+       size_t i;
+
+       SHA1Init(&ctx);
+       for (i = 0; i < num_elem; i++)
+               SHA1Update(&ctx, addr[i], len[i]);
+       SHA1Final(mac, &ctx);
+       return 0;
+}
+
+
+/* ===== start - public domain SHA1 implementation ===== */
+
+/*
+SHA-1 in C
+By Steve Reid <sreid@sea-to-sky.net>
+100% Public Domain
+
+-----------------
+Modified 7/98 
+By James H. Brown <jbrown@burgoyne.com>
+Still 100% Public Domain
+
+Corrected a problem which generated improper hash values on 16 bit machines
+Routine SHA1Update changed from
+       void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
+len)
+to
+       void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
+long len)
+
+The 'len' parameter was declared an int which works fine on 32 bit machines.
+However, on 16 bit machines an int is too small for the shifts being done
+against
+it.  This caused the hash function to generate incorrect values if len was
+greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
+
+Since the file IO in main() reads 16K at a time, any file 8K or larger would
+be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
+"a"s).
+
+I also changed the declaration of variables i & j in SHA1Update to 
+unsigned long from unsigned int for the same reason.
+
+These changes should make no difference to any 32 bit implementations since
+an
+int and a long are the same size in those environments.
+
+--
+I also corrected a few compiler warnings generated by Borland C.
+1. Added #include <process.h> for exit() prototype
+2. Removed unused variable 'j' in SHA1Final
+3. Changed exit(0) to return(0) at end of main.
+
+ALL changes I made can be located by searching for comments containing 'JHB'
+-----------------
+Modified 8/98
+By Steve Reid <sreid@sea-to-sky.net>
+Still 100% public domain
+
+1- Removed #include <process.h> and used return() instead of exit()
+2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
+3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
+
+-----------------
+Modified 4/01
+By Saul Kravitz <Saul.Kravitz@celera.com>
+Still 100% PD
+Modified to run on Compaq Alpha hardware.  
+
+-----------------
+Modified 4/01
+By Jouni Malinen <j@w1.fi>
+Minor changes to match the coding style used in Dynamics.
+
+Modified September 24, 2004
+By Jouni Malinen <j@w1.fi>
+Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined.
+
+*/
+
+/*
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+#define SHA1HANDSOFF
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef WORDS_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \
+       (rol(block->l[i], 8) & 0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \
+       block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) \
+       z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+       w = rol(w, 30);
+#define R1(v,w,x,y,z,i) \
+       z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+       w = rol(w, 30);
+#define R2(v,w,x,y,z,i) \
+       z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30);
+#define R3(v,w,x,y,z,i) \
+       z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+       w = rol(w, 30);
+#define R4(v,w,x,y,z,i) \
+       z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+       w=rol(w, 30);
+
+
+#ifdef VERBOSE  /* SAK */
+void SHAPrintContext(SHA1_CTX *context, char *msg)
+{
+       printf("%s (%d,%d) %x %x %x %x %x\n",
+              msg,
+              context->count[0], context->count[1], 
+              context->state[0],
+              context->state[1],
+              context->state[2],
+              context->state[3],
+              context->state[4]);
+}
+#endif
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(u32 state[5], const unsigned char buffer[64])
+{
+       u32 a, b, c, d, e;
+       typedef union {
+               unsigned char c[64];
+               u32 l[16];
+       } CHAR64LONG16;
+       CHAR64LONG16* block;
+#ifdef SHA1HANDSOFF
+       CHAR64LONG16 workspace;
+       block = &workspace;
+       os_memcpy(block, buffer, 64);
+#else
+       block = (CHAR64LONG16 *) buffer;
+#endif
+       /* Copy context->state[] to working vars */
+       a = state[0];
+       b = state[1];
+       c = state[2];
+       d = state[3];
+       e = state[4];
+       /* 4 rounds of 20 operations each. Loop unrolled. */
+       R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+       R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+       R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+       R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+       R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+       R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+       R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+       R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+       R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+       R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+       R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+       R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+       R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+       R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+       R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+       R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+       R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+       R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+       R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+       R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+       /* Add the working vars back into context.state[] */
+       state[0] += a;
+       state[1] += b;
+       state[2] += c;
+       state[3] += d;
+       state[4] += e;
+       /* Wipe variables */
+       a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+       os_memset(block, 0, 64);
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+       /* SHA1 initialization constants */
+       context->state[0] = 0x67452301;
+       context->state[1] = 0xEFCDAB89;
+       context->state[2] = 0x98BADCFE;
+       context->state[3] = 0x10325476;
+       context->state[4] = 0xC3D2E1F0;
+       context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const void *_data, u32 len)
+{
+       u32 i, j;
+       const unsigned char *data = _data;
+
+#ifdef VERBOSE
+       SHAPrintContext(context, "before");
+#endif
+       j = (context->count[0] >> 3) & 63;
+       if ((context->count[0] += len << 3) < (len << 3))
+               context->count[1]++;
+       context->count[1] += (len >> 29);
+       if ((j + len) > 63) {
+               os_memcpy(&context->buffer[j], data, (i = 64-j));
+               SHA1Transform(context->state, context->buffer);
+               for ( ; i + 63 < len; i += 64) {
+                       SHA1Transform(context->state, &data[i]);
+               }
+               j = 0;
+       }
+       else i = 0;
+       os_memcpy(&context->buffer[j], &data[i], len - i);
+#ifdef VERBOSE
+       SHAPrintContext(context, "after ");
+#endif
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+       u32 i;
+       unsigned char finalcount[8];
+
+       for (i = 0; i < 8; i++) {
+               finalcount[i] = (unsigned char)
+                       ((context->count[(i >= 4 ? 0 : 1)] >>
+                         ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+       }
+       SHA1Update(context, (unsigned char *) "\200", 1);
+       while ((context->count[0] & 504) != 448) {
+               SHA1Update(context, (unsigned char *) "\0", 1);
+       }
+       SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform()
+                                             */
+       for (i = 0; i < 20; i++) {
+               digest[i] = (unsigned char)
+                       ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) &
+                        255);
+       }
+       /* Wipe variables */
+       i = 0;
+       os_memset(context->buffer, 0, 64);
+       os_memset(context->state, 0, 20);
+       os_memset(context->count, 0, 8);
+       os_memset(finalcount, 0, 8);
+}
+
+/* ===== end - public domain SHA1 implementation ===== */
diff --git a/src/crypto/sha1-pbkdf2.c b/src/crypto/sha1-pbkdf2.c
new file mode 100644 (file)
index 0000000..11323de
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "md5.h"
+#include "crypto.h"
+
+static int pbkdf2_sha1_f(const char *passphrase, const char *ssid,
+                        size_t ssid_len, int iterations, unsigned int count,
+                        u8 *digest)
+{
+       unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN];
+       int i, j;
+       unsigned char count_buf[4];
+       const u8 *addr[2];
+       size_t len[2];
+       size_t passphrase_len = os_strlen(passphrase);
+
+       addr[0] = (u8 *) ssid;
+       len[0] = ssid_len;
+       addr[1] = count_buf;
+       len[1] = 4;
+
+       /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+        * U1 = PRF(P, S || i)
+        * U2 = PRF(P, U1)
+        * Uc = PRF(P, Uc-1)
+        */
+
+       count_buf[0] = (count >> 24) & 0xff;
+       count_buf[1] = (count >> 16) & 0xff;
+       count_buf[2] = (count >> 8) & 0xff;
+       count_buf[3] = count & 0xff;
+       if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len,
+                            tmp))
+               return -1;
+       os_memcpy(digest, tmp, SHA1_MAC_LEN);
+
+       for (i = 1; i < iterations; i++) {
+               if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp,
+                             SHA1_MAC_LEN, tmp2))
+                       return -1;
+               os_memcpy(tmp, tmp2, SHA1_MAC_LEN);
+               for (j = 0; j < SHA1_MAC_LEN; j++)
+                       digest[j] ^= tmp2[j];
+       }
+
+       return 0;
+}
+
+
+/**
+ * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i
+ * @passphrase: ASCII passphrase
+ * @ssid: SSID
+ * @ssid_len: SSID length in bytes
+ * @iterations: Number of iterations to run
+ * @buf: Buffer for the generated key
+ * @buflen: Length of the buffer in bytes
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive PSK for WPA-PSK. For this protocol,
+ * iterations is set to 4096 and buflen to 32. This function is described in
+ * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0.
+ */
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+               int iterations, u8 *buf, size_t buflen)
+{
+       unsigned int count = 0;
+       unsigned char *pos = buf;
+       size_t left = buflen, plen;
+       unsigned char digest[SHA1_MAC_LEN];
+
+       while (left > 0) {
+               count++;
+               if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations,
+                                 count, digest))
+                       return -1;
+               plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left;
+               os_memcpy(pos, digest, plen);
+               pos += plen;
+               left -= plen;
+       }
+
+       return 0;
+}
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
new file mode 100644 (file)
index 0000000..2c8c029
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * TLS PRF (SHA1 + MD5)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "md5.h"
+#include "crypto.h"
+
+
+/**
+ * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 2246, Chapter 5.
+ */
+int tls_prf(const u8 *secret, size_t secret_len, const char *label,
+           const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+       size_t L_S1, L_S2, i;
+       const u8 *S1, *S2;
+       u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN];
+       u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN];
+       int MD5_pos, SHA1_pos;
+       const u8 *MD5_addr[3];
+       size_t MD5_len[3];
+       const unsigned char *SHA1_addr[3];
+       size_t SHA1_len[3];
+
+       if (secret_len & 1)
+               return -1;
+
+       MD5_addr[0] = A_MD5;
+       MD5_len[0] = MD5_MAC_LEN;
+       MD5_addr[1] = (unsigned char *) label;
+       MD5_len[1] = os_strlen(label);
+       MD5_addr[2] = seed;
+       MD5_len[2] = seed_len;
+
+       SHA1_addr[0] = A_SHA1;
+       SHA1_len[0] = SHA1_MAC_LEN;
+       SHA1_addr[1] = (unsigned char *) label;
+       SHA1_len[1] = os_strlen(label);
+       SHA1_addr[2] = seed;
+       SHA1_len[2] = seed_len;
+
+       /* RFC 2246, Chapter 5
+        * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+        * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+        * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed)
+        */
+
+       L_S1 = L_S2 = (secret_len + 1) / 2;
+       S1 = secret;
+       S2 = secret + L_S1;
+       if (secret_len & 1) {
+               /* The last byte of S1 will be shared with S2 */
+               S2--;
+       }
+
+       hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1],
+                                      A_MD5);
+       hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1);
+
+       MD5_pos = MD5_MAC_LEN;
+       SHA1_pos = SHA1_MAC_LEN;
+       for (i = 0; i < outlen; i++) {
+               if (MD5_pos == MD5_MAC_LEN) {
+                       hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr,
+                                                      MD5_len, P_MD5);
+                       MD5_pos = 0;
+                       hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN,
+                                               A_MD5);
+               }
+               if (SHA1_pos == SHA1_MAC_LEN) {
+                       hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len,
+                                        P_SHA1);
+                       SHA1_pos = 0;
+                       hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1);
+               }
+
+               out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos];
+
+               MD5_pos++;
+               SHA1_pos++;
+       }
+
+       return 0;
+}
diff --git a/src/crypto/sha1-tprf.c b/src/crypto/sha1-tprf.c
new file mode 100644 (file)
index 0000000..4a80e96
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * SHA1 T-PRF for EAP-FAST
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+/**
+ * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5.
+ */
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+              const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len)
+{
+       unsigned char counter = 0;
+       size_t pos, plen;
+       u8 hash[SHA1_MAC_LEN];
+       size_t label_len = os_strlen(label);
+       u8 output_len[2];
+       const unsigned char *addr[5];
+       size_t len[5];
+
+       addr[0] = hash;
+       len[0] = 0;
+       addr[1] = (unsigned char *) label;
+       len[1] = label_len + 1;
+       addr[2] = seed;
+       len[2] = seed_len;
+       addr[3] = output_len;
+       len[3] = 2;
+       addr[4] = &counter;
+       len[4] = 1;
+
+       output_len[0] = (buf_len >> 8) & 0xff;
+       output_len[1] = buf_len & 0xff;
+       pos = 0;
+       while (pos < buf_len) {
+               counter++;
+               plen = buf_len - pos;
+               if (hmac_sha1_vector(key, key_len, 5, addr, len, hash))
+                       return -1;
+               if (plen >= SHA1_MAC_LEN) {
+                       os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+                       pos += SHA1_MAC_LEN;
+               } else {
+                       os_memcpy(&buf[pos], hash, plen);
+                       break;
+               }
+               len[0] = SHA1_MAC_LEN;
+       }
+
+       return 0;
+}
diff --git a/src/crypto/sha1.c b/src/crypto/sha1.c
new file mode 100644 (file)
index 0000000..fe00bdb
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha1.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
+{
+       unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+       unsigned char tk[20];
+       const u8 *_addr[6];
+       size_t _len[6], i;
+
+       if (num_elem > 5) {
+               /*
+                * Fixed limit on the number of fragments to avoid having to
+                * allocate memory (which could fail).
+                */
+               return -1;
+       }
+
+        /* if key is longer than 64 bytes reset it to key = SHA1(key) */
+        if (key_len > 64) {
+               if (sha1_vector(1, &key, &key_len, tk))
+                       return -1;
+               key = tk;
+               key_len = 20;
+        }
+
+       /* the HMAC_SHA1 transform looks like:
+        *
+        * SHA1(K XOR opad, SHA1(K XOR ipad, text))
+        *
+        * where K is an n byte key
+        * ipad is the byte 0x36 repeated 64 times
+        * opad is the byte 0x5c repeated 64 times
+        * and text is the data being protected */
+
+       /* start out by storing key in ipad */
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+       /* XOR key with ipad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x36;
+
+       /* perform inner SHA1 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       for (i = 0; i < num_elem; i++) {
+               _addr[i + 1] = addr[i];
+               _len[i + 1] = len[i];
+       }
+       if (sha1_vector(1 + num_elem, _addr, _len, mac))
+               return -1;
+
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+       /* XOR key with opad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x5c;
+
+       /* perform outer SHA1 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       _addr[1] = mac;
+       _len[1] = SHA1_MAC_LEN;
+       return sha1_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ * Returns: 0 on success, -1 of failure
+ */
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+              u8 *mac)
+{
+       return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success, -1 of failure
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., PMK in IEEE 802.11i).
+ */
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+            const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+       u8 counter = 0;
+       size_t pos, plen;
+       u8 hash[SHA1_MAC_LEN];
+       size_t label_len = os_strlen(label) + 1;
+       const unsigned char *addr[3];
+       size_t len[3];
+
+       addr[0] = (u8 *) label;
+       len[0] = label_len;
+       addr[1] = data;
+       len[1] = data_len;
+       addr[2] = &counter;
+       len[2] = 1;
+
+       pos = 0;
+       while (pos < buf_len) {
+               plen = buf_len - pos;
+               if (plen >= SHA1_MAC_LEN) {
+                       if (hmac_sha1_vector(key, key_len, 3, addr, len,
+                                            &buf[pos]))
+                               return -1;
+                       pos += SHA1_MAC_LEN;
+               } else {
+                       if (hmac_sha1_vector(key, key_len, 3, addr, len,
+                                            hash))
+                               return -1;
+                       os_memcpy(&buf[pos], hash, plen);
+                       break;
+               }
+               counter++;
+       }
+
+       return 0;
+}
diff --git a/src/crypto/sha1.h b/src/crypto/sha1.h
new file mode 100644 (file)
index 0000000..c1a6233
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * SHA1 hash implementation and interface functions
+ * Copyright (c) 2003-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.
+ */
+
+#ifndef SHA1_H
+#define SHA1_H
+
+#define SHA1_MAC_LEN 20
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+              u8 *mac);
+int sha1_prf(const u8 *key, size_t key_len, const char *label,
+            const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+int sha1_t_prf(const u8 *key, size_t key_len, const char *label,
+              const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len);
+int __must_check tls_prf(const u8 *secret, size_t secret_len,
+                        const char *label, const u8 *seed, size_t seed_len,
+                        u8 *out, size_t outlen);
+int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len,
+               int iterations, u8 *buf, size_t buflen);
+#endif /* SHA1_H */
diff --git a/src/crypto/sha1_i.h b/src/crypto/sha1_i.h
new file mode 100644 (file)
index 0000000..ec2f82f
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * SHA1 internal definitions
+ * 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 SHA1_I_H
+#define SHA1_I_H
+
+struct SHA1Context {
+       u32 state[5];
+       u32 count[2];
+       unsigned char buffer[64];
+};
+
+void SHA1Init(struct SHA1Context *context);
+void SHA1Update(struct SHA1Context *context, const void *data, u32 len);
+void SHA1Final(unsigned char digest[20], struct SHA1Context *context);
+void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
+
+#endif /* SHA1_I_H */
diff --git a/src/crypto/sha256-internal.c b/src/crypto/sha256-internal.c
new file mode 100644 (file)
index 0000000..b061373
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+struct sha256_state {
+       u64 length;
+       u32 state[8], curlen;
+       u8 buf[64];
+};
+
+static void sha256_init(struct sha256_state *md);
+static int sha256_process(struct sha256_state *md, const unsigned char *in,
+                         unsigned long inlen);
+static int sha256_done(struct sha256_state *md, unsigned char *out);
+
+
+/**
+ * sha256_vector - SHA256 hash for data vector
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash
+ * Returns: 0 on success, -1 of failure
+ */
+int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
+                 u8 *mac)
+{
+       struct sha256_state ctx;
+       size_t i;
+
+       sha256_init(&ctx);
+       for (i = 0; i < num_elem; i++)
+               if (sha256_process(&ctx, addr[i], len[i]))
+                       return -1;
+       if (sha256_done(&ctx, mac))
+               return -1;
+       return 0;
+}
+
+
+/* ===== start - public domain SHA256 implementation ===== */
+
+/* This is based on SHA256 implementation in LibTomCrypt that was released into
+ * public domain by Tom St Denis. */
+
+/* the K array */
+static const unsigned long K[64] = {
+       0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL,
+       0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL,
+       0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL,
+       0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
+       0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL,
+       0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL,
+       0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL,
+       0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
+       0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL,
+       0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL,
+       0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL,
+       0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
+       0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL
+};
+
+
+/* Various logical functions */
+#define RORc(x, y) \
+( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \
+   ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL)
+#define Ch(x,y,z)       (z ^ (x & (y ^ z)))
+#define Maj(x,y,z)      (((x | y) & z) | (x & y)) 
+#define S(x, n)         RORc((x), (n))
+#define R(x, n)         (((x)&0xFFFFFFFFUL)>>(n))
+#define Sigma0(x)       (S(x, 2) ^ S(x, 13) ^ S(x, 22))
+#define Sigma1(x)       (S(x, 6) ^ S(x, 11) ^ S(x, 25))
+#define Gamma0(x)       (S(x, 7) ^ S(x, 18) ^ R(x, 3))
+#define Gamma1(x)       (S(x, 17) ^ S(x, 19) ^ R(x, 10))
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+/* compress 512-bits */
+static int sha256_compress(struct sha256_state *md, unsigned char *buf)
+{
+       u32 S[8], W[64], t0, t1;
+       u32 t;
+       int i;
+
+       /* copy state into S */
+       for (i = 0; i < 8; i++) {
+               S[i] = md->state[i];
+       }
+
+       /* copy the state into 512-bits into W[0..15] */
+       for (i = 0; i < 16; i++)
+               W[i] = WPA_GET_BE32(buf + (4 * i));
+
+       /* fill W[16..63] */
+       for (i = 16; i < 64; i++) {
+               W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) +
+                       W[i - 16];
+       }        
+
+       /* Compress */
+#define RND(a,b,c,d,e,f,g,h,i)                          \
+       t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \
+       t1 = Sigma0(a) + Maj(a, b, c);                  \
+       d += t0;                                        \
+       h  = t0 + t1;
+
+       for (i = 0; i < 64; ++i) {
+               RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i);
+               t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; 
+               S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t;
+       }
+
+       /* feedback */
+       for (i = 0; i < 8; i++) {
+               md->state[i] = md->state[i] + S[i];
+       }
+       return 0;
+}
+
+
+/* Initialize the hash state */
+static void sha256_init(struct sha256_state *md)
+{
+       md->curlen = 0;
+       md->length = 0;
+       md->state[0] = 0x6A09E667UL;
+       md->state[1] = 0xBB67AE85UL;
+       md->state[2] = 0x3C6EF372UL;
+       md->state[3] = 0xA54FF53AUL;
+       md->state[4] = 0x510E527FUL;
+       md->state[5] = 0x9B05688CUL;
+       md->state[6] = 0x1F83D9ABUL;
+       md->state[7] = 0x5BE0CD19UL;
+}
+
+/**
+   Process a block of memory though the hash
+   @param md     The hash state
+   @param in     The data to hash
+   @param inlen  The length of the data (octets)
+   @return CRYPT_OK if successful
+*/
+static int sha256_process(struct sha256_state *md, const unsigned char *in,
+                         unsigned long inlen)
+{
+       unsigned long n;
+#define block_size 64
+
+       if (md->curlen > sizeof(md->buf))
+               return -1;
+
+       while (inlen > 0) {
+               if (md->curlen == 0 && inlen >= block_size) {
+                       if (sha256_compress(md, (unsigned char *) in) < 0)
+                               return -1;
+                       md->length += block_size * 8;
+                       in += block_size;
+                       inlen -= block_size;
+               } else {
+                       n = MIN(inlen, (block_size - md->curlen));
+                       os_memcpy(md->buf + md->curlen, in, n);
+                       md->curlen += n;
+                       in += n;
+                       inlen -= n;
+                       if (md->curlen == block_size) {
+                               if (sha256_compress(md, md->buf) < 0)
+                                       return -1;
+                               md->length += 8 * block_size;
+                               md->curlen = 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+   Terminate the hash to get the digest
+   @param md  The hash state
+   @param out [out] The destination of the hash (32 bytes)
+   @return CRYPT_OK if successful
+*/
+static int sha256_done(struct sha256_state *md, unsigned char *out)
+{
+       int i;
+
+       if (md->curlen >= sizeof(md->buf))
+               return -1;
+
+       /* increase the length of the message */
+       md->length += md->curlen * 8;
+
+       /* append the '1' bit */
+       md->buf[md->curlen++] = (unsigned char) 0x80;
+
+       /* if the length is currently above 56 bytes we append zeros
+        * then compress.  Then we can fall back to padding zeros and length
+        * encoding like normal.
+        */
+       if (md->curlen > 56) {
+               while (md->curlen < 64) {
+                       md->buf[md->curlen++] = (unsigned char) 0;
+               }
+               sha256_compress(md, md->buf);
+               md->curlen = 0;
+       }
+
+       /* pad upto 56 bytes of zeroes */
+       while (md->curlen < 56) {
+               md->buf[md->curlen++] = (unsigned char) 0;
+       }
+
+       /* store length */
+       WPA_PUT_BE64(md->buf + 56, md->length);
+       sha256_compress(md, md->buf);
+
+       /* copy output */
+       for (i = 0; i < 8; i++)
+               WPA_PUT_BE32(out + (4 * i), md->state[i]);
+
+       return 0;
+}
+
+/* ===== end - public domain SHA256 implementation ===== */
diff --git a/src/crypto/sha256.c b/src/crypto/sha256.c
new file mode 100644 (file)
index 0000000..7f320f9
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * SHA-256 hash implementation and interface functions
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha256.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (32 bytes)
+ */
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+                       const u8 *addr[], const size_t *len, u8 *mac)
+{
+       unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */
+       unsigned char tk[32];
+       const u8 *_addr[6];
+       size_t _len[6], i;
+
+       if (num_elem > 5) {
+               /*
+                * Fixed limit on the number of fragments to avoid having to
+                * allocate memory (which could fail).
+                */
+               return;
+       }
+
+        /* if key is longer than 64 bytes reset it to key = SHA256(key) */
+        if (key_len > 64) {
+               sha256_vector(1, &key, &key_len, tk);
+               key = tk;
+               key_len = 32;
+        }
+
+       /* the HMAC_SHA256 transform looks like:
+        *
+        * SHA256(K XOR opad, SHA256(K XOR ipad, text))
+        *
+        * where K is an n byte key
+        * ipad is the byte 0x36 repeated 64 times
+        * opad is the byte 0x5c repeated 64 times
+        * and text is the data being protected */
+
+       /* start out by storing key in ipad */
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+       /* XOR key with ipad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x36;
+
+       /* perform inner SHA256 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       for (i = 0; i < num_elem; i++) {
+               _addr[i + 1] = addr[i];
+               _len[i + 1] = len[i];
+       }
+       sha256_vector(1 + num_elem, _addr, _len, mac);
+
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memcpy(k_pad, key, key_len);
+       /* XOR key with opad values */
+       for (i = 0; i < 64; i++)
+               k_pad[i] ^= 0x5c;
+
+       /* perform outer SHA256 */
+       _addr[0] = k_pad;
+       _len[0] = 64;
+       _addr[1] = mac;
+       _len[1] = SHA256_MAC_LEN;
+       sha256_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (20 bytes)
+ */
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+                size_t data_len, u8 *mac)
+{
+       hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+
+/**
+ * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5.1.5.2)
+ * @key: Key for PRF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @data: Extra data to bind into the key
+ * @data_len: Length of the data
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key.
+ */
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+               const u8 *data, size_t data_len, u8 *buf, size_t buf_len)
+{
+       u16 counter = 1;
+       size_t pos, plen;
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+       u8 counter_le[2], length_le[2];
+
+       addr[0] = counter_le;
+       len[0] = 2;
+       addr[1] = (u8 *) label;
+       len[1] = os_strlen(label);
+       addr[2] = data;
+       len[2] = data_len;
+       addr[3] = length_le;
+       len[3] = sizeof(length_le);
+
+       WPA_PUT_LE16(length_le, buf_len * 8);
+       pos = 0;
+       while (pos < buf_len) {
+               plen = buf_len - pos;
+               WPA_PUT_LE16(counter_le, counter);
+               if (plen >= SHA256_MAC_LEN) {
+                       hmac_sha256_vector(key, key_len, 4, addr, len,
+                                          &buf[pos]);
+                       pos += SHA256_MAC_LEN;
+               } else {
+                       hmac_sha256_vector(key, key_len, 4, addr, len, hash);
+                       os_memcpy(&buf[pos], hash, plen);
+                       break;
+               }
+               counter++;
+       }
+}
diff --git a/src/crypto/sha256.h b/src/crypto/sha256.h
new file mode 100644 (file)
index 0000000..dc597f0
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * SHA256 hash implementation and interface functions
+ * Copyright (c) 2003-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 SHA256_H
+#define SHA256_H
+
+#define SHA256_MAC_LEN 32
+
+void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
+                     const u8 *addr[], const size_t *len, u8 *mac);
+void hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+                size_t data_len, u8 *mac);
+void sha256_prf(const u8 *key, size_t key_len, const char *label,
+             const u8 *data, size_t data_len, u8 *buf, size_t buf_len);
+
+#endif /* SHA256_H */
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
new file mode 100644 (file)
index 0000000..0928b5b
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+ * SSL/TLS interface definition
+ * Copyright (c) 2004-2010, 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 TLS_H
+#define TLS_H
+
+struct tls_connection;
+
+struct tls_keys {
+       const u8 *master_key; /* TLS master secret */
+       size_t master_key_len;
+       const u8 *client_random;
+       size_t client_random_len;
+       const u8 *server_random;
+       size_t server_random_len;
+       const u8 *inner_secret; /* TLS/IA inner secret */
+       size_t inner_secret_len;
+};
+
+enum tls_event {
+       TLS_CERT_CHAIN_FAILURE,
+       TLS_PEER_CERTIFICATE
+};
+
+/*
+ * Note: These are used as identifier with external programs and as such, the
+ * values must not be changed.
+ */
+enum tls_fail_reason {
+       TLS_FAIL_UNSPECIFIED = 0,
+       TLS_FAIL_UNTRUSTED = 1,
+       TLS_FAIL_REVOKED = 2,
+       TLS_FAIL_NOT_YET_VALID = 3,
+       TLS_FAIL_EXPIRED = 4,
+       TLS_FAIL_SUBJECT_MISMATCH = 5,
+       TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
+       TLS_FAIL_BAD_CERTIFICATE = 7,
+       TLS_FAIL_SERVER_CHAIN_PROBE = 8
+};
+
+union tls_event_data {
+       struct {
+               int depth;
+               const char *subject;
+               enum tls_fail_reason reason;
+               const char *reason_txt;
+               const struct wpabuf *cert;
+       } cert_fail;
+
+       struct {
+               int depth;
+               const char *subject;
+               const struct wpabuf *cert;
+               const u8 *hash;
+               size_t hash_len;
+       } peer_cert;
+};
+
+struct tls_config {
+       const char *opensc_engine_path;
+       const char *pkcs11_engine_path;
+       const char *pkcs11_module_path;
+       int fips_mode;
+
+       void (*event_cb)(void *ctx, enum tls_event ev,
+                        union tls_event_data *data);
+       void *cb_ctx;
+};
+
+#define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0)
+#define TLS_CONN_DISABLE_TIME_CHECKS BIT(1)
+
+/**
+ * struct tls_connection_params - Parameters for TLS connection
+ * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER
+ * format
+ * @ca_cert_blob: ca_cert as inlined data or %NULL if not used
+ * @ca_cert_blob_len: ca_cert_blob length
+ * @ca_path: Path to CA certificates (OpenSSL specific)
+ * @subject_match: String to match in the subject of the peer certificate or
+ * %NULL to allow all subjects
+ * @altsubject_match: String to match in the alternative subject of the peer
+ * certificate or %NULL to allow all alternative subjects
+ * @client_cert: File or reference name for client X.509 certificate in PEM or
+ * DER format
+ * @client_cert_blob: client_cert as inlined data or %NULL if not used
+ * @client_cert_blob_len: client_cert_blob length
+ * @private_key: File or reference name for client private key in PEM or DER
+ * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY)
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used
+ * @dh_blob: dh_file as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * @engine: 1 = use engine (e.g., a smartcard) for private key operations
+ * (this is OpenSSL specific for now)
+ * @engine_id: engine id string (this is OpenSSL specific for now)
+ * @ppin: pointer to the pin variable in the configuration
+ * (this is OpenSSL specific for now)
+ * @key_id: the private key's id when using engine (this is OpenSSL
+ * specific for now)
+ * @cert_id: the certificate's id when using engine
+ * @ca_cert_id: the CA certificate's id when using engine
+ * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1)
+ * @flags: Parameter options (TLS_CONN_*)
+ *
+ * TLS connection parameters to be configured with tls_connection_set_params()
+ * and tls_global_set_params().
+ *
+ * Certificates and private key can be configured either as a reference name
+ * (file path or reference to certificate store) or by providing the same data
+ * as a pointer to the data in memory. Only one option will be used for each
+ * field.
+ */
+struct tls_connection_params {
+       const char *ca_cert;
+       const u8 *ca_cert_blob;
+       size_t ca_cert_blob_len;
+       const char *ca_path;
+       const char *subject_match;
+       const char *altsubject_match;
+       const char *client_cert;
+       const u8 *client_cert_blob;
+       size_t client_cert_blob_len;
+       const char *private_key;
+       const u8 *private_key_blob;
+       size_t private_key_blob_len;
+       const char *private_key_passwd;
+       const char *dh_file;
+       const u8 *dh_blob;
+       size_t dh_blob_len;
+       int tls_ia;
+
+       /* OpenSSL specific variables */
+       int engine;
+       const char *engine_id;
+       const char *pin;
+       const char *key_id;
+       const char *cert_id;
+       const char *ca_cert_id;
+
+       unsigned int flags;
+};
+
+
+/**
+ * tls_init - Initialize TLS library
+ * @conf: Configuration data for TLS library
+ * Returns: Context data to be used as tls_ctx in calls to other functions,
+ * or %NULL on failure.
+ *
+ * Called once during program startup and once for each RSN pre-authentication
+ * session. In other words, there can be two concurrent TLS contexts. If global
+ * library initialization is needed (i.e., one that is shared between both
+ * authentication types), the TLS library wrapper should maintain a reference
+ * counter and do global initialization only when moving from 0 to 1 reference.
+ */
+void * tls_init(const struct tls_config *conf);
+
+/**
+ * tls_deinit - Deinitialize TLS library
+ * @tls_ctx: TLS context data from tls_init()
+ *
+ * Called once during program shutdown and once for each RSN pre-authentication
+ * session. If global library deinitialization is needed (i.e., one that is
+ * shared between both authentication types), the TLS library wrapper should
+ * maintain a reference counter and do global deinitialization only when moving
+ * from 1 to 0 references.
+ */
+void tls_deinit(void *tls_ctx);
+
+/**
+ * tls_get_errors - Process pending errors
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Number of found error, 0 if no errors detected.
+ *
+ * Process all pending TLS errors.
+ */
+int tls_get_errors(void *tls_ctx);
+
+/**
+ * tls_connection_init - Initialize a new TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Connection context data, conn for other function calls
+ */
+struct tls_connection * tls_connection_init(void *tls_ctx);
+
+/**
+ * tls_connection_deinit - Free TLS connection data
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Release all resources allocated for TLS connection.
+ */
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_established - Has the TLS connection been completed?
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if TLS connection has been completed, 0 if not.
+ */
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_shutdown - Shutdown TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Shutdown current TLS connection without releasing all resources. New
+ * connection can be started by using the same conn without having to call
+ * tls_connection_init() or setting certificates etc. again. The new
+ * connection should try to use session resumption.
+ */
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+       TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3,
+       TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2
+};
+
+/**
+ * tls_connection_set_params - Set TLS connection parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @params: Connection parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
+ * PKCS#11 engine failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key.
+ */
+int __must_check
+tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                         const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_params - Set TLS parameters for all TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @params: Global TLS parameters
+ * Returns: 0 on success, -1 on failure,
+ * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing
+ * PKCS#11 engine failure, or
+ * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the
+ * PKCS#11 engine private key.
+ */
+int __must_check tls_global_set_params(
+       void *tls_ctx, const struct tls_connection_params *params);
+
+/**
+ * tls_global_set_verify - Set global certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
+ * 2 = verify CRL for all certificates
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
+
+/**
+ * tls_connection_set_verify - Set certificate verification options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @verify_peer: 1 = verify peer certificate
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_verify(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          int verify_peer);
+
+/**
+ * tls_connection_set_ia - Set TLS/IA parameters
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @tls_ia: 1 = enable TLS/IA
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to configure TLS/IA in server mode where
+ * tls_connection_set_params() is not used.
+ */
+int __must_check tls_connection_set_ia(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      int tls_ia);
+
+/**
+ * tls_connection_get_keys - Get master key and random data from TLS connection
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_get_keys(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        struct tls_keys *keys);
+
+/**
+ * tls_connection_prf - Use TLS-PRF to derive keying material
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is optional to implement if tls_connection_get_keys() provides
+ * access to master secret and server/client random values. If these values are
+ * not exported from the TLS library, tls_connection_prf() is required so that
+ * further keying material can be derived from the master secret. If not
+ * implemented, the function will still need to be defined, but it can just
+ * return -1. Example implementation of this function is in tls_prf() function
+ * when it is called with seed set to client_random|server_random (or
+ * server_random|client_random).
+ */
+int __must_check  tls_connection_prf(void *tls_ctx,
+                                    struct tls_connection *conn,
+                                    const char *label,
+                                    int server_random_first,
+                                    u8 *out, size_t out_len);
+
+/**
+ * tls_connection_handshake - Process TLS handshake (client side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS server
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data. If the final
+ * handshake message includes application data, this is decrypted and
+ * appl_data (if not %NULL) is set to point this data. The caller is
+ * responsible for freeing appl_data.
+ *
+ * This function is used during TLS handshake. The first call is done with
+ * in_data == %NULL and the library is expected to return ClientHello packet.
+ * This packet is then send to the server and a response from server is given
+ * to TLS library by calling this function again with in_data pointing to the
+ * TLS message from the server.
+ *
+ * If the TLS handshake fails, this function may return %NULL. However, if the
+ * TLS library has a TLS alert to send out, that should be returned as the
+ * output data. In this case, tls_connection_get_failed() must return failure
+ * (> 0).
+ *
+ * tls_connection_established() should return 1 once the TLS handshake has been
+ * completed successfully.
+ */
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data);
+
+/**
+ * tls_connection_server_handshake - Process TLS handshake (server side)
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Input data from TLS peer
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * Returns: Output data, %NULL on failure
+ *
+ * The caller is responsible for freeing the returned output data.
+ */
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data);
+
+/**
+ * tls_connection_encrypt - Encrypt data into TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Plaintext data to be encrypted
+ * Returns: Encrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel. The caller is responsible for freeing the
+ * returned output data.
+ */
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data);
+
+/**
+ * tls_connection_decrypt - Decrypt data from TLS tunnel
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @in_data: Encrypted TLS data
+ * Returns: Decrypted TLS data or %NULL on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel. The caller is responsible for
+ * freeing the returned output data.
+ */
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data);
+
+/**
+ * tls_connection_resumed - Was session resumption used
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn);
+
+enum {
+       TLS_CIPHER_NONE,
+       TLS_CIPHER_RC4_SHA /* 0x0005 */,
+       TLS_CIPHER_AES128_SHA /* 0x002f */,
+       TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */,
+       TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */
+};
+
+/**
+ * tls_connection_set_cipher_list - Configure acceptable cipher suites
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_set_cipher_list(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               u8 *ciphers);
+
+/**
+ * tls_get_cipher - Get current cipher name
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int __must_check tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+                               char *buf, size_t buflen);
+
+/**
+ * tls_connection_enable_workaround - Enable TLS workaround options
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to enable connection-specific workaround options for
+ * buffer SSL/TLS implementations.
+ */
+int __must_check tls_connection_enable_workaround(void *tls_ctx,
+                                                 struct tls_connection *conn);
+
+/**
+ * tls_connection_client_hello_ext - Set TLS extension for ClientHello
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_client_hello_ext(void *tls_ctx,
+                                                struct tls_connection *conn,
+                                                int ext_type, const u8 *data,
+                                                size_t data_len);
+
+/**
+ * tls_connection_get_failed - Get connection failure status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ *
+ * Returns >0 if connection has failed, 0 if not.
+ */
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_read_alerts - Get connection read alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal read (remote end reported error) has
+ * happened during this connection.
+ */
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_get_write_alerts - Get connection write alert status
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Number of times a fatal write (locally detected error) has happened
+ * during this connection.
+ */
+int tls_connection_get_write_alerts(void *tls_ctx,
+                                   struct tls_connection *conn);
+
+/**
+ * tls_connection_get_keyblock_size - Get TLS key_block size
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn);
+
+#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */
+/**
+ * tls_capabilities - Get supported TLS capabilities
+ * @tls_ctx: TLS context data from tls_init()
+ * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*)
+ */
+unsigned int tls_capabilities(void *tls_ctx);
+
+/**
+ * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished
+ * Returns: Encrypted TLS/IA data, %NULL on failure
+ *
+ * This function is used to send the TLS/IA end phase message, e.g., when the
+ * EAP server completes EAP-TTLSv1.
+ */
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final);
+
+/**
+ * tls_connection_ia_final_phase_finished - Has final phase been completed
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * Returns: 1 if valid FinalPhaseFinished has been received, 0 if not, or -1
+ * on failure
+ */
+int __must_check tls_connection_ia_final_phase_finished(
+       void *tls_ctx, struct tls_connection *conn);
+
+/**
+ * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret
+ * @tls_ctx: TLS context data from tls_init()
+ * @conn: Connection context data from tls_connection_init()
+ * @key: Session key material (session_key vectors with 2-octet length), or
+ * %NULL if no session key was generating in the current phase
+ * @key_len: Length of session key material
+ * Returns: 0 on success, -1 on failure
+ */
+int __must_check tls_connection_ia_permute_inner_secret(
+       void *tls_ctx, struct tls_connection *conn,
+       const u8 *key, size_t key_len);
+
+typedef int (*tls_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+int __must_check  tls_connection_set_session_ticket_cb(
+       void *tls_ctx, struct tls_connection *conn,
+       tls_session_ticket_cb cb, void *ctx);
+
+#endif /* TLS_H */
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
new file mode 100644 (file)
index 0000000..c3a7358
--- /dev/null
@@ -0,0 +1,1457 @@
+/*
+ * SSL/TLS interface functions for GnuTLS
+ * 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 <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#ifdef PKCS12_FUNCS
+#include <gnutls/pkcs12.h>
+#endif /* PKCS12_FUNCS */
+
+#ifdef CONFIG_GNUTLS_EXTRA
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+#define GNUTLS_IA
+#include <gnutls/extra.h>
+#if LIBGNUTLS_VERSION_NUMBER == 0x010302
+/* This function is not included in the current gnutls/extra.h even though it
+ * should be, so define it here as a workaround for the time being. */
+int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum);
+#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#endif /* CONFIG_GNUTLS_EXTRA */
+
+#include "common.h"
+#include "tls.h"
+
+
+#ifndef TLS_RANDOM_SIZE
+#define TLS_RANDOM_SIZE 32
+#endif
+#ifndef TLS_MASTER_SIZE
+#define TLS_MASTER_SIZE 48
+#endif
+
+
+#if LIBGNUTLS_VERSION_NUMBER < 0x010302
+/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
+ * use of internal structures to get the master_secret and
+ * {server,client}_random.
+ */
+#define GNUTLS_INTERNAL_STRUCTURE_HACK
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
+
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+/*
+ * It looks like gnutls does not provide access to client/server_random and
+ * master_key. This is somewhat unfortunate since these are needed for key
+ * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
+ * hack that copies the gnutls_session_int definition from gnutls_int.h so that
+ * we can get the needed information.
+ */
+
+typedef u8 uint8;
+typedef unsigned char opaque;
+typedef struct {
+    uint8 suite[2];
+} cipher_suite_st;
+
+typedef struct {
+       gnutls_connection_end_t entity;
+       gnutls_kx_algorithm_t kx_algorithm;
+       gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
+       gnutls_mac_algorithm_t read_mac_algorithm;
+       gnutls_compression_method_t read_compression_algorithm;
+       gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
+       gnutls_mac_algorithm_t write_mac_algorithm;
+       gnutls_compression_method_t write_compression_algorithm;
+       cipher_suite_st current_cipher_suite;
+       opaque master_secret[TLS_MASTER_SIZE];
+       opaque client_random[TLS_RANDOM_SIZE];
+       opaque server_random[TLS_RANDOM_SIZE];
+       /* followed by stuff we are not interested in */
+} security_parameters_st;
+
+struct gnutls_session_int {
+       security_parameters_st security_parameters;
+       /* followed by things we are not interested in */
+};
+#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
+
+static int tls_gnutls_ref_count = 0;
+
+struct tls_global {
+       /* Data for session resumption */
+       void *session_data;
+       size_t session_data_size;
+
+       int server;
+
+       int params_set;
+       gnutls_certificate_credentials_t xcred;
+};
+
+struct tls_connection {
+       gnutls_session session;
+       char *subject_match, *altsubject_match;
+       int read_alerts, write_alerts, failed;
+
+       u8 *pre_shared_secret;
+       size_t pre_shared_secret_len;
+       int established;
+       int verify_peer;
+
+       struct wpabuf *push_buf;
+       struct wpabuf *pull_buf;
+       const u8 *pull_buf_offset;
+
+       int params_set;
+       gnutls_certificate_credentials_t xcred;
+
+       int tls_ia;
+       int final_phase_finished;
+
+#ifdef GNUTLS_IA
+       gnutls_ia_server_credentials_t iacred_srv;
+       gnutls_ia_client_credentials_t iacred_cli;
+
+       /* Session keys generated in the current phase for inner secret
+        * permutation before generating/verifying PhaseFinished. */
+       u8 *session_keys;
+       size_t session_keys_len;
+
+       u8 inner_secret[TLS_MASTER_SIZE];
+#endif /* GNUTLS_IA */
+};
+
+
+static void tls_log_func(int level, const char *msg)
+{
+       char *s, *pos;
+       if (level == 6 || level == 7) {
+               /* These levels seem to be mostly I/O debug and msg dumps */
+               return;
+       }
+
+       s = os_strdup(msg);
+       if (s == NULL)
+               return;
+
+       pos = s;
+       while (*pos != '\0') {
+               if (*pos == '\n') {
+                       *pos = '\0';
+                       break;
+               }
+               pos++;
+       }
+       wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG,
+                  "gnutls<%d> %s", level, s);
+       os_free(s);
+}
+
+
+extern int wpa_debug_show_keys;
+
+void * tls_init(const struct tls_config *conf)
+{
+       struct tls_global *global;
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+       /* Because of the horrible hack to get master_secret and client/server
+        * random, we need to make sure that the gnutls version is something
+        * that is expected to have same structure definition for the session
+        * data.. */
+       const char *ver;
+       const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
+                                "1.3.2",
+                                NULL };
+       int i;
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+       global = os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+
+       if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) {
+               os_free(global);
+               return NULL;
+       }
+       tls_gnutls_ref_count++;
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+       ver = gnutls_check_version(NULL);
+       if (ver == NULL) {
+               tls_deinit(global);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
+       for (i = 0; ok_ver[i]; i++) {
+               if (strcmp(ok_ver[i], ver) == 0)
+                       break;
+       }
+       if (ok_ver[i] == NULL) {
+               wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
+                          "to be tested and enabled in tls_gnutls.c", ver);
+               tls_deinit(global);
+               return NULL;
+       }
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+       gnutls_global_set_log_function(tls_log_func);
+       if (wpa_debug_show_keys)
+               gnutls_global_set_log_level(11);
+       return global;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+       struct tls_global *global = ssl_ctx;
+       if (global) {
+               if (global->params_set)
+                       gnutls_certificate_free_credentials(global->xcred);
+               os_free(global->session_data);
+               os_free(global);
+       }
+
+       tls_gnutls_ref_count--;
+       if (tls_gnutls_ref_count == 0)
+               gnutls_global_deinit();
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+       return 0;
+}
+
+
+static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
+                            size_t len)
+{
+       struct tls_connection *conn = (struct tls_connection *) ptr;
+       const u8 *end;
+       if (conn->pull_buf == NULL) {
+               errno = EWOULDBLOCK;
+               return -1;
+       }
+
+       end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf);
+       if ((size_t) (end - conn->pull_buf_offset) < len)
+               len = end - conn->pull_buf_offset;
+       os_memcpy(buf, conn->pull_buf_offset, len);
+       conn->pull_buf_offset += len;
+       if (conn->pull_buf_offset == end) {
+               wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
+               wpabuf_free(conn->pull_buf);
+               conn->pull_buf = NULL;
+               conn->pull_buf_offset = NULL;
+       } else {
+               wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+                          __func__,
+                          (unsigned long) (end - conn->pull_buf_offset));
+       }
+       return len;
+}
+
+
+static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
+                            size_t len)
+{
+       struct tls_connection *conn = (struct tls_connection *) ptr;
+
+       if (wpabuf_resize(&conn->push_buf, len) < 0) {
+               errno = ENOMEM;
+               return -1;
+       }
+       wpabuf_put_data(conn->push_buf, buf, len);
+
+       return len;
+}
+
+
+static int tls_gnutls_init_session(struct tls_global *global,
+                                  struct tls_connection *conn)
+{
+       const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
+       const int protos[2] = { GNUTLS_TLS1, 0 };
+       int ret;
+
+       ret = gnutls_init(&conn->session,
+                         global->server ? GNUTLS_SERVER : GNUTLS_CLIENT);
+       if (ret < 0) {
+               wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS "
+                          "connection: %s", gnutls_strerror(ret));
+               return -1;
+       }
+
+       ret = gnutls_set_default_priority(conn->session);
+       if (ret < 0)
+               goto fail;
+
+       ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
+       if (ret < 0)
+               goto fail;
+
+       ret = gnutls_protocol_set_priority(conn->session, protos);
+       if (ret < 0)
+               goto fail;
+
+       gnutls_transport_set_pull_function(conn->session, tls_pull_func);
+       gnutls_transport_set_push_function(conn->session, tls_push_func);
+       gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
+
+       return 0;
+
+fail:
+       wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s",
+                  gnutls_strerror(ret));
+       gnutls_deinit(conn->session);
+       return -1;
+}
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+       struct tls_global *global = ssl_ctx;
+       struct tls_connection *conn;
+       int ret;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+       if (tls_gnutls_init_session(global, conn)) {
+               os_free(conn);
+               return NULL;
+       }
+
+       if (global->params_set) {
+               ret = gnutls_credentials_set(conn->session,
+                                            GNUTLS_CRD_CERTIFICATE,
+                                            global->xcred);
+               if (ret < 0) {
+                       wpa_printf(MSG_INFO, "Failed to configure "
+                                  "credentials: %s", gnutls_strerror(ret));
+                       os_free(conn);
+                       return NULL;
+               }
+       }
+
+       if (gnutls_certificate_allocate_credentials(&conn->xcred)) {
+               os_free(conn);
+               return NULL;
+       }
+
+       return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return;
+
+#ifdef GNUTLS_IA
+       if (conn->iacred_srv)
+               gnutls_ia_free_server_credentials(conn->iacred_srv);
+       if (conn->iacred_cli)
+               gnutls_ia_free_client_credentials(conn->iacred_cli);
+       if (conn->session_keys) {
+               os_memset(conn->session_keys, 0, conn->session_keys_len);
+               os_free(conn->session_keys);
+       }
+#endif /* GNUTLS_IA */
+
+       gnutls_certificate_free_credentials(conn->xcred);
+       gnutls_deinit(conn->session);
+       os_free(conn->pre_shared_secret);
+       os_free(conn->subject_match);
+       os_free(conn->altsubject_match);
+       wpabuf_free(conn->push_buf);
+       wpabuf_free(conn->pull_buf);
+       os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+       return conn ? conn->established : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+       struct tls_global *global = ssl_ctx;
+       int ret;
+
+       if (conn == NULL)
+               return -1;
+
+       /* Shutdown previous TLS connection without notifying the peer
+        * because the connection was already terminated in practice
+        * and "close notify" shutdown alert would confuse AS. */
+       gnutls_bye(conn->session, GNUTLS_SHUT_RDWR);
+       wpabuf_free(conn->push_buf);
+       conn->push_buf = NULL;
+       conn->established = 0;
+       conn->final_phase_finished = 0;
+#ifdef GNUTLS_IA
+       if (conn->session_keys) {
+               os_memset(conn->session_keys, 0, conn->session_keys_len);
+               os_free(conn->session_keys);
+       }
+       conn->session_keys_len = 0;
+#endif /* GNUTLS_IA */
+
+       gnutls_deinit(conn->session);
+       if (tls_gnutls_init_session(global, conn)) {
+               wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session "
+                          "for session resumption use");
+               return -1;
+       }
+
+       ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+                                    conn->params_set ? conn->xcred :
+                                    global->xcred);
+       if (ret < 0) {
+               wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials "
+                          "for session resumption: %s", gnutls_strerror(ret));
+               return -1;
+       }
+
+       if (global->session_data) {
+               ret = gnutls_session_set_data(conn->session,
+                                             global->session_data,
+                                             global->session_data_size);
+               if (ret < 0) {
+                       wpa_printf(MSG_INFO, "GnuTLS: Failed to set session "
+                                  "data: %s", gnutls_strerror(ret));
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+#if 0
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+       GENERAL_NAME *gen;
+       char *field, *tmp;
+       void *ext;
+       int i, found = 0;
+       size_t len;
+
+       ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+       for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+               gen = sk_GENERAL_NAME_value(ext, i);
+               switch (gen->type) {
+               case GEN_EMAIL:
+                       field = "EMAIL";
+                       break;
+               case GEN_DNS:
+                       field = "DNS";
+                       break;
+               case GEN_URI:
+                       field = "URI";
+                       break;
+               default:
+                       field = NULL;
+                       wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
+                                  "unsupported type=%d", gen->type);
+                       break;
+               }
+
+               if (!field)
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
+                          field, gen->d.ia5->data);
+               len = os_strlen(field) + 1 +
+                       strlen((char *) gen->d.ia5->data) + 1;
+               tmp = os_malloc(len);
+               if (tmp == NULL)
+                       continue;
+               snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
+               if (strstr(tmp, match))
+                       found++;
+               os_free(tmp);
+       }
+
+       return found;
+}
+#endif
+
+
+#if 0
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+       char buf[256];
+       X509 *err_cert;
+       int err, depth;
+       SSL *ssl;
+       struct tls_connection *conn;
+       char *match, *altmatch;
+
+       err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+       err = X509_STORE_CTX_get_error(x509_ctx);
+       depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+       ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+                                        SSL_get_ex_data_X509_STORE_CTX_idx());
+       X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+       conn = SSL_get_app_data(ssl);
+       match = conn ? conn->subject_match : NULL;
+       altmatch = conn ? conn->altsubject_match : NULL;
+
+       if (!preverify_ok) {
+               wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+                          " error %d (%s) depth %d for '%s'", err,
+                          X509_verify_cert_error_string(err), depth, buf);
+       } else {
+               wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
+                          "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
+                          preverify_ok, err,
+                          X509_verify_cert_error_string(err), depth, buf);
+               if (depth == 0 && match && strstr(buf, match) == NULL) {
+                       wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+                                  "match with '%s'", buf, match);
+                       preverify_ok = 0;
+               } else if (depth == 0 && altmatch &&
+                          !tls_match_altsubject(err_cert, altmatch)) {
+                       wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+                                  "'%s' not found", altmatch);
+                       preverify_ok = 0;
+               }
+       }
+
+       return preverify_ok;
+}
+#endif
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+       int ret;
+
+       if (conn == NULL || params == NULL)
+               return -1;
+
+       os_free(conn->subject_match);
+       conn->subject_match = NULL;
+       if (params->subject_match) {
+               conn->subject_match = os_strdup(params->subject_match);
+               if (conn->subject_match == NULL)
+                       return -1;
+       }
+
+       os_free(conn->altsubject_match);
+       conn->altsubject_match = NULL;
+       if (params->altsubject_match) {
+               conn->altsubject_match = os_strdup(params->altsubject_match);
+               if (conn->altsubject_match == NULL)
+                       return -1;
+       }
+
+       /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 
+        * to force peer validation(?) */
+
+       if (params->ca_cert) {
+               conn->verify_peer = 1;
+               ret = gnutls_certificate_set_x509_trust_file(
+                       conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
+                                  "in PEM format: %s", params->ca_cert,
+                                  gnutls_strerror(ret));
+                       ret = gnutls_certificate_set_x509_trust_file(
+                               conn->xcred, params->ca_cert,
+                               GNUTLS_X509_FMT_DER);
+                       if (ret < 0) {
+                               wpa_printf(MSG_DEBUG, "Failed to read CA cert "
+                                          "'%s' in DER format: %s",
+                                          params->ca_cert,
+                                          gnutls_strerror(ret));
+                               return -1;
+                       }
+               }
+
+               if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+                       gnutls_certificate_set_verify_flags(
+                               conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+               }
+
+               if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+                       gnutls_certificate_set_verify_flags(
+                               conn->xcred,
+                               GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+               }
+       }
+
+       if (params->client_cert && params->private_key) {
+               /* TODO: private_key_passwd? */
+               ret = gnutls_certificate_set_x509_key_file(
+                       conn->xcred, params->client_cert, params->private_key,
+                       GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+                                  "in PEM format: %s", gnutls_strerror(ret));
+                       ret = gnutls_certificate_set_x509_key_file(
+                               conn->xcred, params->client_cert,
+                               params->private_key, GNUTLS_X509_FMT_DER);
+                       if (ret < 0) {
+                               wpa_printf(MSG_DEBUG, "Failed to read client "
+                                          "cert/key in DER format: %s",
+                                          gnutls_strerror(ret));
+                               return ret;
+                       }
+               }
+       } else if (params->private_key) {
+               int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+               /* Try to load in PKCS#12 format */
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+               ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+                       conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
+                       params->private_key_passwd);
+               if (ret != 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+                                  "PKCS#12 format: %s", gnutls_strerror(ret));
+                       return -1;
+               } else
+                       pkcs12_ok = 1;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#endif /* PKCS12_FUNCS */
+
+               if (!pkcs12_ok) {
+                       wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+                                  "included");
+                       return -1;
+               }
+       }
+
+       conn->tls_ia = params->tls_ia;
+       conn->params_set = 1;
+
+       ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
+                                    conn->xcred);
+       if (ret < 0) {
+               wpa_printf(MSG_INFO, "Failed to configure credentials: %s",
+                          gnutls_strerror(ret));
+       }
+
+#ifdef GNUTLS_IA
+       if (conn->iacred_cli)
+               gnutls_ia_free_client_credentials(conn->iacred_cli);
+
+       ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
+                          gnutls_strerror(ret));
+               return -1;
+       }
+
+       ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
+                                    conn->iacred_cli);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
+                          gnutls_strerror(ret));
+               gnutls_ia_free_client_credentials(conn->iacred_cli);
+               conn->iacred_cli = NULL;
+               return -1;
+       }
+#endif /* GNUTLS_IE */
+
+       return ret;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+       struct tls_global *global = tls_ctx;
+       int ret;
+
+       /* Currently, global parameters are only set when running in server
+        * mode. */
+       global->server = 1;
+
+       if (global->params_set) {
+               gnutls_certificate_free_credentials(global->xcred);
+               global->params_set = 0;
+       }
+
+       ret = gnutls_certificate_allocate_credentials(&global->xcred);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "Failed to allocate global credentials "
+                          "%s", gnutls_strerror(ret));
+               return -1;
+       }
+
+       if (params->ca_cert) {
+               ret = gnutls_certificate_set_x509_trust_file(
+                       global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
+                                  "in PEM format: %s", params->ca_cert,
+                                  gnutls_strerror(ret));
+                       ret = gnutls_certificate_set_x509_trust_file(
+                               global->xcred, params->ca_cert,
+                               GNUTLS_X509_FMT_DER);
+                       if (ret < 0) {
+                               wpa_printf(MSG_DEBUG, "Failed to read CA cert "
+                                          "'%s' in DER format: %s",
+                                          params->ca_cert,
+                                          gnutls_strerror(ret));
+                               goto fail;
+                       }
+               }
+
+               if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
+                       gnutls_certificate_set_verify_flags(
+                               global->xcred,
+                               GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
+               }
+
+               if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+                       gnutls_certificate_set_verify_flags(
+                               global->xcred,
+                               GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
+               }
+       }
+
+       if (params->client_cert && params->private_key) {
+               /* TODO: private_key_passwd? */
+               ret = gnutls_certificate_set_x509_key_file(
+                       global->xcred, params->client_cert,
+                       params->private_key, GNUTLS_X509_FMT_PEM);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+                                  "in PEM format: %s", gnutls_strerror(ret));
+                       ret = gnutls_certificate_set_x509_key_file(
+                               global->xcred, params->client_cert,
+                               params->private_key, GNUTLS_X509_FMT_DER);
+                       if (ret < 0) {
+                               wpa_printf(MSG_DEBUG, "Failed to read client "
+                                          "cert/key in DER format: %s",
+                                          gnutls_strerror(ret));
+                               goto fail;
+                       }
+               }
+       } else if (params->private_key) {
+               int pkcs12_ok = 0;
+#ifdef PKCS12_FUNCS
+               /* Try to load in PKCS#12 format */
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+               ret = gnutls_certificate_set_x509_simple_pkcs12_file(
+                       global->xcred, params->private_key,
+                       GNUTLS_X509_FMT_DER, params->private_key_passwd);
+               if (ret != 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+                                  "PKCS#12 format: %s", gnutls_strerror(ret));
+                       goto fail;
+               } else
+                       pkcs12_ok = 1;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#endif /* PKCS12_FUNCS */
+
+               if (!pkcs12_ok) {
+                       wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not "
+                                  "included");
+                       goto fail;
+               }
+       }
+
+       global->params_set = 1;
+
+       return 0;
+
+fail:
+       gnutls_certificate_free_credentials(global->xcred);
+       return -1;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+       /* TODO */
+       return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+       if (conn == NULL || conn->session == NULL)
+               return -1;
+
+       conn->verify_peer = verify_peer;
+       gnutls_certificate_server_set_request(conn->session,
+                                             verify_peer ? GNUTLS_CERT_REQUIRE
+                                             : GNUTLS_CERT_REQUEST);
+
+       return 0;
+}
+
+
+int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+       security_parameters_st *sec;
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+       if (conn == NULL || conn->session == NULL || keys == NULL)
+               return -1;
+
+       os_memset(keys, 0, sizeof(*keys));
+
+#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
+       sec = &conn->session->security_parameters;
+       keys->master_key = sec->master_secret;
+       keys->master_key_len = TLS_MASTER_SIZE;
+       keys->client_random = sec->client_random;
+       keys->server_random = sec->server_random;
+#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+       keys->client_random =
+               (u8 *) gnutls_session_get_client_random(conn->session);
+       keys->server_random =
+               (u8 *) gnutls_session_get_server_random(conn->session);
+       /* No access to master_secret */
+#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+
+#ifdef GNUTLS_IA
+       gnutls_ia_extract_inner_secret(conn->session,
+                                      (char *) conn->inner_secret);
+       keys->inner_secret = conn->inner_secret;
+       keys->inner_secret_len = TLS_MASTER_SIZE;
+#endif /* GNUTLS_IA */
+
+       keys->client_random_len = TLS_RANDOM_SIZE;
+       keys->server_random_len = TLS_RANDOM_SIZE;
+
+       return 0;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
+       if (conn == NULL || conn->session == NULL)
+               return -1;
+
+       return gnutls_prf(conn->session, os_strlen(label), label,
+                         server_random_first, 0, NULL, out_len, (char *) out);
+#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+       return -1;
+#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+}
+
+
+static int tls_connection_verify_peer(struct tls_connection *conn,
+                                     gnutls_alert_description_t *err)
+{
+       unsigned int status, num_certs, i;
+       struct os_time now;
+       const gnutls_datum_t *certs;
+       gnutls_x509_crt_t cert;
+
+       if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
+               wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
+                          "certificate chain");
+               *err = GNUTLS_A_INTERNAL_ERROR;
+               return -1;
+       }
+
+       if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
+               wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
+               if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
+                       wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
+                                  "algorithm");
+                       *err = GNUTLS_A_INSUFFICIENT_SECURITY;
+               }
+               if (status & GNUTLS_CERT_NOT_ACTIVATED) {
+                       wpa_printf(MSG_INFO, "TLS: Certificate not yet "
+                                  "activated");
+                       *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+               }
+               if (status & GNUTLS_CERT_EXPIRED) {
+                       wpa_printf(MSG_INFO, "TLS: Certificate expired");
+                       *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+               }
+               return -1;
+       }
+
+       if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
+               wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
+                          "known issuer");
+               *err = GNUTLS_A_UNKNOWN_CA;
+               return -1;
+       }
+
+       if (status & GNUTLS_CERT_REVOKED) {
+               wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
+               *err = GNUTLS_A_CERTIFICATE_REVOKED;
+               return -1;
+       }
+
+       os_get_time(&now);
+
+       certs = gnutls_certificate_get_peers(conn->session, &num_certs);
+       if (certs == NULL) {
+               wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
+                          "received");
+               *err = GNUTLS_A_UNKNOWN_CA;
+               return -1;
+       }
+
+       for (i = 0; i < num_certs; i++) {
+               char *buf;
+               size_t len;
+               if (gnutls_x509_crt_init(&cert) < 0) {
+                       wpa_printf(MSG_INFO, "TLS: Certificate initialization "
+                                  "failed");
+                       *err = GNUTLS_A_BAD_CERTIFICATE;
+                       return -1;
+               }
+
+               if (gnutls_x509_crt_import(cert, &certs[i],
+                                          GNUTLS_X509_FMT_DER) < 0) {
+                       wpa_printf(MSG_INFO, "TLS: Could not parse peer "
+                                  "certificate %d/%d", i + 1, num_certs);
+                       gnutls_x509_crt_deinit(cert);
+                       *err = GNUTLS_A_BAD_CERTIFICATE;
+                       return -1;
+               }
+
+               gnutls_x509_crt_get_dn(cert, NULL, &len);
+               len++;
+               buf = os_malloc(len + 1);
+               if (buf) {
+                       buf[0] = buf[len] = '\0';
+                       gnutls_x509_crt_get_dn(cert, buf, &len);
+               }
+               wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
+                          i + 1, num_certs, buf);
+
+               if (i == 0) {
+                       /* TODO: validate subject_match and altsubject_match */
+               }
+
+               os_free(buf);
+
+               if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+                   gnutls_x509_crt_get_activation_time(cert) > now.sec) {
+                       wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
+                                  "not valid at this time",
+                                  i + 1, num_certs);
+                       gnutls_x509_crt_deinit(cert);
+                       *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+                       return -1;
+               }
+
+               gnutls_x509_crt_deinit(cert);
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn)
+{
+       int res;
+       struct wpabuf *ad;
+       wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data");
+       ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3);
+       if (ad == NULL)
+               return NULL;
+
+       res = gnutls_record_recv(conn->session, wpabuf_mhead(ad),
+                                wpabuf_size(ad));
+       wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+                          "(%s)", __func__, (int) res,
+                          gnutls_strerror(res));
+               wpabuf_free(ad);
+               return NULL;
+       }
+
+       wpabuf_put(ad, res);
+       wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data",
+                  res);
+       return ad;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data)
+{
+       struct tls_global *global = tls_ctx;
+       struct wpabuf *out_data;
+       int ret;
+
+       if (appl_data)
+               *appl_data = NULL;
+
+       if (in_data && wpabuf_len(in_data) > 0) {
+               if (conn->pull_buf) {
+                       wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+                                  "pull_buf", __func__,
+                                  (unsigned long) wpabuf_len(conn->pull_buf));
+                       wpabuf_free(conn->pull_buf);
+               }
+               conn->pull_buf = wpabuf_dup(in_data);
+               if (conn->pull_buf == NULL)
+                       return NULL;
+               conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+       }
+
+       ret = gnutls_handshake(conn->session);
+       if (ret < 0) {
+               switch (ret) {
+               case GNUTLS_E_AGAIN:
+                       if (global->server && conn->established &&
+                           conn->push_buf == NULL) {
+                               /* Need to return something to trigger
+                                * completion of EAP-TLS. */
+                               conn->push_buf = wpabuf_alloc(0);
+                       }
+                       break;
+               case GNUTLS_E_FATAL_ALERT_RECEIVED:
+                       wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
+                                  __func__, gnutls_alert_get_name(
+                                          gnutls_alert_get(conn->session)));
+                       conn->read_alerts++;
+                       /* continue */
+               default:
+                       wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
+                                  "-> %s", __func__, gnutls_strerror(ret));
+                       conn->failed++;
+               }
+       } else {
+               size_t size;
+               gnutls_alert_description_t err;
+
+               if (conn->verify_peer &&
+                   tls_connection_verify_peer(conn, &err)) {
+                       wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
+                                  "failed validation");
+                       conn->failed++;
+                       gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
+                       goto out;
+               }
+
+#ifdef CONFIG_GNUTLS_EXTRA
+               if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) {
+                       wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation");
+                       conn->failed++;
+                       return NULL;
+               }
+#endif /* CONFIG_GNUTLS_EXTRA */
+
+               if (conn->tls_ia)
+                       wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake");
+               else {
+                       wpa_printf(MSG_DEBUG, "TLS: Handshake completed "
+                                  "successfully");
+               }
+               conn->established = 1;
+               if (conn->push_buf == NULL) {
+                       /* Need to return something to get final TLS ACK. */
+                       conn->push_buf = wpabuf_alloc(0);
+               }
+
+               gnutls_session_get_data(conn->session, NULL, &size);
+               if (global->session_data == NULL ||
+                   global->session_data_size < size) {
+                       os_free(global->session_data);
+                       global->session_data = os_malloc(size);
+               }
+               if (global->session_data) {
+                       global->session_data_size = size;
+                       gnutls_session_get_data(conn->session,
+                                               global->session_data,
+                                               &global->session_data_size);
+               }
+
+               if (conn->pull_buf && appl_data)
+                       *appl_data = gnutls_get_appl_data(conn);
+       }
+
+out:
+       out_data = conn->push_buf;
+       conn->push_buf = NULL;
+       return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+       return tls_connection_handshake(tls_ctx, conn, in_data, appl_data);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       ssize_t res;
+       struct wpabuf *buf;
+
+#ifdef GNUTLS_IA
+       if (conn->tls_ia)
+               res = gnutls_ia_send(conn->session, wpabuf_head(in_data),
+                                    wpabuf_len(in_data));
+       else
+#endif /* GNUTLS_IA */
+       res = gnutls_record_send(conn->session, wpabuf_head(in_data),
+                                wpabuf_len(in_data));
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "%s: Encryption failed: %s",
+                          __func__, gnutls_strerror(res));
+               return NULL;
+       }
+
+       buf = conn->push_buf;
+       conn->push_buf = NULL;
+       return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       ssize_t res;
+       struct wpabuf *out;
+
+       if (conn->pull_buf) {
+               wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+                          "pull_buf", __func__,
+                          (unsigned long) wpabuf_len(conn->pull_buf));
+               wpabuf_free(conn->pull_buf);
+       }
+       conn->pull_buf = wpabuf_dup(in_data);
+       if (conn->pull_buf == NULL)
+               return NULL;
+       conn->pull_buf_offset = wpabuf_head(conn->pull_buf);
+
+       /*
+        * Even though we try to disable TLS compression, it is possible that
+        * this cannot be done with all TLS libraries. Add extra buffer space
+        * to handle the possibility of the decrypted data being longer than
+        * input data.
+        */
+       out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+       if (out == NULL)
+               return NULL;
+
+#ifdef GNUTLS_IA
+       if (conn->tls_ia) {
+               res = gnutls_ia_recv(conn->session, wpabuf_mhead(out),
+                                    wpabuf_size(out));
+               if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED ||
+                   res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) {
+                       int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED;
+                       wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished",
+                                  __func__, final ? "Final" : "Intermediate");
+
+                       res = gnutls_ia_permute_inner_secret(
+                               conn->session, conn->session_keys_len,
+                               (char *) conn->session_keys);
+                       if (conn->session_keys) {
+                               os_memset(conn->session_keys, 0,
+                                         conn->session_keys_len);
+                               os_free(conn->session_keys);
+                       }
+                       conn->session_keys = NULL;
+                       conn->session_keys_len = 0;
+                       if (res) {
+                               wpa_printf(MSG_DEBUG, "%s: Failed to permute "
+                                          "inner secret: %s",
+                                          __func__, gnutls_strerror(res));
+                               wpabuf_free(out);
+                               return NULL;
+                       }
+
+                       res = gnutls_ia_verify_endphase(conn->session,
+                                                       wpabuf_head(out));
+                       if (res == 0) {
+                               wpa_printf(MSG_DEBUG, "%s: Correct endphase "
+                                          "checksum", __func__);
+                       } else {
+                               wpa_printf(MSG_INFO, "%s: Endphase "
+                                          "verification failed: %s",
+                                          __func__, gnutls_strerror(res));
+                               wpabuf_free(out);
+                               return NULL;
+                       }
+
+                       if (final)
+                               conn->final_phase_finished = 1;
+
+                       return out;
+               }
+
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d "
+                                  "(%s)", __func__, (int) res,
+                                  gnutls_strerror(res));
+                       wpabuf_free(out);
+                       return NULL;
+               }
+               wpabuf_put(out, res);
+               return out;
+       }
+#endif /* GNUTLS_IA */
+
+       res = gnutls_record_recv(conn->session, wpabuf_mhead(out),
+                                wpabuf_size(out));
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d "
+                          "(%s)", __func__, (int) res, gnutls_strerror(res));
+               wpabuf_free(out);
+               return NULL;
+       }
+       wpabuf_put(out, res);
+
+       return out;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return 0;
+       return gnutls_session_is_resumed(conn->session);
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+       /* TODO */
+       return -1;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       /* TODO */
+       buf[0] = '\0';
+       return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+                                    struct tls_connection *conn)
+{
+       gnutls_record_disable_padding(conn->session);
+       return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+       /* TODO */
+       return -1;
+}
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->write_alerts;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       /* TODO */
+       return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       unsigned int capa = 0;
+
+#ifdef GNUTLS_IA
+       capa |= TLS_CAPABILITY_IA;
+#endif /* GNUTLS_IA */
+
+       return capa;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+                         int tls_ia)
+{
+#ifdef GNUTLS_IA
+       int ret;
+
+       if (conn == NULL)
+               return -1;
+
+       conn->tls_ia = tls_ia;
+       if (!tls_ia)
+               return 0;
+
+       ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s",
+                          gnutls_strerror(ret));
+               return -1;
+       }
+
+       ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA,
+                                    conn->iacred_srv);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s",
+                          gnutls_strerror(ret));
+               gnutls_ia_free_server_credentials(conn->iacred_srv);
+               conn->iacred_srv = NULL;
+               return -1;
+       }
+
+       return 0;
+#else /* GNUTLS_IA */
+       return -1;
+#endif /* GNUTLS_IA */
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final)
+{
+#ifdef GNUTLS_IA
+       int ret;
+       struct wpabuf *buf;
+
+       if (conn == NULL || conn->session == NULL || !conn->tls_ia)
+               return NULL;
+
+       ret = gnutls_ia_permute_inner_secret(conn->session,
+                                            conn->session_keys_len,
+                                            (char *) conn->session_keys);
+       if (conn->session_keys) {
+               os_memset(conn->session_keys, 0, conn->session_keys_len);
+               os_free(conn->session_keys);
+       }
+       conn->session_keys = NULL;
+       conn->session_keys_len = 0;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s",
+                          __func__, gnutls_strerror(ret));
+               return NULL;
+       }
+
+       ret = gnutls_ia_endphase_send(conn->session, final);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s",
+                          __func__, gnutls_strerror(ret));
+               return NULL;
+       }
+
+       buf = conn->push_buf;
+       conn->push_buf = NULL;
+       return buf;
+#else /* GNUTLS_IA */
+       return NULL;
+#endif /* GNUTLS_IA */
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+                                          struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+
+       return conn->final_phase_finished;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          const u8 *key, size_t key_len)
+{
+#ifdef GNUTLS_IA
+       if (conn == NULL || !conn->tls_ia)
+               return -1;
+
+       if (conn->session_keys) {
+               os_memset(conn->session_keys, 0, conn->session_keys_len);
+               os_free(conn->session_keys);
+       }
+       conn->session_keys_len = 0;
+
+       if (key) {
+               conn->session_keys = os_malloc(key_len);
+               if (conn->session_keys == NULL)
+                       return -1;
+               os_memcpy(conn->session_keys, key, key_len);
+               conn->session_keys_len = key_len;
+       } else {
+               conn->session_keys = NULL;
+               conn->session_keys_len = 0;
+       }
+
+       return 0;
+#else /* GNUTLS_IA */
+       return -1;
+#endif /* GNUTLS_IA */
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        tls_session_ticket_cb cb, void *ctx)
+{
+       return -1;
+}
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
new file mode 100644 (file)
index 0000000..64124d8
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * TLS interface functions and an internal TLS implementation
+ * 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.
+ *
+ * This file interface functions for hostapd/wpa_supplicant to use the
+ * integrated TLSv1 implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "tls.h"
+#include "tls/tlsv1_client.h"
+#include "tls/tlsv1_server.h"
+
+
+static int tls_ref_count = 0;
+
+struct tls_global {
+       int server;
+       struct tlsv1_credentials *server_cred;
+       int check_crl;
+};
+
+struct tls_connection {
+       struct tlsv1_client *client;
+       struct tlsv1_server *server;
+};
+
+
+void * tls_init(const struct tls_config *conf)
+{
+       struct tls_global *global;
+
+       if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+               if (tlsv1_client_global_init())
+                       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+               if (tlsv1_server_global_init())
+                       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       }
+       tls_ref_count++;
+
+       global = os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+
+       return global;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+       struct tls_global *global = ssl_ctx;
+       tls_ref_count--;
+       if (tls_ref_count == 0) {
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+               tlsv1_client_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+               tlsv1_cred_free(global->server_cred);
+               tlsv1_server_global_deinit();
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       }
+       os_free(global);
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+       return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+       struct tls_connection *conn;
+       struct tls_global *global = tls_ctx;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (!global->server) {
+               conn->client = tlsv1_client_init();
+               if (conn->client == NULL) {
+                       os_free(conn);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (global->server) {
+               conn->server = tlsv1_server_init(global->server_cred);
+               if (conn->server == NULL) {
+                       os_free(conn);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+
+       return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               tlsv1_client_deinit(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               tlsv1_server_deinit(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_established(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_established(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return 0;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_shutdown(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_shutdown(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       struct tlsv1_credentials *cred;
+
+       if (conn->client == NULL)
+               return -1;
+
+       cred = tlsv1_cred_alloc();
+       if (cred == NULL)
+               return -1;
+
+       if (tlsv1_set_ca_cert(cred, params->ca_cert,
+                             params->ca_cert_blob, params->ca_cert_blob_len,
+                             params->ca_path)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+                          "certificates");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_set_cert(cred, params->client_cert,
+                          params->client_cert_blob,
+                          params->client_cert_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure client "
+                          "certificate");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_set_private_key(cred, params->private_key,
+                                 params->private_key_passwd,
+                                 params->private_key_blob,
+                                 params->private_key_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+                              params->dh_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       if (tlsv1_client_set_cred(conn->client, cred) < 0) {
+               tlsv1_cred_free(cred);
+               return -1;
+       }
+
+       return 0;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+       return -1;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       struct tls_global *global = tls_ctx;
+       struct tlsv1_credentials *cred;
+
+       /* Currently, global parameters are only set when running in server
+        * mode. */
+       global->server = 1;
+       tlsv1_cred_free(global->server_cred);
+       global->server_cred = cred = tlsv1_cred_alloc();
+       if (cred == NULL)
+               return -1;
+
+       if (tlsv1_set_ca_cert(cred, params->ca_cert, params->ca_cert_blob,
+                             params->ca_cert_blob_len, params->ca_path)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure trusted CA "
+                          "certificates");
+               return -1;
+       }
+
+       if (tlsv1_set_cert(cred, params->client_cert, params->client_cert_blob,
+                          params->client_cert_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to configure server "
+                          "certificate");
+               return -1;
+       }
+
+       if (tlsv1_set_private_key(cred, params->private_key,
+                                 params->private_key_passwd,
+                                 params->private_key_blob,
+                                 params->private_key_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+               return -1;
+       }
+
+       if (tlsv1_set_dhparams(cred, params->dh_file, params->dh_blob,
+                              params->dh_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load DH parameters");
+               return -1;
+       }
+
+       return 0;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+       struct tls_global *global = tls_ctx;
+       global->check_crl = check_crl;
+       return 0;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_set_verify(conn->server, verify_peer);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+                         int tls_ia)
+{
+       return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_keys(conn->client, keys);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_keys(conn->server, keys);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               return tlsv1_client_prf(conn->client, label,
+                                       server_random_first,
+                                       out, out_len);
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               return tlsv1_server_prf(conn->server, label,
+                                       server_random_first,
+                                       out, out_len);
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       u8 *res, *ad;
+       size_t res_len, ad_len;
+       struct wpabuf *out;
+
+       if (conn->client == NULL)
+               return NULL;
+
+       ad = NULL;
+       res = tlsv1_client_handshake(conn->client,
+                                    in_data ? wpabuf_head(in_data) : NULL,
+                                    in_data ? wpabuf_len(in_data) : 0,
+                                    &res_len, &ad, &ad_len);
+       if (res == NULL)
+               return NULL;
+       out = wpabuf_alloc_ext_data(res, res_len);
+       if (out == NULL) {
+               os_free(res);
+               os_free(ad);
+               return NULL;
+       }
+       if (appl_data) {
+               if (ad) {
+                       *appl_data = wpabuf_alloc_ext_data(ad, ad_len);
+                       if (*appl_data == NULL)
+                               os_free(ad);
+               } else
+                       *appl_data = NULL;
+       } else
+               os_free(ad);
+
+       return out;
+#else /* CONFIG_TLS_INTERNAL_CLIENT */
+       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       u8 *res;
+       size_t res_len;
+       struct wpabuf *out;
+
+       if (conn->server == NULL)
+               return NULL;
+
+       if (appl_data)
+               *appl_data = NULL;
+
+       res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data),
+                                    wpabuf_len(in_data), &res_len);
+       if (res == NULL && tlsv1_server_established(conn->server))
+               return wpabuf_alloc(0);
+       if (res == NULL)
+               return NULL;
+       out = wpabuf_alloc_ext_data(res, res_len);
+       if (out == NULL) {
+               os_free(res);
+               return NULL;
+       }
+
+       return out;
+#else /* CONFIG_TLS_INTERNAL_SERVER */
+       return NULL;
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               struct wpabuf *buf;
+               int res;
+               buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+               if (buf == NULL)
+                       return NULL;
+               res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data),
+                                          wpabuf_len(in_data),
+                                          wpabuf_mhead(buf),
+                                          wpabuf_size(buf));
+               if (res < 0) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+               wpabuf_put(buf, res);
+               return buf;
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_resumed(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_resumed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_set_cipher_list(conn->client, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_set_cipher_list(conn->server, ciphers);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       if (conn == NULL)
+               return -1;
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_cipher(conn->client, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_cipher(conn->server, buf, buflen);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               return tlsv1_client_hello_ext(conn->client, ext_type,
+                                             data, data_len);
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+       return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+                                   struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client)
+               return tlsv1_client_get_keyblock_size(conn->client);
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               return tlsv1_server_get_keyblock_size(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       return 0;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final)
+{
+       return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+                                          struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          const u8 *key, size_t key_len)
+{
+       return -1;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        tls_session_ticket_cb cb,
+                                        void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_CLIENT
+       if (conn->client) {
+               tlsv1_client_set_session_ticket_cb(conn->client, cb, ctx);
+               return 0;
+       }
+#endif /* CONFIG_TLS_INTERNAL_CLIENT */
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server) {
+               tlsv1_server_set_session_ticket_cb(conn->server, cb, ctx);
+               return 0;
+       }
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+       return -1;
+}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
new file mode 100644 (file)
index 0000000..0c836bb
--- /dev/null
@@ -0,0 +1,229 @@
+/*
+ * SSL/TLS interface functions for no TLS case
+ * 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 "common.h"
+#include "tls.h"
+
+void * tls_init(const struct tls_config *conf)
+{
+       return (void *) 1;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+       return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+       return NULL;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+       return -1;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+       return -1;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+       return -1;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+       return -1;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+                         int tls_ia)
+{
+       return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+       return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+       return -1;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data)
+{
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       return NULL;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+       return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+       return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+                                   struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       return 0;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final)
+{
+       return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+                                          struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          const u8 *key, size_t key_len)
+{
+       return -1;
+}
diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c
new file mode 100644 (file)
index 0000000..ad834b6
--- /dev/null
@@ -0,0 +1,680 @@
+/*
+ * SSL/TLS interface functions for NSS
+ * Copyright (c) 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 <nspr/prtypes.h>
+#include <nspr/plarenas.h>
+#include <nspr/plhash.h>
+#include <nspr/prio.h>
+#include <nspr/prclist.h>
+#include <nspr/prlock.h>
+#include <nspr/prinit.h>
+#include <nspr/prerror.h>
+#include <nspr/prmem.h>
+#include <nss/nss.h>
+#include <nss/nssilckt.h>
+#include <nss/ssl.h>
+#include <nss/pk11func.h>
+#include <nss/secerr.h>
+
+#include "common.h"
+#include "tls.h"
+
+static int tls_nss_ref_count = 0;
+
+static PRDescIdentity nss_layer_id;
+
+
+struct tls_connection {
+       PRFileDesc *fd;
+
+       int established;
+       int verify_peer;
+       u8 *push_buf, *pull_buf, *pull_buf_offset;
+       size_t push_buf_len, pull_buf_len;
+};
+
+
+static PRStatus nss_io_close(PRFileDesc *fd)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O close");
+       return PR_SUCCESS;
+}
+
+
+static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount);
+       return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount);
+       return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov,
+                            PRInt32 iov_size, PRIntervalTime timeout)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size);
+       return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
+                          PRIntn flags, PRIntervalTime timeout)
+{
+       struct tls_connection *conn = (struct tls_connection *) fd->secret;
+       u8 *end;
+
+       wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount);
+
+       if (conn->pull_buf == NULL) {
+               wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet");
+               return PR_FAILURE;
+       }
+
+       end = conn->pull_buf + conn->pull_buf_len;
+       if (end - conn->pull_buf_offset < amount)
+               amount = end - conn->pull_buf_offset;
+       os_memcpy(buf, conn->pull_buf_offset, amount);
+       conn->pull_buf_offset += amount;
+       if (conn->pull_buf_offset == end) {
+               wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
+               os_free(conn->pull_buf);
+               conn->pull_buf = conn->pull_buf_offset = NULL;
+               conn->pull_buf_len = 0;
+       } else {
+               wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
+                          __func__,
+                          (unsigned long) (end - conn->pull_buf_offset));
+       }
+       return amount;
+}
+
+
+static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
+                          PRIntn flags, PRIntervalTime timeout)
+{
+       struct tls_connection *conn = (struct tls_connection *) fd->secret;
+       u8 *nbuf;
+
+       wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
+       wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount);
+
+       nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount);
+       if (nbuf == NULL) {
+               wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the "
+                          "data to be sent");
+               return PR_FAILURE;
+       }
+       os_memcpy(nbuf + conn->push_buf_len, buf, amount);
+       conn->push_buf = nbuf;
+       conn->push_buf_len += amount;
+
+       return amount;
+}
+
+
+static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
+                              PRIntn flags, PRNetAddr *addr,
+                              PRIntervalTime timeout)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
+       return PR_FAILURE;
+}
+
+
+static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
+                            PRIntn flags, const PRNetAddr *addr,
+                            PRIntervalTime timeout)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
+       return PR_FAILURE;
+}
+
+
+static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr)
+{
+       wpa_printf(MSG_DEBUG, "NSS: I/O getpeername");
+
+       /*
+        * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a
+        * fake IPv4 address to work around this even though we are not really
+        * using TCP.
+        */
+       os_memset(addr, 0, sizeof(*addr));
+       addr->inet.family = PR_AF_INET;
+
+       return PR_SUCCESS;
+}
+
+
+static PRStatus nss_io_getsocketoption(PRFileDesc *fd,
+                                      PRSocketOptionData *data)
+{
+       switch (data->option) {
+       case PR_SockOpt_Nonblocking:
+               wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)");
+               data->value.non_blocking = PR_TRUE;
+               return PR_SUCCESS;
+       default:
+               wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)",
+                          data->option);
+               return PR_FAILURE;
+       }
+}
+
+
+static const PRIOMethods nss_io = {
+       PR_DESC_LAYERED,
+       nss_io_close,
+       nss_io_read,
+       nss_io_write,
+       NULL /* available */,
+       NULL /* available64 */,
+       NULL /* fsync */,
+       NULL /* fseek */,
+       NULL /* fseek64 */,
+       NULL /* fileinfo */,
+       NULL /* fileinfo64 */,
+       nss_io_writev,
+       NULL /* connect */,
+       NULL /* accept */,
+       NULL /* bind */,
+       NULL /* listen */,
+       NULL /* shutdown */,
+       nss_io_recv,
+       nss_io_send,
+       nss_io_recvfrom,
+       nss_io_sendto,
+       NULL /* poll */,
+       NULL /* acceptread */,
+       NULL /* transmitfile */,
+       NULL /* getsockname */,
+       nss_io_getpeername,
+       NULL /* reserved_fn_6 */,
+       NULL /* reserved_fn_5 */,
+       nss_io_getsocketoption,
+       NULL /* setsocketoption */,
+       NULL /* sendfile */,
+       NULL /* connectcontinue */,
+       NULL /* reserved_fn_3 */,
+       NULL /* reserved_fn_2 */,
+       NULL /* reserved_fn_1 */,
+       NULL /* reserved_fn_0 */
+};
+
+
+static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
+{
+       wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
+       return NULL;
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+       char *dir;
+
+       tls_nss_ref_count++;
+       if (tls_nss_ref_count > 1)
+               return (void *) 1;
+
+       PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+       nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant");
+
+       PK11_SetPasswordFunc(nss_password_cb);
+
+       dir = getenv("SSL_DIR");
+       if (dir) {
+               if (NSS_Init(dir) != SECSuccess) {
+                       wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) "
+                                  "failed", dir);
+                       return NULL;
+               }
+       } else {
+               if (NSS_NoDB_Init(NULL) != SECSuccess) {
+                       wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) "
+                                  "failed");
+                       return NULL;
+               }
+       }
+
+       if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) !=
+           SECSuccess ||
+           SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess ||
+           SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess ||
+           SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) {
+               wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed");
+               return NULL;
+       }
+
+       if (NSS_SetDomesticPolicy() != SECSuccess) {
+               wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed");
+               return NULL;
+       }
+
+       return (void *) 1;
+}
+
+void tls_deinit(void *ssl_ctx)
+{
+       tls_nss_ref_count--;
+       if (tls_nss_ref_count == 0) {
+               if (NSS_Shutdown() != SECSuccess)
+                       wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed");
+       }
+}
+
+
+int tls_get_errors(void *tls_ctx)
+{
+       return 0;
+}
+
+
+static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd)
+{
+       struct tls_connection *conn = arg;
+       SECStatus res = SECSuccess;
+       PRErrorCode err;
+       CERTCertificate *cert;
+       char *subject, *issuer;
+
+       err = PR_GetError();
+       if (IS_SEC_ERROR(err))
+               wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err "
+                          "%d)", err - SEC_ERROR_BASE);
+       else
+               wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)",
+                          err);
+       cert = SSL_PeerCertificate(fd);
+       subject = CERT_NameToAscii(&cert->subject);
+       issuer = CERT_NameToAscii(&cert->issuer);
+       wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'",
+                  subject, issuer);
+       CERT_DestroyCertificate(cert);
+       PR_Free(subject);
+       PR_Free(issuer);
+       if (conn->verify_peer)
+               res = SECFailure;
+
+       return res;
+}
+
+
+static void nss_handshake_cb(PRFileDesc *fd, void *client_data)
+{
+       struct tls_connection *conn = client_data;
+       wpa_printf(MSG_DEBUG, "NSS: Handshake completed");
+       conn->established = 1;
+}
+
+
+struct tls_connection * tls_connection_init(void *tls_ctx)
+{
+       struct tls_connection *conn;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+       conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io);
+       if (conn->fd == NULL) {
+               os_free(conn);
+               return NULL;
+       }
+       conn->fd->secret = (void *) conn;
+
+       conn->fd = SSL_ImportFD(NULL, conn->fd);
+       if (conn->fd == NULL) {
+               os_free(conn);
+               return NULL;
+       }
+
+       if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess ||
+           SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) !=
+           SECSuccess ||
+           SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) !=
+           SECSuccess ||
+           SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess ||
+           SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess ||
+           SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) !=
+           SECSuccess) {
+               wpa_printf(MSG_ERROR, "NSS: Failed to set options");
+               PR_Close(conn->fd);
+               os_free(conn);
+               return NULL;
+       }
+
+       SSL_ResetHandshake(conn->fd, PR_FALSE);
+
+       return conn;
+}
+
+
+void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
+{
+       PR_Close(conn->fd);
+       os_free(conn->push_buf);
+       os_free(conn->pull_buf);
+       os_free(conn);
+}
+
+
+int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
+{
+       return conn->established;
+}
+
+
+int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+       wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
+       return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+       return -1;
+}
+
+
+int tls_global_set_verify(void *tls_ctx, int check_crl)
+{
+       return -1;
+}
+
+
+int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+       conn->verify_peer = verify_peer;
+       return 0;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+                         int tls_ia)
+{
+       return -1;
+}
+
+
+int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+       /* NSS does not export master secret or client/server random. */
+       return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+       if (conn == NULL || server_random_first) {
+               wpa_printf(MSG_INFO, "NSS: Unsupported PRF request "
+                          "(server_random_first=%d)",
+                          server_random_first);
+               return -1;
+       }
+
+       if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) !=
+           SECSuccess) {
+               wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor "
+                          "(label='%s' out_len=%d", label, (int) out_len);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data)
+{
+       struct wpabuf *out_data;
+
+       wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u",
+                  in_data ? (unsigned int) wpabuf_len(in_data) : 0);
+
+       if (appl_data)
+               *appl_data = NULL;
+
+       if (in_data && wpabuf_len(in_data) > 0) {
+               if (conn->pull_buf) {
+                       wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+                                  "pull_buf", __func__,
+                                  (unsigned long) conn->pull_buf_len);
+                       os_free(conn->pull_buf);
+               }
+               conn->pull_buf = os_malloc(wpabuf_len(in_data));
+               if (conn->pull_buf == NULL)
+                       return NULL;
+               os_memcpy(conn->pull_buf, wpabuf_head(in_data),
+                         wpabuf_len(in_data));
+               conn->pull_buf_offset = conn->pull_buf;
+               conn->pull_buf_len = wpabuf_len(in_data);
+       }
+
+       SSL_ForceHandshake(conn->fd);
+
+       if (conn->established && conn->push_buf == NULL) {
+               /* Need to return something to get final TLS ACK. */
+               conn->push_buf = os_malloc(1);
+       }
+
+       if (conn->push_buf == NULL)
+               return NULL;
+       out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
+       if (out_data == NULL)
+               os_free(conn->push_buf);
+       conn->push_buf = NULL;
+       conn->push_buf_len = 0;
+       return out_data;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       PRInt32 res;
+       struct wpabuf *buf;
+
+       wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes",
+                  (int) wpabuf_len(in_data));
+       res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0,
+                     0);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "NSS: Encryption failed");
+               return NULL;
+       }
+       if (conn->push_buf == NULL)
+               return NULL;
+       buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
+       if (buf == NULL)
+               os_free(conn->push_buf);
+       conn->push_buf = NULL;
+       conn->push_buf_len = 0;
+       return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       PRInt32 res;
+       struct wpabuf *out;
+
+       wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes",
+                  (int) wpabuf_len(in_data));
+       if (conn->pull_buf) {
+               wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
+                          "pull_buf", __func__,
+                          (unsigned long) conn->pull_buf_len);
+               os_free(conn->pull_buf);
+       }
+       conn->pull_buf = os_malloc(wpabuf_len(in_data));
+       if (conn->pull_buf == NULL)
+               return NULL;
+       os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data));
+       conn->pull_buf_offset = conn->pull_buf;
+       conn->pull_buf_len = wpabuf_len(in_data);
+
+       /*
+        * Even though we try to disable TLS compression, it is possible that
+        * this cannot be done with all TLS libraries. Add extra buffer space
+        * to handle the possibility of the decrypted data being longer than
+        * input data.
+        */
+       out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+       if (out == NULL)
+               return NULL;
+
+       res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0);
+       wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res);
+       if (res < 0) {
+               wpabuf_free(out);
+               return NULL;
+       }
+       wpabuf_put(out, res);
+
+       return out;
+}
+
+
+int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+       return -1;
+}
+
+
+int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       return -1;
+}
+
+
+int tls_connection_enable_workaround(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+       return -1;
+}
+
+
+int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_write_alerts(void *tls_ctx,
+                                   struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       return 0;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final)
+{
+       return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+                                          struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          const u8 *key, size_t key_len)
+{
+       return -1;
+}
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        tls_session_ticket_cb cb,
+                                        void *ctx)
+{
+       return -1;
+}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
new file mode 100644 (file)
index 0000000..c0a40f9
--- /dev/null
@@ -0,0 +1,2925 @@
+/*
+ * SSL/TLS interface functions for OpenSSL
+ * Copyright (c) 2004-2010, 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_SMARTCARD
+#ifndef OPENSSL_NO_ENGINE
+#define OPENSSL_NO_ENGINE
+#endif
+#endif
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+#include <openssl/pkcs12.h>
+#include <openssl/x509v3.h>
+#ifndef OPENSSL_NO_ENGINE
+#include <openssl/engine.h>
+#endif /* OPENSSL_NO_ENGINE */
+
+#include "common.h"
+#include "crypto.h"
+#include "tls.h"
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
+#define OPENSSL_d2i_TYPE const unsigned char **
+#else
+#define OPENSSL_d2i_TYPE unsigned char **
+#endif
+
+#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT
+#ifdef SSL_OP_NO_TICKET
+/*
+ * Session ticket override patch was merged into OpenSSL 0.9.9 tree on
+ * 2008-11-15. This version uses a bit different API compared to the old patch.
+ */
+#define CONFIG_OPENSSL_TICKET_OVERRIDE
+#endif
+#endif
+
+static int tls_openssl_ref_count = 0;
+
+struct tls_global {
+       void (*event_cb)(void *ctx, enum tls_event ev,
+                        union tls_event_data *data);
+       void *cb_ctx;
+};
+
+static struct tls_global *tls_global = NULL;
+
+
+struct tls_connection {
+       SSL *ssl;
+       BIO *ssl_in, *ssl_out;
+#ifndef OPENSSL_NO_ENGINE
+       ENGINE *engine;        /* functional reference to the engine */
+       EVP_PKEY *private_key; /* the private key if using engine */
+#endif /* OPENSSL_NO_ENGINE */
+       char *subject_match, *altsubject_match;
+       int read_alerts, write_alerts, failed;
+
+       tls_session_ticket_cb session_ticket_cb;
+       void *session_ticket_cb_ctx;
+
+       /* SessionTicket received from OpenSSL hello_extension_cb (server) */
+       u8 *session_ticket;
+       size_t session_ticket_len;
+
+       unsigned int ca_cert_verify:1;
+       unsigned int cert_probe:1;
+       unsigned int server_cert_only:1;
+
+       u8 srv_cert_hash[32];
+};
+
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+static void _tls_show_errors(void)
+{
+       unsigned long err;
+
+       while ((err = ERR_get_error())) {
+               /* Just ignore the errors, since stdout is disabled */
+       }
+}
+#define tls_show_errors(l, f, t) _tls_show_errors()
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+static void tls_show_errors(int level, const char *func, const char *txt)
+{
+       unsigned long err;
+
+       wpa_printf(level, "OpenSSL: %s - %s %s",
+                  func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+       while ((err = ERR_get_error())) {
+               wpa_printf(MSG_INFO, "OpenSSL: pending error: %s",
+                          ERR_error_string(err, NULL));
+       }
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+/* Windows CryptoAPI and access to certificate stores */
+#include <wincrypt.h>
+
+#ifdef __MINGW32_VERSION
+/*
+ * MinGW does not yet include all the needed definitions for CryptoAPI, so
+ * define here whatever extra is needed.
+ */
+#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16)
+#define CERT_STORE_READONLY_FLAG 0x00008000
+#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000
+
+#endif /* __MINGW32_VERSION */
+
+
+struct cryptoapi_rsa_data {
+       const CERT_CONTEXT *cert;
+       HCRYPTPROV crypt_prov;
+       DWORD key_spec;
+       BOOL free_crypt_prov;
+};
+
+
+static void cryptoapi_error(const char *msg)
+{
+       wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u",
+                  msg, (unsigned int) GetLastError());
+}
+
+
+static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from,
+                                unsigned char *to, RSA *rsa, int padding)
+{
+       wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+       return 0;
+}
+
+
+static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from,
+                                unsigned char *to, RSA *rsa, int padding)
+{
+       wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+       return 0;
+}
+
+
+static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from,
+                                 unsigned char *to, RSA *rsa, int padding)
+{
+       struct cryptoapi_rsa_data *priv =
+               (struct cryptoapi_rsa_data *) rsa->meth->app_data;
+       HCRYPTHASH hash;
+       DWORD hash_size, len, i;
+       unsigned char *buf = NULL;
+       int ret = 0;
+
+       if (priv == NULL) {
+               RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+                      ERR_R_PASSED_NULL_PARAMETER);
+               return 0;
+       }
+
+       if (padding != RSA_PKCS1_PADDING) {
+               RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+                      RSA_R_UNKNOWN_PADDING_TYPE);
+               return 0;
+       }
+
+       if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) {
+               wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported",
+                          __func__);
+               RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+                      RSA_R_INVALID_MESSAGE_LENGTH);
+               return 0;
+       }
+
+       if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash))
+       {
+               cryptoapi_error("CryptCreateHash failed");
+               return 0;
+       }
+
+       len = sizeof(hash_size);
+       if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len,
+                              0)) {
+               cryptoapi_error("CryptGetHashParam failed");
+               goto err;
+       }
+
+       if ((int) hash_size != flen) {
+               wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)",
+                          (unsigned) hash_size, flen);
+               RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT,
+                      RSA_R_INVALID_MESSAGE_LENGTH);
+               goto err;
+       }
+       if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) {
+               cryptoapi_error("CryptSetHashParam failed");
+               goto err;
+       }
+
+       len = RSA_size(rsa);
+       buf = os_malloc(len);
+       if (buf == NULL) {
+               RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) {
+               cryptoapi_error("CryptSignHash failed");
+               goto err;
+       }
+
+       for (i = 0; i < len; i++)
+               to[i] = buf[len - i - 1];
+       ret = len;
+
+err:
+       os_free(buf);
+       CryptDestroyHash(hash);
+
+       return ret;
+}
+
+
+static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from,
+                                 unsigned char *to, RSA *rsa, int padding)
+{
+       wpa_printf(MSG_DEBUG, "%s - not implemented", __func__);
+       return 0;
+}
+
+
+static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv)
+{
+       if (priv == NULL)
+               return;
+       if (priv->crypt_prov && priv->free_crypt_prov)
+               CryptReleaseContext(priv->crypt_prov, 0);
+       if (priv->cert)
+               CertFreeCertificateContext(priv->cert);
+       os_free(priv);
+}
+
+
+static int cryptoapi_finish(RSA *rsa)
+{
+       cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data);
+       os_free((void *) rsa->meth);
+       rsa->meth = NULL;
+       return 1;
+}
+
+
+static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store)
+{
+       HCERTSTORE cs;
+       const CERT_CONTEXT *ret = NULL;
+
+       cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0,
+                          store | CERT_STORE_OPEN_EXISTING_FLAG |
+                          CERT_STORE_READONLY_FLAG, L"MY");
+       if (cs == NULL) {
+               cryptoapi_error("Failed to open 'My system store'");
+               return NULL;
+       }
+
+       if (strncmp(name, "cert://", 7) == 0) {
+               unsigned short wbuf[255];
+               MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255);
+               ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING |
+                                                PKCS_7_ASN_ENCODING,
+                                                0, CERT_FIND_SUBJECT_STR,
+                                                wbuf, NULL);
+       } else if (strncmp(name, "hash://", 7) == 0) {
+               CRYPT_HASH_BLOB blob;
+               int len;
+               const char *hash = name + 7;
+               unsigned char *buf;
+
+               len = os_strlen(hash) / 2;
+               buf = os_malloc(len);
+               if (buf && hexstr2bin(hash, buf, len) == 0) {
+                       blob.cbData = len;
+                       blob.pbData = buf;
+                       ret = CertFindCertificateInStore(cs,
+                                                        X509_ASN_ENCODING |
+                                                        PKCS_7_ASN_ENCODING,
+                                                        0, CERT_FIND_HASH,
+                                                        &blob, NULL);
+               }
+               os_free(buf);
+       }
+
+       CertCloseStore(cs, 0);
+
+       return ret;
+}
+
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+       X509 *cert = NULL;
+       RSA *rsa = NULL, *pub_rsa;
+       struct cryptoapi_rsa_data *priv;
+       RSA_METHOD *rsa_meth;
+
+       if (name == NULL ||
+           (strncmp(name, "cert://", 7) != 0 &&
+            strncmp(name, "hash://", 7) != 0))
+               return -1;
+
+       priv = os_zalloc(sizeof(*priv));
+       rsa_meth = os_zalloc(sizeof(*rsa_meth));
+       if (priv == NULL || rsa_meth == NULL) {
+               wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory "
+                          "for CryptoAPI RSA method");
+               os_free(priv);
+               os_free(rsa_meth);
+               return -1;
+       }
+
+       priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER);
+       if (priv->cert == NULL) {
+               priv->cert = cryptoapi_find_cert(
+                       name, CERT_SYSTEM_STORE_LOCAL_MACHINE);
+       }
+       if (priv->cert == NULL) {
+               wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate "
+                          "'%s'", name);
+               goto err;
+       }
+
+       cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded,
+                       priv->cert->cbCertEncoded);
+       if (cert == NULL) {
+               wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
+                          "encoding");
+               goto err;
+       }
+
+       if (!CryptAcquireCertificatePrivateKey(priv->cert,
+                                              CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
+                                              NULL, &priv->crypt_prov,
+                                              &priv->key_spec,
+                                              &priv->free_crypt_prov)) {
+               cryptoapi_error("Failed to acquire a private key for the "
+                               "certificate");
+               goto err;
+       }
+
+       rsa_meth->name = "Microsoft CryptoAPI RSA Method";
+       rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc;
+       rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec;
+       rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc;
+       rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec;
+       rsa_meth->finish = cryptoapi_finish;
+       rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK;
+       rsa_meth->app_data = (char *) priv;
+
+       rsa = RSA_new();
+       if (rsa == NULL) {
+               SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE,
+                      ERR_R_MALLOC_FAILURE);
+               goto err;
+       }
+
+       if (!SSL_use_certificate(ssl, cert)) {
+               RSA_free(rsa);
+               rsa = NULL;
+               goto err;
+       }
+       pub_rsa = cert->cert_info->key->pkey->pkey.rsa;
+       X509_free(cert);
+       cert = NULL;
+
+       rsa->n = BN_dup(pub_rsa->n);
+       rsa->e = BN_dup(pub_rsa->e);
+       if (!RSA_set_method(rsa, rsa_meth))
+               goto err;
+
+       if (!SSL_use_RSAPrivateKey(ssl, rsa))
+               goto err;
+       RSA_free(rsa);
+
+       return 0;
+
+err:
+       if (cert)
+               X509_free(cert);
+       if (rsa)
+               RSA_free(rsa);
+       else {
+               os_free(rsa_meth);
+               cryptoapi_free_data(priv);
+       }
+       return -1;
+}
+
+
+static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
+{
+       HCERTSTORE cs;
+       PCCERT_CONTEXT ctx = NULL;
+       X509 *cert;
+       char buf[128];
+       const char *store;
+#ifdef UNICODE
+       WCHAR *wstore;
+#endif /* UNICODE */
+
+       if (name == NULL || strncmp(name, "cert_store://", 13) != 0)
+               return -1;
+
+       store = name + 13;
+#ifdef UNICODE
+       wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR));
+       if (wstore == NULL)
+               return -1;
+       wsprintf(wstore, L"%S", store);
+       cs = CertOpenSystemStore(0, wstore);
+       os_free(wstore);
+#else /* UNICODE */
+       cs = CertOpenSystemStore(0, store);
+#endif /* UNICODE */
+       if (cs == NULL) {
+               wpa_printf(MSG_DEBUG, "%s: failed to open system cert store "
+                          "'%s': error=%d", __func__, store,
+                          (int) GetLastError());
+               return -1;
+       }
+
+       while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
+               cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded,
+                               ctx->cbCertEncoded);
+               if (cert == NULL) {
+                       wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
+                                  "X509 DER encoding for CA cert");
+                       continue;
+               }
+
+               X509_NAME_oneline(X509_get_subject_name(cert), buf,
+                                 sizeof(buf));
+               wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for "
+                          "system certificate store: subject='%s'", buf);
+
+               if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+                       tls_show_errors(MSG_WARNING, __func__,
+                                       "Failed to add ca_cert to OpenSSL "
+                                       "certificate store");
+               }
+
+               X509_free(cert);
+       }
+
+       if (!CertCloseStore(cs, 0)) {
+               wpa_printf(MSG_DEBUG, "%s: failed to close system cert store "
+                          "'%s': error=%d", __func__, name + 13,
+                          (int) GetLastError());
+       }
+
+       return 0;
+}
+
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static int tls_cryptoapi_cert(SSL *ssl, const char *name)
+{
+       return -1;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void ssl_info_cb(const SSL *ssl, int where, int ret)
+{
+       const char *str;
+       int w;
+
+       wpa_printf(MSG_DEBUG, "SSL: (where=0x%x ret=0x%x)", where, ret);
+       w = where & ~SSL_ST_MASK;
+       if (w & SSL_ST_CONNECT)
+               str = "SSL_connect";
+       else if (w & SSL_ST_ACCEPT)
+               str = "SSL_accept";
+       else
+               str = "undefined";
+
+       if (where & SSL_CB_LOOP) {
+               wpa_printf(MSG_DEBUG, "SSL: %s:%s",
+                          str, SSL_state_string_long(ssl));
+       } else if (where & SSL_CB_ALERT) {
+               wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s",
+                          where & SSL_CB_READ ?
+                          "read (remote end reported an error)" :
+                          "write (local SSL3 detected an error)",
+                          SSL_alert_type_string_long(ret),
+                          SSL_alert_desc_string_long(ret));
+               if ((ret >> 8) == SSL3_AL_FATAL) {
+                       struct tls_connection *conn =
+                               SSL_get_app_data((SSL *) ssl);
+                       if (where & SSL_CB_READ)
+                               conn->read_alerts++;
+                       else
+                               conn->write_alerts++;
+               }
+       } else if (where & SSL_CB_EXIT && ret <= 0) {
+               wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s",
+                          str, ret == 0 ? "failed" : "error",
+                          SSL_state_string_long(ssl));
+       }
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+/**
+ * tls_engine_load_dynamic_generic - load any openssl engine
+ * @pre: an array of commands and values that load an engine initialized
+ *       in the engine specific function
+ * @post: an array of commands and values that initialize an already loaded
+ *        engine (or %NULL if not required)
+ * @id: the engine id of the engine to load (only required if post is not %NULL
+ *
+ * This function is a generic function that loads any openssl engine.
+ *
+ * Returns: 0 on success, -1 on failure
+ */
+static int tls_engine_load_dynamic_generic(const char *pre[],
+                                          const char *post[], const char *id)
+{
+       ENGINE *engine;
+       const char *dynamic_id = "dynamic";
+
+       engine = ENGINE_by_id(id);
+       if (engine) {
+               ENGINE_free(engine);
+               wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already "
+                          "available", id);
+               return 0;
+       }
+       ERR_clear_error();
+
+       engine = ENGINE_by_id(dynamic_id);
+       if (engine == NULL) {
+               wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+                          dynamic_id,
+                          ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+
+       /* Perform the pre commands. This will load the engine. */
+       while (pre && pre[0]) {
+               wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]);
+               if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) {
+                       wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: "
+                                  "%s %s [%s]", pre[0], pre[1],
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       ENGINE_free(engine);
+                       return -1;
+               }
+               pre += 2;
+       }
+
+       /*
+        * Free the reference to the "dynamic" engine. The loaded engine can
+        * now be looked up using ENGINE_by_id().
+        */
+       ENGINE_free(engine);
+
+       engine = ENGINE_by_id(id);
+       if (engine == NULL) {
+               wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]",
+                          id, ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+
+       while (post && post[0]) {
+               wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]);
+               if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) {
+                       wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:"
+                               " %s %s [%s]", post[0], post[1],
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       ENGINE_remove(engine);
+                       ENGINE_free(engine);
+                       return -1;
+               }
+               post += 2;
+       }
+       ENGINE_free(engine);
+
+       return 0;
+}
+
+
+/**
+ * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc
+ * @pkcs11_so_path: pksc11_so_path from the configuration
+ * @pcks11_module_path: pkcs11_module_path from the configuration
+ */
+static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
+                                         const char *pkcs11_module_path)
+{
+       char *engine_id = "pkcs11";
+       const char *pre_cmd[] = {
+               "SO_PATH", NULL /* pkcs11_so_path */,
+               "ID", NULL /* engine_id */,
+               "LIST_ADD", "1",
+               /* "NO_VCHECK", "1", */
+               "LOAD", NULL,
+               NULL, NULL
+       };
+       const char *post_cmd[] = {
+               "MODULE_PATH", NULL /* pkcs11_module_path */,
+               NULL, NULL
+       };
+
+       if (!pkcs11_so_path || !pkcs11_module_path)
+               return 0;
+
+       pre_cmd[1] = pkcs11_so_path;
+       pre_cmd[3] = engine_id;
+       post_cmd[1] = pkcs11_module_path;
+
+       wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
+                  pkcs11_so_path);
+
+       return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id);
+}
+
+
+/**
+ * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc
+ * @opensc_so_path: opensc_so_path from the configuration
+ */
+static int tls_engine_load_dynamic_opensc(const char *opensc_so_path)
+{
+       char *engine_id = "opensc";
+       const char *pre_cmd[] = {
+               "SO_PATH", NULL /* opensc_so_path */,
+               "ID", NULL /* engine_id */,
+               "LIST_ADD", "1",
+               "LOAD", NULL,
+               NULL, NULL
+       };
+
+       if (!opensc_so_path)
+               return 0;
+
+       pre_cmd[1] = opensc_so_path;
+       pre_cmd[3] = engine_id;
+
+       wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s",
+                  opensc_so_path);
+
+       return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id);
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+void * tls_init(const struct tls_config *conf)
+{
+       SSL_CTX *ssl;
+
+       if (tls_openssl_ref_count == 0) {
+               tls_global = os_zalloc(sizeof(*tls_global));
+               if (tls_global == NULL)
+                       return NULL;
+               if (conf) {
+                       tls_global->event_cb = conf->event_cb;
+                       tls_global->cb_ctx = conf->cb_ctx;
+               }
+
+#ifdef CONFIG_FIPS
+#ifdef OPENSSL_FIPS
+               if (conf && conf->fips_mode) {
+                       if (!FIPS_mode_set(1)) {
+                               wpa_printf(MSG_ERROR, "Failed to enable FIPS "
+                                          "mode");
+                               ERR_load_crypto_strings();
+                               ERR_print_errors_fp(stderr);
+                               return NULL;
+                       } else
+                               wpa_printf(MSG_INFO, "Running in FIPS mode");
+               }
+#else /* OPENSSL_FIPS */
+               if (conf && conf->fips_mode) {
+                       wpa_printf(MSG_ERROR, "FIPS mode requested, but not "
+                                  "supported");
+                       return NULL;
+               }
+#endif /* OPENSSL_FIPS */
+#endif /* CONFIG_FIPS */
+               SSL_load_error_strings();
+               SSL_library_init();
+#ifndef OPENSSL_NO_SHA256
+               EVP_add_digest(EVP_sha256());
+#endif /* OPENSSL_NO_SHA256 */
+               /* TODO: if /dev/urandom is available, PRNG is seeded
+                * automatically. If this is not the case, random data should
+                * be added here. */
+
+#ifdef PKCS12_FUNCS
+#ifndef OPENSSL_NO_RC2
+               /*
+                * 40-bit RC2 is commonly used in PKCS#12 files, so enable it.
+                * This is enabled by PKCS12_PBE_add() in OpenSSL 0.9.8
+                * versions, but it looks like OpenSSL 1.0.0 does not do that
+                * anymore.
+                */
+               EVP_add_cipher(EVP_rc2_40_cbc());
+#endif /* OPENSSL_NO_RC2 */
+               PKCS12_PBE_add();
+#endif  /* PKCS12_FUNCS */
+       }
+       tls_openssl_ref_count++;
+
+       ssl = SSL_CTX_new(TLSv1_method());
+       if (ssl == NULL)
+               return NULL;
+
+       SSL_CTX_set_info_callback(ssl, ssl_info_cb);
+
+#ifndef OPENSSL_NO_ENGINE
+       if (conf &&
+           (conf->opensc_engine_path || conf->pkcs11_engine_path ||
+            conf->pkcs11_module_path)) {
+               wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
+               ERR_load_ENGINE_strings();
+               ENGINE_load_dynamic();
+
+               if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
+                   tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
+                                                  conf->pkcs11_module_path)) {
+                       tls_deinit(ssl);
+                       return NULL;
+               }
+       }
+#endif /* OPENSSL_NO_ENGINE */
+
+       return ssl;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+       SSL_CTX *ssl = ssl_ctx;
+       SSL_CTX_free(ssl);
+
+       tls_openssl_ref_count--;
+       if (tls_openssl_ref_count == 0) {
+#ifndef OPENSSL_NO_ENGINE
+               ENGINE_cleanup();
+#endif /* OPENSSL_NO_ENGINE */
+               CRYPTO_cleanup_all_ex_data();
+               ERR_remove_state(0);
+               ERR_free_strings();
+               EVP_cleanup();
+               os_free(tls_global);
+               tls_global = NULL;
+       }
+}
+
+
+static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
+                          const char *pin, const char *key_id,
+                          const char *cert_id, const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+       int ret = -1;
+       if (engine_id == NULL) {
+               wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
+               return -1;
+       }
+       if (pin == NULL) {
+               wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set");
+               return -1;
+       }
+       if (key_id == NULL) {
+               wpa_printf(MSG_ERROR, "ENGINE: Key Id not set");
+               return -1;
+       }
+
+       ERR_clear_error();
+       conn->engine = ENGINE_by_id(engine_id);
+       if (!conn->engine) {
+               wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
+                          engine_id, ERR_error_string(ERR_get_error(), NULL));
+               goto err;
+       }
+       if (ENGINE_init(conn->engine) != 1) {
+               wpa_printf(MSG_ERROR, "ENGINE: engine init failed "
+                          "(engine: %s) [%s]", engine_id,
+                          ERR_error_string(ERR_get_error(), NULL));
+               goto err;
+       }
+       wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
+
+       if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+               wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
+                          ERR_error_string(ERR_get_error(), NULL));
+               goto err;
+       }
+       /* load private key first in-case PIN is required for cert */
+       conn->private_key = ENGINE_load_private_key(conn->engine,
+                                                   key_id, NULL, NULL);
+       if (!conn->private_key) {
+               wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id"
+                               " '%s' [%s]", key_id,
+                          ERR_error_string(ERR_get_error(), NULL));
+               ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+               goto err;
+       }
+
+       /* handle a certificate and/or CA certificate */
+       if (cert_id || ca_cert_id) {
+               const char *cmd_name = "LOAD_CERT_CTRL";
+
+               /* test if the engine supports a LOAD_CERT_CTRL */
+               if (!ENGINE_ctrl(conn->engine, ENGINE_CTRL_GET_CMD_FROM_NAME,
+                                0, (void *)cmd_name, NULL)) {
+                       wpa_printf(MSG_ERROR, "ENGINE: engine does not support"
+                                  " loading certificates");
+                       ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+                       goto err;
+               }
+       }
+
+       return 0;
+
+err:
+       if (conn->engine) {
+               ENGINE_free(conn->engine);
+               conn->engine = NULL;
+       }
+
+       if (conn->private_key) {
+               EVP_PKEY_free(conn->private_key);
+               conn->private_key = NULL;
+       }
+
+       return ret;
+#else /* OPENSSL_NO_ENGINE */
+       return 0;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static void tls_engine_deinit(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+       wpa_printf(MSG_DEBUG, "ENGINE: engine deinit");
+       if (conn->private_key) {
+               EVP_PKEY_free(conn->private_key);
+               conn->private_key = NULL;
+       }
+       if (conn->engine) {
+               ENGINE_finish(conn->engine);
+               conn->engine = NULL;
+       }
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+       int count = 0;
+       unsigned long err;
+
+       while ((err = ERR_get_error())) {
+               wpa_printf(MSG_INFO, "TLS - SSL error: %s",
+                          ERR_error_string(err, NULL));
+               count++;
+       }
+
+       return count;
+}
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+       SSL_CTX *ssl = ssl_ctx;
+       struct tls_connection *conn;
+       long options;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+       conn->ssl = SSL_new(ssl);
+       if (conn->ssl == NULL) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to initialize new SSL connection");
+               os_free(conn);
+               return NULL;
+       }
+
+       SSL_set_app_data(conn->ssl, conn);
+       options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+               SSL_OP_SINGLE_DH_USE;
+#ifdef SSL_OP_NO_COMPRESSION
+       options |= SSL_OP_NO_COMPRESSION;
+#endif /* SSL_OP_NO_COMPRESSION */
+       SSL_set_options(conn->ssl, options);
+
+       conn->ssl_in = BIO_new(BIO_s_mem());
+       if (!conn->ssl_in) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to create a new BIO for ssl_in");
+               SSL_free(conn->ssl);
+               os_free(conn);
+               return NULL;
+       }
+
+       conn->ssl_out = BIO_new(BIO_s_mem());
+       if (!conn->ssl_out) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to create a new BIO for ssl_out");
+               SSL_free(conn->ssl);
+               BIO_free(conn->ssl_in);
+               os_free(conn);
+               return NULL;
+       }
+
+       SSL_set_bio(conn->ssl, conn->ssl_in, conn->ssl_out);
+
+       return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return;
+       SSL_free(conn->ssl);
+       tls_engine_deinit(conn);
+       os_free(conn->subject_match);
+       os_free(conn->altsubject_match);
+       os_free(conn->session_ticket);
+       os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+       return conn ? SSL_is_init_finished(conn->ssl) : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+
+       /* Shutdown previous TLS connection without notifying the peer
+        * because the connection was already terminated in practice
+        * and "close notify" shutdown alert would confuse AS. */
+       SSL_set_quiet_shutdown(conn->ssl, 1);
+       SSL_shutdown(conn->ssl);
+       return 0;
+}
+
+
+static int tls_match_altsubject_component(X509 *cert, int type,
+                                         const char *value, size_t len)
+{
+       GENERAL_NAME *gen;
+       void *ext;
+       int i, found = 0;
+
+       ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+       for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+               gen = sk_GENERAL_NAME_value(ext, i);
+               if (gen->type != type)
+                       continue;
+               if (os_strlen((char *) gen->d.ia5->data) == len &&
+                   os_memcmp(value, gen->d.ia5->data, len) == 0)
+                       found++;
+       }
+
+       return found;
+}
+
+
+static int tls_match_altsubject(X509 *cert, const char *match)
+{
+       int type;
+       const char *pos, *end;
+       size_t len;
+
+       pos = match;
+       do {
+               if (os_strncmp(pos, "EMAIL:", 6) == 0) {
+                       type = GEN_EMAIL;
+                       pos += 6;
+               } else if (os_strncmp(pos, "DNS:", 4) == 0) {
+                       type = GEN_DNS;
+                       pos += 4;
+               } else if (os_strncmp(pos, "URI:", 4) == 0) {
+                       type = GEN_URI;
+                       pos += 4;
+               } else {
+                       wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName "
+                                  "match '%s'", pos);
+                       return 0;
+               }
+               end = os_strchr(pos, ';');
+               while (end) {
+                       if (os_strncmp(end + 1, "EMAIL:", 6) == 0 ||
+                           os_strncmp(end + 1, "DNS:", 4) == 0 ||
+                           os_strncmp(end + 1, "URI:", 4) == 0)
+                               break;
+                       end = os_strchr(end + 1, ';');
+               }
+               if (end)
+                       len = end - pos;
+               else
+                       len = os_strlen(pos);
+               if (tls_match_altsubject_component(cert, type, pos, len) > 0)
+                       return 1;
+               pos = end + 1;
+       } while (end);
+
+       return 0;
+}
+
+
+static enum tls_fail_reason openssl_tls_fail_reason(int err)
+{
+       switch (err) {
+       case X509_V_ERR_CERT_REVOKED:
+               return TLS_FAIL_REVOKED;
+       case X509_V_ERR_CERT_NOT_YET_VALID:
+       case X509_V_ERR_CRL_NOT_YET_VALID:
+               return TLS_FAIL_NOT_YET_VALID;
+       case X509_V_ERR_CERT_HAS_EXPIRED:
+       case X509_V_ERR_CRL_HAS_EXPIRED:
+               return TLS_FAIL_EXPIRED;
+       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT:
+       case X509_V_ERR_UNABLE_TO_GET_CRL:
+       case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER:
+       case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+       case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+       case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+       case X509_V_ERR_CERT_CHAIN_TOO_LONG:
+       case X509_V_ERR_PATH_LENGTH_EXCEEDED:
+       case X509_V_ERR_INVALID_CA:
+               return TLS_FAIL_UNTRUSTED;
+       case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE:
+       case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE:
+       case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY:
+       case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
+       case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
+       case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD:
+       case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD:
+       case X509_V_ERR_CERT_UNTRUSTED:
+       case X509_V_ERR_CERT_REJECTED:
+               return TLS_FAIL_BAD_CERTIFICATE;
+       default:
+               return TLS_FAIL_UNSPECIFIED;
+       }
+}
+
+
+static struct wpabuf * get_x509_cert(X509 *cert)
+{
+       struct wpabuf *buf;
+       u8 *tmp;
+
+       int cert_len = i2d_X509(cert, NULL);
+       if (cert_len <= 0)
+               return NULL;
+
+       buf = wpabuf_alloc(cert_len);
+       if (buf == NULL)
+               return NULL;
+
+       tmp = wpabuf_put(buf, cert_len);
+       i2d_X509(cert, &tmp);
+       return buf;
+}
+
+
+static void openssl_tls_fail_event(struct tls_connection *conn,
+                                  X509 *err_cert, int err, int depth,
+                                  const char *subject, const char *err_str,
+                                  enum tls_fail_reason reason)
+{
+       union tls_event_data ev;
+       struct wpabuf *cert = NULL;
+
+       if (tls_global->event_cb == NULL)
+               return;
+
+       cert = get_x509_cert(err_cert);
+       os_memset(&ev, 0, sizeof(ev));
+       ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ?
+               reason : openssl_tls_fail_reason(err);
+       ev.cert_fail.depth = depth;
+       ev.cert_fail.subject = subject;
+       ev.cert_fail.reason_txt = err_str;
+       ev.cert_fail.cert = cert;
+       tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+       wpabuf_free(cert);
+}
+
+
+static void openssl_tls_cert_event(struct tls_connection *conn,
+                                  X509 *err_cert, int depth,
+                                  const char *subject)
+{
+       struct wpabuf *cert = NULL;
+       union tls_event_data ev;
+#ifdef CONFIG_SHA256
+       u8 hash[32];
+#endif /* CONFIG_SHA256 */
+
+       if (tls_global->event_cb == NULL)
+               return;
+
+       os_memset(&ev, 0, sizeof(ev));
+       if (conn->cert_probe) {
+               cert = get_x509_cert(err_cert);
+               ev.peer_cert.cert = cert;
+       }
+#ifdef CONFIG_SHA256
+       if (cert) {
+               const u8 *addr[1];
+               size_t len[1];
+               addr[0] = wpabuf_head(cert);
+               len[0] = wpabuf_len(cert);
+               if (sha256_vector(1, addr, len, hash) == 0) {
+                       ev.peer_cert.hash = hash;
+                       ev.peer_cert.hash_len = sizeof(hash);
+               }
+       }
+#endif /* CONFIG_SHA256 */
+       ev.peer_cert.depth = depth;
+       ev.peer_cert.subject = subject;
+       tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
+       wpabuf_free(cert);
+}
+
+
+static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+       char buf[256];
+       X509 *err_cert;
+       int err, depth;
+       SSL *ssl;
+       struct tls_connection *conn;
+       char *match, *altmatch;
+       const char *err_str;
+
+       err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+       err = X509_STORE_CTX_get_error(x509_ctx);
+       depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+       ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+                                        SSL_get_ex_data_X509_STORE_CTX_idx());
+       X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
+
+       conn = SSL_get_app_data(ssl);
+       match = conn ? conn->subject_match : NULL;
+       altmatch = conn ? conn->altsubject_match : NULL;
+
+       if (!preverify_ok && !conn->ca_cert_verify)
+               preverify_ok = 1;
+       if (!preverify_ok && depth > 0 && conn->server_cert_only)
+               preverify_ok = 1;
+
+       err_str = X509_verify_cert_error_string(err);
+
+#ifdef CONFIG_SHA256
+       if (preverify_ok && depth == 0 && conn->server_cert_only) {
+               struct wpabuf *cert;
+               cert = get_x509_cert(err_cert);
+               if (!cert) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch "
+                                  "server certificate data");
+                       preverify_ok = 0;
+               } else {
+                       u8 hash[32];
+                       const u8 *addr[1];
+                       size_t len[1];
+                       addr[0] = wpabuf_head(cert);
+                       len[0] = wpabuf_len(cert);
+                       if (sha256_vector(1, addr, len, hash) < 0 ||
+                           os_memcmp(conn->srv_cert_hash, hash, 32) != 0) {
+                               err_str = "Server certificate mismatch";
+                               err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
+                               preverify_ok = 0;
+                       }
+                       wpabuf_free(cert);
+               }
+       }
+#endif /* CONFIG_SHA256 */
+
+       if (!preverify_ok) {
+               wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
+                          " error %d (%s) depth %d for '%s'", err, err_str,
+                          depth, buf);
+               openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+                                      err_str, TLS_FAIL_UNSPECIFIED);
+               return preverify_ok;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d "
+                  "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
+                  preverify_ok, err, err_str,
+                  conn->ca_cert_verify, depth, buf);
+       if (depth == 0 && match && os_strstr(buf, match) == NULL) {
+               wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
+                          "match with '%s'", buf, match);
+               preverify_ok = 0;
+               openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+                                      "Subject mismatch",
+                                      TLS_FAIL_SUBJECT_MISMATCH);
+       } else if (depth == 0 && altmatch &&
+                  !tls_match_altsubject(err_cert, altmatch)) {
+               wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
+                          "'%s' not found", altmatch);
+               preverify_ok = 0;
+               openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+                                      "AltSubject mismatch",
+                                      TLS_FAIL_ALTSUBJECT_MISMATCH);
+       } else
+               openssl_tls_cert_event(conn, err_cert, depth, buf);
+
+       if (conn->cert_probe && preverify_ok && depth == 0) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate "
+                          "on probe-only run");
+               preverify_ok = 0;
+               openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+                                      "Server certificate chain probe",
+                                      TLS_FAIL_SERVER_CHAIN_PROBE);
+       }
+
+       return preverify_ok;
+}
+
+
+#ifndef OPENSSL_NO_STDIO
+static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
+{
+       SSL_CTX *ssl_ctx = _ssl_ctx;
+       X509_LOOKUP *lookup;
+       int ret = 0;
+
+       lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
+                                      X509_LOOKUP_file());
+       if (lookup == NULL) {
+               tls_show_errors(MSG_WARNING, __func__,
+                               "Failed add lookup for X509 store");
+               return -1;
+       }
+
+       if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) {
+               unsigned long err = ERR_peek_error();
+               tls_show_errors(MSG_WARNING, __func__,
+                               "Failed load CA in DER format");
+               if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+                   ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+                                  "cert already in hash table error",
+                                  __func__);
+               } else
+                       ret = -1;
+       }
+
+       return ret;
+}
+#endif /* OPENSSL_NO_STDIO */
+
+
+static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
+                                 const char *ca_cert, const u8 *ca_cert_blob,
+                                 size_t ca_cert_blob_len, const char *ca_path)
+{
+       SSL_CTX *ssl_ctx = _ssl_ctx;
+
+       /*
+        * Remove previously configured trusted CA certificates before adding
+        * new ones.
+        */
+       X509_STORE_free(ssl_ctx->cert_store);
+       ssl_ctx->cert_store = X509_STORE_new();
+       if (ssl_ctx->cert_store == NULL) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+                          "certificate store", __func__);
+               return -1;
+       }
+
+       SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+       conn->ca_cert_verify = 1;
+
+       if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate "
+                          "chain");
+               conn->cert_probe = 1;
+               conn->ca_cert_verify = 0;
+               return 0;
+       }
+
+       if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) {
+#ifdef CONFIG_SHA256
+               const char *pos = ca_cert + 7;
+               if (os_strncmp(pos, "server/sha256/", 14) != 0) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert "
+                                  "hash value '%s'", ca_cert);
+                       return -1;
+               }
+               pos += 14;
+               if (os_strlen(pos) != 32 * 2) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 "
+                                  "hash length in ca_cert '%s'", ca_cert);
+                       return -1;
+               }
+               if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash "
+                                  "value in ca_cert '%s'", ca_cert);
+                       return -1;
+               }
+               conn->server_cert_only = 1;
+               wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server "
+                          "certificate match");
+               return 0;
+#else /* CONFIG_SHA256 */
+               wpa_printf(MSG_INFO, "No SHA256 included in the build - "
+                          "cannot validate server certificate hash");
+               return -1;
+#endif /* CONFIG_SHA256 */
+       }
+
+       if (ca_cert_blob) {
+               X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
+                                     ca_cert_blob_len);
+               if (cert == NULL) {
+                       tls_show_errors(MSG_WARNING, __func__,
+                                       "Failed to parse ca_cert_blob");
+                       return -1;
+               }
+
+               if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+                       unsigned long err = ERR_peek_error();
+                       tls_show_errors(MSG_WARNING, __func__,
+                                       "Failed to add ca_cert_blob to "
+                                       "certificate store");
+                       if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+                           ERR_GET_REASON(err) ==
+                           X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+                               wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring "
+                                          "cert already in hash table error",
+                                          __func__);
+                       } else {
+                               X509_free(cert);
+                               return -1;
+                       }
+               }
+               X509_free(cert);
+               wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob "
+                          "to certificate store", __func__);
+               return 0;
+       }
+
+#ifdef CONFIG_NATIVE_WINDOWS
+       if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) ==
+           0) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from "
+                          "system certificate store");
+               return 0;
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       if (ca_cert || ca_path) {
+#ifndef OPENSSL_NO_STDIO
+               if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) !=
+                   1) {
+                       tls_show_errors(MSG_WARNING, __func__,
+                                       "Failed to load root certificates");
+                       if (ca_cert &&
+                           tls_load_ca_der(ssl_ctx, ca_cert) == 0) {
+                               wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded "
+                                          "DER format CA certificate",
+                                          __func__);
+                       } else
+                               return -1;
+               } else {
+                       wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+                                  "certificate(s) loaded");
+                       tls_get_errors(ssl_ctx);
+               }
+#else /* OPENSSL_NO_STDIO */
+               wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+                          __func__);
+               return -1;
+#endif /* OPENSSL_NO_STDIO */
+       } else {
+               /* No ca_cert configured - do not try to verify server
+                * certificate */
+               conn->ca_cert_verify = 0;
+       }
+
+       return 0;
+}
+
+
+static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert)
+{
+       if (ca_cert) {
+               if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1)
+               {
+                       tls_show_errors(MSG_WARNING, __func__,
+                                       "Failed to load root certificates");
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLS: Trusted root "
+                          "certificate(s) loaded");
+
+#ifndef OPENSSL_NO_STDIO
+               /* Add the same CAs to the client certificate requests */
+               SSL_CTX_set_client_CA_list(ssl_ctx,
+                                          SSL_load_client_CA_file(ca_cert));
+#endif /* OPENSSL_NO_STDIO */
+       }
+
+       return 0;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+       int flags;
+
+       if (check_crl) {
+               X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx);
+               if (cs == NULL) {
+                       tls_show_errors(MSG_INFO, __func__, "Failed to get "
+                                       "certificate store when enabling "
+                                       "check_crl");
+                       return -1;
+               }
+               flags = X509_V_FLAG_CRL_CHECK;
+               if (check_crl == 2)
+                       flags |= X509_V_FLAG_CRL_CHECK_ALL;
+               X509_STORE_set_flags(cs, flags);
+       }
+       return 0;
+}
+
+
+static int tls_connection_set_subject_match(struct tls_connection *conn,
+                                           const char *subject_match,
+                                           const char *altsubject_match)
+{
+       os_free(conn->subject_match);
+       conn->subject_match = NULL;
+       if (subject_match) {
+               conn->subject_match = os_strdup(subject_match);
+               if (conn->subject_match == NULL)
+                       return -1;
+       }
+
+       os_free(conn->altsubject_match);
+       conn->altsubject_match = NULL;
+       if (altsubject_match) {
+               conn->altsubject_match = os_strdup(altsubject_match);
+               if (conn->altsubject_match == NULL)
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+       static int counter = 0;
+
+       if (conn == NULL)
+               return -1;
+
+       if (verify_peer) {
+               conn->ca_cert_verify = 1;
+               SSL_set_verify(conn->ssl, SSL_VERIFY_PEER |
+                              SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+                              SSL_VERIFY_CLIENT_ONCE, tls_verify_cb);
+       } else {
+               conn->ca_cert_verify = 0;
+               SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL);
+       }
+
+       SSL_set_accept_state(conn->ssl);
+
+       /*
+        * Set session id context in order to avoid fatal errors when client
+        * tries to resume a session. However, set the context to a unique
+        * value in order to effectively disable session resumption for now
+        * since not all areas of the server code are ready for it (e.g.,
+        * EAP-TTLS needs special handling for Phase 2 after abbreviated TLS
+        * handshake).
+        */
+       counter++;
+       SSL_set_session_id_context(conn->ssl,
+                                  (const unsigned char *) &counter,
+                                  sizeof(counter));
+
+       return 0;
+}
+
+
+static int tls_connection_client_cert(struct tls_connection *conn,
+                                     const char *client_cert,
+                                     const u8 *client_cert_blob,
+                                     size_t client_cert_blob_len)
+{
+       if (client_cert == NULL && client_cert_blob == NULL)
+               return 0;
+
+       if (client_cert_blob &&
+           SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob,
+                                    client_cert_blob_len) == 1) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> "
+                          "OK");
+               return 0;
+       } else if (client_cert_blob) {
+               tls_show_errors(MSG_DEBUG, __func__,
+                               "SSL_use_certificate_ASN1 failed");
+       }
+
+       if (client_cert == NULL)
+               return -1;
+
+#ifndef OPENSSL_NO_STDIO
+       if (SSL_use_certificate_file(conn->ssl, client_cert,
+                                    SSL_FILETYPE_ASN1) == 1) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)"
+                          " --> OK");
+               return 0;
+       } else {
+               tls_show_errors(MSG_DEBUG, __func__,
+                               "SSL_use_certificate_file (DER) failed");
+       }
+
+       if (SSL_use_certificate_file(conn->ssl, client_cert,
+                                    SSL_FILETYPE_PEM) == 1) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)"
+                          " --> OK");
+               return 0;
+       } else {
+               tls_show_errors(MSG_DEBUG, __func__,
+                               "SSL_use_certificate_file (PEM) failed");
+       }
+#else /* OPENSSL_NO_STDIO */
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+       return -1;
+}
+
+
+static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert)
+{
+#ifndef OPENSSL_NO_STDIO
+       if (client_cert == NULL)
+               return 0;
+
+       if (SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+                                        SSL_FILETYPE_ASN1) != 1 &&
+           SSL_CTX_use_certificate_file(ssl_ctx, client_cert,
+                                        SSL_FILETYPE_PEM) != 1) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to load client certificate");
+               return -1;
+       }
+       return 0;
+#else /* OPENSSL_NO_STDIO */
+       if (client_cert == NULL)
+               return 0;
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__);
+       return -1;
+#endif /* OPENSSL_NO_STDIO */
+}
+
+
+static int tls_passwd_cb(char *buf, int size, int rwflag, void *password)
+{
+       if (password == NULL) {
+               return 0;
+       }
+       os_strlcpy(buf, (char *) password, size);
+       return os_strlen(buf);
+}
+
+
+#ifdef PKCS12_FUNCS
+static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12,
+                           const char *passwd)
+{
+       EVP_PKEY *pkey;
+       X509 *cert;
+       STACK_OF(X509) *certs;
+       int res = 0;
+       char buf[256];
+
+       pkey = NULL;
+       cert = NULL;
+       certs = NULL;
+       if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) {
+               tls_show_errors(MSG_DEBUG, __func__,
+                               "Failed to parse PKCS12 file");
+               PKCS12_free(p12);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data");
+
+       if (cert) {
+               X509_NAME_oneline(X509_get_subject_name(cert), buf,
+                                 sizeof(buf));
+               wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: "
+                          "subject='%s'", buf);
+               if (ssl) {
+                       if (SSL_use_certificate(ssl, cert) != 1)
+                               res = -1;
+               } else {
+                       if (SSL_CTX_use_certificate(ssl_ctx, cert) != 1)
+                               res = -1;
+               }
+               X509_free(cert);
+       }
+
+       if (pkey) {
+               wpa_printf(MSG_DEBUG, "TLS: Got private key from PKCS12");
+               if (ssl) {
+                       if (SSL_use_PrivateKey(ssl, pkey) != 1)
+                               res = -1;
+               } else {
+                       if (SSL_CTX_use_PrivateKey(ssl_ctx, pkey) != 1)
+                               res = -1;
+               }
+               EVP_PKEY_free(pkey);
+       }
+
+       if (certs) {
+               while ((cert = sk_X509_pop(certs)) != NULL) {
+                       X509_NAME_oneline(X509_get_subject_name(cert), buf,
+                                         sizeof(buf));
+                       wpa_printf(MSG_DEBUG, "TLS: additional certificate"
+                                  " from PKCS12: subject='%s'", buf);
+                       /*
+                        * There is no SSL equivalent for the chain cert - so
+                        * always add it to the context...
+                        */
+                       if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) {
+                               res = -1;
+                               break;
+                       }
+               }
+               sk_X509_free(certs);
+       }
+
+       PKCS12_free(p12);
+
+       if (res < 0)
+               tls_get_errors(ssl_ctx);
+
+       return res;
+}
+#endif  /* PKCS12_FUNCS */
+
+
+static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key,
+                          const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+       FILE *f;
+       PKCS12 *p12;
+
+       f = fopen(private_key, "rb");
+       if (f == NULL)
+               return -1;
+
+       p12 = d2i_PKCS12_fp(f, NULL);
+       fclose(f);
+
+       if (p12 == NULL) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to use PKCS#12 file");
+               return -1;
+       }
+
+       return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+       wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read "
+                  "p12/pfx files");
+       return -1;
+#endif  /* PKCS12_FUNCS */
+}
+
+
+static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
+                               const u8 *blob, size_t len, const char *passwd)
+{
+#ifdef PKCS12_FUNCS
+       PKCS12 *p12;
+
+       p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len);
+       if (p12 == NULL) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to use PKCS#12 blob");
+               return -1;
+       }
+
+       return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd);
+
+#else /* PKCS12_FUNCS */
+       wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse "
+                  "p12/pfx blobs");
+       return -1;
+#endif  /* PKCS12_FUNCS */
+}
+
+
+#ifndef OPENSSL_NO_ENGINE
+static int tls_engine_get_cert(struct tls_connection *conn,
+                              const char *cert_id,
+                              X509 **cert)
+{
+       /* this runs after the private key is loaded so no PIN is required */
+       struct {
+               const char *cert_id;
+               X509 *cert;
+       } params;
+       params.cert_id = cert_id;
+       params.cert = NULL;
+
+       if (!ENGINE_ctrl_cmd(conn->engine, "LOAD_CERT_CTRL",
+                            0, &params, NULL, 1)) {
+               wpa_printf(MSG_ERROR, "ENGINE: cannot load client cert with id"
+                          " '%s' [%s]", cert_id,
+                          ERR_error_string(ERR_get_error(), NULL));
+               return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+       }
+       if (!params.cert) {
+               wpa_printf(MSG_ERROR, "ENGINE: did not properly cert with id"
+                          " '%s'", cert_id);
+               return TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+       }
+       *cert = params.cert;
+       return 0;
+}
+#endif /* OPENSSL_NO_ENGINE */
+
+
+static int tls_connection_engine_client_cert(struct tls_connection *conn,
+                                            const char *cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+       X509 *cert;
+
+       if (tls_engine_get_cert(conn, cert_id, &cert))
+               return -1;
+
+       if (!SSL_use_certificate(conn->ssl, cert)) {
+               tls_show_errors(MSG_ERROR, __func__,
+                               "SSL_use_certificate failed");
+                X509_free(cert);
+               return -1;
+       }
+       X509_free(cert);
+       wpa_printf(MSG_DEBUG, "ENGINE: SSL_use_certificate --> "
+                  "OK");
+       return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+       return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_ca_cert(void *_ssl_ctx,
+                                        struct tls_connection *conn,
+                                        const char *ca_cert_id)
+{
+#ifndef OPENSSL_NO_ENGINE
+       X509 *cert;
+       SSL_CTX *ssl_ctx = _ssl_ctx;
+
+       if (tls_engine_get_cert(conn, ca_cert_id, &cert))
+               return -1;
+
+       /* start off the same as tls_connection_ca_cert */
+       X509_STORE_free(ssl_ctx->cert_store);
+       ssl_ctx->cert_store = X509_STORE_new();
+       if (ssl_ctx->cert_store == NULL) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
+                          "certificate store", __func__);
+               X509_free(cert);
+               return -1;
+       }
+       if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+               unsigned long err = ERR_peek_error();
+               tls_show_errors(MSG_WARNING, __func__,
+                               "Failed to add CA certificate from engine "
+                               "to certificate store");
+               if (ERR_GET_LIB(err) == ERR_LIB_X509 &&
+                   ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring cert"
+                                  " already in hash table error",
+                                  __func__);
+               } else {
+                       X509_free(cert);
+                       return -1;
+               }
+       }
+       X509_free(cert);
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s - added CA certificate from engine "
+                  "to certificate store", __func__);
+       SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
+       return 0;
+
+#else /* OPENSSL_NO_ENGINE */
+       return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_engine_private_key(struct tls_connection *conn)
+{
+#ifndef OPENSSL_NO_ENGINE
+       if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) {
+               tls_show_errors(MSG_ERROR, __func__,
+                               "ENGINE: cannot use private key for TLS");
+               return -1;
+       }
+       if (!SSL_check_private_key(conn->ssl)) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Private key failed verification");
+               return -1;
+       }
+       return 0;
+#else /* OPENSSL_NO_ENGINE */
+       wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but "
+                  "engine support was not compiled in");
+       return -1;
+#endif /* OPENSSL_NO_ENGINE */
+}
+
+
+static int tls_connection_private_key(void *_ssl_ctx,
+                                     struct tls_connection *conn,
+                                     const char *private_key,
+                                     const char *private_key_passwd,
+                                     const u8 *private_key_blob,
+                                     size_t private_key_blob_len)
+{
+       SSL_CTX *ssl_ctx = _ssl_ctx;
+       char *passwd;
+       int ok;
+
+       if (private_key == NULL && private_key_blob == NULL)
+               return 0;
+
+       if (private_key_passwd) {
+               passwd = os_strdup(private_key_passwd);
+               if (passwd == NULL)
+                       return -1;
+       } else
+               passwd = NULL;
+
+       SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+       SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+
+       ok = 0;
+       while (private_key_blob) {
+               if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl,
+                                           (u8 *) private_key_blob,
+                                           private_key_blob_len) == 1) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+                                  "ASN1(EVP_PKEY_RSA) --> OK");
+                       ok = 1;
+                       break;
+               } else {
+                       tls_show_errors(MSG_DEBUG, __func__,
+                                       "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)"
+                                       " failed");
+               }
+
+               if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl,
+                                           (u8 *) private_key_blob,
+                                           private_key_blob_len) == 1) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_"
+                                  "ASN1(EVP_PKEY_DSA) --> OK");
+                       ok = 1;
+                       break;
+               } else {
+                       tls_show_errors(MSG_DEBUG, __func__,
+                                       "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)"
+                                       " failed");
+               }
+
+               if (SSL_use_RSAPrivateKey_ASN1(conn->ssl,
+                                              (u8 *) private_key_blob,
+                                              private_key_blob_len) == 1) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: "
+                                  "SSL_use_RSAPrivateKey_ASN1 --> OK");
+                       ok = 1;
+                       break;
+               } else {
+                       tls_show_errors(MSG_DEBUG, __func__,
+                                       "SSL_use_RSAPrivateKey_ASN1 failed");
+               }
+
+               if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob,
+                                        private_key_blob_len, passwd) == 0) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> "
+                                  "OK");
+                       ok = 1;
+                       break;
+               }
+
+               break;
+       }
+
+       while (!ok && private_key) {
+#ifndef OPENSSL_NO_STDIO
+               if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+                                           SSL_FILETYPE_ASN1) == 1) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: "
+                                  "SSL_use_PrivateKey_File (DER) --> OK");
+                       ok = 1;
+                       break;
+               } else {
+                       tls_show_errors(MSG_DEBUG, __func__,
+                                       "SSL_use_PrivateKey_File (DER) "
+                                       "failed");
+               }
+
+               if (SSL_use_PrivateKey_file(conn->ssl, private_key,
+                                           SSL_FILETYPE_PEM) == 1) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: "
+                                  "SSL_use_PrivateKey_File (PEM) --> OK");
+                       ok = 1;
+                       break;
+               } else {
+                       tls_show_errors(MSG_DEBUG, __func__,
+                                       "SSL_use_PrivateKey_File (PEM) "
+                                       "failed");
+               }
+#else /* OPENSSL_NO_STDIO */
+               wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO",
+                          __func__);
+#endif /* OPENSSL_NO_STDIO */
+
+               if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)
+                   == 0) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file "
+                                  "--> OK");
+                       ok = 1;
+                       break;
+               }
+
+               if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to "
+                                  "access certificate store --> OK");
+                       ok = 1;
+                       break;
+               }
+
+               break;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key");
+               os_free(passwd);
+               ERR_clear_error();
+               return -1;
+       }
+       ERR_clear_error();
+       SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+       os_free(passwd);
+       
+       if (!SSL_check_private_key(conn->ssl)) {
+               tls_show_errors(MSG_INFO, __func__, "Private key failed "
+                               "verification");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully");
+       return 0;
+}
+
+
+static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key,
+                                 const char *private_key_passwd)
+{
+       char *passwd;
+
+       if (private_key == NULL)
+               return 0;
+
+       if (private_key_passwd) {
+               passwd = os_strdup(private_key_passwd);
+               if (passwd == NULL)
+                       return -1;
+       } else
+               passwd = NULL;
+
+       SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb);
+       SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd);
+       if (
+#ifndef OPENSSL_NO_STDIO
+           SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+                                       SSL_FILETYPE_ASN1) != 1 &&
+           SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key,
+                                       SSL_FILETYPE_PEM) != 1 &&
+#endif /* OPENSSL_NO_STDIO */
+           tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Failed to load private key");
+               os_free(passwd);
+               ERR_clear_error();
+               return -1;
+       }
+       os_free(passwd);
+       ERR_clear_error();
+       SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL);
+       
+       if (!SSL_CTX_check_private_key(ssl_ctx)) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Private key failed verification");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int tls_connection_dh(struct tls_connection *conn, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+       if (dh_file == NULL)
+               return 0;
+       wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+                  "dh_file specified");
+       return -1;
+#else /* OPENSSL_NO_DH */
+       DH *dh;
+       BIO *bio;
+
+       /* TODO: add support for dh_blob */
+       if (dh_file == NULL)
+               return 0;
+       if (conn == NULL)
+               return -1;
+
+       bio = BIO_new_file(dh_file, "r");
+       if (bio == NULL) {
+               wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+                          dh_file, ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+       BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+       while (dh == NULL) {
+               DSA *dsa;
+               wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+                          " trying to parse as DSA params", dh_file,
+                          ERR_error_string(ERR_get_error(), NULL));
+               bio = BIO_new_file(dh_file, "r");
+               if (bio == NULL)
+                       break;
+               dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+               BIO_free(bio);
+               if (!dsa) {
+                       wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+                                  "'%s': %s", dh_file,
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       break;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+               dh = DSA_dup_DH(dsa);
+               DSA_free(dsa);
+               if (dh == NULL) {
+                       wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+                                  "params into DH params");
+                       break;
+               }
+               break;
+       }
+#endif /* !OPENSSL_NO_DSA */
+       if (dh == NULL) {
+               wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+                          "'%s'", dh_file);
+               return -1;
+       }
+
+       if (SSL_set_tmp_dh(conn->ssl, dh) != 1) {
+               wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+                          "%s", dh_file,
+                          ERR_error_string(ERR_get_error(), NULL));
+               DH_free(dh);
+               return -1;
+       }
+       DH_free(dh);
+       return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
+static int tls_global_dh(SSL_CTX *ssl_ctx, const char *dh_file)
+{
+#ifdef OPENSSL_NO_DH
+       if (dh_file == NULL)
+               return 0;
+       wpa_printf(MSG_ERROR, "TLS: openssl does not include DH support, but "
+                  "dh_file specified");
+       return -1;
+#else /* OPENSSL_NO_DH */
+       DH *dh;
+       BIO *bio;
+
+       /* TODO: add support for dh_blob */
+       if (dh_file == NULL)
+               return 0;
+       if (ssl_ctx == NULL)
+               return -1;
+
+       bio = BIO_new_file(dh_file, "r");
+       if (bio == NULL) {
+               wpa_printf(MSG_INFO, "TLS: Failed to open DH file '%s': %s",
+                          dh_file, ERR_error_string(ERR_get_error(), NULL));
+               return -1;
+       }
+       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
+       BIO_free(bio);
+#ifndef OPENSSL_NO_DSA
+       while (dh == NULL) {
+               DSA *dsa;
+               wpa_printf(MSG_DEBUG, "TLS: Failed to parse DH file '%s': %s -"
+                          " trying to parse as DSA params", dh_file,
+                          ERR_error_string(ERR_get_error(), NULL));
+               bio = BIO_new_file(dh_file, "r");
+               if (bio == NULL)
+                       break;
+               dsa = PEM_read_bio_DSAparams(bio, NULL, NULL, NULL);
+               BIO_free(bio);
+               if (!dsa) {
+                       wpa_printf(MSG_DEBUG, "TLS: Failed to parse DSA file "
+                                  "'%s': %s", dh_file,
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       break;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLS: DH file in DSA param format");
+               dh = DSA_dup_DH(dsa);
+               DSA_free(dsa);
+               if (dh == NULL) {
+                       wpa_printf(MSG_INFO, "TLS: Failed to convert DSA "
+                                  "params into DH params");
+                       break;
+               }
+               break;
+       }
+#endif /* !OPENSSL_NO_DSA */
+       if (dh == NULL) {
+               wpa_printf(MSG_INFO, "TLS: Failed to read/parse DH/DSA file "
+                          "'%s'", dh_file);
+               return -1;
+       }
+
+       if (SSL_CTX_set_tmp_dh(ssl_ctx, dh) != 1) {
+               wpa_printf(MSG_INFO, "TLS: Failed to set DH params from '%s': "
+                          "%s", dh_file,
+                          ERR_error_string(ERR_get_error(), NULL));
+               DH_free(dh);
+               return -1;
+       }
+       DH_free(dh);
+       return 0;
+#endif /* OPENSSL_NO_DH */
+}
+
+
+int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+       SSL *ssl;
+
+       if (conn == NULL || keys == NULL)
+               return -1;
+       ssl = conn->ssl;
+       if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL)
+               return -1;
+
+       os_memset(keys, 0, sizeof(*keys));
+       keys->master_key = ssl->session->master_key;
+       keys->master_key_len = ssl->session->master_key_length;
+       keys->client_random = ssl->s3->client_random;
+       keys->client_random_len = SSL3_RANDOM_SIZE;
+       keys->server_random = ssl->s3->server_random;
+       keys->server_random_len = SSL3_RANDOM_SIZE;
+
+       return 0;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+       return -1;
+}
+
+
+static struct wpabuf *
+openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data,
+                 int server)
+{
+       int res;
+       struct wpabuf *out_data;
+
+       /*
+        * Give TLS handshake data from the server (if available) to OpenSSL
+        * for processing.
+        */
+       if (in_data &&
+           BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data))
+           < 0) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Handshake failed - BIO_write");
+               return NULL;
+       }
+
+       /* Initiate TLS handshake or continue the existing handshake */
+       if (server)
+               res = SSL_accept(conn->ssl);
+       else
+               res = SSL_connect(conn->ssl);
+       if (res != 1) {
+               int err = SSL_get_error(conn->ssl, res);
+               if (err == SSL_ERROR_WANT_READ)
+                       wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want "
+                                  "more data");
+               else if (err == SSL_ERROR_WANT_WRITE)
+                       wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to "
+                                  "write");
+               else {
+                       tls_show_errors(MSG_INFO, __func__, "SSL_connect");
+                       conn->failed++;
+               }
+       }
+
+       /* Get the TLS handshake data to be sent to the server */
+       res = BIO_ctrl_pending(conn->ssl_out);
+       wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res);
+       out_data = wpabuf_alloc(res);
+       if (out_data == NULL) {
+               wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for "
+                          "handshake output (%d bytes)", res);
+               if (BIO_reset(conn->ssl_out) < 0) {
+                       tls_show_errors(MSG_INFO, __func__,
+                                       "BIO_reset failed");
+               }
+               return NULL;
+       }
+       res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data),
+                                     res);
+       if (res < 0) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Handshake failed - BIO_read");
+               if (BIO_reset(conn->ssl_out) < 0) {
+                       tls_show_errors(MSG_INFO, __func__,
+                                       "BIO_reset failed");
+               }
+               wpabuf_free(out_data);
+               return NULL;
+       }
+       wpabuf_put(out_data, res);
+
+       return out_data;
+}
+
+
+static struct wpabuf *
+openssl_get_appl_data(struct tls_connection *conn, size_t max_len)
+{
+       struct wpabuf *appl_data;
+       int res;
+
+       appl_data = wpabuf_alloc(max_len + 100);
+       if (appl_data == NULL)
+               return NULL;
+
+       res = SSL_read(conn->ssl, wpabuf_mhead(appl_data),
+                      wpabuf_size(appl_data));
+       if (res < 0) {
+               int err = SSL_get_error(conn->ssl, res);
+               if (err == SSL_ERROR_WANT_READ ||
+                   err == SSL_ERROR_WANT_WRITE) {
+                       wpa_printf(MSG_DEBUG, "SSL: No Application Data "
+                                  "included");
+               } else {
+                       tls_show_errors(MSG_INFO, __func__,
+                                       "Failed to read possible "
+                                       "Application Data");
+               }
+               wpabuf_free(appl_data);
+               return NULL;
+       }
+
+       wpabuf_put(appl_data, res);
+       wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished "
+                           "message", appl_data);
+
+       return appl_data;
+}
+
+
+static struct wpabuf *
+openssl_connection_handshake(struct tls_connection *conn,
+                            const struct wpabuf *in_data,
+                            struct wpabuf **appl_data, int server)
+{
+       struct wpabuf *out_data;
+
+       if (appl_data)
+               *appl_data = NULL;
+
+       out_data = openssl_handshake(conn, in_data, server);
+       if (out_data == NULL)
+               return NULL;
+
+       if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
+               *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
+
+       return out_data;
+}
+
+
+struct wpabuf *
+tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **appl_data)
+{
+       return openssl_connection_handshake(conn, in_data, appl_data, 0);
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+       return openssl_connection_handshake(conn, in_data, appl_data, 1);
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       int res;
+       struct wpabuf *buf;
+
+       if (conn == NULL)
+               return NULL;
+
+       /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */
+       if ((res = BIO_reset(conn->ssl_in)) < 0 ||
+           (res = BIO_reset(conn->ssl_out)) < 0) {
+               tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+               return NULL;
+       }
+       res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data));
+       if (res < 0) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Encryption failed - SSL_write");
+               return NULL;
+       }
+
+       /* Read encrypted data to be sent to the server */
+       buf = wpabuf_alloc(wpabuf_len(in_data) + 300);
+       if (buf == NULL)
+               return NULL;
+       res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf));
+       if (res < 0) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Encryption failed - BIO_read");
+               wpabuf_free(buf);
+               return NULL;
+       }
+       wpabuf_put(buf, res);
+
+       return buf;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       int res;
+       struct wpabuf *buf;
+
+       /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */
+       res = BIO_write(conn->ssl_in, wpabuf_head(in_data),
+                       wpabuf_len(in_data));
+       if (res < 0) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Decryption failed - BIO_write");
+               return NULL;
+       }
+       if (BIO_reset(conn->ssl_out) < 0) {
+               tls_show_errors(MSG_INFO, __func__, "BIO_reset failed");
+               return NULL;
+       }
+
+       /* Read decrypted data for further processing */
+       /*
+        * Even though we try to disable TLS compression, it is possible that
+        * this cannot be done with all TLS libraries. Add extra buffer space
+        * to handle the possibility of the decrypted data being longer than
+        * input data.
+        */
+       buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
+       if (buf == NULL)
+               return NULL;
+       res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf));
+       if (res < 0) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Decryption failed - SSL_read");
+               wpabuf_free(buf);
+               return NULL;
+       }
+       wpabuf_put(buf, res);
+
+       return buf;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+       return conn ? conn->ssl->hit : 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+       char buf[100], *pos, *end;
+       u8 *c;
+       int ret;
+
+       if (conn == NULL || conn->ssl == NULL || ciphers == NULL)
+               return -1;
+
+       buf[0] = '\0';
+       pos = buf;
+       end = pos + sizeof(buf);
+
+       c = ciphers;
+       while (*c != TLS_CIPHER_NONE) {
+               const char *suite;
+
+               switch (*c) {
+               case TLS_CIPHER_RC4_SHA:
+                       suite = "RC4-SHA";
+                       break;
+               case TLS_CIPHER_AES128_SHA:
+                       suite = "AES128-SHA";
+                       break;
+               case TLS_CIPHER_RSA_DHE_AES128_SHA:
+                       suite = "DHE-RSA-AES128-SHA";
+                       break;
+               case TLS_CIPHER_ANON_DH_AES128_SHA:
+                       suite = "ADH-AES128-SHA";
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG, "TLS: Unsupported "
+                                  "cipher selection: %d", *c);
+                       return -1;
+               }
+               ret = os_snprintf(pos, end - pos, ":%s", suite);
+               if (ret < 0 || ret >= end - pos)
+                       break;
+               pos += ret;
+
+               c++;
+       }
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1);
+
+       if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) {
+               tls_show_errors(MSG_INFO, __func__,
+                               "Cipher suite configuration failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       const char *name;
+       if (conn == NULL || conn->ssl == NULL)
+               return -1;
+
+       name = SSL_get_cipher(conn->ssl);
+       if (name == NULL)
+               return -1;
+
+       os_strlcpy(buf, name, buflen);
+       return 0;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+                                    struct tls_connection *conn)
+{
+       SSL_set_options(conn->ssl, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+
+       return 0;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* ClientHello TLS extensions require a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+       if (conn == NULL || conn->ssl == NULL || ext_type != 35)
+               return -1;
+
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+       if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
+                                      data_len) != 1)
+               return -1;
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+       if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data,
+                                   data_len) != 1)
+               return -1;
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+
+       return 0;
+}
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->write_alerts;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+       int ret;
+       unsigned long err;
+
+       if (conn == NULL)
+               return -1;
+
+       while ((err = ERR_get_error())) {
+               wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+                          __func__, ERR_error_string(err, NULL));
+       }
+
+       if (params->engine) {
+               wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
+               ret = tls_engine_init(conn, params->engine_id, params->pin,
+                                     params->key_id, params->cert_id,
+                                     params->ca_cert_id);
+               if (ret)
+                       return ret;
+       }
+       if (tls_connection_set_subject_match(conn,
+                                            params->subject_match,
+                                            params->altsubject_match))
+               return -1;
+
+       if (params->engine && params->ca_cert_id) {
+               if (tls_connection_engine_ca_cert(tls_ctx, conn,
+                                                 params->ca_cert_id))
+                       return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+       } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
+                                         params->ca_cert_blob,
+                                         params->ca_cert_blob_len,
+                                         params->ca_path))
+               return -1;
+
+       if (params->engine && params->cert_id) {
+               if (tls_connection_engine_client_cert(conn, params->cert_id))
+                       return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+       } else if (tls_connection_client_cert(conn, params->client_cert,
+                                             params->client_cert_blob,
+                                             params->client_cert_blob_len))
+               return -1;
+
+       if (params->engine && params->key_id) {
+               wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
+               if (tls_connection_engine_private_key(conn))
+                       return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
+       } else if (tls_connection_private_key(tls_ctx, conn,
+                                             params->private_key,
+                                             params->private_key_passwd,
+                                             params->private_key_blob,
+                                             params->private_key_blob_len)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'",
+                          params->private_key);
+               return -1;
+       }
+
+       if (tls_connection_dh(conn, params->dh_file)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+                          params->dh_file);
+               return -1;
+       }
+
+       tls_get_errors(tls_ctx);
+
+       return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+       SSL_CTX *ssl_ctx = tls_ctx;
+       unsigned long err;
+
+       while ((err = ERR_get_error())) {
+               wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
+                          __func__, ERR_error_string(err, NULL));
+       }
+
+       if (tls_global_ca_cert(ssl_ctx, params->ca_cert))
+               return -1;
+
+       if (tls_global_client_cert(ssl_ctx, params->client_cert))
+               return -1;
+
+       if (tls_global_private_key(ssl_ctx, params->private_key,
+                                  params->private_key_passwd))
+               return -1;
+
+       if (tls_global_dh(ssl_ctx, params->dh_file)) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'",
+                          params->dh_file);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int tls_connection_get_keyblock_size(void *tls_ctx,
+                                    struct tls_connection *conn)
+{
+       const EVP_CIPHER *c;
+       const EVP_MD *h;
+
+       if (conn == NULL || conn->ssl == NULL ||
+           conn->ssl->enc_read_ctx == NULL ||
+           conn->ssl->enc_read_ctx->cipher == NULL ||
+           conn->ssl->read_hash == NULL)
+               return -1;
+
+       c = conn->ssl->enc_read_ctx->cipher;
+#if OPENSSL_VERSION_NUMBER >= 0x00909000L
+       h = EVP_MD_CTX_md(conn->ssl->read_hash);
+#else
+       h = conn->ssl->read_hash;
+#endif
+
+       return 2 * (EVP_CIPHER_key_length(c) +
+                   EVP_MD_size(h) +
+                   EVP_CIPHER_iv_length(c));
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       return 0;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+                         int tls_ia)
+{
+       return -1;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final)
+{
+       return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+                                          struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          const u8 *key, size_t key_len)
+{
+       return -1;
+}
+
+
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+/* Pre-shared secred requires a patch to openssl, so this function is
+ * commented out unless explicitly needed for EAP-FAST in order to be able to
+ * build this file with unmodified openssl. */
+
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+                          STACK_OF(SSL_CIPHER) *peer_ciphers,
+                          SSL_CIPHER **cipher, void *arg)
+{
+       struct tls_connection *conn = arg;
+       int ret;
+
+       if (conn == NULL || conn->session_ticket_cb == NULL)
+               return 0;
+
+       ret = conn->session_ticket_cb(conn->session_ticket_cb_ctx,
+                                     conn->session_ticket,
+                                     conn->session_ticket_len,
+                                     s->s3->client_random,
+                                     s->s3->server_random, secret);
+       os_free(conn->session_ticket);
+       conn->session_ticket = NULL;
+
+       if (ret <= 0)
+               return 0;
+
+       *secret_len = SSL_MAX_MASTER_KEY_LENGTH;
+       return 1;
+}
+
+
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
+                                    int len, void *arg)
+{
+       struct tls_connection *conn = arg;
+
+       if (conn == NULL || conn->session_ticket_cb == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s: length=%d", __func__, len);
+
+       os_free(conn->session_ticket);
+       conn->session_ticket = NULL;
+
+       wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+                   "extension", data, len);
+
+       conn->session_ticket = os_malloc(len);
+       if (conn->session_ticket == NULL)
+               return 0;
+
+       os_memcpy(conn->session_ticket, data, len);
+       conn->session_ticket_len = len;
+
+       return 1;
+}
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#ifdef SSL_OP_NO_TICKET
+static void tls_hello_ext_cb(SSL *s, int client_server, int type,
+                            unsigned char *data, int len, void *arg)
+{
+       struct tls_connection *conn = arg;
+
+       if (conn == NULL || conn->session_ticket_cb == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
+                  type, len);
+
+       if (type == TLSEXT_TYPE_session_ticket && !client_server) {
+               os_free(conn->session_ticket);
+               conn->session_ticket = NULL;
+
+               wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+                           "extension", data, len);
+               conn->session_ticket = os_malloc(len);
+               if (conn->session_ticket == NULL)
+                       return;
+
+               os_memcpy(conn->session_ticket, data, len);
+               conn->session_ticket_len = len;
+       }
+}
+#else /* SSL_OP_NO_TICKET */
+static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg)
+{
+       struct tls_connection *conn = arg;
+
+       if (conn == NULL || conn->session_ticket_cb == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
+                  ext->type, ext->length);
+
+       os_free(conn->session_ticket);
+       conn->session_ticket = NULL;
+
+       if (ext->type == 35) {
+               wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
+                           "extension", ext->data, ext->length);
+               conn->session_ticket = os_malloc(ext->length);
+               if (conn->session_ticket == NULL)
+                       return SSL_AD_INTERNAL_ERROR;
+
+               os_memcpy(conn->session_ticket, ext->data, ext->length);
+               conn->session_ticket_len = ext->length;
+       }
+
+       return 0;
+}
+#endif /* SSL_OP_NO_TICKET */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+
+
+int tls_connection_set_session_ticket_cb(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        tls_session_ticket_cb cb,
+                                        void *ctx)
+{
+#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST)
+       conn->session_ticket_cb = cb;
+       conn->session_ticket_cb_ctx = ctx;
+
+       if (cb) {
+               if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
+                                             conn) != 1)
+                       return -1;
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+               SSL_set_session_ticket_ext_cb(conn->ssl,
+                                             tls_session_ticket_ext_cb, conn);
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#ifdef SSL_OP_NO_TICKET
+               SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb);
+               SSL_set_tlsext_debug_arg(conn->ssl, conn);
+#else /* SSL_OP_NO_TICKET */
+               if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb,
+                                              conn) != 1)
+                       return -1;
+#endif /* SSL_OP_NO_TICKET */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+       } else {
+               if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
+                       return -1;
+#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
+               SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
+#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+#ifdef SSL_OP_NO_TICKET
+               SSL_set_tlsext_debug_callback(conn->ssl, NULL);
+               SSL_set_tlsext_debug_arg(conn->ssl, conn);
+#else /* SSL_OP_NO_TICKET */
+               if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1)
+                       return -1;
+#endif /* SSL_OP_NO_TICKET */
+#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
+       }
+
+       return 0;
+#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+       return -1;
+#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
+}
diff --git a/src/crypto/tls_schannel.c b/src/crypto/tls_schannel.c
new file mode 100644 (file)
index 0000000..4a94e99
--- /dev/null
@@ -0,0 +1,767 @@
+/*
+ * SSL/TLS interface functions for Microsoft Schannel
+ * Copyright (c) 2005-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.
+ */
+
+/*
+ * FIX: Go through all SSPI functions and verify what needs to be freed
+ * FIX: session resumption
+ * TODO: add support for server cert chain validation
+ * TODO: add support for CA cert validation
+ * TODO: add support for EAP-TLS (client cert/key conf)
+ */
+
+#include "includes.h"
+#include <windows.h>
+#include <wincrypt.h>
+#include <schannel.h>
+#define SECURITY_WIN32
+#include <security.h>
+#include <sspi.h>
+
+#include "common.h"
+#include "tls.h"
+
+
+struct tls_global {
+       HMODULE hsecurity;
+       PSecurityFunctionTable sspi;
+       HCERTSTORE my_cert_store;
+};
+
+struct tls_connection {
+       int established, start;
+       int failed, read_alerts, write_alerts;
+
+       SCHANNEL_CRED schannel_cred;
+       CredHandle creds;
+       CtxtHandle context;
+
+       u8 eap_tls_prf[128];
+       int eap_tls_prf_set;
+};
+
+
+static int schannel_load_lib(struct tls_global *global)
+{
+       INIT_SECURITY_INTERFACE pInitSecurityInterface;
+
+       global->hsecurity = LoadLibrary(TEXT("Secur32.dll"));
+       if (global->hsecurity == NULL) {
+               wpa_printf(MSG_ERROR, "%s: Could not load Secur32.dll - 0x%x",
+                          __func__, (unsigned int) GetLastError());
+               return -1;
+       }
+
+       pInitSecurityInterface = (INIT_SECURITY_INTERFACE) GetProcAddress(
+               global->hsecurity, "InitSecurityInterfaceA");
+       if (pInitSecurityInterface == NULL) {
+               wpa_printf(MSG_ERROR, "%s: Could not find "
+                          "InitSecurityInterfaceA from Secur32.dll",
+                          __func__);
+               FreeLibrary(global->hsecurity);
+               global->hsecurity = NULL;
+               return -1;
+       }
+
+       global->sspi = pInitSecurityInterface();
+       if (global->sspi == NULL) {
+               wpa_printf(MSG_ERROR, "%s: Could not read security "
+                          "interface - 0x%x",
+                          __func__, (unsigned int) GetLastError());
+               FreeLibrary(global->hsecurity);
+               global->hsecurity = NULL;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void * tls_init(const struct tls_config *conf)
+{
+       struct tls_global *global;
+
+       global = os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+       if (schannel_load_lib(global)) {
+               os_free(global);
+               return NULL;
+       }
+       return global;
+}
+
+
+void tls_deinit(void *ssl_ctx)
+{
+       struct tls_global *global = ssl_ctx;
+
+       if (global->my_cert_store)
+               CertCloseStore(global->my_cert_store, 0);
+       FreeLibrary(global->hsecurity);
+       os_free(global);
+}
+
+
+int tls_get_errors(void *ssl_ctx)
+{
+       return 0;
+}
+
+
+struct tls_connection * tls_connection_init(void *ssl_ctx)
+{
+       struct tls_connection *conn;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+       conn->start = 1;
+
+       return conn;
+}
+
+
+void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return;
+
+       os_free(conn);
+}
+
+
+int tls_connection_established(void *ssl_ctx, struct tls_connection *conn)
+{
+       return conn ? conn->established : 0;
+}
+
+
+int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
+{
+       struct tls_global *global = ssl_ctx;
+       if (conn == NULL)
+               return -1;
+
+       conn->eap_tls_prf_set = 0;
+       conn->established = conn->failed = 0;
+       conn->read_alerts = conn->write_alerts = 0;
+       global->sspi->DeleteSecurityContext(&conn->context);
+       /* FIX: what else needs to be reseted? */
+
+       return 0;
+}
+
+
+int tls_global_set_params(void *tls_ctx,
+                         const struct tls_connection_params *params)
+{
+       return -1;
+}
+
+
+int tls_global_set_verify(void *ssl_ctx, int check_crl)
+{
+       return -1;
+}
+
+
+int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
+                             int verify_peer)
+{
+       return -1;
+}
+
+
+int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
+                           struct tls_keys *keys)
+{
+       /* Schannel does not export master secret or client/server random. */
+       return -1;
+}
+
+
+int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
+                      const char *label, int server_random_first,
+                      u8 *out, size_t out_len)
+{
+       /*
+        * Cannot get master_key from Schannel, but EapKeyBlock can be used to
+        * generate session keys for EAP-TLS and EAP-PEAPv0. EAP-PEAPv2 and
+        * EAP-TTLS cannot use this, though, since they are using different
+        * labels. The only option could be to implement TLSv1 completely here
+        * and just use Schannel or CryptoAPI for low-level crypto
+        * functionality..
+        */
+
+       if (conn == NULL || !conn->eap_tls_prf_set || server_random_first ||
+           os_strcmp(label, "client EAP encryption") != 0 ||
+           out_len > sizeof(conn->eap_tls_prf))
+               return -1;
+
+       os_memcpy(out, conn->eap_tls_prf, out_len);
+
+       return 0;
+}
+
+
+static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global,
+                                              struct tls_connection *conn)
+{
+       DWORD sspi_flags, sspi_flags_out;
+       SecBufferDesc outbuf;
+       SecBuffer outbufs[1];
+       SECURITY_STATUS status;
+       TimeStamp ts_expiry;
+
+       sspi_flags = ISC_REQ_REPLAY_DETECT |
+               ISC_REQ_CONFIDENTIALITY |
+               ISC_RET_EXTENDED_ERROR |
+               ISC_REQ_ALLOCATE_MEMORY |
+               ISC_REQ_MANUAL_CRED_VALIDATION;
+
+       wpa_printf(MSG_DEBUG, "%s: Generating ClientHello", __func__);
+
+       outbufs[0].pvBuffer = NULL;
+       outbufs[0].BufferType = SECBUFFER_TOKEN;
+       outbufs[0].cbBuffer = 0;
+
+       outbuf.cBuffers = 1;
+       outbuf.pBuffers = outbufs;
+       outbuf.ulVersion = SECBUFFER_VERSION;
+
+#ifdef UNICODE
+       status = global->sspi->InitializeSecurityContextW(
+               &conn->creds, NULL, NULL /* server name */, sspi_flags, 0,
+               SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
+               &outbuf, &sspi_flags_out, &ts_expiry);
+#else /* UNICODE */
+       status = global->sspi->InitializeSecurityContextA(
+               &conn->creds, NULL, NULL /* server name */, sspi_flags, 0,
+               SECURITY_NATIVE_DREP, NULL, 0, &conn->context,
+               &outbuf, &sspi_flags_out, &ts_expiry);
+#endif /* UNICODE */
+       if (status != SEC_I_CONTINUE_NEEDED) {
+               wpa_printf(MSG_ERROR, "%s: InitializeSecurityContextA "
+                          "failed - 0x%x",
+                          __func__, (unsigned int) status);
+               return NULL;
+       }
+
+       if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) {
+               struct wpabuf *buf;
+               wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello",
+                           outbufs[0].pvBuffer, outbufs[0].cbBuffer);
+               conn->start = 0;
+               buf = wpabuf_alloc_copy(outbufs[0].pvBuffer,
+                                       outbufs[0].cbBuffer);
+               if (buf == NULL)
+                       return NULL;
+               global->sspi->FreeContextBuffer(outbufs[0].pvBuffer);
+               return buf;
+       }
+
+       wpa_printf(MSG_ERROR, "SChannel: Failed to generate ClientHello");
+
+       return NULL;
+}
+
+
+#ifndef SECPKG_ATTR_EAP_KEY_BLOCK
+#define SECPKG_ATTR_EAP_KEY_BLOCK 0x5b
+
+typedef struct _SecPkgContext_EapKeyBlock {
+       BYTE rgbKeys[128];
+       BYTE rgbIVs[64];
+} SecPkgContext_EapKeyBlock, *PSecPkgContext_EapKeyBlock;
+#endif /* !SECPKG_ATTR_EAP_KEY_BLOCK */
+
+static int tls_get_eap(struct tls_global *global, struct tls_connection *conn)
+{
+       SECURITY_STATUS status;
+       SecPkgContext_EapKeyBlock kb;
+
+       /* Note: Windows NT and Windows Me/98/95 do not support getting
+        * EapKeyBlock */
+
+       status = global->sspi->QueryContextAttributes(
+               &conn->context, SECPKG_ATTR_EAP_KEY_BLOCK, &kb);
+       if (status != SEC_E_OK) {
+               wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes("
+                          "SECPKG_ATTR_EAP_KEY_BLOCK) failed (%d)",
+                          __func__, (int) status);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbKeys",
+                       kb.rgbKeys, sizeof(kb.rgbKeys));
+       wpa_hexdump_key(MSG_MSGDUMP, "Schannel - EapKeyBlock - rgbIVs",
+                       kb.rgbIVs, sizeof(kb.rgbIVs));
+
+       os_memcpy(conn->eap_tls_prf, kb.rgbKeys, sizeof(kb.rgbKeys));
+       conn->eap_tls_prf_set = 1;
+       return 0;
+}
+
+
+struct wpabuf * tls_connection_handshake(void *tls_ctx,
+                                        struct tls_connection *conn,
+                                        const struct wpabuf *in_data,
+                                        struct wpabuf **appl_data)
+{
+       struct tls_global *global = tls_ctx;
+       DWORD sspi_flags, sspi_flags_out;
+       SecBufferDesc inbuf, outbuf;
+       SecBuffer inbufs[2], outbufs[1];
+       SECURITY_STATUS status;
+       TimeStamp ts_expiry;
+       struct wpabuf *out_buf = NULL;
+
+       if (appl_data)
+               *appl_data = NULL;
+
+       if (conn->start)
+               return tls_conn_hs_clienthello(global, conn);
+
+       wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process",
+                  (int) wpabuf_len(in_data));
+
+       sspi_flags = ISC_REQ_REPLAY_DETECT |
+               ISC_REQ_CONFIDENTIALITY |
+               ISC_RET_EXTENDED_ERROR |
+               ISC_REQ_ALLOCATE_MEMORY |
+               ISC_REQ_MANUAL_CRED_VALIDATION;
+
+       /* Input buffer for Schannel */
+       inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data);
+       inbufs[0].cbBuffer = wpabuf_len(in_data);
+       inbufs[0].BufferType = SECBUFFER_TOKEN;
+
+       /* Place for leftover data from Schannel */
+       inbufs[1].pvBuffer = NULL;
+       inbufs[1].cbBuffer = 0;
+       inbufs[1].BufferType = SECBUFFER_EMPTY;
+
+       inbuf.cBuffers = 2;
+       inbuf.pBuffers = inbufs;
+       inbuf.ulVersion = SECBUFFER_VERSION;
+
+       /* Output buffer for Schannel */
+       outbufs[0].pvBuffer = NULL;
+       outbufs[0].cbBuffer = 0;
+       outbufs[0].BufferType = SECBUFFER_TOKEN;
+
+       outbuf.cBuffers = 1;
+       outbuf.pBuffers = outbufs;
+       outbuf.ulVersion = SECBUFFER_VERSION;
+
+#ifdef UNICODE
+       status = global->sspi->InitializeSecurityContextW(
+               &conn->creds, &conn->context, NULL, sspi_flags, 0,
+               SECURITY_NATIVE_DREP, &inbuf, 0, NULL,
+               &outbuf, &sspi_flags_out, &ts_expiry);
+#else /* UNICODE */
+       status = global->sspi->InitializeSecurityContextA(
+               &conn->creds, &conn->context, NULL, sspi_flags, 0,
+               SECURITY_NATIVE_DREP, &inbuf, 0, NULL,
+               &outbuf, &sspi_flags_out, &ts_expiry);
+#endif /* UNICODE */
+
+       wpa_printf(MSG_MSGDUMP, "Schannel: InitializeSecurityContext -> "
+                  "status=%d inlen[0]=%d intype[0]=%d inlen[1]=%d "
+                  "intype[1]=%d outlen[0]=%d",
+                  (int) status, (int) inbufs[0].cbBuffer,
+                  (int) inbufs[0].BufferType, (int) inbufs[1].cbBuffer,
+                  (int) inbufs[1].BufferType,
+                  (int) outbufs[0].cbBuffer);
+       if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED ||
+           (FAILED(status) && (sspi_flags_out & ISC_RET_EXTENDED_ERROR))) {
+               if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) {
+                       wpa_hexdump(MSG_MSGDUMP, "SChannel - output",
+                                   outbufs[0].pvBuffer, outbufs[0].cbBuffer);
+                       out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer,
+                                                   outbufs[0].cbBuffer);
+                       global->sspi->FreeContextBuffer(outbufs[0].pvBuffer);
+                       outbufs[0].pvBuffer = NULL;
+                       if (out_buf == NULL)
+                               return NULL;
+               }
+       }
+
+       switch (status) {
+       case SEC_E_INCOMPLETE_MESSAGE:
+               wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INCOMPLETE_MESSAGE");
+               break;
+       case SEC_I_CONTINUE_NEEDED:
+               wpa_printf(MSG_DEBUG, "Schannel: SEC_I_CONTINUE_NEEDED");
+               break;
+       case SEC_E_OK:
+               /* TODO: verify server certificate chain */
+               wpa_printf(MSG_DEBUG, "Schannel: SEC_E_OK - Handshake "
+                          "completed successfully");
+               conn->established = 1;
+               tls_get_eap(global, conn);
+
+               /* Need to return something to get final TLS ACK. */
+               if (out_buf == NULL)
+                       out_buf = wpabuf_alloc(0);
+
+               if (inbufs[1].BufferType == SECBUFFER_EXTRA) {
+                       wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted "
+                                   "application data",
+                                   inbufs[1].pvBuffer, inbufs[1].cbBuffer);
+                       if (appl_data) {
+                               *appl_data = wpabuf_alloc_copy(
+                                       outbufs[1].pvBuffer,
+                                       outbufs[1].cbBuffer);
+                       }
+                       global->sspi->FreeContextBuffer(inbufs[1].pvBuffer);
+                       inbufs[1].pvBuffer = NULL;
+               }
+               break;
+       case SEC_I_INCOMPLETE_CREDENTIALS:
+               wpa_printf(MSG_DEBUG,
+                          "Schannel: SEC_I_INCOMPLETE_CREDENTIALS");
+               break;
+       case SEC_E_WRONG_PRINCIPAL:
+               wpa_printf(MSG_DEBUG, "Schannel: SEC_E_WRONG_PRINCIPAL");
+               break;
+       case SEC_E_INTERNAL_ERROR:
+               wpa_printf(MSG_DEBUG, "Schannel: SEC_E_INTERNAL_ERROR");
+               break;
+       }
+
+       if (FAILED(status)) {
+               wpa_printf(MSG_DEBUG, "Schannel: Handshake failed "
+                          "(out_buf=%p)", out_buf);
+               conn->failed++;
+               global->sspi->DeleteSecurityContext(&conn->context);
+               return out_buf;
+       }
+
+       if (inbufs[1].BufferType == SECBUFFER_EXTRA) {
+               /* TODO: Can this happen? What to do with this data? */
+               wpa_hexdump(MSG_MSGDUMP, "SChannel - Leftover data",
+                           inbufs[1].pvBuffer, inbufs[1].cbBuffer);
+               global->sspi->FreeContextBuffer(inbufs[1].pvBuffer);
+               inbufs[1].pvBuffer = NULL;
+       }
+
+       return out_buf;
+}
+
+
+struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
+                                               struct tls_connection *conn,
+                                               const struct wpabuf *in_data,
+                                               struct wpabuf **appl_data)
+{
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_encrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       struct tls_global *global = tls_ctx;
+       SECURITY_STATUS status;
+       SecBufferDesc buf;
+       SecBuffer bufs[4];
+       SecPkgContext_StreamSizes sizes;
+       int i;
+       struct wpabuf *out;
+
+       status = global->sspi->QueryContextAttributes(&conn->context,
+                                                     SECPKG_ATTR_STREAM_SIZES,
+                                                     &sizes);
+       if (status != SEC_E_OK) {
+               wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed",
+                          __func__);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u",
+                  __func__,
+                  (unsigned int) sizes.cbHeader,
+                  (unsigned int) sizes.cbTrailer);
+
+       out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) +
+                          sizes.cbTrailer);
+
+       os_memset(&bufs, 0, sizeof(bufs));
+       bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader);
+       bufs[0].cbBuffer = sizes.cbHeader;
+       bufs[0].BufferType = SECBUFFER_STREAM_HEADER;
+
+       bufs[1].pvBuffer = wpabuf_put(out, 0);
+       wpabuf_put_buf(out, in_data);
+       bufs[1].cbBuffer = wpabuf_len(in_data);
+       bufs[1].BufferType = SECBUFFER_DATA;
+
+       bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer);
+       bufs[2].cbBuffer = sizes.cbTrailer;
+       bufs[2].BufferType = SECBUFFER_STREAM_TRAILER;
+
+       buf.ulVersion = SECBUFFER_VERSION;
+       buf.cBuffers = 3;
+       buf.pBuffers = bufs;
+
+       status = global->sspi->EncryptMessage(&conn->context, 0, &buf, 0);
+
+       wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage -> "
+                  "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
+                  "len[2]=%d type[2]=%d",
+                  (int) status,
+                  (int) bufs[0].cbBuffer, (int) bufs[0].BufferType,
+                  (int) bufs[1].cbBuffer, (int) bufs[1].BufferType,
+                  (int) bufs[2].cbBuffer, (int) bufs[2].BufferType);
+       wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: "
+                  "out_data=%p bufs %p %p %p",
+                  wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer,
+                  bufs[2].pvBuffer);
+
+       for (i = 0; i < 3; i++) {
+               if (bufs[i].pvBuffer && bufs[i].BufferType != SECBUFFER_EMPTY)
+               {
+                       wpa_hexdump(MSG_MSGDUMP, "SChannel: bufs",
+                                   bufs[i].pvBuffer, bufs[i].cbBuffer);
+               }
+       }
+
+       if (status == SEC_E_OK) {
+               wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__);
+               wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data "
+                                   "from EncryptMessage", out);
+               return out;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: Failed - status=%d",
+                  __func__, (int) status);
+       wpabuf_free(out);
+       return NULL;
+}
+
+
+struct wpabuf * tls_connection_decrypt(void *tls_ctx,
+                                      struct tls_connection *conn,
+                                      const struct wpabuf *in_data)
+{
+       struct tls_global *global = tls_ctx;
+       SECURITY_STATUS status;
+       SecBufferDesc buf;
+       SecBuffer bufs[4];
+       int i;
+       struct wpabuf *out, *tmp;
+
+       wpa_hexdump_buf(MSG_MSGDUMP,
+                       "Schannel: Encrypted data to DecryptMessage", in_data);
+       os_memset(&bufs, 0, sizeof(bufs));
+       tmp = wpabuf_dup(in_data);
+       if (tmp == NULL)
+               return NULL;
+       bufs[0].pvBuffer = wpabuf_mhead(tmp);
+       bufs[0].cbBuffer = wpabuf_len(in_data);
+       bufs[0].BufferType = SECBUFFER_DATA;
+
+       bufs[1].BufferType = SECBUFFER_EMPTY;
+       bufs[2].BufferType = SECBUFFER_EMPTY;
+       bufs[3].BufferType = SECBUFFER_EMPTY;
+
+       buf.ulVersion = SECBUFFER_VERSION;
+       buf.cBuffers = 4;
+       buf.pBuffers = bufs;
+
+       status = global->sspi->DecryptMessage(&conn->context, &buf, 0,
+                                                   NULL);
+       wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage -> "
+                  "status=%d len[0]=%d type[0]=%d len[1]=%d type[1]=%d "
+                  "len[2]=%d type[2]=%d len[3]=%d type[3]=%d",
+                  (int) status,
+                  (int) bufs[0].cbBuffer, (int) bufs[0].BufferType,
+                  (int) bufs[1].cbBuffer, (int) bufs[1].BufferType,
+                  (int) bufs[2].cbBuffer, (int) bufs[2].BufferType,
+                  (int) bufs[3].cbBuffer, (int) bufs[3].BufferType);
+       wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: "
+                  "out_data=%p bufs %p %p %p %p",
+                  wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer,
+                  bufs[2].pvBuffer, bufs[3].pvBuffer);
+
+       switch (status) {
+       case SEC_E_INCOMPLETE_MESSAGE:
+               wpa_printf(MSG_DEBUG, "%s: SEC_E_INCOMPLETE_MESSAGE",
+                          __func__);
+               break;
+       case SEC_E_OK:
+               wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__);
+               for (i = 0; i < 4; i++) {
+                       if (bufs[i].BufferType == SECBUFFER_DATA)
+                               break;
+               }
+               if (i == 4) {
+                       wpa_printf(MSG_DEBUG, "%s: No output data from "
+                                  "DecryptMessage", __func__);
+                       wpabuf_free(tmp);
+                       return NULL;
+               }
+               wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from "
+                               "DecryptMessage",
+                               bufs[i].pvBuffer, bufs[i].cbBuffer);
+               out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer);
+               wpabuf_free(tmp);
+               return out;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: Failed - status=%d",
+                  __func__, (int) status);
+       wpabuf_free(tmp);
+       return NULL;
+}
+
+
+int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
+                                  u8 *ciphers)
+{
+       return -1;
+}
+
+
+int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn,
+                  char *buf, size_t buflen)
+{
+       return -1;
+}
+
+
+int tls_connection_enable_workaround(void *ssl_ctx,
+                                    struct tls_connection *conn)
+{
+       return 0;
+}
+
+
+int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
+                                   int ext_type, const u8 *data,
+                                   size_t data_len)
+{
+       return -1;
+}
+
+
+int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->failed;
+}
+
+
+int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->read_alerts;
+}
+
+
+int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
+{
+       if (conn == NULL)
+               return -1;
+       return conn->write_alerts;
+}
+
+
+int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
+                             const struct tls_connection_params *params)
+{
+       struct tls_global *global = tls_ctx;
+       ALG_ID algs[1];
+       SECURITY_STATUS status;
+       TimeStamp ts_expiry;
+
+       if (conn == NULL)
+               return -1;
+
+       if (global->my_cert_store == NULL &&
+           (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) ==
+           NULL) {
+               wpa_printf(MSG_ERROR, "%s: CertOpenSystemStore failed - 0x%x",
+                          __func__, (unsigned int) GetLastError());
+               return -1;
+       }
+
+       os_memset(&conn->schannel_cred, 0, sizeof(conn->schannel_cred));
+       conn->schannel_cred.dwVersion = SCHANNEL_CRED_VERSION;
+       conn->schannel_cred.grbitEnabledProtocols = SP_PROT_TLS1;
+       algs[0] = CALG_RSA_KEYX;
+       conn->schannel_cred.cSupportedAlgs = 1;
+       conn->schannel_cred.palgSupportedAlgs = algs;
+       conn->schannel_cred.dwFlags |= SCH_CRED_NO_DEFAULT_CREDS;
+#ifdef UNICODE
+       status = global->sspi->AcquireCredentialsHandleW(
+               NULL, UNISP_NAME_W, SECPKG_CRED_OUTBOUND, NULL,
+               &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry);
+#else /* UNICODE */
+       status = global->sspi->AcquireCredentialsHandleA(
+               NULL, UNISP_NAME_A, SECPKG_CRED_OUTBOUND, NULL,
+               &conn->schannel_cred, NULL, NULL, &conn->creds, &ts_expiry);
+#endif /* UNICODE */
+       if (status != SEC_E_OK) {
+               wpa_printf(MSG_DEBUG, "%s: AcquireCredentialsHandleA failed - "
+                          "0x%x", __func__, (unsigned int) status);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+unsigned int tls_capabilities(void *tls_ctx)
+{
+       return 0;
+}
+
+
+int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn,
+                         int tls_ia)
+{
+       return -1;
+}
+
+
+struct wpabuf * tls_connection_ia_send_phase_finished(
+       void *tls_ctx, struct tls_connection *conn, int final);
+{
+       return NULL;
+}
+
+
+int tls_connection_ia_final_phase_finished(void *tls_ctx,
+                                          struct tls_connection *conn)
+{
+       return -1;
+}
+
+
+int tls_connection_ia_permute_inner_secret(void *tls_ctx,
+                                          struct tls_connection *conn,
+                                          const u8 *key, size_t key_len)
+{
+       return -1;
+}
diff --git a/src/drivers/.gitignore b/src/drivers/.gitignore
new file mode 100644 (file)
index 0000000..1d9e0e6
--- /dev/null
@@ -0,0 +1,2 @@
+build.wpa_supplicant
+build.hostapd
diff --git a/src/drivers/Apple80211.h b/src/drivers/Apple80211.h
new file mode 100644 (file)
index 0000000..2a612e7
--- /dev/null
@@ -0,0 +1,156 @@
+#ifndef APPLE80211_H
+#define APPLE80211_H
+
+/*
+ * Apple80211 framework definitions
+ * This is an undocumented interface and the definitions here are based on
+ * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and
+ * whatever related information can be found with google and experiments ;-).
+ */
+
+typedef struct __WirelessRef *WirelessRef;
+typedef SInt32 WirelessError;
+#define errWirelessNoError 0
+
+typedef struct WirelessInfo {
+       UInt16 link_qual;
+       UInt16 comms_qual;
+       UInt16 signal;
+       UInt16 noise;
+       UInt16 port_stat;
+       UInt16 client_mode;
+       UInt16 res1;
+       UInt16 power;
+       UInt16 res2;
+       UInt8 bssID[6];
+       UInt8 ssid[34];
+} WirelessInfo;
+
+typedef struct WirelessInfo2 {
+       /* TODO - these are probably not in correct order or complete */
+       WirelessInfo info1;
+       UInt8 macAddress[6];
+} WirelessInfo2;
+
+typedef struct WirelessNetworkInfo {
+       UInt16 channel;
+       UInt16 noise;
+       UInt16 signal;
+       UInt8 bssid[6];
+       UInt16 beacon_int;
+       UInt16 capability;
+       UInt16 ssid_len;
+       UInt8 ssid[32];
+} WirelessNetworkInfo;
+
+typedef int wirelessKeyType; /* TODO */
+
+int WirelessIsAvailable(void);
+WirelessError WirelessAttach(WirelessRef *ref, UInt32 res);
+WirelessError WirelessDetach(WirelessRef ref);
+WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes,
+                             void *out_ptr, int out_bytes);
+WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled);
+WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled);
+WirelessError WirelessSetPower(WirelessRef ref, UInt8 power);
+WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power);
+WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info);
+WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info);
+WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results,
+                          UInt32 strip_dups);
+WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results,
+                               CFArrayRef *ibss_results, UInt32 strip_dups);
+WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results,
+                                  UInt32 strip_dups, CFStringRef ssid);
+WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid,
+                                   UInt32 strip_dups, CFArrayRef *results);
+WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid);
+WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid,
+                             CFStringRef passwd);
+WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid);
+/*
+ * Set WEP key
+ * ref: wireless reference from WirelessAttach()
+ * type: ?
+ * key_idx: 0..3
+ * key_len: 13 for WEP-104 or 0 for clearing the key
+ * key: Pointer to the key or %NULL if key_len = 0
+ */
+WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type,
+                            int key_idx, int key_len,
+                            const unsigned char *key);
+/*
+ * Set WPA key (e.g., PMK for 4-way handshake)
+ * ref: wireless reference from WirelessAttach()
+ * type: 0..4; 1 = PMK
+ * key_len: 16, 32, or 0
+ * key: Pointer to the key or %NULL if key_len = 0
+ */
+WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type,
+                               int key_len, const unsigned char *key);
+WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid,
+                               CFStringRef key);
+WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res,
+                                CFStringRef key);
+WirelessError WirelessDisassociate(WirelessRef ref);
+
+/*
+ * Get a copy of scan results for the given SSID
+ * The returned dictionary includes following entries:
+ * beaconInterval: CFNumber(kCFNumberSInt32Type)
+ * SSID: CFData buffer of the SSID
+ * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2
+ * name: Name of the network (SSID string)
+ * BSSID: CFData buffer of the BSSID
+ * channel: CFNumber(kCFNumberSInt32Type)
+ * signal: CFNumber(kCFNumberSInt32Type)
+ * appleIE: CFData
+ * WPSNOPINRequired: CFBoolean
+ * noise: CFNumber(kCFNumberSInt32Type)
+ * capability: CFNumber(kCFNumberSInt32Type)
+ * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type)
+ * appleIE_Version: CFNumber(kCFNumberSInt32Type)
+ * appleIE_Robust: CFBoolean
+ * WPSConfigured: CFBoolean
+ * scanWasDirected: CFBoolean
+ * appleIE_Product: CFNumber(kCFNumberSInt32Type)
+ * authModes: CFArray of CFNumber(kCFNumberSInt32Type)
+ * multiCipher: CFNumber(kCFNumberSInt32Type)
+ */
+CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid);
+
+/*
+ * Get information about the current association
+ * The returned dictionary includes following entries:
+ * keyData: CFData buffer of the key (e.g., 32-octet PSK)
+ * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP?
+ * channel: CFNumber(kCFNumberSInt32Type)
+ * isIBSS: CFBoolean
+ * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open,
+ *     129 = WPA2-Enterprise
+ * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2
+ * SSID: CFData buffer of the SSID
+ * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP?
+ */
+CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref);
+
+WirelessError WirelessConfigure(WirelessRef ref);
+
+/*
+ * Get ASP information
+ * The returned dictionary includes following entries:
+ * Version: version number (e.g., 3.0)
+ * Channel: channel (e.g., 1)
+ * Vendor: vendor (e.g., 2)
+ */
+CFDictionaryRef WirelessGetInfoASP(void);
+
+/*
+ * Get a copy of the interface dictionary
+ * The returned dictionary has a key,value pairs for wireless interfaces.
+ * The key is the interface name and the value is the driver identifier, e.g.,
+ * en1: com.apple.driver.AirPort.Atheros
+ */
+CFDictionaryRef WirelessCopyInterfaceDict(void);
+
+#endif /* APPLE80211_H */
diff --git a/src/drivers/Makefile b/src/drivers/Makefile
new file mode 100644 (file)
index 0000000..07600e5
--- /dev/null
@@ -0,0 +1,9 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+       rm -f build.wpa_supplicant build.hostapd
+
+install:
+       @echo Nothing to be made.
diff --git a/src/drivers/MobileApple80211.c b/src/drivers/MobileApple80211.c
new file mode 100644 (file)
index 0000000..ce004fe
--- /dev/null
@@ -0,0 +1,189 @@
+#include "includes.h"
+#include <dlfcn.h>
+
+#include "common.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+#include "MobileApple80211.h"
+
+/*
+ * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid
+ * having to link with full Preferences.framework.
+ */
+
+static void *aeropuerto = NULL;
+
+
+int _Apple80211Initialized(void)
+{
+       return aeropuerto ? 1 : 0;
+}
+
+
+static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL;
+
+int Apple80211Open(Apple80211Ref *ctx)
+{
+       return __Apple80211Open(ctx);
+}
+
+
+static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL;
+
+int Apple80211Close(Apple80211Ref ctx)
+{
+       return __Apple80211Close(ctx);
+}
+
+
+static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list)
+       = NULL;
+
+int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list)
+{
+       return __Apple80211GetIfListCopy(handle, list);
+}
+
+
+static int (*__Apple80211BindToInterface)(Apple80211Ref handle,
+                                         CFStringRef interface) = NULL;
+
+int Apple80211BindToInterface(Apple80211Ref handle,
+                             CFStringRef interface)
+{
+       return __Apple80211BindToInterface(handle, interface);
+}
+
+
+static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle,
+                                              CFStringRef *name) = NULL;
+
+int Apple80211GetInterfaceNameCopy(Apple80211Ref handle,
+                                  CFStringRef *name)
+{
+       return __Apple80211GetInterfaceNameCopy(handle, name);
+}
+
+
+static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle,
+                                     CFDictionaryRef *info) = NULL;
+
+int Apple80211GetInfoCopy(Apple80211Ref handle,
+                         CFDictionaryRef *info)
+{
+       return __Apple80211GetInfoCopy(handle, info);
+}
+
+
+static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL;
+
+int Apple80211GetPower(Apple80211Ref handle, char *pwr)
+{
+       return __Apple80211GetPower(handle, pwr);
+}
+
+
+static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL;
+
+int Apple80211SetPower(Apple80211Ref handle, char pwr)
+{
+       return __Apple80211SetPower(handle, pwr);
+}
+
+
+static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list,
+                              CFDictionaryRef parameters) = NULL;
+
+int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list,
+                  CFDictionaryRef parameters)
+{
+       return __Apple80211Scan(handle, list, parameters);
+}
+
+
+static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss,
+                                   CFStringRef password) = NULL;
+
+int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss,
+                       CFStringRef password)
+{
+       return __Apple80211Associate(handle, bss, password);
+}
+
+
+static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle,
+                                              CFDictionaryRef bss,
+                                              CFStringRef password,
+                                              CFDictionaryRef *info) =
+       NULL;
+
+int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss,
+                                  CFStringRef password, CFDictionaryRef *info)
+{
+       return __Apple80211AssociateAndCopyInfo(handle, bss, password, info);
+}
+
+
+static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field,
+                                   CFDictionaryRef arg2, void *value) = NULL;
+
+int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2,
+                       void *value)
+{
+       return __Apple80211CopyValue(handle, field, arg2, value);
+}
+
+
+#define DLSYM(s) \
+do { \
+       __ ## s = dlsym(aeropuerto, #s); \
+       if (__ ## s == NULL) { \
+               wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \
+                          "symbol '" #s "' (%s)", dlerror()); \
+               err = 1; \
+       } \
+} while (0)
+
+
+__attribute__ ((constructor))
+void _Apple80211_constructor(void)
+{
+       const char *fname = "/System/Library/SystemConfiguration/"
+               "Aeropuerto.bundle/Aeropuerto";
+       int err = 0;
+
+       aeropuerto = dlopen(fname, RTLD_LAZY);
+       if (!aeropuerto) {
+               wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s "
+                          "for symbols", fname);
+               return;
+       }
+
+       DLSYM(Apple80211Open);
+       DLSYM(Apple80211Close);
+       DLSYM(Apple80211GetIfListCopy);
+       DLSYM(Apple80211BindToInterface);
+       DLSYM(Apple80211GetInterfaceNameCopy);
+       DLSYM(Apple80211GetInfoCopy);
+       DLSYM(Apple80211GetPower);
+       DLSYM(Apple80211SetPower);
+       DLSYM(Apple80211Scan);
+       DLSYM(Apple80211Associate);
+       DLSYM(Apple80211AssociateAndCopyInfo);
+       DLSYM(Apple80211CopyValue);
+
+       if (err) {
+               dlclose(aeropuerto);
+               aeropuerto = NULL;
+       }
+}
+
+
+__attribute__ ((destructor))
+void _Apple80211_destructor(void)
+{
+       if (aeropuerto) {
+               dlclose(aeropuerto);
+               aeropuerto = NULL;
+       }
+}
diff --git a/src/drivers/MobileApple80211.h b/src/drivers/MobileApple80211.h
new file mode 100644 (file)
index 0000000..64d439d
--- /dev/null
@@ -0,0 +1,43 @@
+#ifndef MOBILEAPPLE80211_H
+#define MOBILEAPPLE80211_H
+
+/*
+ * MobileApple80211 interface for iPhone/iPod touch
+ * These functions are available from Aeropuerto.
+ */
+
+struct Apple80211;
+typedef struct Apple80211 *Apple80211Ref;
+
+int Apple80211Open(Apple80211Ref *ctx);
+int Apple80211Close(Apple80211Ref ctx);
+int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list);
+int Apple80211BindToInterface(Apple80211Ref handle,
+                             CFStringRef interface);
+int Apple80211GetInterfaceNameCopy(Apple80211Ref handle,
+                                  CFStringRef *name);
+int Apple80211GetInfoCopy(Apple80211Ref handle,
+                         CFDictionaryRef *info);
+int Apple80211GetPower(Apple80211Ref handle, char *pwr);
+int Apple80211SetPower(Apple80211Ref handle, char pwr);
+
+/* parameters can be NULL; returns scan results in CFArrayRef *list;
+ * caller will need to free with CFRelease() */
+int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list,
+                  CFDictionaryRef parameters);
+
+int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss,
+                       CFStringRef password);
+int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss,
+                                  CFStringRef password,
+                                  CFDictionaryRef *info);
+
+enum {
+       APPLE80211_VALUE_SSID = 1,
+       APPLE80211_VALUE_BSSID = 9
+};
+
+int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2,
+                       void *value);
+
+#endif /* MOBILEAPPLE80211_H */
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
new file mode 100644 (file)
index 0000000..fa49da4
--- /dev/null
@@ -0,0 +1,2491 @@
+/*
+ * Driver interface definition
+ * Copyright (c) 2003-2010, 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.
+ *
+ * This file defines a driver interface used by both %wpa_supplicant and
+ * hostapd. The first part of the file defines data structures used in various
+ * driver operations. This is followed by the struct wpa_driver_ops that each
+ * driver wrapper will beed to define with callback functions for requesting
+ * driver operations. After this, there are definitions for driver event
+ * reporting with wpa_supplicant_event() and some convenience helper functions
+ * that can be used to report events.
+ */
+
+#ifndef DRIVER_H
+#define DRIVER_H
+
+#define WPA_SUPPLICANT_DRIVER_VERSION 4
+
+#include "common/defs.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 - Channel information
+ */
+struct hostapd_channel_data {
+       /**
+        * chan - Channel number (IEEE 802.11)
+        */
+       short chan;
+
+       /**
+        * freq - Frequency in MHz
+        */
+       short freq;
+
+       /**
+        * flag - Channel flags (HOSTAPD_CHAN_*)
+        */
+       int flag;
+
+       /**
+        * max_tx_power - maximum transmit power in dBm
+        */
+       u8 max_tx_power;
+};
+
+/**
+ * struct hostapd_hw_modes - Supported hardware mode information
+ */
+struct hostapd_hw_modes {
+       /**
+        * mode - Hardware mode
+        */
+       enum hostapd_hw_mode mode;
+
+       /**
+        * num_channels - Number of entries in the channels array
+        */
+       int num_channels;
+
+       /**
+        * channels - Array of supported channels
+        */
+       struct hostapd_channel_data *channels;
+
+       /**
+        * num_rates - Number of entries in the rates array
+        */
+       int num_rates;
+
+       /**
+        * rates - Array of supported rates in 100 kbps units
+        */
+       int *rates;
+
+       /**
+        * ht_capab - HT (IEEE 802.11n) capabilities
+        */
+       u16 ht_capab;
+
+       /**
+        * mcs_set - MCS (IEEE 802.11n) rate parameters
+        */
+       u8 mcs_set[16];
+
+       /**
+        * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters
+        */
+       u8 a_mpdu_params;
+};
+
+
+#define IEEE80211_MODE_INFRA   0
+#define IEEE80211_MODE_IBSS    1
+#define IEEE80211_MODE_AP      2
+
+#define IEEE80211_CAP_ESS      0x0001
+#define IEEE80211_CAP_IBSS     0x0002
+#define IEEE80211_CAP_PRIVACY  0x0010
+
+#define WPA_SCAN_QUAL_INVALID          BIT(0)
+#define WPA_SCAN_NOISE_INVALID         BIT(1)
+#define WPA_SCAN_LEVEL_INVALID         BIT(2)
+#define WPA_SCAN_LEVEL_DBM             BIT(3)
+#define WPA_SCAN_AUTHENTICATED         BIT(4)
+#define WPA_SCAN_ASSOCIATED            BIT(5)
+
+/**
+ * struct wpa_scan_res - Scan result for an BSS/IBSS
+ * @flags: information flags about the BSS/IBSS (WPA_SCAN_*)
+ * @bssid: BSSID
+ * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @beacon_int: beacon interval in TUs (host byte order)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @noise: noise level
+ * @level: signal level
+ * @tsf: Timestamp
+ * @age: Age of the information in milliseconds (i.e., how many milliseconds
+ * ago the last Beacon or Probe Response frame was received)
+ * @ie_len: length of the following IE field in octets
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+ * This structure is used as a generic format for scan results from the
+ * driver. Each driver interface implementation is responsible for converting
+ * the driver or OS specific scan results into this format.
+ *
+ * If the driver does not support reporting all IEs, the IE data structure is
+ * constructed of the IEs that are available. This field will also need to
+ * include SSID in IE format. All drivers are encouraged to be extended to
+ * report all IEs to make it easier to support future additions.
+ */
+struct wpa_scan_res {
+       unsigned int flags;
+       u8 bssid[ETH_ALEN];
+       int freq;
+       u16 beacon_int;
+       u16 caps;
+       int qual;
+       int noise;
+       int level;
+       u64 tsf;
+       unsigned int age;
+       size_t ie_len;
+       size_t beacon_ie_len;
+       /*
+        * Followed by ie_len octets of IEs from Probe Response frame (or if
+        * the driver does not indicate source of IEs, these may also be from
+        * Beacon frame). After the first set of IEs, another set of IEs may
+        * follow (with beacon_ie_len octets of data) if the driver provides
+        * both IE sets.
+        */
+};
+
+/**
+ * struct wpa_scan_results - Scan results
+ * @res: Array of pointers to allocated variable length scan result entries
+ * @num: Number of entries in the scan result array
+ */
+struct wpa_scan_results {
+       struct wpa_scan_res **res;
+       size_t num;
+};
+
+/**
+ * struct wpa_interface_info - Network interface information
+ * @next: Pointer to the next interface or NULL if this is the last one
+ * @ifname: Interface name that can be used with init() or init2()
+ * @desc: Human readable adapter description (e.g., vendor/model) or NULL if
+ *     not available
+ * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one
+ *     is not an allocated copy, i.e., get_interfaces() caller will not free
+ *     this)
+ */
+struct wpa_interface_info {
+       struct wpa_interface_info *next;
+       char *ifname;
+       char *desc;
+       const char *drv_name;
+};
+
+#define WPAS_MAX_SCAN_SSIDS 4
+
+/**
+ * struct wpa_driver_scan_params - Scan parameters
+ * Data for struct wpa_driver_ops::scan2().
+ */
+struct wpa_driver_scan_params {
+       /**
+        * ssids - SSIDs to scan for
+        */
+       struct wpa_driver_scan_ssid {
+               /**
+                * ssid - specific SSID to scan for (ProbeReq)
+                * %NULL or zero-length SSID is used to indicate active scan
+                * with wildcard SSID.
+                */
+               const u8 *ssid;
+               /**
+                * ssid_len: Length of the SSID in octets
+                */
+               size_t ssid_len;
+       } ssids[WPAS_MAX_SCAN_SSIDS];
+
+       /**
+        * num_ssids - Number of entries in ssids array
+        * Zero indicates a request for a passive scan.
+        */
+       size_t num_ssids;
+
+       /**
+        * extra_ies - Extra IE(s) to add into Probe Request or %NULL
+        */
+       const u8 *extra_ies;
+
+       /**
+        * extra_ies_len - Length of extra_ies in octets
+        */
+       size_t extra_ies_len;
+
+       /**
+        * freqs - Array of frequencies to scan or %NULL for all frequencies
+        *
+        * The frequency is set in MHz. The array is zero-terminated.
+        */
+       int *freqs;
+
+       /**
+        * filter_ssids - Filter for reporting SSIDs
+        *
+        * This optional parameter can be used to request the driver wrapper to
+        * filter scan results to include only the specified SSIDs. %NULL
+        * indicates that no filtering is to be done. This can be used to
+        * reduce memory needs for scan results in environments that have large
+        * number of APs with different SSIDs.
+        *
+        * The driver wrapper is allowed to take this allocated buffer into its
+        * own use by setting the pointer to %NULL. In that case, the driver
+        * wrapper is responsible for freeing the buffer with os_free() once it
+        * is not needed anymore.
+        */
+       struct wpa_driver_scan_filter {
+               u8 ssid[32];
+               size_t ssid_len;
+       } *filter_ssids;
+
+       /**
+        * num_filter_ssids - Number of entries in filter_ssids array
+        */
+       size_t num_filter_ssids;
+};
+
+/**
+ * struct wpa_driver_auth_params - Authentication parameters
+ * Data for struct wpa_driver_ops::authenticate().
+ */
+struct wpa_driver_auth_params {
+       int freq;
+       const u8 *bssid;
+       const u8 *ssid;
+       size_t ssid_len;
+       int auth_alg;
+       const u8 *ie;
+       size_t ie_len;
+       const u8 *wep_key[4];
+       size_t wep_key_len[4];
+       int wep_tx_keyidx;
+       int local_state_change;
+};
+
+/**
+ * struct wpa_driver_associate_params - Association parameters
+ * Data for struct wpa_driver_ops::associate().
+ */
+struct wpa_driver_associate_params {
+       /**
+        * bssid - BSSID of the selected AP
+        * This can be %NULL, if ap_scan=2 mode is used and the driver is
+        * responsible for selecting with which BSS to associate. */
+       const u8 *bssid;
+
+       /**
+        * ssid - The selected SSID
+        */
+       const u8 *ssid;
+
+       /**
+        * ssid_len - Length of the SSID (1..32)
+        */
+       size_t ssid_len;
+
+       /**
+        * freq - Frequency of the channel the selected AP is using
+        * Frequency that the selected AP is using (in MHz as
+        * reported in the scan results)
+        */
+       int freq;
+
+       /**
+        * wpa_ie - WPA information element for (Re)Association Request
+        * WPA information element to be included in (Re)Association
+        * Request (including information element id and length). Use
+        * of this WPA IE is optional. If the driver generates the WPA
+        * IE, it can use pairwise_suite, group_suite, and
+        * key_mgmt_suite to select proper algorithms. In this case,
+        * the driver has to notify wpa_supplicant about the used WPA
+        * IE by generating an event that the interface code will
+        * convert into EVENT_ASSOCINFO data (see below).
+        *
+        * When using WPA2/IEEE 802.11i, wpa_ie is used for RSN IE
+        * instead. The driver can determine which version is used by
+        * looking at the first byte of the IE (0xdd for WPA, 0x30 for
+        * WPA2/RSN).
+        *
+        * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE.
+        */
+       const u8 *wpa_ie;
+
+       /**
+        * wpa_ie_len - length of the wpa_ie
+        */
+       size_t wpa_ie_len;
+
+       /**
+        * pairwise_suite - Selected pairwise cipher suite
+        *
+        * This is usually ignored if @wpa_ie is used.
+        */
+       enum wpa_cipher pairwise_suite;
+
+       /**
+        * group_suite - Selected group cipher suite
+        *
+        * This is usually ignored if @wpa_ie is used.
+        */
+       enum wpa_cipher group_suite;
+
+       /**
+        * key_mgmt_suite - Selected key management suite
+        *
+        * This is usually ignored if @wpa_ie is used.
+        */
+       enum wpa_key_mgmt key_mgmt_suite;
+
+       /**
+        * auth_alg - Allowed authentication algorithms
+        * Bit field of WPA_AUTH_ALG_*
+        */
+       int auth_alg;
+
+       /**
+        * mode - Operation mode (infra/ibss) IEEE80211_MODE_*
+        */
+       int mode;
+
+       /**
+        * wep_key - WEP keys for static WEP configuration
+        */
+       const u8 *wep_key[4];
+
+       /**
+        * wep_key_len - WEP key length for static WEP configuration
+        */
+       size_t wep_key_len[4];
+
+       /**
+        * wep_tx_keyidx - WEP TX key index for static WEP configuration
+        */
+       int wep_tx_keyidx;
+
+       /**
+        * mgmt_frame_protection - IEEE 802.11w management frame protection
+        */
+       enum mfp_options mgmt_frame_protection;
+
+       /**
+        * ft_ies - IEEE 802.11r / FT information elements
+        * If the supplicant is using IEEE 802.11r (FT) and has the needed keys
+        * for fast transition, this parameter is set to include the IEs that
+        * are to be sent in the next FT Authentication Request message.
+        * update_ft_ies() handler is called to update the IEs for further
+        * FT messages in the sequence.
+        *
+        * The driver should use these IEs only if the target AP is advertising
+        * the same mobility domain as the one included in the MDIE here.
+        *
+        * In ap_scan=2 mode, the driver can use these IEs when moving to a new
+        * AP after the initial association. These IEs can only be used if the
+        * target AP is advertising support for FT and is using the same MDIE
+        * and SSID as the current AP.
+        *
+        * The driver is responsible for reporting the FT IEs received from the
+        * AP's response using wpa_supplicant_event() with EVENT_FT_RESPONSE
+        * type. update_ft_ies() handler will then be called with the FT IEs to
+        * include in the next frame in the authentication sequence.
+        */
+       const u8 *ft_ies;
+
+       /**
+        * ft_ies_len - Length of ft_ies in bytes
+        */
+       size_t ft_ies_len;
+
+       /**
+        * ft_md - FT Mobility domain (6 octets) (also included inside ft_ies)
+        *
+        * This value is provided to allow the driver interface easier access
+        * to the current mobility domain. This value is set to %NULL if no
+        * mobility domain is currently active.
+        */
+       const u8 *ft_md;
+
+       /**
+        * passphrase - RSN passphrase for PSK
+        *
+        * This value is made available only for WPA/WPA2-Personal (PSK) and
+        * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+        * the 8..63 character ASCII passphrase, if available. Please note that
+        * this can be %NULL if passphrase was not used to generate the PSK. In
+        * that case, the psk field must be used to fetch the PSK.
+        */
+       const char *passphrase;
+
+       /**
+        * psk - RSN PSK (alternative for passphrase for PSK)
+        *
+        * This value is made available only for WPA/WPA2-Personal (PSK) and
+        * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
+        * the 32-octet (256-bit) PSK, if available. The driver wrapper should
+        * be prepared to handle %NULL value as an error.
+        */
+       const u8 *psk;
+
+       /**
+        * drop_unencrypted - Enable/disable unencrypted frame filtering
+        *
+        * Configure the driver to drop all non-EAPOL frames (both receive and
+        * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must
+        * still be allowed for key negotiation.
+        */
+       int drop_unencrypted;
+
+       /**
+        * prev_bssid - Previously used BSSID in this ESS
+        *
+        * When not %NULL, this is a request to use reassociation instead of
+        * association.
+        */
+       const u8 *prev_bssid;
+};
+
+/**
+ * struct wpa_driver_capa - Driver capability information
+ */
+struct wpa_driver_capa {
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA           0x00000001
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2          0x00000002
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK       0x00000004
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK      0x00000008
+#define WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE      0x00000010
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT            0x00000020
+#define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK                0x00000040
+       unsigned int key_mgmt;
+
+#define WPA_DRIVER_CAPA_ENC_WEP40      0x00000001
+#define WPA_DRIVER_CAPA_ENC_WEP104     0x00000002
+#define WPA_DRIVER_CAPA_ENC_TKIP       0x00000004
+#define WPA_DRIVER_CAPA_ENC_CCMP       0x00000008
+       unsigned int enc;
+
+#define WPA_DRIVER_AUTH_OPEN           0x00000001
+#define WPA_DRIVER_AUTH_SHARED         0x00000002
+#define WPA_DRIVER_AUTH_LEAP           0x00000004
+       unsigned int auth;
+
+/* Driver generated WPA/RSN IE */
+#define WPA_DRIVER_FLAGS_DRIVER_IE     0x00000001
+/* Driver needs static WEP key setup after association command */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
+#define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004
+/* Driver takes care of RSN 4-way handshake internally; PMK is configured with
+ * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
+#define WPA_DRIVER_FLAGS_WIRED         0x00000010
+/* Driver provides separate commands for authentication and association (SME in
+ * wpa_supplicant). */
+#define WPA_DRIVER_FLAGS_SME           0x00000020
+/* Driver supports AP mode */
+#define WPA_DRIVER_FLAGS_AP            0x00000040
+/* Driver needs static WEP key setup after association has been completed */
+#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE     0x00000080
+       unsigned int flags;
+
+       int max_scan_ssids;
+
+       /**
+        * max_remain_on_chan - Maximum remain-on-channel duration in msec
+        */
+       unsigned int max_remain_on_chan;
+};
+
+
+struct hostapd_data;
+
+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 hostapd_sta_add_params {
+       const u8 *addr;
+       u16 aid;
+       u16 capability;
+       const u8 *supp_rates;
+       size_t supp_rates_len;
+       u16 listen_interval;
+       const struct ieee80211_ht_capabilities *ht_capabilities;
+};
+
+struct hostapd_freq_params {
+       int mode;
+       int freq;
+       int channel;
+       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 wpa_driver_if_type {
+       /**
+        * WPA_IF_STATION - Station mode interface
+        */
+       WPA_IF_STATION,
+
+       /**
+        * WPA_IF_AP_VLAN - AP mode VLAN interface
+        *
+        * This interface shares its address and Beacon frame with the main
+        * BSS.
+        */
+       WPA_IF_AP_VLAN,
+
+       /**
+        * WPA_IF_AP_BSS - AP mode BSS interface
+        *
+        * This interface has its own address and Beacon frame.
+        */
+       WPA_IF_AP_BSS,
+};
+
+struct wpa_init_params {
+       const u8 *bssid;
+       const char *ifname;
+       const u8 *ssid;
+       size_t ssid_len;
+       const char *test_socket;
+       int use_pae_group_addr;
+       char **bridge;
+       size_t num_bridge;
+
+       u8 *own_addr; /* buffer for writing own MAC address */
+};
+
+
+struct wpa_bss_params {
+       /** Interface name (for multi-SSID/VLAN support) */
+       const char *ifname;
+       /** Whether IEEE 802.1X or WPA/WPA2 is enabled */
+       int enabled;
+
+       int wpa;
+       int ieee802_1x;
+       int wpa_group;
+       int wpa_pairwise;
+       int wpa_key_mgmt;
+       int rsn_preauth;
+};
+
+#define WPA_STA_AUTHORIZED BIT(0)
+#define WPA_STA_WMM BIT(1)
+#define WPA_STA_SHORT_PREAMBLE BIT(2)
+#define WPA_STA_MFP BIT(3)
+
+/**
+ * struct wpa_driver_ops - Driver interface API definition
+ *
+ * This structure defines the API that each driver interface needs to implement
+ * for core wpa_supplicant code. All driver specific functionality is captured
+ * in this wrapper.
+ */
+struct wpa_driver_ops {
+       /** Name of the driver interface */
+       const char *name;
+       /** One line description of the driver interface */
+       const char *desc;
+
+       /**
+        * get_bssid - Get the current BSSID
+        * @priv: private driver interface data
+        * @bssid: buffer for BSSID (ETH_ALEN = 6 bytes)
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * Query kernel driver for the current BSSID and copy it to bssid.
+        * Setting bssid to 00:00:00:00:00:00 is recommended if the STA is not
+        * associated.
+        */
+       int (*get_bssid)(void *priv, u8 *bssid);
+
+       /**
+        * get_ssid - Get the current SSID
+        * @priv: private driver interface data
+        * @ssid: buffer for SSID (at least 32 bytes)
+        *
+        * Returns: Length of the SSID on success, -1 on failure
+        *
+        * Query kernel driver for the current SSID and copy it to ssid.
+        * Returning zero is recommended if the STA is not associated.
+        *
+        * Note: SSID is an array of octets, i.e., it is not nul terminated and
+        * can, at least in theory, contain control characters (including nul)
+        * and as such, should be processed as binary data, not a printable
+        * string.
+        */
+       int (*get_ssid)(void *priv, u8 *ssid);
+
+       /**
+        * set_key - Configure encryption key
+        * @ifname: Interface name (for multi-SSID/VLAN support)
+        * @priv: private driver interface data
+        * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+        *      %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK);
+        *      %WPA_ALG_NONE clears the key.
+        * @addr: address of the peer STA or ff:ff:ff:ff:ff:ff for
+        *      broadcast/default keys
+        * @key_idx: key index (0..3), usually 0 for unicast keys; 0..4095 for
+        *      IGTK
+        * @set_tx: configure this key as the default Tx key (only used when
+        *      driver does not support separate unicast/individual key
+        * @seq: sequence number/packet number, seq_len octets, the next
+        *      packet number to be used for in replay protection; configured
+        *      for Rx keys (in most cases, this is only used with broadcast
+        *      keys and set to zero for unicast keys)
+        * @seq_len: length of the seq, depends on the algorithm:
+        *      TKIP: 6 octets, CCMP: 6 octets, IGTK: 6 octets
+        * @key: key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+        *      8-byte Rx Mic Key
+        * @key_len: length of the key buffer in octets (WEP: 5 or 13,
+        *      TKIP: 32, CCMP: 16, IGTK: 16)
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * Configure the given key for the kernel driver. If the driver
+        * supports separate individual keys (4 default keys + 1 individual),
+        * addr can be used to determine whether the key is default or
+        * individual. If only 4 keys are supported, the default key with key
+        * index 0 is used as the individual key. STA must be configured to use
+        * it as the default Tx key (set_tx is set) and accept Rx for all the
+        * key indexes. In most cases, WPA uses only key indexes 1 and 2 for
+        * broadcast keys, so key index 0 is available for this kind of
+        * configuration.
+        *
+        * Please note that TKIP keys include separate TX and RX MIC keys and
+        * some drivers may expect them in different order than wpa_supplicant
+        * is using. If the TX/RX keys are swapped, all TKIP encrypted packets
+        * will tricker Michael MIC errors. This can be fixed by changing the
+        * order of MIC keys by swapping te bytes 16..23 and 24..31 of the key
+        * in driver_*.c set_key() implementation, see driver_ndis.c for an
+        * example on how this can be done.
+        */
+       int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg,
+                      const u8 *addr, int key_idx, int set_tx,
+                      const u8 *seq, size_t seq_len,
+                      const u8 *key, size_t key_len);
+
+       /**
+        * init - Initialize driver interface
+        * @ctx: context to be used when calling wpa_supplicant functions,
+        * e.g., wpa_supplicant_event()
+        * @ifname: interface name, e.g., wlan0
+        *
+        * Returns: Pointer to private data, %NULL on failure
+        *
+        * Initialize driver interface, including event processing for kernel
+        * driver events (e.g., associated, scan results, Michael MIC failure).
+        * This function can allocate a private configuration data area for
+        * @ctx, file descriptor, interface name, etc. information that may be
+        * needed in future driver operations. If this is not used, non-NULL
+        * value will need to be returned because %NULL is used to indicate
+        * failure. The returned value will be used as 'void *priv' data for
+        * all other driver_ops functions.
+        *
+        * The main event loop (eloop.c) of wpa_supplicant can be used to
+        * register callback for read sockets (eloop_register_read_sock()).
+        *
+        * See below for more information about events and
+        * wpa_supplicant_event() function.
+        */
+       void * (*init)(void *ctx, const char *ifname);
+
+       /**
+        * deinit - Deinitialize driver interface
+        * @priv: private driver interface data from init()
+        *
+        * Shut down driver interface and processing of driver events. Free
+        * private data buffer if one was allocated in init() handler.
+        */
+       void (*deinit)(void *priv);
+
+       /**
+        * set_param - Set driver configuration parameters
+        * @priv: private driver interface data from init()
+        * @param: driver specific configuration parameters
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * Optional handler for notifying driver interface about configuration
+        * parameters (driver_param).
+        */
+       int (*set_param)(void *priv, const char *param);
+
+       /**
+        * set_countermeasures - Enable/disable TKIP countermeasures
+        * @priv: private driver interface data
+        * @enabled: 1 = countermeasures enabled, 0 = disabled
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * Configure TKIP countermeasures. When these are enabled, the driver
+        * should drop all received and queued frames that are using TKIP.
+        */
+       int (*set_countermeasures)(void *priv, int enabled);
+
+       /**
+        * deauthenticate - Request driver to deauthenticate
+        * @priv: private driver interface data
+        * @addr: peer address (BSSID of the AP)
+        * @reason_code: 16-bit reason code to be sent in the deauthentication
+        *      frame
+        *
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
+
+       /**
+        * disassociate - Request driver to disassociate
+        * @priv: private driver interface data
+        * @addr: peer address (BSSID of the AP)
+        * @reason_code: 16-bit reason code to be sent in the disassociation
+        *      frame
+        *
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*disassociate)(void *priv, const u8 *addr, int reason_code);
+
+       /**
+        * associate - Request driver to associate
+        * @priv: private driver interface data
+        * @params: association parameters
+        *
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*associate)(void *priv,
+                        struct wpa_driver_associate_params *params);
+
+       /**
+        * add_pmkid - Add PMKSA cache entry to the driver
+        * @priv: private driver interface data
+        * @bssid: BSSID for the PMKSA cache entry
+        * @pmkid: PMKID for the PMKSA cache entry
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is called when a new PMK is received, as a result of
+        * either normal authentication or RSN pre-authentication.
+        *
+        * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+        * associate(), add_pmkid() can be used to add new PMKSA cache entries
+        * in the driver. If the driver uses wpa_ie from wpa_supplicant, this
+        * driver_ops function does not need to be implemented. Likewise, if
+        * the driver does not support WPA, this function is not needed.
+        */
+       int (*add_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+       /**
+        * remove_pmkid - Remove PMKSA cache entry to the driver
+        * @priv: private driver interface data
+        * @bssid: BSSID for the PMKSA cache entry
+        * @pmkid: PMKID for the PMKSA cache entry
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is called when the supplicant drops a PMKSA cache
+        * entry for any reason.
+        *
+        * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+        * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+        * between the driver and wpa_supplicant. If the driver uses wpa_ie
+        * from wpa_supplicant, this driver_ops function does not need to be
+        * implemented. Likewise, if the driver does not support WPA, this
+        * function is not needed.
+        */
+       int (*remove_pmkid)(void *priv, const u8 *bssid, const u8 *pmkid);
+
+       /**
+        * flush_pmkid - Flush PMKSA cache
+        * @priv: private driver interface data
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is called when the supplicant drops all PMKSA cache
+        * entries for any reason.
+        *
+        * If the driver generates RSN IE, i.e., it does not use wpa_ie in
+        * associate(), remove_pmkid() can be used to synchronize PMKSA caches
+        * between the driver and wpa_supplicant. If the driver uses wpa_ie
+        * from wpa_supplicant, this driver_ops function does not need to be
+        * implemented. Likewise, if the driver does not support WPA, this
+        * function is not needed.
+        */
+       int (*flush_pmkid)(void *priv);
+
+       /**
+        * get_capa - Get driver capabilities
+        * @priv: private driver interface data
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * Get driver/firmware/hardware capabilities.
+        */
+       int (*get_capa)(void *priv, struct wpa_driver_capa *capa);
+
+       /**
+        * poll - Poll driver for association information
+        * @priv: private driver interface data
+        *
+        * This is an option callback that can be used when the driver does not
+        * provide event mechanism for association events. This is called when
+        * receiving WPA EAPOL-Key messages that require association
+        * information. The driver interface is supposed to generate associnfo
+        * event before returning from this callback function. In addition, the
+        * driver interface should generate an association event after having
+        * sent out associnfo.
+        */
+       void (*poll)(void *priv);
+
+       /**
+        * get_ifname - Get interface name
+        * @priv: private driver interface data
+        *
+        * Returns: Pointer to the interface name. This can differ from the
+        * interface name used in init() call. Init() is called first.
+        *
+        * This optional function can be used to allow the driver interface to
+        * replace the interface name with something else, e.g., based on an
+        * interface mapping from a more descriptive name.
+        */
+       const char * (*get_ifname)(void *priv);
+
+       /**
+        * get_mac_addr - Get own MAC address
+        * @priv: private driver interface data
+        *
+        * Returns: Pointer to own MAC address or %NULL on failure
+        *
+        * This optional function can be used to get the own MAC address of the
+        * device from the driver interface code. This is only needed if the
+        * l2_packet implementation for the OS does not provide easy access to
+        * a MAC address. */
+       const u8 * (*get_mac_addr)(void *priv);
+
+       /**
+        * send_eapol - Optional function for sending EAPOL packets
+        * @priv: private driver interface data
+        * @dest: Destination MAC address
+        * @proto: Ethertype
+        * @data: EAPOL packet starting with IEEE 802.1X header
+        * @data_len: Size of the EAPOL packet
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * This optional function can be used to override l2_packet operations
+        * with driver specific functionality. If this function pointer is set,
+        * l2_packet module is not used at all and the driver interface code is
+        * responsible for receiving and sending all EAPOL packets. The
+        * received EAPOL packets are sent to core code with EVENT_EAPOL_RX
+        * event. The driver interface is required to implement get_mac_addr()
+        * handler if send_eapol() is used.
+        */
+       int (*send_eapol)(void *priv, const u8 *dest, u16 proto,
+                         const u8 *data, size_t data_len);
+
+       /**
+        * set_operstate - Sets device operating state to DORMANT or UP
+        * @priv: private driver interface data
+        * @state: 0 = dormant, 1 = up
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function that can be used on operating systems
+        * that support a concept of controlling network device state from user
+        * space applications. This function, if set, gets called with
+        * state = 1 when authentication has been completed and with state = 0
+        * when connection is lost.
+        */
+       int (*set_operstate)(void *priv, int state);
+
+       /**
+        * mlme_setprotection - MLME-SETPROTECTION.request primitive
+        * @priv: Private driver interface data
+        * @addr: Address of the station for which to set protection (may be
+        * %NULL for group keys)
+        * @protect_type: MLME_SETPROTECTION_PROTECT_TYPE_*
+        * @key_type: MLME_SETPROTECTION_KEY_TYPE_*
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function that can be used to set the driver to
+        * require protection for Tx and/or Rx frames. This uses the layer
+        * interface defined in IEEE 802.11i-2004 clause 10.3.22.1
+        * (MLME-SETPROTECTION.request). Many drivers do not use explicit
+        * set protection operation; instead, they set protection implicitly
+        * based on configured keys.
+        */
+       int (*mlme_setprotection)(void *priv, const u8 *addr, int protect_type,
+                                 int key_type);
+
+       /**
+        * get_hw_feature_data - Get hardware support data (channels and rates)
+        * @priv: Private driver interface data
+        * @num_modes: Variable for returning the number of returned modes
+        * flags: Variable for returning hardware feature flags
+        * Returns: Pointer to allocated hardware data on success or %NULL on
+        * failure. Caller is responsible for freeing this.
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to %wpa_supplicant or hostapd.
+        */
+       struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv,
+                                                        u16 *num_modes,
+                                                        u16 *flags);
+
+       /**
+        * set_channel - Set channel
+        * @priv: Private driver interface data
+        * @phymode: HOSTAPD_MODE_IEEE80211B, ..
+        * @chan: IEEE 802.11 channel number
+        * @freq: Frequency of the channel in MHz
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to wpa_supplicant.
+        */
+       int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan,
+                          int freq);
+
+       /**
+        * set_ssid - Set SSID
+        * @priv: Private driver interface data
+        * @ssid: SSID
+        * @ssid_len: SSID length
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to wpa_supplicant.
+        */
+       int (*set_ssid)(void *priv, const u8 *ssid, size_t ssid_len);
+
+       /**
+        * set_bssid - Set BSSID
+        * @priv: Private driver interface data
+        * @bssid: BSSID
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to wpa_supplicant.
+        */
+       int (*set_bssid)(void *priv, const u8 *bssid);
+
+       /**
+        * send_mlme - Send management frame from MLME
+        * @priv: Private driver interface data
+        * @data: IEEE 802.11 management frame with IEEE 802.11 header
+        * @data_len: Size of the management frame
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to wpa_supplicant.
+        */
+       int (*send_mlme)(void *priv, const u8 *data, size_t data_len);
+
+       /**
+        * mlme_add_sta - Add a STA entry into the driver/netstack
+        * @priv: Private driver interface data
+        * @addr: MAC address of the STA (e.g., BSSID of the AP)
+        * @supp_rates: Supported rate set (from (Re)AssocResp); in IEEE 802.11
+        * format (one octet per rate, 1 = 0.5 Mbps)
+        * @supp_rates_len: Number of entries in supp_rates
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to wpa_supplicant. When the MLME code
+        * completes association with an AP, this function is called to
+        * configure the driver/netstack with a STA entry for data frame
+        * processing (TX rate control, encryption/decryption).
+        */
+       int (*mlme_add_sta)(void *priv, const u8 *addr, const u8 *supp_rates,
+                           size_t supp_rates_len);
+
+       /**
+        * mlme_remove_sta - Remove a STA entry from the driver/netstack
+        * @priv: Private driver interface data
+        * @addr: MAC address of the STA (e.g., BSSID of the AP)
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only needed for drivers that export MLME
+        * (management frame processing) to wpa_supplicant.
+        */
+       int (*mlme_remove_sta)(void *priv, const u8 *addr);
+
+       /**
+        * update_ft_ies - Update FT (IEEE 802.11r) IEs
+        * @priv: Private driver interface data
+        * @md: Mobility domain (2 octets) (also included inside ies)
+        * @ies: FT IEs (MDIE, FTIE, ...) or %NULL to remove IEs
+        * @ies_len: Length of FT IEs in bytes
+        * Returns: 0 on success, -1 on failure
+        *
+        * The supplicant uses this callback to let the driver know that keying
+        * material for FT is available and that the driver can use the
+        * provided IEs in the next message in FT authentication sequence.
+        *
+        * This function is only needed for driver that support IEEE 802.11r
+        * (Fast BSS Transition).
+        */
+       int (*update_ft_ies)(void *priv, const u8 *md, const u8 *ies,
+                            size_t ies_len);
+
+       /**
+        * send_ft_action - Send FT Action frame (IEEE 802.11r)
+        * @priv: Private driver interface data
+        * @action: Action field value
+        * @target_ap: Target AP address
+        * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body)
+        * @ies_len: Length of FT IEs in bytes
+        * Returns: 0 on success, -1 on failure
+        *
+        * The supplicant uses this callback to request the driver to transmit
+        * an FT Action frame (action category 6) for over-the-DS fast BSS
+        * transition.
+        */
+       int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap,
+                             const u8 *ies, size_t ies_len);
+
+       /**
+        * get_scan_results2 - Fetch the latest scan results
+        * @priv: private driver interface data
+        *
+        * Returns: Allocated buffer of scan results (caller is responsible for
+        * freeing the data structure) on success, NULL on failure
+        */
+        struct wpa_scan_results * (*get_scan_results2)(void *priv);
+
+       /**
+        * set_country - Set country
+        * @priv: Private driver interface data
+        * @alpha2: country to which to switch to
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is for drivers which support some form
+        * of setting a regulatory domain.
+        */
+       int (*set_country)(void *priv, const char *alpha2);
+
+       /**
+        * global_init - Global driver initialization
+        * Returns: Pointer to private data (global), %NULL on failure
+        *
+        * This optional function is called to initialize the driver wrapper
+        * for global data, i.e., data that applies to all interfaces. If this
+        * function is implemented, global_deinit() will also need to be
+        * implemented to free the private data. The driver will also likely
+        * use init2() function instead of init() to get the pointer to global
+        * data available to per-interface initializer.
+        */
+       void * (*global_init)(void);
+
+       /**
+        * global_deinit - Global driver deinitialization
+        * @priv: private driver global data from global_init()
+        *
+        * Terminate any global driver related functionality and free the
+        * global data structure.
+        */
+       void (*global_deinit)(void *priv);
+
+       /**
+        * init2 - Initialize driver interface (with global data)
+        * @ctx: context to be used when calling wpa_supplicant functions,
+        * e.g., wpa_supplicant_event()
+        * @ifname: interface name, e.g., wlan0
+        * @global_priv: private driver global data from global_init()
+        * Returns: Pointer to private data, %NULL on failure
+        *
+        * This function can be used instead of init() if the driver wrapper
+        * uses global data.
+        */
+       void * (*init2)(void *ctx, const char *ifname, void *global_priv);
+
+       /**
+        * get_interfaces - Get information about available interfaces
+        * @global_priv: private driver global data from global_init()
+        * Returns: Allocated buffer of interface information (caller is
+        * responsible for freeing the data structure) on success, NULL on
+        * failure
+        */
+       struct wpa_interface_info * (*get_interfaces)(void *global_priv);
+
+       /**
+        * scan2 - Request the driver to initiate scan
+        * @priv: private driver interface data
+        * @params: Scan parameters
+        *
+        * Returns: 0 on success, -1 on failure
+        *
+        * Once the scan results are ready, the driver should report scan
+        * results event for wpa_supplicant which will eventually request the
+        * results with wpa_driver_get_scan_results2().
+        */
+       int (*scan2)(void *priv, struct wpa_driver_scan_params *params);
+
+       /**
+        * authenticate - Request driver to authenticate
+        * @priv: private driver interface data
+        * @params: authentication parameters
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function that can be used with drivers that
+        * support separate authentication and association steps, i.e., when
+        * wpa_supplicant can act as the SME. If not implemented, associate()
+        * function is expected to take care of IEEE 802.11 authentication,
+        * too.
+        */
+       int (*authenticate)(void *priv,
+                           struct wpa_driver_auth_params *params);
+
+       /**
+        * set_beacon - Set Beacon frame template
+        * @priv: Private driver interface data
+        * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE
+        * @head_len: Length of the head buffer in octets
+        * @tail: Beacon tail following TIM IE
+        * @tail_len: Length of the tail buffer in octets
+        * @dtim_period: DTIM period
+        * @beacon_int: Beacon interval
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is used to configure Beacon template for the driver in
+        * AP mode. The driver is responsible for building the full Beacon
+        * frame by concatenating the head part with TIM IE generated by the
+        * driver/firmware and finishing with the tail part.
+        */
+       int (*set_beacon)(void *priv, const u8 *head, size_t head_len,
+                         const u8 *tail, size_t tail_len, int dtim_period,
+                         int beacon_int);
+
+       /**
+        * hapd_init - Initialize driver interface (hostapd only)
+        * @hapd: Pointer to hostapd context
+        * @params: Configuration for the driver wrapper
+        * Returns: Pointer to private data, %NULL on failure
+        *
+        * This function is used instead of init() or init2() when the driver
+        * wrapper is used withh hostapd.
+        */
+       void * (*hapd_init)(struct hostapd_data *hapd,
+                           struct wpa_init_params *params);
+
+       /**
+        * hapd_deinit - Deinitialize driver interface (hostapd only)
+        * @priv: Private driver interface data from hapd_init()
+        */
+       void (*hapd_deinit)(void *priv);
+
+       /**
+        * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only)
+        * @priv: Private driver interface data
+        * @params: BSS parameters
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function to configure the kernel driver to
+        * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This
+        * can be left undefined (set to %NULL) if IEEE 802.1X support is
+        * always enabled and the driver uses set_beacon() to set WPA/RSN IE
+        * for Beacon frames.
+        */
+       int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params);
+
+       /**
+        * set_privacy - Enable/disable privacy (AP only)
+        * @priv: Private driver interface data
+        * @enabled: 1 = privacy enabled, 0 = disabled
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function to configure privacy field in the
+        * kernel driver for Beacon frames. This can be left undefined (set to
+        * %NULL) if the driver uses the Beacon template from set_beacon().
+        */
+       int (*set_privacy)(void *priv, int enabled);
+
+       /**
+        * get_seqnum - Fetch the current TSC/packet number (AP only)
+        * @ifname: The interface name (main or virtual)
+        * @priv: Private driver interface data
+        * @addr: MAC address of the station or %NULL for group keys
+        * @idx: Key index
+        * @seq: Buffer for returning the latest used TSC/packet number
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is used to fetch the last used TSC/packet number for
+        * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so
+        * there is no strict requirement on implementing support for unicast
+        * keys (i.e., addr != %NULL).
+        */
+       int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr,
+                         int idx, u8 *seq);
+
+       /**
+        * flush - Flush all association stations (AP only)
+        * @priv: Private driver interface data
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function requests the driver to disassociate all associated
+        * stations. This function does not need to be implemented if the
+        * driver does not process association frames internally.
+        */
+       int (*flush)(void *priv);
+
+       /**
+        * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP)
+        * @priv: Private driver interface data
+        * @elem: Information elements
+        * @elem_len: Length of the elem buffer in octets
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function to add information elements in the
+        * kernel driver for Beacon and Probe Response frames. This can be left
+        * undefined (set to %NULL) if the driver uses the Beacon template from
+        * set_beacon().
+        */
+       int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len);
+
+       /**
+        * read_sta_data - Fetch station data (AP only)
+        * @priv: Private driver interface data
+        * @data: Buffer for returning station information
+        * @addr: MAC address of the station
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data,
+                            const u8 *addr);
+
+       /**
+        * hapd_send_eapol - Send an EAPOL packet (AP only)
+        * @priv: private driver interface data
+        * @addr: Destination MAC address
+        * @data: EAPOL packet starting with IEEE 802.1X header
+        * @data_len: Length of the EAPOL packet in octets
+        * @encrypt: Whether the frame should be encrypted
+        * @own_addr: Source MAC address
+        *
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data,
+                              size_t data_len, int encrypt,
+                              const u8 *own_addr);
+
+       /**
+        * sta_deauth - Deauthenticate a station (AP only)
+        * @priv: Private driver interface data
+        * @own_addr: Source address and BSSID for the Deauthentication frame
+        * @addr: MAC address of the station to deauthenticate
+        * @reason: Reason code for the Deauthentiation frame
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function requests a specific station to be deauthenticated and
+        * a Deauthentication frame to be sent to it.
+        */
+       int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr,
+                         int reason);
+
+       /**
+        * sta_disassoc - Disassociate a station (AP only)
+        * @priv: Private driver interface data
+        * @own_addr: Source address and BSSID for the Disassociation frame
+        * @addr: MAC address of the station to disassociate
+        * @reason: Reason code for the Disassociation frame
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function requests a specific station to be disassociated and
+        * a Disassociation frame to be sent to it.
+        */
+       int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr,
+                           int reason);
+
+       /**
+        * sta_remove - Remove a station entry (AP only)
+        * @priv: Private driver interface data
+        * @addr: MAC address of the station to be removed
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*sta_remove)(void *priv, const u8 *addr);
+
+       /**
+        * hapd_get_ssid - Get the current SSID (AP only)
+        * @priv: Private driver interface data
+        * @buf: Buffer for returning the SSID
+        * @len: Maximum length of the buffer
+        * Returns: Length of the SSID on success, -1 on failure
+        *
+        * This function need not be implemented if the driver uses Beacon
+        * template from set_beacon() and does not reply to Probe Request
+        * frames.
+        */
+       int (*hapd_get_ssid)(void *priv, u8 *buf, int len);
+
+       /**
+        * hapd_set_ssid - Set SSID (AP only)
+        * @priv: Private driver interface data
+        * @buf: SSID
+        * @len: Length of the SSID in octets
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*hapd_set_ssid)(void *priv, const u8 *buf, int len);
+
+       /**
+        * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP)
+        * @priv: Private driver interface data
+        * @enabled: 1 = countermeasures enabled, 0 = disabled
+        * Returns: 0 on success, -1 on failure
+        *
+        * This need not be implemented if the driver does not take care of
+        * association processing.
+        */
+       int (*hapd_set_countermeasures)(void *priv, int enabled);
+
+       /**
+        * sta_add - Add a station entry
+        * @priv: Private driver interface data
+        * @params: Station parameters
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is used to add a station entry to the driver once the
+        * station has completed association. This is only used if the driver
+        * does not take care of association processing.
+        */
+       int (*sta_add)(void *priv, struct hostapd_sta_add_params *params);
+
+       /**
+        * get_inact_sec - Get station inactivity duration (AP only)
+        * @priv: Private driver interface data
+        * @addr: Station address
+        * Returns: Number of seconds station has been inactive, -1 on failure
+        */
+       int (*get_inact_sec)(void *priv, const u8 *addr);
+
+       /**
+        * sta_clear_stats - Clear station statistics (AP only)
+        * @priv: Private driver interface data
+        * @addr: Station address
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*sta_clear_stats)(void *priv, const u8 *addr);
+
+       /**
+        * set_freq - Set channel/frequency (AP only)
+        * @priv: Private driver interface data
+        * @freq: Channel parameters
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_freq)(void *priv, struct hostapd_freq_params *freq);
+
+       /**
+        * set_rts - Set RTS threshold
+        * @priv: Private driver interface data
+        * @rts: RTS threshold in octets
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_rts)(void *priv, int rts);
+
+       /**
+        * set_frag - Set fragmentation threshold
+        * @priv: Private driver interface data
+        * @frag: Fragmentation threshold in octets
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_frag)(void *priv, int frag);
+
+       /**
+        * sta_set_flags - Set station flags (AP only)
+        * @priv: Private driver interface data
+        * @addr: Station address
+        * @total_flags: Bitmap of all WPA_STA_* flags currently set
+        * @flags_or: Bitmap of WPA_STA_* flags to add
+        * @flags_and: Bitmap of WPA_STA_* flags to us as a mask
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*sta_set_flags)(void *priv, const u8 *addr,
+                            int total_flags, int flags_or, int flags_and);
+
+       /**
+        * set_rate_sets - Set supported and basic rate sets (AP only)
+        * @priv: Private driver interface data
+        * @supp_rates: -1 terminated array of supported rates in 100 kbps
+        * @basic_rates: -1 terminated array of basic rates in 100 kbps
+        * @mode: hardware mode (HOSTAPD_MODE_*)
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates,
+                            int mode);
+
+       /**
+        * set_cts_protect - Set CTS protection mode (AP only)
+        * @priv: Private driver interface data
+        * @value: Whether CTS protection is enabled
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_cts_protect)(void *priv, int value);
+
+       /**
+        * set_preamble - Set preamble mode (AP only)
+        * @priv: Private driver interface data
+        * @value: Whether short preamble is enabled
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_preamble)(void *priv, int value);
+
+       /**
+        * set_short_slot_time - Set short slot time (AP only)
+        * @priv: Private driver interface data
+        * @value: Whether short slot time is enabled
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_short_slot_time)(void *priv, int value);
+
+       /**
+        * set_tx_queue_params - Set TX queue parameters
+        * @priv: Private driver interface data
+        * @queue: Queue number
+        * @aifs: AIFS
+        * @cw_min: cwMin
+        * @cw_max: cwMax
+        * @burst_time: Maximum length for bursting in 0.1 msec units
+        */
+       int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min,
+                                  int cw_max, int burst_time);
+
+       /**
+        * valid_bss_mask - Validate BSSID mask
+        * @priv: Private driver interface data
+        * @addr: Address
+        * @mask: Mask
+        * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can
+        * be used, but the main interface address must be the first address in
+        * the block if mask is applied
+        */
+       int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask);
+
+       /**
+        * if_add - Add a virtual interface
+        * @priv: Private driver interface data
+        * @type: Interface type
+        * @ifname: Interface name for the new virtual interface
+        * @addr: Local address to use for the interface or %NULL to use the
+        *      parent interface address
+        * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces
+        * @drv_priv: Pointer for overwriting the driver context or %NULL if
+        *      not allowed (applies only to %WPA_IF_AP_BSS type)
+        * @force_ifname: Buffer for returning an interface name that the
+        *      driver ended up using if it differs from the requested ifname
+        * @if_addr: Buffer for returning the allocated interface address
+        *      (this may differ from the requested addr if the driver cannot
+        *      change interface address)
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*if_add)(void *priv, enum wpa_driver_if_type type,
+                     const char *ifname, const u8 *addr, void *bss_ctx,
+                     void **drv_priv, char *force_ifname, u8 *if_addr);
+
+       /**
+        * if_remove - Remove a virtual interface
+        * @priv: Private driver interface data
+        * @type: Interface type
+        * @ifname: Interface name of the virtual interface to be removed
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*if_remove)(void *priv, enum wpa_driver_if_type type,
+                        const char *ifname);
+
+       /**
+        * set_sta_vlan - Bind a station into a specific interface (AP only)
+        * @priv: Private driver interface data
+        * @ifname: Interface (main or virtual BSS or VLAN)
+        * @addr: MAC address of the associated station
+        * @vlan_id: VLAN ID
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is used to bind a station to a specific virtual
+        * interface. It is only used if when virtual interfaces are supported,
+        * e.g., to assign stations to different VLAN interfaces based on
+        * information from a RADIUS server. This allows separate broadcast
+        * domains to be used with a single BSS.
+        */
+       int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname,
+                           int vlan_id);
+
+       /**
+        * commit - Optional commit changes handler (AP only)
+        * @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);
+
+       /**
+        * send_ether - Send an ethernet packet (AP only)
+        * @priv: private driver interface data
+        * @dst: Destination MAC address
+        * @src: Source MAC address
+        * @proto: Ethertype
+        * @data: EAPOL packet starting with IEEE 802.1X header
+        * @data_len: Length of the EAPOL packet in octets
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto,
+                         const u8 *data, size_t data_len);
+
+       /**
+        * set_radius_acl_auth - Notification of RADIUS ACL change
+        * @priv: Private driver interface data
+        * @mac: MAC address of the station
+        * @accepted: Whether the station was accepted
+        * @session_timeout: Session timeout for the station
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, 
+                                  u32 session_timeout);
+
+       /**
+        * set_radius_acl_expire - Notification of RADIUS ACL expiration
+        * @priv: Private driver interface data
+        * @mac: MAC address of the station
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_radius_acl_expire)(void *priv, const u8 *mac);
+
+       /**
+        * set_ht_params - Set HT parameters (AP only)
+        * @priv: Private driver interface data
+        * @ht_capab: HT Capabilities IE
+        * @ht_capab_len: Length of ht_capab in octets
+        * @ht_oper: HT Operation IE
+        * @ht_oper_len: Length of ht_oper in octets
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_ht_params)(void *priv,
+                            const u8 *ht_capab, size_t ht_capab_len,
+                            const u8 *ht_oper, size_t ht_oper_len);
+
+       /**
+        * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP)
+        * @priv: Private driver interface data
+        * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s)
+        * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove
+        *      extra IE(s)
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is an optional function to add WPS IE in the kernel driver for
+        * Beacon and Probe Response frames. This can be left undefined (set
+        * to %NULL) if the driver uses the Beacon template from set_beacon()
+        * and does not process Probe Request frames.
+        */
+       int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon,
+                            const struct wpabuf *proberesp);
+
+       /**
+        * set_supp_port - Set IEEE 802.1X Supplicant Port status
+        * @priv: Private driver interface data
+        * @authorized: Whether the port is authorized
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_supp_port)(void *priv, int authorized);
+
+       /**
+        * set_wds_sta - Bind a station into a 4-address WDS (AP only)
+        * @priv: Private driver interface data
+        * @addr: MAC address of the associated station
+        * @aid: Association ID
+        * @val: 1 = bind to 4-address WDS; 0 = unbind
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val);
+
+       /**
+        * send_action - Transmit an Action frame
+        * @priv: Private driver interface data
+        * @freq: Frequency (in MHz) of the channel
+        * @dst: Destination MAC address (Address 1)
+        * @src: Source MAC address (Address 2)
+        * @bssid: BSSID (Address 3)
+        * @data: Frame body
+        * @data_len: data length in octets
+        * Returns: 0 on success, -1 on failure
+        *
+        * This command can be used to request the driver to transmit an action
+        * frame to the specified destination. If a remain-on-channel duration
+        * is in progress, the frame is transmitted on that channel. Otherwise,
+        * the frame is transmitted on the current operational channel if in
+        * associated state in station mode or if operating as an AP. If none
+        * of these conditions is in effect, send_action() cannot be used.
+        */
+       int (*send_action)(void *priv, unsigned int freq,
+                          const u8 *dst, const u8 *src, const u8 *bssid,
+                          const u8 *data, size_t data_len);
+
+       /**
+        * remain_on_channel - Remain awake on a channel
+        * @priv: Private driver interface data
+        * @freq: Frequency (in MHz) of the channel
+        * @duration: Duration in milliseconds
+        * Returns: 0 on success, -1 on failure
+        *
+        * This command is used to request the driver to remain awake on the
+        * specified channel for the specified duration and report received
+        * Action frames with EVENT_RX_ACTION events. Optionally, received
+        * Probe Request frames may also be requested to be reported by calling
+        * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
+        *
+        * The driver may not be at the requested channel when this function
+        * returns, i.e., the return code is only indicating whether the
+        * request was accepted. The caller will need to wait until the
+        * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has
+        * completed the channel change. This may take some time due to other
+        * need for the radio and the caller should be prepared to timing out
+        * its wait since there are no guarantees on when this request can be
+        * executed.
+        */
+       int (*remain_on_channel)(void *priv, unsigned int freq,
+                                unsigned int duration);
+
+       /**
+        * cancel_remain_on_channel - Cancel remain-on-channel operation
+        * @priv: Private driver interface data
+        *
+        * This command can be used to cancel a remain-on-channel operation
+        * before its originally requested duration has passed. This could be
+        * used, e.g., when remain_on_channel() is used to request extra time
+        * to receive a response to an Action frame and the response is
+        * received when there is still unneeded time remaining on the
+        * remain-on-channel operation.
+        */
+       int (*cancel_remain_on_channel)(void *priv);
+
+       /**
+        * probe_req_report - Request Probe Request frames to be indicated
+        * @priv: Private driver interface data
+        * @report: Whether to report received Probe Request frames
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        *
+        * This command can be used to request the driver to indicate when
+        * Probe Request frames are received with EVENT_RX_PROBE_REQ events.
+        * Since this operation may require extra resources, e.g., due to less
+        * optimal hardware/firmware RX filtering, many drivers may disable
+        * Probe Request reporting at least in station mode. This command is
+        * used to notify the driver when the Probe Request frames need to be
+        * reported, e.g., during remain-on-channel operations.
+        */
+       int (*probe_req_report)(void *priv, int report);
+
+       /**
+        * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX
+        * @priv: Private driver interface data
+        * @disabled: Whether IEEE 802.11b rates are disabled
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        *
+        * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and
+        * 11 Mbps) as TX rates for data and management frames. This can be
+        * used to optimize channel use when there is no need to support IEEE
+        * 802.11b-only devices.
+        */
+       int (*disable_11b_rates)(void *priv, int disabled);
+
+       /**
+        * deinit_ap - Deinitialize AP mode
+        * @priv: Private driver interface data
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        *
+        * This optional function can be used to disable AP mode related
+        * configuration and change the driver mode to station mode to allow
+        * normal station operations like scanning to be completed.
+        */
+       int (*deinit_ap)(void *priv);
+
+       /**
+        * suspend - Notification on system suspend/hibernate event
+        * @priv: Private driver interface data
+        */
+       void (*suspend)(void *priv);
+
+       /**
+        * resume - Notification on system resume/thaw event
+        * @priv: Private driver interface data
+        */
+       void (*resume)(void *priv);
+
+       /**
+        * signal_monitor - Set signal monitoring parameters
+        * @priv: Private driver interface data
+        * @threshold: Threshold value for signal change events; 0 = disabled
+        * @hysteresis: Minimum change in signal strength before indicating a
+        *      new event
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        *
+        * This function can be used to configure monitoring of signal strength
+        * with the current AP. Whenever signal strength drops below the
+        * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event
+        * should be generated assuming the signal strength has changed at
+        * least %hysteresis from the previously indicated signal change event.
+        */
+       int (*signal_monitor)(void *priv, int threshold, int hysteresis);
+
+       /**
+        * send_frame - Send IEEE 802.11 frame (testing use only)
+        * @priv: Private driver interface data
+        * @data: IEEE 802.11 frame with IEEE 802.11 header
+        * @data_len: Size of the frame
+        * @encrypt: Whether to encrypt the frame (if keys are set)
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function is only used for debugging purposes and is not
+        * required to be implemented for normal operations.
+        */
+       int (*send_frame)(void *priv, const u8 *data, size_t data_len,
+                         int encrypt);
+};
+
+
+/**
+ * enum wpa_event_type - Event type for wpa_supplicant_event() calls
+ */
+enum wpa_event_type {
+       /**
+        * EVENT_ASSOC - Association completed
+        *
+        * This event needs to be delivered when the driver completes IEEE
+        * 802.11 association or reassociation successfully.
+        * wpa_driver_ops::get_bssid() is expected to provide the current BSSID
+        * after this event has been generated. In addition, optional
+        * EVENT_ASSOCINFO may be generated just before EVENT_ASSOC to provide
+        * more information about the association. If the driver interface gets
+        * both of these events at the same time, it can also include the
+        * assoc_info data in EVENT_ASSOC call.
+        */
+       EVENT_ASSOC,
+
+       /**
+        * EVENT_DISASSOC - Association lost
+        *
+        * This event should be called when association is lost either due to
+        * receiving deauthenticate or disassociate frame from the AP or when
+        * sending either of these frames to the current AP. If the driver
+        * supports separate deauthentication event, EVENT_DISASSOC should only
+        * be used for disassociation and EVENT_DEAUTH for deauthentication.
+        * In AP mode, union wpa_event_data::disassoc_info is required.
+        */
+       EVENT_DISASSOC,
+
+       /**
+        * EVENT_MICHAEL_MIC_FAILURE - Michael MIC (TKIP) detected
+        *
+        * This event must be delivered when a Michael MIC error is detected by
+        * the local driver. Additional data for event processing is
+        * provided with union wpa_event_data::michael_mic_failure. This
+        * information is used to request new encyption key and to initiate
+        * TKIP countermeasures if needed.
+        */
+       EVENT_MICHAEL_MIC_FAILURE,
+
+       /**
+        * EVENT_SCAN_RESULTS - Scan results available
+        *
+        * This event must be called whenever scan results are available to be
+        * fetched with struct wpa_driver_ops::get_scan_results(). This event
+        * is expected to be used some time after struct wpa_driver_ops::scan()
+        * is called. If the driver provides an unsolicited event when the scan
+        * has been completed, this event can be used to trigger
+        * EVENT_SCAN_RESULTS call. If such event is not available from the
+        * driver, the driver wrapper code is expected to use a registered
+        * timeout to generate EVENT_SCAN_RESULTS call after the time that the
+        * scan is expected to be completed. Optional information about
+        * completed scan can be provided with union wpa_event_data::scan_info.
+        */
+       EVENT_SCAN_RESULTS,
+
+       /**
+        * EVENT_ASSOCINFO - Report optional extra information for association
+        *
+        * This event can be used to report extra association information for
+        * EVENT_ASSOC processing. This extra information includes IEs from
+        * association frames and Beacon/Probe Response frames in union
+        * wpa_event_data::assoc_info. EVENT_ASSOCINFO must be send just before
+        * EVENT_ASSOC. Alternatively, the driver interface can include
+        * assoc_info data in the EVENT_ASSOC call if it has all the
+        * information available at the same point.
+        */
+       EVENT_ASSOCINFO,
+
+       /**
+        * EVENT_INTERFACE_STATUS - Report interface status changes
+        *
+        * This optional event can be used to report changes in interface
+        * status (interface added/removed) using union
+        * wpa_event_data::interface_status. This can be used to trigger
+        * wpa_supplicant to stop and re-start processing for the interface,
+        * e.g., when a cardbus card is ejected/inserted.
+        */
+       EVENT_INTERFACE_STATUS,
+
+       /**
+        * EVENT_PMKID_CANDIDATE - Report a candidate AP for pre-authentication
+        *
+        * This event can be used to inform wpa_supplicant about candidates for
+        * RSN (WPA2) pre-authentication. If wpa_supplicant is not responsible
+        * for scan request (ap_scan=2 mode), this event is required for
+        * pre-authentication. If wpa_supplicant is performing scan request
+        * (ap_scan=1), this event is optional since scan results can be used
+        * to add pre-authentication candidates. union
+        * wpa_event_data::pmkid_candidate is used to report the BSSID of the
+        * candidate and priority of the candidate, e.g., based on the signal
+        * strength, in order to try to pre-authenticate first with candidates
+        * that are most likely targets for re-association.
+        *
+        * EVENT_PMKID_CANDIDATE can be called whenever the driver has updates
+        * on the candidate list. In addition, it can be called for the current
+        * AP and APs that have existing PMKSA cache entries. wpa_supplicant
+        * will automatically skip pre-authentication in cases where a valid
+        * PMKSA exists. When more than one candidate exists, this event should
+        * be generated once for each candidate.
+        *
+        * Driver will be notified about successful pre-authentication with
+        * struct wpa_driver_ops::add_pmkid() calls.
+        */
+       EVENT_PMKID_CANDIDATE,
+
+       /**
+        * EVENT_STKSTART - Request STK handshake (MLME-STKSTART.request)
+        *
+        * This event can be used to inform wpa_supplicant about desire to set
+        * up secure direct link connection between two stations as defined in
+        * IEEE 802.11e with a new PeerKey mechanism that replaced the original
+        * STAKey negotiation. The caller will need to set peer address for the
+        * event.
+        */
+       EVENT_STKSTART,
+
+       /**
+        * EVENT_FT_RESPONSE - Report FT (IEEE 802.11r) response IEs
+        *
+        * The driver is expected to report the received FT IEs from
+        * FT authentication sequence from the AP. The FT IEs are included in
+        * the extra information in union wpa_event_data::ft_ies.
+        */
+       EVENT_FT_RESPONSE,
+
+       /**
+        * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS
+        *
+        * The driver can use this event to inform wpa_supplicant about a STA
+        * in an IBSS with which protected frames could be exchanged. This
+        * event starts RSN authentication with the other STA to authenticate
+        * the STA and set up encryption keys with it.
+        */
+       EVENT_IBSS_RSN_START,
+
+       /**
+        * EVENT_AUTH - Authentication result
+        *
+        * This event should be called when authentication attempt has been
+        * completed. This is only used if the driver supports separate
+        * authentication step (struct wpa_driver_ops::authenticate).
+        * Information about authentication result is included in
+        * union wpa_event_data::auth.
+        */
+       EVENT_AUTH,
+
+       /**
+        * EVENT_DEAUTH - Authentication lost
+        *
+        * This event should be called when authentication is lost either due
+        * to receiving deauthenticate frame from the AP or when sending that
+        * frame to the current AP.
+        * In AP mode, union wpa_event_data::deauth_info is required.
+        */
+       EVENT_DEAUTH,
+
+       /**
+        * EVENT_ASSOC_REJECT - Association rejected
+        *
+        * This event should be called when (re)association attempt has been
+        * rejected by the AP. Information about authentication result is
+        * included in union wpa_event_data::assoc_reject.
+        */
+       EVENT_ASSOC_REJECT,
+
+       /**
+        * EVENT_AUTH_TIMED_OUT - Authentication timed out
+        */
+       EVENT_AUTH_TIMED_OUT,
+
+       /**
+        * EVENT_ASSOC_TIMED_OUT - Association timed out
+        */
+       EVENT_ASSOC_TIMED_OUT,
+
+       /**
+        * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received
+        */
+       EVENT_FT_RRB_RX,
+
+       /**
+        * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
+        */
+       EVENT_WPS_BUTTON_PUSHED,
+
+       /**
+        * EVENT_TX_STATUS - Report TX status
+        */
+       EVENT_TX_STATUS,
+
+       /**
+        * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA
+        */
+       EVENT_RX_FROM_UNKNOWN,
+
+       /**
+        * EVENT_RX_MGMT - Report RX of a management frame
+        */
+       EVENT_RX_MGMT,
+
+       /**
+        * EVENT_RX_ACTION - Action frame received
+        *
+        * This event is used to indicate when an Action frame has been
+        * received. Information about the received frame is included in
+        * union wpa_event_data::rx_action.
+        */
+       EVENT_RX_ACTION,
+
+       /**
+        * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
+        *
+        * This event is used to indicate when the driver has started the
+        * requested remain-on-channel duration. Information about the
+        * operation is included in union wpa_event_data::remain_on_channel.
+        */
+       EVENT_REMAIN_ON_CHANNEL,
+
+       /**
+        * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out
+        *
+        * This event is used to indicate when the driver has completed
+        * remain-on-channel duration, i.e., may noot be available on the
+        * requested channel anymore. Information about the
+        * operation is included in union wpa_event_data::remain_on_channel.
+        */
+       EVENT_CANCEL_REMAIN_ON_CHANNEL,
+
+       /**
+        * EVENT_MLME_RX - Report reception of frame for MLME (test use only)
+        *
+        * This event is used only by driver_test.c and userspace MLME.
+        */
+       EVENT_MLME_RX,
+
+       /**
+        * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
+        *
+        * This event is used to indicate when a Probe Request frame has been
+        * received. Information about the received frame is included in
+        * union wpa_event_data::rx_probe_req. The driver is required to report
+        * these events only after successfully completed probe_req_report()
+        * commands to request the events (i.e., report parameter is non-zero)
+        * in station mode. In AP mode, Probe Request frames should always be
+        * reported.
+        */
+       EVENT_RX_PROBE_REQ,
+
+       /**
+        * EVENT_NEW_STA - New wired device noticed
+        *
+        * This event is used to indicate that a new device has been detected
+        * in a network that does not use association-like functionality (i.e.,
+        * mainly wired Ethernet). This can be used to start EAPOL
+        * authenticator when receiving a frame from a device. The address of
+        * the device is included in union wpa_event_data::new_sta.
+        */
+       EVENT_NEW_STA,
+
+       /**
+        * EVENT_EAPOL_RX - Report received EAPOL frame
+        *
+        * When in AP mode with hostapd, this event is required to be used to
+        * deliver the receive EAPOL frames from the driver. With
+        * %wpa_supplicant, this event is used only if the send_eapol() handler
+        * is used to override the use of l2_packet for EAPOL frame TX.
+        */
+       EVENT_EAPOL_RX,
+
+       /**
+        * EVENT_SIGNAL_CHANGE - Indicate change in signal strength
+        *
+        * This event is used to indicate changes in the signal strength
+        * observed in frames received from the current AP if signal strength
+        * monitoring has been enabled with signal_monitor().
+        */
+       EVENT_SIGNAL_CHANGE
+};
+
+
+/**
+ * union wpa_event_data - Additional data for wpa_supplicant_event() calls
+ */
+union wpa_event_data {
+       /**
+        * struct assoc_info - Data for EVENT_ASSOC and EVENT_ASSOCINFO events
+        *
+        * This structure is optional for EVENT_ASSOC calls and required for
+        * EVENT_ASSOCINFO calls. By using EVENT_ASSOC with this data, the
+        * driver interface does not need to generate separate EVENT_ASSOCINFO
+        * calls.
+        */
+       struct assoc_info {
+               /**
+                * req_ies - (Re)Association Request IEs
+                *
+                * If the driver generates WPA/RSN IE, this event data must be
+                * returned for WPA handshake to have needed information. If
+                * wpa_supplicant-generated WPA/RSN IE is used, this
+                * information event is optional.
+                *
+                * This should start with the first IE (fixed fields before IEs
+                * are not included).
+                */
+               const u8 *req_ies;
+
+               /**
+                * req_ies_len - Length of req_ies in bytes
+                */
+               size_t req_ies_len;
+
+               /**
+                * resp_ies - (Re)Association Response IEs
+                *
+                * Optional association data from the driver. This data is not
+                * required WPA, but may be useful for some protocols and as
+                * such, should be reported if this is available to the driver
+                * interface.
+                *
+                * This should start with the first IE (fixed fields before IEs
+                * are not included).
+                */
+               const u8 *resp_ies;
+
+               /**
+                * resp_ies_len - Length of resp_ies in bytes
+                */
+               size_t resp_ies_len;
+
+               /**
+                * beacon_ies - Beacon or Probe Response IEs
+                *
+                * Optional Beacon/ProbeResp data: IEs included in Beacon or
+                * Probe Response frames from the current AP (i.e., the one
+                * that the client just associated with). This information is
+                * used to update WPA/RSN IE for the AP. If this field is not
+                * set, the results from previous scan will be used. If no
+                * data for the new AP is found, scan results will be requested
+                * again (without scan request). At this point, the driver is
+                * expected to provide WPA/RSN IE for the AP (if WPA/WPA2 is
+                * used).
+                *
+                * This should start with the first IE (fixed fields before IEs
+                * are not included).
+                */
+               const u8 *beacon_ies;
+
+               /**
+                * beacon_ies_len - Length of beacon_ies */
+               size_t beacon_ies_len;
+
+               /**
+                * freq - Frequency of the operational channel in MHz
+                */
+               unsigned int freq;
+
+               /**
+                * addr - Station address (for AP mode)
+                */
+               const u8 *addr;
+       } assoc_info;
+
+       /**
+        * struct disassoc_info - Data for EVENT_DISASSOC events
+        */
+       struct disassoc_info {
+               /**
+                * addr - Station address (for AP mode)
+                */
+               const u8 *addr;
+
+               /**
+                * reason_code - Reason Code (host byte order) used in
+                *      Deauthentication frame
+                */
+               u16 reason_code;
+       } disassoc_info;
+
+       /**
+        * struct deauth_info - Data for EVENT_DEAUTH events
+        */
+       struct deauth_info {
+               /**
+                * addr - Station address (for AP mode)
+                */
+               const u8 *addr;
+
+               /**
+                * reason_code - Reason Code (host byte order) used in
+                *      Deauthentication frame
+                */
+               u16 reason_code;
+       } deauth_info;
+
+       /**
+        * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE
+        */
+       struct michael_mic_failure {
+               int unicast;
+               const u8 *src;
+       } michael_mic_failure;
+
+       /**
+        * struct interface_status - Data for EVENT_INTERFACE_STATUS
+        */
+       struct interface_status {
+               char ifname[100];
+               enum {
+                       EVENT_INTERFACE_ADDED, EVENT_INTERFACE_REMOVED
+               } ievent;
+       } interface_status;
+
+       /**
+        * struct pmkid_candidate - Data for EVENT_PMKID_CANDIDATE
+        */
+       struct pmkid_candidate {
+               /** BSSID of the PMKID candidate */
+               u8 bssid[ETH_ALEN];
+               /** Smaller the index, higher the priority */
+               int index;
+               /** Whether RSN IE includes pre-authenticate flag */
+               int preauth;
+       } pmkid_candidate;
+
+       /**
+        * struct stkstart - Data for EVENT_STKSTART
+        */
+       struct stkstart {
+               u8 peer[ETH_ALEN];
+       } stkstart;
+
+       /**
+        * struct ft_ies - FT information elements (EVENT_FT_RESPONSE)
+        *
+        * During FT (IEEE 802.11r) authentication sequence, the driver is
+        * expected to use this event to report received FT IEs (MDIE, FTIE,
+        * RSN IE, TIE, possible resource request) to the supplicant. The FT
+        * IEs for the next message will be delivered through the
+        * struct wpa_driver_ops::update_ft_ies() callback.
+        */
+       struct ft_ies {
+               const u8 *ies;
+               size_t ies_len;
+               int ft_action;
+               u8 target_ap[ETH_ALEN];
+               /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */
+               const u8 *ric_ies;
+               /** Length of ric_ies buffer in octets */
+               size_t ric_ies_len;
+       } ft_ies;
+
+       /**
+        * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START
+        */
+       struct ibss_rsn_start {
+               u8 peer[ETH_ALEN];
+       } ibss_rsn_start;
+
+       /**
+        * struct auth_info - Data for EVENT_AUTH events
+        */
+       struct auth_info {
+               u8 peer[ETH_ALEN];
+               u16 auth_type;
+               u16 status_code;
+               const u8 *ies;
+               size_t ies_len;
+       } auth;
+
+       /**
+        * struct assoc_reject - Data for EVENT_ASSOC_REJECT events
+        */
+       struct assoc_reject {
+               /**
+                * resp_ies - (Re)Association Response IEs
+                *
+                * Optional association data from the driver. This data is not
+                * required WPA, but may be useful for some protocols and as
+                * such, should be reported if this is available to the driver
+                * interface.
+                *
+                * This should start with the first IE (fixed fields before IEs
+                * are not included).
+                */
+               u8 *resp_ies;
+
+               /**
+                * resp_ies_len - Length of resp_ies in bytes
+                */
+               size_t resp_ies_len;
+
+               /**
+                * status_code - Status Code from (Re)association Response
+                */
+               u16 status_code;
+       } assoc_reject;
+
+       struct timeout_event {
+               u8 addr[ETH_ALEN];
+       } timeout_event;
+
+       /**
+        * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events
+        */
+       struct ft_rrb_rx {
+               const u8 *src;
+               const u8 *data;
+               size_t data_len;
+       } ft_rrb_rx;
+
+       /**
+        * struct tx_status - Data for EVENT_TX_STATUS events
+        */
+       struct tx_status {
+               u16 type;
+               u16 stype;
+               const u8 *dst;
+               const u8 *data;
+               size_t data_len;
+               int ack;
+       } tx_status;
+
+       /**
+        * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events
+        */
+       struct rx_from_unknown {
+               const u8 *frame;
+               size_t len;
+       } rx_from_unknown;
+
+       /**
+        * struct rx_mgmt - Data for EVENT_RX_MGMT events
+        */
+       struct rx_mgmt {
+               const u8 *frame;
+               size_t frame_len;
+               u32 datarate;
+               u32 ssi_signal;
+       } rx_mgmt;
+
+       /**
+        * struct rx_action - Data for EVENT_RX_ACTION events
+        */
+       struct rx_action {
+               /**
+                * da - Destination address of the received Action frame
+                */
+               const u8 *da;
+
+               /**
+                * sa - Source address of the received Action frame
+                */
+               const u8 *sa;
+
+               /**
+                * bssid - Address 3 of the received Action frame
+                */
+               const u8 *bssid;
+
+               /**
+                * category - Action frame category
+                */
+               u8 category;
+
+               /**
+                * data - Action frame body after category field
+                */
+               const u8 *data;
+
+               /**
+                * len - Length of data in octets
+                */
+               size_t len;
+
+               /**
+                * freq - Frequency (in MHz) on which the frame was received
+                */
+               int freq;
+       } rx_action;
+
+       /**
+        * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
+        *
+        * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events.
+        */
+       struct remain_on_channel {
+               /**
+                * freq - Channel frequency in MHz
+                */
+               unsigned int freq;
+
+               /**
+                * duration - Duration to remain on the channel in milliseconds
+                */
+               unsigned int duration;
+       } remain_on_channel;
+
+       /**
+        * struct scan_info - Optional data for EVENT_SCAN_RESULTS events
+        * @aborted: Whether the scan was aborted
+        * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned)
+        * @num_freqs: Number of entries in freqs array
+        * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard
+        *      SSID)
+        * @num_ssids: Number of entries in ssids array
+        */
+       struct scan_info {
+               int aborted;
+               const int *freqs;
+               size_t num_freqs;
+               struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS];
+               size_t num_ssids;
+       } scan_info;
+
+       /**
+        * struct mlme_rx - Data for EVENT_MLME_RX events
+        */
+       struct mlme_rx {
+               const u8 *buf;
+               size_t len;
+               int freq;
+               int channel;
+               int ssi;
+       } mlme_rx;
+
+       /**
+        * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
+        */
+       struct rx_probe_req {
+               /**
+                * sa - Source address of the received Probe Request frame
+                */
+               const u8 *sa;
+
+               /**
+                * ie - IEs from the Probe Request body
+                */
+               const u8 *ie;
+
+               /**
+                * ie_len - Length of ie buffer in octets
+                */
+               size_t ie_len;
+       } rx_probe_req;
+
+       /**
+        * struct new_sta - Data for EVENT_NEW_STA events
+        */
+       struct new_sta {
+               const u8 *addr;
+       } new_sta;
+
+       /**
+        * struct eapol_rx - Data for EVENT_EAPOL_RX events
+        */
+       struct eapol_rx {
+               const u8 *src;
+               const u8 *data;
+               size_t data_len;
+       } eapol_rx;
+
+       /**
+        * struct signal_change - Data for EVENT_SIGNAL_CHANGE events
+        */
+       struct signal_change {
+               int above_threshold;
+       } signal_change;
+};
+
+/**
+ * wpa_supplicant_event - Report a driver event for wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *     with struct wpa_driver_ops::init()
+ * @event: event type (defined above)
+ * @data: possible extra data for the event
+ *
+ * Driver wrapper code should call this function whenever an event is received
+ * from the driver.
+ */
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+                         union wpa_event_data *data);
+
+
+/*
+ * The following inline functions are provided for convenience to simplify
+ * event indication for some of the common events.
+ */
+
+static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie,
+                                  size_t ielen)
+{
+       union wpa_event_data event;
+       os_memset(&event, 0, sizeof(event));
+       event.assoc_info.req_ies = ie;
+       event.assoc_info.req_ies_len = ielen;
+       event.assoc_info.addr = addr;
+       wpa_supplicant_event(ctx, EVENT_ASSOC, &event);
+}
+
+static inline void drv_event_disassoc(void *ctx, const u8 *addr)
+{
+       union wpa_event_data event;
+       os_memset(&event, 0, sizeof(event));
+       event.disassoc_info.addr = addr;
+       wpa_supplicant_event(ctx, EVENT_DISASSOC, &event);
+}
+
+static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data,
+                                     size_t data_len)
+{
+       union wpa_event_data event;
+       os_memset(&event, 0, sizeof(event));
+       event.eapol_rx.src = src;
+       event.eapol_rx.data = data;
+       event.eapol_rx.data_len = data_len;
+       wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event);
+}
+
+#endif /* DRIVER_H */
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
new file mode 100644 (file)
index 0000000..5c25f00
--- /dev/null
@@ -0,0 +1,1298 @@
+/*
+ * hostapd / Driver interaction with Atheros driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, Video54 Technologies
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 <net/if.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#ifndef _BYTE_ORDER
+#ifdef WORDS_BIGENDIAN
+#define _BYTE_ORDER _BIG_ENDIAN
+#else
+#define _BYTE_ORDER _LITTLE_ENDIAN
+#endif
+#endif /* _BYTE_ORDER */
+
+/*
+ * Note, the ATH_WPS_IE setting must match with the driver build.. If the
+ * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail.
+ */
+#define ATH_WPS_IE
+
+#include "os/linux/include/ieee80211_external.h"
+
+
+#ifdef CONFIG_WPS
+#include <netpacket/packet.h>
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+#endif /* CONFIG_WPS */
+
+#include "wireless_copy.h"
+
+#include "driver.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "l2_packet/l2_packet.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+
+
+struct madwifi_driver_data {
+       struct hostapd_data *hapd;              /* back pointer */
+
+       char    iface[IFNAMSIZ + 1];
+       int     ifindex;
+       struct l2_packet_data *sock_xmit;       /* raw packet xmit socket */
+       struct l2_packet_data *sock_recv;       /* raw packet recv socket */
+       int     ioctl_sock;                     /* socket for ioctl() use */
+       struct netlink_data *netlink;
+       int     we_version;
+       u8      acct_mac[ETH_ALEN];
+       struct hostap_sta_driver_data acct_data;
+
+       struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+};
+
+static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                             int reason_code);
+static int madwifi_set_privacy(void *priv, int enabled);
+
+static const char * athr_get_ioctl_name(int op)
+{
+       switch (op) {
+       case IEEE80211_IOCTL_SETPARAM:
+               return "SETPARAM";
+       case IEEE80211_IOCTL_GETPARAM:
+               return "GETPARAM";
+       case IEEE80211_IOCTL_SETKEY:
+               return "SETKEY";
+       case IEEE80211_IOCTL_SETWMMPARAMS:
+               return "SETWMMPARAMS";
+       case IEEE80211_IOCTL_DELKEY:
+               return "DELKEY";
+       case IEEE80211_IOCTL_GETWMMPARAMS:
+               return "GETWMMPARAMS";
+       case IEEE80211_IOCTL_SETMLME:
+               return "SETMLME";
+       case IEEE80211_IOCTL_GETCHANINFO:
+               return "GETCHANINFO";
+       case IEEE80211_IOCTL_SETOPTIE:
+               return "SETOPTIE";
+       case IEEE80211_IOCTL_GETOPTIE:
+               return "GETOPTIE";
+       case IEEE80211_IOCTL_ADDMAC:
+               return "ADDMAC";
+       case IEEE80211_IOCTL_DELMAC:
+               return "DELMAC";
+       case IEEE80211_IOCTL_GETCHANLIST:
+               return "GETCHANLIST";
+       case IEEE80211_IOCTL_SETCHANLIST:
+               return "SETCHANLIST";
+       case IEEE80211_IOCTL_KICKMAC:
+               return "KICKMAC";
+       case IEEE80211_IOCTL_CHANSWITCH:
+               return "CHANSWITCH";
+       case IEEE80211_IOCTL_GETMODE:
+               return "GETMODE";
+       case IEEE80211_IOCTL_SETMODE:
+               return "SETMODE";
+       case IEEE80211_IOCTL_GET_APPIEBUF:
+               return "GET_APPIEBUF";
+       case IEEE80211_IOCTL_SET_APPIEBUF:
+               return "SET_APPIEBUF";
+       case IEEE80211_IOCTL_SET_ACPARAMS:
+               return "SET_ACPARAMS";
+       case IEEE80211_IOCTL_FILTERFRAME:
+               return "FILTERFRAME";
+       case IEEE80211_IOCTL_SET_RTPARAMS:
+               return "SET_RTPARAMS";
+       case IEEE80211_IOCTL_SENDADDBA:
+               return "SENDADDBA";
+       case IEEE80211_IOCTL_GETADDBASTATUS:
+               return "GETADDBASTATUS";
+       case IEEE80211_IOCTL_SENDDELBA:
+               return "SENDDELBA";
+       case IEEE80211_IOCTL_SET_MEDENYENTRY:
+               return "SET_MEDENYENTRY";
+       case IEEE80211_IOCTL_SET_ADDBARESP:
+               return "SET_ADDBARESP";
+       case IEEE80211_IOCTL_GET_MACADDR:
+               return "GET_MACADDR";
+       case IEEE80211_IOCTL_SET_HBRPARAMS:
+               return "SET_HBRPARAMS";
+       case IEEE80211_IOCTL_SET_RXTIMEOUT:
+               return "SET_RXTIMEOUT";
+       case IEEE80211_IOCTL_STA_STATS:
+               return "STA_STATS";
+       case IEEE80211_IOCTL_GETWPAIE:
+               return "GETWPAIE";
+       default:
+               return "??";
+       }
+}
+
+
+static const char * athr_get_param_name(int op)
+{
+       switch (op) {
+       case IEEE80211_IOC_MCASTCIPHER:
+               return "MCASTCIPHER";
+       case IEEE80211_PARAM_MCASTKEYLEN:
+               return "MCASTKEYLEN";
+       case IEEE80211_PARAM_UCASTCIPHERS:
+               return "UCASTCIPHERS";
+       case IEEE80211_PARAM_KEYMGTALGS:
+               return "KEYMGTALGS";
+       case IEEE80211_PARAM_RSNCAPS:
+               return "RSNCAPS";
+       case IEEE80211_PARAM_WPA:
+               return "WPA";
+       case IEEE80211_PARAM_AUTHMODE:
+               return "AUTHMODE";
+       case IEEE80211_PARAM_PRIVACY:
+               return "PRIVACY";
+       case IEEE80211_PARAM_COUNTERMEASURES:
+               return "COUNTERMEASURES";
+       default:
+               return "??";
+       }
+}
+
+
+static int
+set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
+{
+       struct iwreq iwr;
+       int do_inline = len < IFNAMSIZ;
+
+       /* Certain ioctls must use the non-inlined method */
+       if (op == IEEE80211_IOCTL_SET_APPIEBUF ||
+           op == IEEE80211_IOCTL_FILTERFRAME)
+               do_inline = 0;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       if (do_inline) {
+               /*
+                * Argument data fits inline; put it there.
+                */
+               memcpy(iwr.u.name, data, len);
+       } else {
+               /*
+                * Argument data too big for inline transfer; setup a
+                * parameter block instead; the kernel will transfer
+                * the data for the driver.
+                */
+               iwr.u.data.pointer = data;
+               iwr.u.data.length = len;
+       }
+
+       if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
+               wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x "
+                          "(%s) len=%d failed: %d (%s)",
+                          __func__, drv->iface, op,
+                          athr_get_ioctl_name(op),
+                          len, errno, strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+static int
+set80211param(struct madwifi_driver_data *drv, int op, int arg)
+{
+       struct iwreq iwr;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.mode = op;
+       memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
+
+       if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+               perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
+               wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d "
+                          "(%s) arg %d)", __func__, drv->iface, op,
+                          athr_get_param_name(op), arg);
+               return -1;
+       }
+       return 0;
+}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char *
+ether_sprintf(const u8 *addr)
+{
+       static char buf[sizeof(MACSTR)];
+
+       if (addr != NULL)
+               snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+       else
+               snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+       return buf;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/*
+ * Configure WPA parameters.
+ */
+static int
+madwifi_configure_wpa(struct madwifi_driver_data *drv,
+                     struct wpa_bss_params *params)
+{
+       int v;
+
+       switch (params->wpa_group) {
+       case WPA_CIPHER_CCMP:
+               v = IEEE80211_CIPHER_AES_CCM;
+               break;
+       case WPA_CIPHER_TKIP:
+               v = IEEE80211_CIPHER_TKIP;
+               break;
+       case WPA_CIPHER_WEP104:
+               v = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_CIPHER_WEP40:
+               v = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_CIPHER_NONE:
+               v = IEEE80211_CIPHER_NONE;
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
+                          params->wpa_group);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
+       if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
+               printf("Unable to set group key cipher to %u\n", v);
+               return -1;
+       }
+       if (v == IEEE80211_CIPHER_WEP) {
+               /* key length is done only for specific ciphers */
+               v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+               if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
+                       printf("Unable to set group key length to %u\n", v);
+                       return -1;
+               }
+       }
+
+       v = 0;
+       if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+               v |= 1<<IEEE80211_CIPHER_AES_CCM;
+       if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+               v |= 1<<IEEE80211_CIPHER_TKIP;
+       if (params->wpa_pairwise & WPA_CIPHER_NONE)
+               v |= 1<<IEEE80211_CIPHER_NONE;
+       wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+       if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
+               printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+                  __func__, params->wpa_key_mgmt);
+       if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
+                         params->wpa_key_mgmt)) {
+               printf("Unable to set key management algorithms to 0x%x\n",
+                       params->wpa_key_mgmt);
+               return -1;
+       }
+
+       v = 0;
+       if (params->rsn_preauth)
+               v |= BIT(0);
+       wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+                  __func__, params->rsn_preauth);
+       if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
+               printf("Unable to set RSN capabilities to 0x%x\n", v);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
+       if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
+               printf("Unable to set WPA to %u\n", params->wpa);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+       struct madwifi_driver_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+       if (!params->enabled) {
+               /* XXX restore state */
+               if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+                                 IEEE80211_AUTH_AUTO) < 0)
+                       return -1;
+               /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */
+               return madwifi_set_privacy(drv, 0);
+       }
+       if (!params->wpa && !params->ieee802_1x) {
+               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+                       HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+               return -1;
+       }
+       if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
+               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+                       HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+               return -1;
+       }
+       if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+               (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+                       HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+madwifi_set_privacy(void *priv, int enabled)
+{
+       struct madwifi_driver_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+       return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
+}
+
+static int
+madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
+                  __func__, ether_sprintf(addr), authorized);
+
+       if (authorized)
+               mlme.im_op = IEEE80211_MLME_AUTHORIZE;
+       else
+               mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
+       mlme.im_reason = 0;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
+                          __func__, authorized ? "" : "un", MAC2STR(addr));
+       }
+
+       return ret;
+}
+
+static int
+madwifi_sta_set_flags(void *priv, const u8 *addr,
+                     int total_flags, int flags_or, int flags_and)
+{
+       /* For now, only support setting Authorized flag */
+       if (flags_or & WPA_STA_AUTHORIZED)
+               return madwifi_set_sta_authorized(priv, addr, 1);
+       if (!(flags_and & WPA_STA_AUTHORIZED))
+               return madwifi_set_sta_authorized(priv, addr, 0);
+       return 0;
+}
+
+static int
+madwifi_del_key(void *priv, const u8 *addr, int key_idx)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_del_key wk;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
+                  __func__, ether_sprintf(addr), key_idx);
+
+       memset(&wk, 0, sizeof(wk));
+       if (addr != NULL) {
+               memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+               wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
+       } else {
+               wk.idk_keyix = key_idx;
+       }
+
+       ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
+                          " key_idx %d)", __func__, ether_sprintf(addr),
+                          key_idx);
+       }
+
+       return ret;
+}
+
+static int
+madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+               const u8 *addr, int key_idx, int set_tx, const u8 *seq,
+               size_t seq_len, const u8 *key, size_t key_len)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_key wk;
+       u_int8_t cipher;
+       int ret;
+
+       if (alg == WPA_ALG_NONE)
+               return madwifi_del_key(drv, addr, key_idx);
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
+                  __func__, alg, ether_sprintf(addr), key_idx);
+
+       switch (alg) {
+       case WPA_ALG_WEP:
+               cipher = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_ALG_TKIP:
+               cipher = IEEE80211_CIPHER_TKIP;
+               break;
+       case WPA_ALG_CCMP:
+               cipher = IEEE80211_CIPHER_AES_CCM;
+               break;
+       default:
+               printf("%s: unknown/unsupported algorithm %d\n",
+                       __func__, alg);
+               return -1;
+       }
+
+       if (key_len > sizeof(wk.ik_keydata)) {
+               printf("%s: key length %lu too big\n", __func__,
+                      (unsigned long) key_len);
+               return -3;
+       }
+
+       memset(&wk, 0, sizeof(wk));
+       wk.ik_type = cipher;
+       wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
+       if (addr == NULL) {
+               memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+               wk.ik_keyix = key_idx;
+               wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+       } else {
+               memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+               wk.ik_keyix = IEEE80211_KEYIX_NONE;
+       }
+       wk.ik_keylen = key_len;
+       memcpy(wk.ik_keydata, key, key_len);
+
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s"
+                          " key_idx %d alg %d key_len %lu set_tx %d)",
+                          __func__, ether_sprintf(wk.ik_macaddr), key_idx,
+                          alg, (unsigned long) key_len, set_tx);
+       }
+
+       return ret;
+}
+
+
+static int
+madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+                  u8 *seq)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_key wk;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+                  __func__, ether_sprintf(addr), idx);
+
+       memset(&wk, 0, sizeof(wk));
+       if (addr == NULL)
+               memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+       else
+               memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+       wk.ik_keyix = idx;
+
+       if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data "
+                          "(addr " MACSTR " key_idx %d)",
+                          __func__, MAC2STR(wk.ik_macaddr), idx);
+               return -1;
+       }
+
+#ifdef WORDS_BIGENDIAN
+       {
+               /*
+                * wk.ik_keytsc is in host byte order (big endian), need to
+                * swap it to match with the byte order used in WPA.
+                */
+               int i;
+#ifndef WPA_KEY_RSC_LEN
+#define WPA_KEY_RSC_LEN 8
+#endif
+               u8 tmp[WPA_KEY_RSC_LEN];
+               memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+               for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+                       seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+               }
+       }
+#else /* WORDS_BIGENDIAN */
+       memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+       return 0;
+}
+
+
+static int
+madwifi_flush(void *priv)
+{
+       u8 allsta[IEEE80211_ADDR_LEN];
+       memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+       return madwifi_sta_deauth(priv, NULL, allsta,
+                                 IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+                            const u8 *addr)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_sta_stats stats;
+
+       memset(data, 0, sizeof(*data));
+
+       /*
+        * Fetch statistics for station from the system.
+        */
+       memset(&stats, 0, sizeof(stats));
+       memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+       if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS,
+                        &stats, sizeof(stats))) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
+                          MACSTR ")", __func__, MAC2STR(addr));
+               if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+                       memcpy(data, &drv->acct_data, sizeof(*data));
+                       return 0;
+               }
+
+               printf("Failed to get station stats information element.\n");
+               return -1;
+       }
+
+       data->rx_packets = stats.is_stats.ns_rx_data;
+       data->rx_bytes = stats.is_stats.ns_rx_bytes;
+       data->tx_packets = stats.is_stats.ns_tx_data;
+       data->tx_bytes = stats.is_stats.ns_tx_bytes;
+       return 0;
+}
+
+
+static int
+madwifi_sta_clear_stats(void *priv, const u8 *addr)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
+
+       mlme.im_op = IEEE80211_MLME_CLEAR_STATS;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+                          sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr "
+                          MACSTR ")", __func__, MAC2STR(addr));
+       }
+
+       return ret;
+}
+
+
+static int
+madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+       /*
+        * Do nothing; we setup parameters at startup that define the
+        * contents of the beacon information element.
+        */
+       return 0;
+}
+
+static int
+madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                  int reason_code)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+                  __func__, ether_sprintf(addr), reason_code);
+
+       mlme.im_op = IEEE80211_MLME_DEAUTH;
+       mlme.im_reason = reason_code;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR
+                          " reason %d)",
+                          __func__, MAC2STR(addr), reason_code);
+       }
+
+       return ret;
+}
+
+static int
+madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                    int reason_code)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+                  __func__, ether_sprintf(addr), reason_code);
+
+       mlme.im_op = IEEE80211_MLME_DISASSOC;
+       mlme.im_reason = reason_code;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr "
+                          MACSTR " reason %d)",
+                          __func__, MAC2STR(addr), reason_code);
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_WPS
+static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+                               size_t len)
+{
+       struct madwifi_driver_data *drv = ctx;
+       const struct ieee80211_mgmt *mgmt;
+       u16 fc;
+       union wpa_event_data event;
+
+       /* Send Probe Request information to WPS processing */
+
+       if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+               return;
+       mgmt = (const struct ieee80211_mgmt *) buf;
+
+       fc = le_to_host16(mgmt->frame_control);
+       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+           WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ)
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_probe_req.sa = mgmt->sa;
+       event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+       event.rx_probe_req.ie_len =
+               len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+       wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+}
+#endif /* CONFIG_WPS */
+
+static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
+{
+       int ret = 0;
+#ifdef CONFIG_WPS
+       struct ieee80211req_set_filter filt;
+
+       wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+       filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
+
+       ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+                          sizeof(struct ieee80211req_set_filter));
+       if (ret)
+               return ret;
+
+       drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
+                                      madwifi_raw_receive, drv, 1);
+       if (drv->sock_raw == NULL)
+               return -1;
+#endif /* CONFIG_WPS */
+       return ret;
+}
+
+#ifdef CONFIG_WPS
+static int
+madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+{
+       struct madwifi_driver_data *drv = priv;
+       u8 buf[256];
+       struct ieee80211req_getset_appiebuf *beac_ie;
+
+       wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+                  (unsigned long) len);
+
+       beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
+       beac_ie->app_frmtype = frametype;
+       beac_ie->app_buflen = len;
+       memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+       return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
+                           sizeof(struct ieee80211req_getset_appiebuf) + len);
+}
+
+static int
+madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+                     const struct wpabuf *proberesp)
+{
+       if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+                              beacon ? wpabuf_len(beacon) : 0,
+                              IEEE80211_APPIE_FRAME_BEACON))
+               return -1;
+       return madwifi_set_wps_ie(priv,
+                                 proberesp ? wpabuf_head(proberesp) : NULL,
+                                 proberesp ? wpabuf_len(proberesp): 0,
+                                 IEEE80211_APPIE_FRAME_PROBE_RESP);
+}
+#else /* CONFIG_WPS */
+#define madwifi_set_ap_wps_ie NULL
+#endif /* CONFIG_WPS */
+
+static void
+madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+{
+       struct hostapd_data *hapd = drv->hapd;
+       struct ieee80211req_wpaie ie;
+       int ielen = 0;
+       u8 *iebuf = NULL;
+
+       /*
+        * Fetch negotiated WPA/RSN parameters from the system.
+        */
+       memset(&ie, 0, sizeof(ie));
+       memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+       if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) {
+               /*
+                * See ATH_WPS_IE comment in the beginning of the file for a
+                * possible cause for the failure..
+                */
+               wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s",
+                          __func__, strerror(errno));
+               goto no_ie;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE",
+                   ie.wpa_ie, IEEE80211_MAX_OPT_IE);
+       wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE",
+                   ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+       iebuf = ie.wpa_ie;
+       /* madwifi seems to return some random data if WPA/RSN IE is not set.
+        * Assume the IE was not included if the IE type is unknown. */
+       if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
+               iebuf[1] = 0;
+       if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
+               /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
+                * set. This is needed for WPA2. */
+               iebuf = ie.rsn_ie;
+               if (iebuf[0] != WLAN_EID_RSN)
+                       iebuf[1] = 0;
+       }
+
+       ielen = iebuf[1];
+       if (ielen == 0)
+               iebuf = NULL;
+       else
+               ielen += 2;
+
+no_ie:
+       drv_event_assoc(hapd, addr, iebuf, ielen);
+
+       if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+               /* Cached accounting data is not valid anymore. */
+               memset(drv->acct_mac, 0, ETH_ALEN);
+               memset(&drv->acct_data, 0, sizeof(drv->acct_data));
+       }
+}
+
+static void
+madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv,
+                                      char *custom, char *end)
+{
+       wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+       if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+               char *pos;
+               u8 addr[ETH_ALEN];
+               pos = strstr(custom, "addr=");
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLME-MICHAELMICFAILURE.indication "
+                                  "without sender address ignored");
+                       return;
+               }
+               pos += 5;
+               if (hwaddr_aton(pos, addr) == 0) {
+                       union wpa_event_data data;
+                       os_memset(&data, 0, sizeof(data));
+                       data.michael_mic_failure.unicast = 1;
+                       data.michael_mic_failure.src = addr;
+                       wpa_supplicant_event(drv->hapd,
+                                            EVENT_MICHAEL_MIC_FAILURE, &data);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLME-MICHAELMICFAILURE.indication "
+                                  "with invalid MAC address");
+               }
+       } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) {
+               char *key, *value;
+               u32 val;
+               key = custom;
+               while ((key = strchr(key, '\n')) != NULL) {
+                       key++;
+                       value = strchr(key, '=');
+                       if (value == NULL)
+                               continue;
+                       *value++ = '\0';
+                       val = strtoul(value, NULL, 10);
+                       if (strcmp(key, "mac") == 0)
+                               hwaddr_aton(value, drv->acct_mac);
+                       else if (strcmp(key, "rx_packets") == 0)
+                               drv->acct_data.rx_packets = val;
+                       else if (strcmp(key, "tx_packets") == 0)
+                               drv->acct_data.tx_packets = val;
+                       else if (strcmp(key, "rx_bytes") == 0)
+                               drv->acct_data.rx_bytes = val;
+                       else if (strcmp(key, "tx_bytes") == 0)
+                               drv->acct_data.tx_bytes = val;
+                       key = value;
+               }
+#ifdef CONFIG_WPS
+       } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) {
+               /* Some atheros kernels send push button as a wireless event */
+               /* PROBLEM! this event is received for ALL BSSs ...
+                * so all are enabled for WPS... ugh.
+                */
+               wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL);
+       } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) {
+               /*
+                * Atheros driver uses a hack to pass Probe Request frames as a
+                * binary data in the custom wireless event. The old way (using
+                * packet sniffing) didn't work when bridging.
+                * Format: "Manage.prob_req <frame len>" | zero padding | frame
+                */
+#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */
+               int len = atoi(custom + 16);
+               if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) {
+                       wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event "
+                                  "length %d", len);
+                       return;
+               }
+               madwifi_raw_receive(drv, NULL,
+                                   (u8 *) custom + WPS_FRAM_TAG_SIZE, len);
+#endif /* CONFIG_WPS */
+       }
+}
+
+static void
+madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
+                               char *data, int len)
+{
+       struct iw_event iwe_buf, *iwe = &iwe_buf;
+       char *pos, *end, *custom, *buf;
+
+       pos = data;
+       end = data + len;
+
+       while (pos + IW_EV_LCP_LEN <= end) {
+               /* Event data may be unaligned, so make a local, aligned copy
+                * before processing. */
+               memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+               wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
+                          iwe->cmd, iwe->len);
+               if (iwe->len <= IW_EV_LCP_LEN)
+                       return;
+
+               custom = pos + IW_EV_POINT_LEN;
+               if (drv->we_version > 18 &&
+                   (iwe->cmd == IWEVMICHAELMICFAILURE ||
+                    iwe->cmd == IWEVASSOCREQIE ||
+                    iwe->cmd == IWEVCUSTOM)) {
+                       /* WE-19 removed the pointer from struct iw_point */
+                       char *dpos = (char *) &iwe_buf.u.data.length;
+                       int dlen = dpos - (char *) &iwe_buf;
+                       memcpy(dpos, pos + IW_EV_LCP_LEN,
+                              sizeof(struct iw_event) - dlen);
+               } else {
+                       memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+                       custom += IW_EV_POINT_OFF;
+               }
+
+               switch (iwe->cmd) {
+               case IWEVEXPIRED:
+                       drv_event_disassoc(drv->hapd,
+                                          (u8 *) iwe->u.addr.sa_data);
+                       break;
+               case IWEVREGISTERED:
+                       madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
+                       break;
+               case IWEVASSOCREQIE:
+                       /* Driver hack.. Use IWEVASSOCREQIE to bypass
+                        * IWEVCUSTOM size limitations. Need to handle this
+                        * just like IWEVCUSTOM.
+                        */
+               case IWEVCUSTOM:
+                       if (custom + iwe->u.data.length > end)
+                               return;
+                       buf = malloc(iwe->u.data.length + 1);
+                       if (buf == NULL)
+                               return;         /* XXX */
+                       memcpy(buf, custom, iwe->u.data.length);
+                       buf[iwe->u.data.length] = '\0';
+                       madwifi_wireless_event_wireless_custom(
+                               drv, buf, buf + iwe->u.data.length);
+                       free(buf);
+                       break;
+               }
+
+               pos += iwe->len;
+       }
+}
+
+
+static void
+madwifi_wireless_event_rtm_newlink(void *ctx,
+                                  struct ifinfomsg *ifi, u8 *buf, size_t len)
+{
+       struct madwifi_driver_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       if (ifi->ifi_index != drv->ifindex)
+               return;
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_WIRELESS) {
+                       madwifi_wireless_event_wireless(
+                               drv, ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len);
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+}
+
+
+static int
+madwifi_get_we_version(struct madwifi_driver_data *drv)
+{
+       struct iw_range *range;
+       struct iwreq iwr;
+       int minlen;
+       size_t buflen;
+
+       drv->we_version = 0;
+
+       /*
+        * Use larger buffer than struct iw_range in order to allow the
+        * structure to grow in the future.
+        */
+       buflen = sizeof(struct iw_range) + 500;
+       range = os_zalloc(buflen);
+       if (range == NULL)
+               return -1;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) range;
+       iwr.u.data.length = buflen;
+
+       minlen = ((char *) &range->enc_capa) - (char *) range +
+               sizeof(range->enc_capa);
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWRANGE]");
+               free(range);
+               return -1;
+       } else if (iwr.u.data.length >= minlen &&
+                  range->we_version_compiled >= 18) {
+               wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+                          "WE(source)=%d enc_capa=0x%x",
+                          range->we_version_compiled,
+                          range->we_version_source,
+                          range->enc_capa);
+               drv->we_version = range->we_version_compiled;
+       }
+
+       free(range);
+       return 0;
+}
+
+
+static int
+madwifi_wireless_event_init(struct madwifi_driver_data *drv)
+{
+       struct netlink_config *cfg;
+
+       madwifi_get_we_version(drv);
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               return -1;
+       cfg->ctx = drv;
+       cfg->newlink_cb = madwifi_wireless_event_rtm_newlink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int
+madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+                  int encrypt, const u8 *own_addr)
+{
+       struct madwifi_driver_data *drv = priv;
+       unsigned char buf[3000];
+       unsigned char *bp = buf;
+       struct l2_ethhdr *eth;
+       size_t len;
+       int status;
+
+       /*
+        * Prepend the Ethernet header.  If the caller left us
+        * space at the front we could just insert it but since
+        * we don't know we copy to a local buffer.  Given the frequency
+        * and size of frames this probably doesn't matter.
+        */
+       len = data_len + sizeof(struct l2_ethhdr);
+       if (len > sizeof(buf)) {
+               bp = malloc(len);
+               if (bp == NULL) {
+                       printf("EAPOL frame discarded, cannot malloc temp "
+                              "buffer of size %lu!\n", (unsigned long) len);
+                       return -1;
+               }
+       }
+       eth = (struct l2_ethhdr *) bp;
+       memcpy(eth->h_dest, addr, ETH_ALEN);
+       memcpy(eth->h_source, own_addr, ETH_ALEN);
+       eth->h_proto = host_to_be16(ETH_P_EAPOL);
+       memcpy(eth+1, data, data_len);
+
+       wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
+
+       status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
+
+       if (bp != buf)
+               free(bp);
+       return status;
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+       struct madwifi_driver_data *drv = ctx;
+       drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
+                          len - sizeof(struct l2_ethhdr));
+}
+
+static void *
+madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+       struct madwifi_driver_data *drv;
+       struct ifreq ifr;
+       struct iwreq iwr;
+       char brname[IFNAMSIZ];
+
+       drv = os_zalloc(sizeof(struct madwifi_driver_data));
+       if (drv == NULL) {
+               printf("Could not allocate memory for madwifi driver data\n");
+               return NULL;
+       }
+
+       drv->hapd = hapd;
+       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->ioctl_sock < 0) {
+               perror("socket[PF_INET,SOCK_DGRAM]");
+               goto bad;
+       }
+       memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+       memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
+       if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
+               perror("ioctl(SIOCGIFINDEX)");
+               goto bad;
+       }
+       drv->ifindex = ifr.ifr_ifindex;
+
+       drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
+                                       handle_read, drv, 1);
+       if (drv->sock_xmit == NULL)
+               goto bad;
+       if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+               goto bad;
+       if (params->bridge[0]) {
+               wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.",
+                          params->bridge[0]);
+               drv->sock_recv = l2_packet_init(params->bridge[0], NULL,
+                                               ETH_P_EAPOL, handle_read, drv,
+                                               1);
+               if (drv->sock_recv == NULL)
+                       goto bad;
+       } else if (linux_br_get(brname, drv->iface) == 0) {
+               wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for "
+                          "EAPOL receive", brname);
+               drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL,
+                                               handle_read, drv, 1);
+               if (drv->sock_recv == NULL)
+                       goto bad;
+       } else
+               drv->sock_recv = drv->sock_xmit;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+       iwr.u.mode = IW_MODE_MASTER;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
+               perror("ioctl[SIOCSIWMODE]");
+               printf("Could not set interface to master mode!\n");
+               goto bad;
+       }
+
+       /* mark down during setup */
+       linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+       madwifi_set_privacy(drv, 0); /* default to no privacy */
+
+       madwifi_receive_probe_req(drv);
+
+       if (madwifi_wireless_event_init(drv))
+               goto bad;
+
+       return drv;
+bad:
+       if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+               l2_packet_deinit(drv->sock_recv);
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+       if (drv != NULL)
+               free(drv);
+       return NULL;
+}
+
+
+static void
+madwifi_deinit(void *priv)
+{
+       struct madwifi_driver_data *drv = priv;
+
+       netlink_deinit(drv->netlink);
+       (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+       if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+               l2_packet_deinit(drv->sock_recv);
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       if (drv->sock_raw)
+               l2_packet_deinit(drv->sock_raw);
+       free(drv);
+}
+
+static int
+madwifi_set_ssid(void *priv, const u8 *buf, int len)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct iwreq iwr;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.essid.flags = 1; /* SSID active */
+       iwr.u.essid.pointer = (caddr_t) buf;
+       iwr.u.essid.length = len + 1;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCSIWESSID]");
+               printf("len=%d\n", len);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+madwifi_get_ssid(void *priv, u8 *buf, int len)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.essid.pointer = (caddr_t) buf;
+       iwr.u.essid.length = len;
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCGIWESSID]");
+               ret = -1;
+       } else
+               ret = iwr.u.essid.length;
+
+       return ret;
+}
+
+static int
+madwifi_set_countermeasures(void *priv, int enabled)
+{
+       struct madwifi_driver_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
+}
+
+static int
+madwifi_commit(void *priv)
+{
+       struct madwifi_driver_data *drv = priv;
+       return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
+}
+
+const struct wpa_driver_ops wpa_driver_atheros_ops = {
+       .name                   = "atheros",
+       .hapd_init              = madwifi_init,
+       .hapd_deinit            = madwifi_deinit,
+       .set_ieee8021x          = madwifi_set_ieee8021x,
+       .set_privacy            = madwifi_set_privacy,
+       .set_key                = madwifi_set_key,
+       .get_seqnum             = madwifi_get_seqnum,
+       .flush                  = madwifi_flush,
+       .set_generic_elem       = madwifi_set_opt_ie,
+       .sta_set_flags          = madwifi_sta_set_flags,
+       .read_sta_data          = madwifi_read_sta_driver_data,
+       .hapd_send_eapol        = madwifi_send_eapol,
+       .sta_disassoc           = madwifi_sta_disassoc,
+       .sta_deauth             = madwifi_sta_deauth,
+       .hapd_set_ssid          = madwifi_set_ssid,
+       .hapd_get_ssid          = madwifi_get_ssid,
+       .set_countermeasures    = madwifi_set_countermeasures,
+       .sta_clear_stats        = madwifi_sta_clear_stats,
+       .commit                 = madwifi_commit,
+       .set_ap_wps_ie          = madwifi_set_ap_wps_ie,
+};
diff --git a/src/drivers/driver_atmel.c b/src/drivers/driver_atmel.c
new file mode 100644 (file)
index 0000000..cbec6c3
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers
+ * Copyright (c) 2000-2005, ATMEL Corporation
+ * 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.
+ */
+
+/******************************************************************************
+       Copyright 2000-2001 ATMEL Corporation.
+       
+    WPA Supplicant - driver interaction with Atmel Wireless lan drivers.
+    
+    This is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Atmel wireless lan drivers; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+
+******************************************************************************/
+
+/*
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+struct wpa_driver_atmel_data {
+       void *wext; /* private data for driver_wext */
+       void *ctx;
+       char ifname[IFNAMSIZ + 1];
+       int sock;
+};
+
+
+#define ATMEL_WPA_IOCTL                (SIOCIWFIRSTPRIV + 2)
+#define ATMEL_WPA_IOCTL_PARAM          (SIOCIWFIRSTPRIV + 3)
+#define ATMEL_WPA_IOCTL_GET_PARAM      (SIOCIWFIRSTPRIV + 4)
+
+
+/* ATMEL_WPA_IOCTL ioctl() cmd: */
+enum {
+    SET_WPA_ENCRYPTION  = 1,
+    SET_CIPHER_SUITES   = 2,
+    MLME_STA_DEAUTH     = 3,
+    MLME_STA_DISASSOC   = 4
+};
+
+/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */
+enum {
+            ATMEL_PARAM_WPA = 1,
+            ATMEL_PARAM_PRIVACY_INVOKED = 2,
+            ATMEL_PARAM_WPA_TYPE = 3
+};
+
+#define MAX_KEY_LENGTH      40
+
+struct atmel_param{
+    unsigned char sta_addr[6];
+        int     cmd;
+        u8      alg;
+        u8      key_idx;
+        u8      set_tx;
+        u8      seq[8];
+        u8      seq_len;
+        u16     key_len;
+        u8      key[MAX_KEY_LENGTH];
+    struct{
+        int     reason_code;
+        u8      state;
+    }mlme;
+    u8          pairwise_suite;
+    u8          group_suite;
+    u8          key_mgmt_suite;
+};
+
+    
+    
+static int atmel_ioctl(struct wpa_driver_atmel_data *drv,
+                      struct atmel_param *param,
+                      int len, int show_err)
+{
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) param;
+       iwr.u.data.length = len;
+
+       if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) {
+               int ret;
+               ret = errno;
+               if (show_err) 
+                       perror("ioctl[ATMEL_WPA_IOCTL]");
+               return ret;
+       }
+
+       return 0;
+}
+
+
+static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value)
+{
+       struct iwreq iwr;
+       int *i, ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       i = (int *) iwr.u.name;
+       *i++ = param;
+       *i++ = value;
+
+       if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) {
+               perror("ioctl[ATMEL_WPA_IOCTL_PARAM]");
+               ret = -1;
+       }
+       return ret;
+}
+
+
+#if 0
+static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv,
+                                      const char *wpa_ie, size_t wpa_ie_len)
+{
+       struct atmel_param *param;
+       int res;
+       size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len;
+       if (blen < sizeof(*param))
+               blen = sizeof(*param);
+
+       param = os_zalloc(blen);
+       if (param == NULL)
+               return -1;
+
+       param->cmd = ATMEL_SET_GENERIC_ELEMENT;
+       param->u.generic_elem.len = wpa_ie_len;
+       os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len);
+       res = atmel_ioctl(drv, param, blen, 1);
+
+       os_free(param);
+
+       return res;
+}
+#endif
+
+
+static int wpa_driver_atmel_set_wpa(void *priv, int enabled)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+        int ret = 0;
+       
+        printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname);
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+#if 0
+       if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0)
+               ret = -1;
+#endif
+       if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0)
+               ret = -1;
+       if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+
+static int wpa_driver_atmel_set_key(const char *ifname, void *priv,
+                                   enum wpa_alg alg, const u8 *addr,
+                                   int key_idx, int set_tx,
+                                   const u8 *seq, size_t seq_len,
+                                   const u8 *key, size_t key_len)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       int ret = 0;
+        struct atmel_param *param;
+       u8 *buf;
+        u8 alg_type;
+        
+       size_t blen;
+       char *alg_name;
+
+       switch (alg) {
+       case WPA_ALG_NONE:
+               alg_name = "none";
+                alg_type = 0;
+               break;
+       case WPA_ALG_WEP:
+               alg_name = "WEP";
+               alg_type = 1;
+                break;
+       case WPA_ALG_TKIP:
+               alg_name = "TKIP";
+               alg_type = 2;
+                break;
+       case WPA_ALG_CCMP:
+               alg_name = "CCMP";
+               alg_type = 3;
+                break;
+       default:
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
+                  "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       if (seq_len > 8)
+               return -2;
+
+       blen = sizeof(*param) + key_len;
+       buf = os_zalloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       param = (struct atmel_param *) buf;
+        
+        param->cmd = SET_WPA_ENCRYPTION; 
+        
+        if (addr == NULL)
+               os_memset(param->sta_addr, 0xff, ETH_ALEN);
+       else
+               os_memcpy(param->sta_addr, addr, ETH_ALEN);
+        
+        param->alg = alg_type;
+        param->key_idx = key_idx;
+        param->set_tx = set_tx;
+        os_memcpy(param->seq, seq, seq_len);
+        param->seq_len = seq_len;
+        param->key_len = key_len;
+       os_memcpy((u8 *)param->key, key, key_len);
+       
+        if (atmel_ioctl(drv, param, blen, 1)) {
+               wpa_printf(MSG_WARNING, "Failed to set encryption.");
+               /* TODO: show key error*/
+               ret = -1;
+       }
+       os_free(buf);
+
+       return ret;
+}
+
+
+static int wpa_driver_atmel_set_countermeasures(void *priv,
+                                                int enabled)
+{
+       /* FIX */
+       printf("wpa_driver_atmel_set_countermeasures - not yet "
+              "implemented\n");
+       return 0;
+}
+
+
+static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd,
+                                int reason_code)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       struct atmel_param param;
+       int ret;
+        int mgmt_error = 0xaa;
+        
+       os_memset(&param, 0, sizeof(param));
+       os_memcpy(param.sta_addr, addr, ETH_ALEN);
+       param.cmd = cmd;
+       param.mlme.reason_code = reason_code;
+        param.mlme.state = mgmt_error;
+       ret = atmel_ioctl(drv, &param, sizeof(param), 1);
+       return ret;
+}
+
+
+#if 0
+static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv,
+                                      u8 pairwise_suite, u8 group_suite,
+                                      u8 key_mgmt_suite)
+{
+       struct atmel_param param;
+       int ret;
+        
+       os_memset(&param, 0, sizeof(param));
+        param.cmd = SET_CIPHER_SUITES;
+        param.pairwise_suite = pairwise_suite;
+        param.group_suite = group_suite;
+        param.key_mgmt_suite = key_mgmt_suite;
+               
+       ret = atmel_ioctl(drv, &param, sizeof(param), 1);
+       return ret;
+}
+#endif
+
+
+static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr,
+                                          int reason_code)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       printf("wpa_driver_atmel_deauthenticate\n");
+        wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH,
+                                    reason_code);
+
+}
+
+
+static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr,
+                                        int reason_code)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       printf("wpa_driver_atmel_disassociate\n");
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC,
+                                    reason_code);
+
+}
+
+
+#if 0
+/* Atmel driver uses specific values for each cipher suite */
+static int convertSuiteToDriver(enum wpa_cipher suite)
+{
+    u8 suite_type;
+    
+    switch(suite) {
+        case CIPHER_NONE:
+                suite_type =  0;
+                break;
+        case CIPHER_WEP40:
+                suite_type =  1;
+                break;
+        case CIPHER_TKIP:
+                suite_type = 2;
+                break;
+        case CIPHER_WEP104:
+                suite_type = 5;
+                break;
+        case CIPHER_CCMP:
+                suite_type = 3;
+                break;
+        default:
+                suite_type = 2;
+    }
+    
+    return suite_type;
+
+}
+#endif
+    
+static int
+wpa_driver_atmel_associate(void *priv,
+                          struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       int ret = 0;
+#if 0
+        u8 pairwise_suite_driver;
+        u8 group_suite_driver;
+        u8 key_mgmt_suite_driver;
+
+        pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite);
+        group_suite_driver    = convertSuiteToDriver(params->group_suite);
+        key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite);
+
+        if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver,
+                                       group_suite_driver,
+                                       key_mgmt_suite_driver) < 0){
+               printf("wpa_driver_atmel_set_suites.\n");
+                ret = -1;
+        }
+        if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) {
+               printf("wpa_driver_atmel_set_freq.\n");
+               ret = -1;
+        }
+#endif
+       if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len)
+           < 0) {
+               printf("FAILED : wpa_driver_atmel_set_ssid.\n");
+               ret = -1;
+        }
+       if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) {
+               printf("FAILED : wpa_driver_atmel_set_bssid.\n");
+               ret = -1;
+        }
+
+       return ret;
+}
+
+
+static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static int wpa_driver_atmel_scan(void *priv,
+                                struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       return wpa_driver_wext_scan(drv->wext, params);
+}
+
+
+static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_atmel_set_operstate(void *priv, int state)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static void * wpa_driver_atmel_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_atmel_data *drv;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->wext = wpa_driver_wext_init(ctx, ifname);
+       if (drv->wext == NULL) {
+               os_free(drv);
+               return NULL;
+       }
+
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->sock < 0) {
+               wpa_driver_wext_deinit(drv->wext);
+               os_free(drv);
+               return NULL;
+       }
+
+       wpa_driver_atmel_set_wpa(drv, 1);
+
+       return drv;
+}
+
+
+static void wpa_driver_atmel_deinit(void *priv)
+{
+       struct wpa_driver_atmel_data *drv = priv;
+       wpa_driver_atmel_set_wpa(drv, 0);
+       wpa_driver_wext_deinit(drv->wext);
+       close(drv->sock);
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_atmel_ops = {
+       .name = "atmel",
+       .desc = "ATMEL AT76C5XXx (USB, PCMCIA)",
+       .get_bssid = wpa_driver_atmel_get_bssid,
+       .get_ssid = wpa_driver_atmel_get_ssid,
+       .set_key = wpa_driver_atmel_set_key,
+       .init = wpa_driver_atmel_init,
+       .deinit = wpa_driver_atmel_deinit,
+       .set_countermeasures = wpa_driver_atmel_set_countermeasures,
+       .scan2 = wpa_driver_atmel_scan,
+       .get_scan_results2 = wpa_driver_atmel_get_scan_results,
+       .deauthenticate = wpa_driver_atmel_deauthenticate,
+       .disassociate = wpa_driver_atmel_disassociate,
+       .associate = wpa_driver_atmel_associate,
+       .set_operstate = wpa_driver_atmel_set_operstate,
+};
diff --git a/src/drivers/driver_broadcom.c b/src/drivers/driver_broadcom.c
new file mode 100644 (file)
index 0000000..cb88543
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * WPA Supplicant - driver interaction with old Broadcom wl.o driver
+ * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru>
+ * 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.
+ *
+ * Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+ * Linux wireless extensions and does not need (or even work) with this old
+ * driver wrapper. Use driver_wext.c with that driver.
+ */
+
+#include "includes.h"
+
+#include <sys/ioctl.h>
+
+#include "common.h"
+
+#if 0
+#include <netpacket/packet.h>
+#include <net/ethernet.h>     /* the L2 protocols */
+#else
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>   /* The L2 protocols */
+#endif
+#include <net/if.h>
+#include <typedefs.h>
+
+/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys
+ * WRT54G GPL tarball. */
+#include <wlioctl.h>
+
+#include "driver.h"
+#include "eloop.h"
+
+struct wpa_driver_broadcom_data {
+       void *ctx;
+       int ioctl_sock;
+       int event_sock;
+       char ifname[IFNAMSIZ + 1];
+};
+
+
+#ifndef WLC_DEAUTHENTICATE
+#define WLC_DEAUTHENTICATE 143
+#endif
+#ifndef WLC_DEAUTHENTICATE_WITH_REASON
+#define WLC_DEAUTHENTICATE_WITH_REASON 201
+#endif
+#ifndef WLC_SET_TKIP_COUNTERMEASURES
+#define WLC_SET_TKIP_COUNTERMEASURES 202
+#endif
+
+#if !defined(PSK_ENABLED) /* NEW driver interface */
+#define WL_VERSION 360130
+/* wireless authentication bit vector */
+#define WPA_ENABLED 1
+#define PSK_ENABLED 2
+                                                                                
+#define WAUTH_WPA_ENABLED(wauth)  ((wauth) & WPA_ENABLED)
+#define WAUTH_PSK_ENABLED(wauth)  ((wauth) & PSK_ENABLED)
+#define WAUTH_ENABLED(wauth)    ((wauth) & (WPA_ENABLED | PSK_ENABLED))
+
+#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY
+
+typedef wl_wsec_key_t wsec_key_t;
+#endif
+
+typedef struct {
+       uint32 val;
+       struct ether_addr ea;
+       uint16 res;
+} wlc_deauth_t;
+
+
+static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
+                                            void *timeout_ctx);
+
+static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd,
+                         void *buf, int len)
+{
+       struct ifreq ifr;
+       wl_ioctl_t ioc;
+       int ret = 0;
+
+       wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)",
+                  drv->ifname, cmd, len, buf);
+       /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */
+
+       ioc.cmd = cmd;
+       ioc.buf = buf;
+       ioc.len = len;
+       os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
+       ifr.ifr_data = (caddr_t) &ioc;
+       if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) {
+               if (cmd != WLC_GET_MAGIC)
+                       perror(ifr.ifr_name);
+               wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d",
+                          cmd, ret);
+       }
+
+       return ret;
+}
+
+static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0)
+               return 0;
+       
+       os_memset(bssid, 0, ETH_ALEN);
+       return -1;
+}
+
+static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       wlc_ssid_t s;
+       
+       if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1)
+               return -1;
+
+       os_memcpy(ssid, s.SSID, s.SSID_len);
+       return s.SSID_len;
+}
+
+static int wpa_driver_broadcom_set_wpa(void *priv, int enable)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       unsigned int wauth, wsec;
+       struct ether_addr ea;
+
+       os_memset(&ea, enable ? 0xff : 0, sizeof(ea));
+       if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) ==
+           -1 ||
+           broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1)
+               return -1;
+
+       if (enable) {
+               wauth = PSK_ENABLED;
+               wsec = TKIP_ENABLED;
+       } else {
+               wauth = 255;
+               wsec &= ~(TKIP_ENABLED | AES_ENABLED);
+       }
+
+       if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) ==
+           -1 ||
+           broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1)
+               return -1;
+
+       /* FIX: magic number / error handling? */
+       broadcom_ioctl(drv, 122, &ea, sizeof(ea));
+
+       return 0;
+}
+
+static int wpa_driver_broadcom_set_key(const char *ifname, void *priv,
+                                      enum wpa_alg alg,
+                                      const u8 *addr, int key_idx, int set_tx,
+                                      const u8 *seq, size_t seq_len,
+                                      const u8 *key, size_t key_len)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       int ret;
+       wsec_key_t wkt;
+
+       os_memset(&wkt, 0, sizeof wkt);
+       wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d",
+                  set_tx ? "PRIMARY " : "", key_idx, alg);
+       if (key && key_len > 0)
+               wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len);
+
+       switch (alg) {
+       case WPA_ALG_NONE:
+               wkt.algo = CRYPTO_ALGO_OFF;
+               break;
+       case WPA_ALG_WEP:
+               wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */
+               break;
+       case WPA_ALG_TKIP:
+               wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */
+               break;
+       case WPA_ALG_CCMP:
+               wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM;
+                              * AES_OCB_MSDU, AES_OCB_MPDU? */
+               break;
+       default:
+               wkt.algo = CRYPTO_ALGO_NALG;
+               break;
+       }
+
+       if (seq && seq_len > 0)
+               wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len);
+
+       if (addr)
+               wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN);
+
+       wkt.index = key_idx;
+       wkt.len = key_len;
+       if (key && key_len > 0) {
+               os_memcpy(wkt.data, key, key_len);
+               if (key_len == 32) {
+                       /* hack hack hack XXX */
+                       os_memcpy(&wkt.data[16], &key[24], 8);
+                       os_memcpy(&wkt.data[24], &key[16], 8);
+               }
+       }
+       /* wkt.algo = CRYPTO_ALGO_...; */
+       wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY;
+       if (addr && set_tx)
+               os_memcpy(&wkt.ea, addr, sizeof(wkt.ea));
+       ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt));
+       if (addr && set_tx) {
+               /* FIX: magic number / error handling? */
+               broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea));
+       }
+       return ret;
+}
+
+
+static void wpa_driver_broadcom_event_receive(int sock, void *ctx,
+                                             void *sock_ctx)
+{
+       char buf[8192];
+       int left;
+       wl_wpa_header_t *wwh;
+       union wpa_event_data data;
+       u8 *resp_ies = NULL;
+
+       if ((left = recv(sock, buf, sizeof buf, 0)) < 0)
+               return;
+
+       wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left);
+
+       if ((size_t) left < sizeof(wl_wpa_header_t))
+               return;
+
+       wwh = (wl_wpa_header_t *) buf;
+
+       if (wwh->snap.type != WL_WPA_ETHER_TYPE)
+               return;
+       if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+
+       switch (wwh->type) {
+       case WLC_ASSOC_MSG:
+               left -= WL_WPA_HEADER_LEN;
+               wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)",
+                          left);
+               if (left > 0) {
+                       resp_ies = os_malloc(left);
+                       if (resp_ies == NULL)
+                               return;
+                       os_memcpy(resp_ies, buf + WL_WPA_HEADER_LEN, left);
+                       data.assoc_info.resp_ies = resp_ies;
+                       data.assoc_info.resp_ies_len = left;
+               }
+
+               wpa_supplicant_event(ctx, EVENT_ASSOC, &data);
+               os_free(resp_ies);
+               break;
+       case WLC_DISASSOC_MSG:
+               wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE");
+               wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+               break;
+       case WLC_PTK_MIC_MSG:
+               wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE");
+               data.michael_mic_failure.unicast = 1;
+               wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+               break;
+       case WLC_GTK_MIC_MSG:
+               wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE");
+               data.michael_mic_failure.unicast = 0;
+               wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)",
+                          wwh->type);
+               break;
+       }
+}      
+
+static void * wpa_driver_broadcom_init(void *ctx, const char *ifname)
+{
+       int s;
+       struct sockaddr_ll ll;
+       struct wpa_driver_broadcom_data *drv;
+       struct ifreq ifr;
+
+       /* open socket to kernel */
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               perror("socket");
+               return NULL;
+       }
+       /* do it */
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+               perror(ifr.ifr_name);
+               return NULL;
+       }
+
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->ioctl_sock = s;
+
+       s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2));
+       if (s < 0) {
+               perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))");
+               close(drv->ioctl_sock);
+               os_free(drv);
+               return NULL;
+       }
+
+       os_memset(&ll, 0, sizeof(ll));
+       ll.sll_family = AF_PACKET;
+       ll.sll_protocol = ntohs(ETH_P_802_2);
+       ll.sll_ifindex = ifr.ifr_ifindex;
+       ll.sll_hatype = 0;
+       ll.sll_pkttype = PACKET_HOST;
+       ll.sll_halen = 0;
+
+       if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+               perror("bind(netlink)");
+               close(s);
+               close(drv->ioctl_sock);
+               os_free(drv);
+               return NULL;
+       }
+
+       eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx,
+                                NULL);
+       drv->event_sock = s;
+       wpa_driver_broadcom_set_wpa(drv, 1);
+
+       return drv;
+}
+
+static void wpa_driver_broadcom_deinit(void *priv)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       wpa_driver_broadcom_set_wpa(drv, 0);
+       eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
+       eloop_unregister_read_sock(drv->event_sock);
+       close(drv->event_sock);
+       close(drv->ioctl_sock);
+       os_free(drv);
+}
+
+static int wpa_driver_broadcom_set_countermeasures(void *priv,
+                                                  int enabled)
+{
+#if 0
+       struct wpa_driver_broadcom_data *drv = priv;
+       /* FIX: ? */
+       return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled,
+                             sizeof(enabled));
+#else
+       return 0;
+#endif
+}
+
+static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */
+       int _restrict = (enabled ? 1 : 0);
+       
+       if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, 
+                          &_restrict, sizeof(_restrict)) < 0 ||
+           broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT,
+                          &_restrict, sizeof(_restrict)) < 0)
+               return -1;
+
+       return 0;
+}
+
+static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx,
+                                            void *timeout_ctx)
+{
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+static int wpa_driver_broadcom_scan(void *priv,
+                                   struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       wlc_ssid_t wst = { 0, "" };
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+
+       if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) {
+               wst.SSID_len = ssid_len;
+               os_memcpy(wst.SSID, ssid, ssid_len);
+       }
+       
+       if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0)
+               return -1;
+
+       eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv,
+                              drv->ctx);
+       return 0;
+}
+
+
+static const int frequency_list[] = { 
+       2412, 2417, 2422, 2427, 2432, 2437, 2442,
+       2447, 2452, 2457, 2462, 2467, 2472, 2484 
+};
+
+struct bss_ie_hdr {
+       u8 elem_id;
+       u8 len;
+       u8 oui[3];
+       /* u8 oui_type; */
+       /* u16 version; */
+} __attribute__ ((packed));
+
+static struct wpa_scan_results *
+wpa_driver_broadcom_get_scan_results(void *priv)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       char *buf;
+       wl_scan_results_t *wsr;
+       wl_bss_info_t *wbi;
+       size_t ap_num;
+       struct wpa_scan_results *res;
+
+       buf = os_malloc(WLC_IOCTL_MAXLEN);
+       if (buf == NULL)
+               return NULL;
+
+       wsr = (wl_scan_results_t *) buf;
+
+       wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr);
+       wsr->version = 107;
+       wsr->count = 0;
+
+       if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) {
+               os_free(buf);
+               return NULL;
+       }
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL) {
+               os_free(buf);
+               return NULL;
+       }
+
+       res->res = os_zalloc(wsr->count * sizeof(struct wpa_scan_res *));
+       if (res->res == NULL) {
+               os_free(res);
+               os_free(buf);
+               return NULL;
+       }
+
+       for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) {
+               struct wpa_scan_res *r;
+               r = os_malloc(sizeof(*r) + wbi->ie_length);
+               if (r == NULL)
+                       break;
+               res->res[res->num++] = r;
+
+               os_memcpy(r->bssid, &wbi->BSSID, ETH_ALEN);
+               r->freq = frequency_list[wbi->channel - 1];
+               /* get ie's */
+               os_memcpy(r + 1, wbi + 1, wbi->ie_length);
+               r->ie_len = wbi->ie_length;
+
+               wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length);
+       }
+
+       wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu "
+                  "BSSes)",
+                  wsr->buflen, (unsigned long) ap_num);
+       
+       os_free(buf);
+       return res;
+       }
+
+static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr,
+                                             int reason_code)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       wlc_deauth_t wdt;
+       wdt.val = reason_code;
+       os_memcpy(&wdt.ea, addr, sizeof wdt.ea);
+       wdt.res = 0x7fff;
+       return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt,
+                             sizeof(wdt));
+}
+
+static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr,
+                                           int reason_code)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       return broadcom_ioctl(drv, WLC_DISASSOC, NULL, 0);
+}
+
+static int
+wpa_driver_broadcom_associate(void *priv,
+                             struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_broadcom_data *drv = priv;
+       wlc_ssid_t s;
+       int infra = 1;
+       int auth = 0;
+       int wsec = 4;
+       int dummy;
+       int wpa_auth;
+       int ret;
+
+       ret = wpa_driver_broadcom_set_drop_unencrypted(
+               drv, params->drop_unencrypted);
+
+       s.SSID_len = params->ssid_len;
+       os_memcpy(s.SSID, params->ssid, params->ssid_len);
+
+       switch (params->pairwise_suite) {
+       case CIPHER_WEP40:
+       case CIPHER_WEP104:
+               wsec = 1;
+               break;
+
+       case CIPHER_TKIP:
+               wsec = 2;
+               break;
+
+       case CIPHER_CCMP:
+               wsec = 4;
+               break;
+
+       default:
+               wsec = 0;
+               break;
+       }
+
+       switch (params->key_mgmt_suite) {
+       case KEY_MGMT_802_1X:
+               wpa_auth = 1;
+               break;
+
+       case KEY_MGMT_PSK:
+               wpa_auth = 2;
+               break;
+
+       default:
+               wpa_auth = 255;
+               break;
+       }
+
+       /* printf("broadcom_associate: %u %u %u\n", pairwise_suite,
+        * group_suite, key_mgmt_suite);
+        * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec));
+        * wl join uses wlc_sec_wep here, not wlc_set_wsec */
+
+       if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 ||
+           broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth,
+                          sizeof(wpa_auth)) < 0 ||
+           broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 ||
+           broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 ||
+           broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 ||
+           broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 ||
+           broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0)
+               return -1;
+
+       return ret;
+}
+
+const struct wpa_driver_ops wpa_driver_broadcom_ops = {
+       .name = "broadcom",
+       .desc = "Broadcom wl.o driver",
+       .get_bssid = wpa_driver_broadcom_get_bssid,
+       .get_ssid = wpa_driver_broadcom_get_ssid,
+       .set_key = wpa_driver_broadcom_set_key,
+       .init = wpa_driver_broadcom_init,
+       .deinit = wpa_driver_broadcom_deinit,
+       .set_countermeasures = wpa_driver_broadcom_set_countermeasures,
+       .scan2 = wpa_driver_broadcom_scan,
+       .get_scan_results2 = wpa_driver_broadcom_get_scan_results,
+       .deauthenticate = wpa_driver_broadcom_deauthenticate,
+       .disassociate = wpa_driver_broadcom_disassociate,
+       .associate = wpa_driver_broadcom_associate,
+};
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
new file mode 100644 (file)
index 0000000..99de6c7
--- /dev/null
@@ -0,0 +1,1542 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, 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 <sys/ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include <net/if.h>
+#include <net/if_media.h>
+
+#ifdef __NetBSD__
+#include <net/if_ether.h>
+#else
+#include <net/ethernet.h>
+#endif
+#include <net/route.h>
+
+#ifdef __DragonFly__
+#include <netproto/802_11/ieee80211_ioctl.h>
+#include <netproto/802_11/ieee80211_dragonfly.h>
+#else /* __DragonFly__ */
+#ifdef __GLIBC__
+#include <netinet/ether.h>
+#endif /* __GLIBC__ */
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+#include <net80211/ieee80211_crypto.h>
+#endif /* __DragonFly__ || __GLIBC__ */
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <net80211/ieee80211_freebsd.h>
+#endif
+#if __NetBSD__
+#include <net80211/ieee80211_netbsd.h>
+#endif
+
+#include "l2_packet/l2_packet.h"
+
+struct bsd_driver_data {
+       struct hostapd_data *hapd;      /* back pointer */
+
+       int     sock;                   /* open socket for 802.11 ioctls */
+       struct l2_packet_data *sock_xmit;/* raw packet xmit socket */
+       int     route;                  /* routing socket for events */
+       char    ifname[IFNAMSIZ+1];     /* interface name */
+       unsigned int ifindex;           /* interface index */
+       void    *ctx;
+       struct wpa_driver_capa capa;    /* driver capability */
+       int     is_ap;                  /* Access point mode */
+       int     prev_roaming;   /* roaming state to restore on deinit */
+       int     prev_privacy;   /* privacy state to restore on deinit */
+       int     prev_wpa;       /* wpa state to restore on deinit */
+};
+
+/* Generic functions for hostapd and wpa_supplicant */
+
+static int
+bsd_set80211(void *priv, int op, int val, const void *arg, int arg_len)
+{
+       struct bsd_driver_data *drv = priv;
+       struct ieee80211req ireq;
+
+       os_memset(&ireq, 0, sizeof(ireq));
+       os_strlcpy(ireq.i_name, drv->ifname, sizeof(ireq.i_name));
+       ireq.i_type = op;
+       ireq.i_val = val;
+       ireq.i_data = (void *) arg;
+       ireq.i_len = arg_len;
+
+       if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, val=%u, "
+                          "arg_len=%u]: %s", op, val, arg_len,
+                          strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+static int
+bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
+            int arg_len)
+{
+       struct bsd_driver_data *drv = priv;
+
+       os_memset(ireq, 0, sizeof(*ireq));
+       os_strlcpy(ireq->i_name, drv->ifname, sizeof(ireq->i_name));
+       ireq->i_type = op;
+       ireq->i_len = arg_len;
+       ireq->i_data = arg;
+
+       if (ioctl(drv->sock, SIOCG80211, ireq) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+                          "arg_len=%u]: %s", op, arg_len, strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+       struct ieee80211req ireq;
+
+       if (bsd_get80211(drv, &ireq, op, arg, arg_len) < 0)
+               return -1;
+       return ireq.i_len;
+}
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+       return bsd_set80211(drv, op, 0, arg, arg_len);
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+       return bsd_set80211(drv, op, arg, NULL, 0);
+}
+
+static int
+bsd_get_ssid(void *priv, u8 *ssid, int len)
+{
+       struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211NWID
+       struct ieee80211_nwid nwid;
+       struct ifreq ifr;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_data = (void *)&nwid;
+       if (ioctl(drv->sock, SIOCG80211NWID, &ifr) < 0 ||
+           nwid.i_len > IEEE80211_NWID_LEN)
+               return -1;
+       os_memcpy(ssid, nwid.i_nwid, nwid.i_len);
+       return nwid.i_len;
+#else
+       return get80211var(drv, IEEE80211_IOC_SSID, ssid, IEEE80211_NWID_LEN);
+#endif
+}
+
+static int
+bsd_set_ssid(void *priv, const u8 *ssid, int ssid_len)
+{
+       struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211NWID
+       struct ieee80211_nwid nwid;
+       struct ifreq ifr;
+
+       os_memcpy(nwid.i_nwid, ssid, ssid_len);
+       nwid.i_len = ssid_len;
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_data = (void *)&nwid;
+       return ioctl(drv->sock, SIOCS80211NWID, &ifr);
+#else
+       return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+#endif
+}
+
+static int
+bsd_get_if_media(void *priv)
+{
+       struct bsd_driver_data *drv = priv;
+       struct ifmediareq ifmr;
+
+       os_memset(&ifmr, 0, sizeof(ifmr));
+       os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+       if (ioctl(drv->sock, SIOCGIFMEDIA, &ifmr) < 0) {
+               wpa_printf(MSG_ERROR, "%s: SIOCGIFMEDIA %s", __func__,
+                          strerror(errno));
+               return -1;
+       }
+
+       return ifmr.ifm_current;
+}
+
+static int
+bsd_set_if_media(void *priv, int media)
+{
+       struct bsd_driver_data *drv = priv;
+       struct ifreq ifr;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+       ifr.ifr_media = media;
+
+       if (ioctl(drv->sock, SIOCSIFMEDIA, &ifr) < 0) {
+               wpa_printf(MSG_ERROR, "%s: SIOCSIFMEDIA %s", __func__,
+                          strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+bsd_set_mediaopt(void *priv, uint32_t mask, uint32_t mode)
+{
+       int media = bsd_get_if_media(priv);
+
+       if (media < 0)
+               return -1;
+       media &= ~mask;
+       media |= mode;
+       if (bsd_set_if_media(priv, media) < 0)
+               return -1;
+       return 0;
+}
+
+static int
+bsd_del_key(void *priv, const u8 *addr, int key_idx)
+{
+       struct ieee80211req_del_key wk;
+
+       os_memset(&wk, 0, sizeof(wk));
+       if (addr == NULL) {
+               wpa_printf(MSG_DEBUG, "%s: key_idx=%d", __func__, key_idx);
+               wk.idk_keyix = key_idx;
+       } else {
+               wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__,
+                          MAC2STR(addr));
+               os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+               wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
+       }
+
+       return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_send_mlme_param(void *priv, const u8 op, const u16 reason, const u8 *addr)
+{
+       struct ieee80211req_mlme mlme;
+
+       os_memset(&mlme, 0, sizeof(mlme));
+       mlme.im_op = op;
+       mlme.im_reason = reason;
+       os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_ctrl_iface(void *priv, int enable)
+{
+       struct bsd_driver_data *drv = priv;
+       struct ifreq ifr;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+
+       if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
+               perror("ioctl[SIOCGIFFLAGS]");
+               return -1;
+       }
+
+       if (enable)
+               ifr.ifr_flags |= IFF_UP;
+       else
+               ifr.ifr_flags &= ~IFF_UP;
+
+       if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
+               perror("ioctl[SIOCSIFFLAGS]");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+           const unsigned char *addr, int key_idx, int set_tx, const u8 *seq,
+           size_t seq_len, const u8 *key, size_t key_len)
+{
+       struct ieee80211req_key wk;
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
+                  "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
+                  set_tx, seq_len, key_len);
+
+       if (alg == WPA_ALG_NONE) {
+#ifndef HOSTAPD
+               if (addr == NULL ||
+                   os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+                             IEEE80211_ADDR_LEN) == 0)
+                       return bsd_del_key(priv, NULL, key_idx);
+               else
+#endif /* HOSTAPD */
+                       return bsd_del_key(priv, addr, key_idx);
+       }
+
+       os_memset(&wk, 0, sizeof(wk));
+       switch (alg) {
+       case WPA_ALG_WEP:
+               wk.ik_type = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_ALG_TKIP:
+               wk.ik_type = IEEE80211_CIPHER_TKIP;
+               break;
+       case WPA_ALG_CCMP:
+               wk.ik_type = IEEE80211_CIPHER_AES_CCM;
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg);
+               return -1;
+       }
+
+       wk.ik_flags = IEEE80211_KEY_RECV;
+       if (set_tx)
+               wk.ik_flags |= IEEE80211_KEY_XMIT;
+
+       if (addr == NULL) {
+               os_memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+               wk.ik_keyix = key_idx;
+       } else {
+               os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+               /*
+                * Deduce whether group/global or unicast key by checking
+                * the address (yech).  Note also that we can only mark global
+                * keys default; doing this for a unicast key is an error.
+                */
+               if (os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+                             IEEE80211_ADDR_LEN) == 0) {
+                       wk.ik_flags |= IEEE80211_KEY_GROUP;
+                       wk.ik_keyix = key_idx;
+               } else {
+                       wk.ik_keyix = key_idx == 0 ? IEEE80211_KEYIX_NONE :
+                               key_idx;
+               }
+       }
+       if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
+               wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+       wk.ik_keylen = key_len;
+       os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+       os_memcpy(wk.ik_keydata, key, key_len);
+
+       return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
+{
+#ifndef IEEE80211_IOC_APPIE
+       static const char *ciphernames[] =
+               { "WEP", "TKIP", "AES-OCB", "AES-CCM", "CKIP", "NONE" };
+       int v;
+
+       switch (params->wpa_group) {
+       case WPA_CIPHER_CCMP:
+               v = IEEE80211_CIPHER_AES_CCM;
+               break;
+       case WPA_CIPHER_TKIP:
+               v = IEEE80211_CIPHER_TKIP;
+               break;
+       case WPA_CIPHER_WEP104:
+               v = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_CIPHER_WEP40:
+               v = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_CIPHER_NONE:
+               v = IEEE80211_CIPHER_NONE;
+               break;
+       default:
+               printf("Unknown group key cipher %u\n",
+                       params->wpa_group);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
+                  __func__, ciphernames[v], v);
+       if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
+               printf("Unable to set group key cipher to %u (%s)\n",
+                       v, ciphernames[v]);
+               return -1;
+       }
+       if (v == IEEE80211_CIPHER_WEP) {
+               /* key length is done only for specific ciphers */
+               v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+               if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
+                       printf("Unable to set group key length to %u\n", v);
+                       return -1;
+               }
+       }
+
+       v = 0;
+       if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+               v |= 1<<IEEE80211_CIPHER_AES_CCM;
+       if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+               v |= 1<<IEEE80211_CIPHER_TKIP;
+       if (params->wpa_pairwise & WPA_CIPHER_NONE)
+               v |= 1<<IEEE80211_CIPHER_NONE;
+       wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+       if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
+               printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+                  __func__, params->wpa_key_mgmt);
+       if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
+                         params->wpa_key_mgmt)) {
+               printf("Unable to set key management algorithms to 0x%x\n",
+                       params->wpa_key_mgmt);
+               return -1;
+       }
+
+       v = 0;
+       if (params->rsn_preauth)
+               v |= BIT(0);
+       wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+                  __func__, params->rsn_preauth);
+       if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
+               printf("Unable to set RSN capabilities to 0x%x\n", v);
+               return -1;
+       }
+#endif /* IEEE80211_IOC_APPIE */
+
+       wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
+       if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
+               printf("Unable to set WPA to %u\n", params->wpa);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+       if (!params->enabled) {
+               /* XXX restore state */
+               return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+                                    IEEE80211_AUTH_AUTO);
+       }
+       if (!params->wpa && !params->ieee802_1x) {
+               wpa_printf(MSG_ERROR, "%s: No 802.1X or WPA enabled",
+                          __func__);
+               return -1;
+       }
+       if (params->wpa && bsd_configure_wpa(priv, params) != 0) {
+               wpa_printf(MSG_ERROR, "%s: Failed to configure WPA state",
+                          __func__);
+               return -1;
+       }
+       if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+               (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+               wpa_printf(MSG_ERROR, "%s: Failed to enable WPA/802.1X",
+                          __func__);
+               return -1;
+       }
+       return bsd_ctrl_iface(priv, 1);
+}
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+                      int total_flags, int flags_or, int flags_and)
+{
+       int authorized = -1;
+
+       /* For now, only support setting Authorized flag */
+       if (flags_or & WPA_STA_AUTHORIZED)
+               authorized = 1;
+       if (!(flags_and & WPA_STA_AUTHORIZED))
+               authorized = 0;
+
+       if (authorized < 0)
+               return 0;
+
+       return bsd_send_mlme_param(priv, authorized ?
+                                  IEEE80211_MLME_AUTHORIZE :
+                                  IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
+
+static void
+bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
+{
+       struct ieee80211req_wpaie ie;
+       int ielen = 0;
+       u8 *iebuf = NULL;
+
+       /*
+        * Fetch and validate any negotiated WPA/RSN parameters.
+        */
+       memset(&ie, 0, sizeof(ie));
+       memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+       if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+               printf("Failed to get WPA/RSN information element.\n");
+               goto no_ie;
+       }
+       iebuf = ie.wpa_ie;
+       ielen = ie.wpa_ie[1];
+       if (ielen == 0)
+               iebuf = NULL;
+       else
+               ielen += 2;
+
+no_ie:
+       drv_event_assoc(ctx, addr, iebuf, ielen);
+}
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+              int encrypt, const u8 *own_addr)
+{
+       struct bsd_driver_data *drv = priv;
+
+       wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", data, data_len);
+
+       return l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, data,
+                             data_len);
+}
+
+static int
+bsd_set_freq(void *priv, u16 channel)
+{
+       struct bsd_driver_data *drv = priv;
+#ifdef SIOCS80211CHANNEL
+       struct ieee80211chanreq creq;
+#endif /* SIOCS80211CHANNEL */
+       u32 mode;
+
+       if (channel < 14)
+               mode = IFM_IEEE80211_11G;
+       else if (channel == 14)
+               mode = IFM_IEEE80211_11B;
+       else
+               mode = IFM_IEEE80211_11A;
+       if (bsd_set_mediaopt(drv, IFM_MMASK, mode) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to set modulation mode",
+                          __func__);
+               return -1;
+       }
+
+#ifdef SIOCS80211CHANNEL
+       os_memset(&creq, 0, sizeof(creq));
+       os_strlcpy(creq.i_name, drv->ifname, sizeof(creq.i_name));
+       creq.i_channel = channel;
+       return ioctl(drv->sock, SIOCS80211CHANNEL, &creq);
+#else /* SIOCS80211CHANNEL */
+       return set80211param(priv, IEEE80211_IOC_CHANNEL, channel);
+#endif /* SIOCS80211CHANNEL */
+}
+
+static int
+bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+       wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %lu)", __func__,
+                  (unsigned long)ie_len);
+       return bsd_set80211(priv, IEEE80211_IOC_APPIE, IEEE80211_APPIE_WPA,
+                           ie, ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+       return 0;
+}
+
+
+#ifdef HOSTAPD
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from net80211 header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+
+static int bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                         int reason_code);
+
+static const char *
+ether_sprintf(const u8 *addr)
+{
+       static char buf[sizeof(MACSTR)];
+
+       if (addr != NULL)
+               snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+       else
+               snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+       return buf;
+}
+
+static int
+bsd_set_privacy(void *priv, int enabled)
+{
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+       return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
+}
+
+static int
+bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+              u8 *seq)
+{
+       struct ieee80211req_key wk;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+                  __func__, ether_sprintf(addr), idx);
+
+       memset(&wk, 0, sizeof(wk));
+       if (addr == NULL)
+               memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+       else
+               memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+       wk.ik_keyix = idx;
+
+       if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
+               printf("Failed to get encryption.\n");
+               return -1;
+       }
+
+#ifdef WORDS_BIGENDIAN
+       {
+               /*
+                * wk.ik_keytsc is in host byte order (big endian), need to
+                * swap it to match with the byte order used in WPA.
+                */
+               int i;
+               u8 tmp[WPA_KEY_RSC_LEN];
+               memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+               for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+                       seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+               }
+       }
+#else /* WORDS_BIGENDIAN */
+       memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+       return 0;
+}
+
+
+static int 
+bsd_flush(void *priv)
+{
+       u8 allsta[IEEE80211_ADDR_LEN];
+
+       memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+       return bsd_sta_deauth(priv, NULL, allsta, IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+                        const u8 *addr)
+{
+       struct ieee80211req_sta_stats stats;
+
+       memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+       if (get80211var(priv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats))
+           > 0) {
+               /* XXX? do packets counts include non-data frames? */
+               data->rx_packets = stats.is_stats.ns_rx_data;
+               data->rx_bytes = stats.is_stats.ns_rx_bytes;
+               data->tx_packets = stats.is_stats.ns_tx_data;
+               data->tx_bytes = stats.is_stats.ns_tx_bytes;
+       }
+       return 0;
+}
+
+static int
+bsd_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, int reason_code)
+{
+       return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+                                  addr);
+}
+
+static int
+bsd_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                int reason_code)
+{
+       return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+                                  addr);
+}
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+       struct bsd_driver_data *drv = ctx;
+       char buf[2048];
+       struct if_announcemsghdr *ifan;
+       struct rt_msghdr *rtm;
+       struct ieee80211_michael_event *mic;
+       struct ieee80211_join_event *join;
+       struct ieee80211_leave_event *leave;
+       int n;
+       union wpa_event_data data;
+
+       n = read(sock, buf, sizeof(buf));
+       if (n < 0) {
+               if (errno != EINTR && errno != EAGAIN)
+                       perror("read(PF_ROUTE)");
+               return;
+       }
+
+       rtm = (struct rt_msghdr *) buf;
+       if (rtm->rtm_version != RTM_VERSION) {
+               wpa_printf(MSG_DEBUG, "Routing message version %d not "
+                       "understood\n", rtm->rtm_version);
+               return;
+       }
+       ifan = (struct if_announcemsghdr *) rtm;
+       switch (rtm->rtm_type) {
+       case RTM_IEEE80211:
+               switch (ifan->ifan_what) {
+               case RTM_IEEE80211_ASSOC:
+               case RTM_IEEE80211_REASSOC:
+               case RTM_IEEE80211_DISASSOC:
+               case RTM_IEEE80211_SCAN:
+                       break;
+               case RTM_IEEE80211_LEAVE:
+                       leave = (struct ieee80211_leave_event *) &ifan[1];
+                       drv_event_disassoc(drv->hapd, leave->iev_addr);
+                       break;
+               case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+               case RTM_IEEE80211_REJOIN:
+#endif
+                       join = (struct ieee80211_join_event *) &ifan[1];
+                       bsd_new_sta(drv, drv->hapd, join->iev_addr);
+                       break;
+               case RTM_IEEE80211_REPLAY:
+                       /* ignore */
+                       break;
+               case RTM_IEEE80211_MICHAEL:
+                       mic = (struct ieee80211_michael_event *) &ifan[1];
+                       wpa_printf(MSG_DEBUG,
+                               "Michael MIC failure wireless event: "
+                               "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+                               MAC2STR(mic->iev_src));
+                       os_memset(&data, 0, sizeof(data));
+                       data.michael_mic_failure.unicast = 1;
+                       data.michael_mic_failure.src = mic->iev_src;
+                       wpa_supplicant_event(drv->hapd,
+                                            EVENT_MICHAEL_MIC_FAILURE, &data);
+                       break;
+               }
+               break;
+       }
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+       struct bsd_driver_data *drv = ctx;
+       drv_event_eapol_rx(drv->hapd, src_addr, buf, len);
+}
+
+static int
+hostapd_bsd_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+       return bsd_set_freq(priv, freq->channel);
+}
+
+static void *
+bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+       struct bsd_driver_data *drv;
+
+       drv = os_zalloc(sizeof(struct bsd_driver_data));
+       if (drv == NULL) {
+               printf("Could not allocate memory for bsd driver data\n");
+               goto bad;
+       }
+
+       drv->hapd = hapd;
+       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->sock < 0) {
+               perror("socket[PF_INET,SOCK_DGRAM]");
+               goto bad;
+       }
+       os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+
+       drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+                                       handle_read, drv, 0);
+       if (drv->sock_xmit == NULL)
+               goto bad;
+       if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+               goto bad;
+
+       /* mark down during setup */
+       if (bsd_ctrl_iface(drv, 0) < 0)
+               goto bad;
+
+       drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+       if (drv->route < 0) {
+               perror("socket(PF_ROUTE,SOCK_RAW)");
+               goto bad;
+       }
+       eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
+                                NULL);
+
+       if (bsd_set_mediaopt(drv, IFM_OMASK, IFM_IEEE80211_HOSTAP) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+                          __func__);
+               goto bad;
+       }
+
+       return drv;
+bad:
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       if (drv->sock >= 0)
+               close(drv->sock);
+       if (drv != NULL)
+               os_free(drv);
+       return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+       struct bsd_driver_data *drv = priv;
+
+       if (drv->route >= 0) {
+               eloop_unregister_read_sock(drv->route);
+               close(drv->route);
+       }
+       bsd_ctrl_iface(drv, 0);
+       if (drv->sock >= 0)
+               close(drv->sock);
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       os_free(drv);
+}
+
+#else /* HOSTAPD */
+
+static int
+get80211param(struct bsd_driver_data *drv, int op)
+{
+       struct ieee80211req ireq;
+
+       if (bsd_get80211(drv, &ireq, op, NULL, 0) < 0)
+               return -1;
+       return ireq.i_val;
+}
+
+static int
+wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
+{
+       struct bsd_driver_data *drv = priv;
+#ifdef SIOCG80211BSSID
+       struct ieee80211_bssid bs;
+
+       os_strlcpy(bs.i_name, drv->ifname, sizeof(bs.i_name));
+       if (ioctl(drv->sock, SIOCG80211BSSID, &bs) < 0)
+               return -1;
+       os_memcpy(bssid, bs.i_bssid, sizeof(bs.i_bssid));
+       return 0;
+#else
+       return get80211var(drv, IEEE80211_IOC_BSSID,
+               bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
+#endif
+}
+
+static int
+wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
+{
+       struct bsd_driver_data *drv = priv;
+       return bsd_get_ssid(drv, ssid, 0);
+}
+
+static int
+wpa_driver_bsd_set_wpa_ie(struct bsd_driver_data *drv, const u8 *wpa_ie,
+                         size_t wpa_ie_len)
+{
+#ifdef IEEE80211_IOC_APPIE
+       return bsd_set_opt_ie(drv, wpa_ie, wpa_ie_len);
+#else /* IEEE80211_IOC_APPIE */
+       return set80211var(drv, IEEE80211_IOC_OPTIE, wpa_ie, wpa_ie_len);
+#endif /* IEEE80211_IOC_APPIE */
+}
+
+static int
+wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
+{
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+               __FUNCTION__, wpa, privacy);
+
+       if (!wpa && wpa_driver_bsd_set_wpa_ie(priv, NULL, 0) < 0)
+               ret = -1;
+       if (set80211param(priv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+               ret = -1;
+       if (set80211param(priv, IEEE80211_IOC_WPA, wpa) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+static int
+wpa_driver_bsd_set_wpa(void *priv, int enabled)
+{
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+       return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+}
+
+static int
+wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
+{
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+       return set80211param(priv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+       return set80211param(priv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+static int
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+       return bsd_send_mlme_param(priv, IEEE80211_MLME_DEAUTH, reason_code,
+                                  addr);
+}
+
+static int
+wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code)
+{
+       return bsd_send_mlme_param(priv, IEEE80211_MLME_DISASSOC, reason_code,
+                                  addr);
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+       int authmode;
+
+       if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+           (auth_alg & WPA_AUTH_ALG_SHARED))
+               authmode = IEEE80211_AUTH_AUTO;
+       else if (auth_alg & WPA_AUTH_ALG_SHARED)
+               authmode = IEEE80211_AUTH_SHARED;
+       else
+               authmode = IEEE80211_AUTH_OPEN;
+
+       return set80211param(priv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+       struct bsd_driver_data *drv = ctx;
+
+       drv_event_eapol_rx(drv->ctx, src_addr, buf, len);
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+       struct bsd_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       u32 mode;
+       u16 channel;
+       int privacy;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG,
+               "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+               , __func__
+                  , (unsigned int) params->ssid_len, params->ssid
+               , (unsigned int) params->wpa_ie_len
+               , params->pairwise_suite
+               , params->group_suite
+               , params->key_mgmt_suite
+       );
+
+       switch (params->mode) {
+       case IEEE80211_MODE_INFRA:
+               mode = 0 /* STA */;
+               break;
+       case IEEE80211_MODE_IBSS:
+               mode = IFM_IEEE80211_IBSS;
+               break;
+       case IEEE80211_MODE_AP:
+               mode = IFM_IEEE80211_HOSTAP;
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "%s: unknown operation mode", __func__);
+               return -1;
+       }
+       if (bsd_set_mediaopt(drv, IFM_OMASK, mode) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+                          __func__);
+               return -1;
+       }
+
+       if (params->mode == IEEE80211_MODE_AP) {
+               if (params->freq >= 2412 && params->freq <= 2472)
+                       channel = (params->freq - 2407) / 5;
+               else if (params->freq == 2484)
+                       channel = 14;
+               else if ((params->freq >= 5180 && params->freq <= 5240) ||
+                        (params->freq >= 5745 && params->freq <= 5825))
+                       channel = (params->freq - 5000) / 5;
+               else
+                       channel = 0;
+               if (bsd_set_freq(drv, channel) < 0)
+                       return -1;
+
+               drv->sock_xmit = l2_packet_init(drv->ifname, NULL, ETH_P_EAPOL,
+                                               handle_read, drv, 0);
+               if (drv->sock_xmit == NULL)
+                       return -1;
+               drv->is_ap = 1;
+               return 0;
+       }
+
+       if (wpa_driver_bsd_set_drop_unencrypted(drv, params->drop_unencrypted)
+           < 0)
+               ret = -1;
+       if (wpa_driver_bsd_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+       /* XXX error handling is wrong but unclear what to do... */
+       if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+               return -1;
+
+       privacy = !(params->pairwise_suite == CIPHER_NONE &&
+           params->group_suite == CIPHER_NONE &&
+           params->key_mgmt_suite == KEY_MGMT_NONE &&
+           params->wpa_ie_len == 0);
+       wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
+
+       if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+               return -1;
+
+       if (params->wpa_ie_len &&
+           set80211param(drv, IEEE80211_IOC_WPA,
+                         params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+               return -1;
+
+       os_memset(&mlme, 0, sizeof(mlme));
+       mlme.im_op = IEEE80211_MLME_ASSOC;
+       if (params->ssid != NULL)
+               os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+       mlme.im_ssid_len = params->ssid_len;
+       if (params->bssid != NULL)
+               os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+       if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+               return -1;
+       return ret;
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+       struct bsd_driver_data *drv = priv;
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+       struct ieee80211_scan_req sr;
+       int i;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+
+       if (bsd_set_mediaopt(drv, IFM_OMASK, 0 /* STA */) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to set operation mode",
+                          __func__);
+               return -1;
+       }
+
+       if (set80211param(drv, IEEE80211_IOC_ROAMING,
+                         IEEE80211_ROAMING_MANUAL) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to set "
+                          "wpa_supplicant-based roaming: %s", __func__,
+                          strerror(errno));
+               return -1;
+       }
+
+       if (wpa_driver_bsd_set_wpa(drv, 1) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to set wpa: %s", __func__,
+                          strerror(errno));
+               return -1;
+       }
+
+       /* NB: interface must be marked UP to do a scan */
+       if (bsd_ctrl_iface(drv, 1) < 0)
+               return -1;
+
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+       os_memset(&sr, 0, sizeof(sr));
+       sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE | IEEE80211_IOC_SCAN_ONCE |
+               IEEE80211_IOC_SCAN_NOJOIN;
+       sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+       if (params->num_ssids > 0) {
+               sr.sr_nssid = params->num_ssids;
+#if 0
+               /* Boundary check is done by upper layer */
+               if (sr.sr_nssid > IEEE80211_IOC_SCAN_MAX_SSID)
+                       sr.sr_nssid = IEEE80211_IOC_SCAN_MAX_SSID;
+#endif
+
+               /* NB: check scan cache first */
+               sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
+       }
+       for (i = 0; i < sr.sr_nssid; i++) {
+               sr.sr_ssid[i].len = params->ssids[i].ssid_len;
+               os_memcpy(sr.sr_ssid[i].ssid, params->ssids[i].ssid,
+                         sr.sr_ssid[i].len);
+       }
+
+       /* NB: net80211 delivers a scan complete event so no need to poll */
+       return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+       /* set desired ssid before scan */
+       if (bsd_set_ssid(drv, params->ssids[0].ssid,
+                        params->ssids[0].ssid_len) < 0)
+               return -1;
+
+       /* NB: net80211 delivers a scan complete event so no need to poll */
+       return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0);
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+}
+
+static void
+wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+       struct bsd_driver_data *drv = sock_ctx;
+       char buf[2048];
+       struct if_announcemsghdr *ifan;
+       struct if_msghdr *ifm;
+       struct rt_msghdr *rtm;
+       union wpa_event_data event;
+       struct ieee80211_michael_event *mic;
+       struct ieee80211_leave_event *leave;
+       struct ieee80211_join_event *join;
+       int n;
+
+       n = read(sock, buf, sizeof(buf));
+       if (n < 0) {
+               if (errno != EINTR && errno != EAGAIN)
+                       perror("read(PF_ROUTE)");
+               return;
+       }
+
+       rtm = (struct rt_msghdr *) buf;
+       if (rtm->rtm_version != RTM_VERSION) {
+               wpa_printf(MSG_DEBUG, "Routing message version %d not "
+                       "understood\n", rtm->rtm_version);
+               return;
+       }
+       os_memset(&event, 0, sizeof(event));
+       switch (rtm->rtm_type) {
+       case RTM_IFANNOUNCE:
+               ifan = (struct if_announcemsghdr *) rtm;
+               if (ifan->ifan_index != drv->ifindex)
+                       break;
+               os_strlcpy(event.interface_status.ifname, drv->ifname,
+                          sizeof(event.interface_status.ifname));
+               switch (ifan->ifan_what) {
+               case IFAN_DEPARTURE:
+                       event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+               default:
+                       return;
+               }
+               wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+                          event.interface_status.ifname,
+                          ifan->ifan_what == IFAN_DEPARTURE ?
+                               "removed" : "added");
+               wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+               break;
+       case RTM_IEEE80211:
+               ifan = (struct if_announcemsghdr *) rtm;
+               if (ifan->ifan_index != drv->ifindex)
+                       break;
+               switch (ifan->ifan_what) {
+               case RTM_IEEE80211_ASSOC:
+               case RTM_IEEE80211_REASSOC:
+                       if (drv->is_ap)
+                               break;
+                       wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+                       break;
+               case RTM_IEEE80211_DISASSOC:
+                       if (drv->is_ap)
+                               break;
+                       wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+                       break;
+               case RTM_IEEE80211_SCAN:
+                       if (drv->is_ap)
+                               break;
+                       wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+                       break;
+               case RTM_IEEE80211_LEAVE:
+                       leave = (struct ieee80211_leave_event *) &ifan[1];
+                       drv_event_disassoc(ctx, leave->iev_addr);
+                       break;
+               case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+               case RTM_IEEE80211_REJOIN:
+#endif
+                       join = (struct ieee80211_join_event *) &ifan[1];
+                       bsd_new_sta(drv, ctx, join->iev_addr);
+                       break;
+               case RTM_IEEE80211_REPLAY:
+                       /* ignore */
+                       break;
+               case RTM_IEEE80211_MICHAEL:
+                       mic = (struct ieee80211_michael_event *) &ifan[1];
+                       wpa_printf(MSG_DEBUG,
+                               "Michael MIC failure wireless event: "
+                               "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+                               MAC2STR(mic->iev_src));
+
+                       os_memset(&event, 0, sizeof(event));
+                       event.michael_mic_failure.unicast =
+                               !IEEE80211_IS_MULTICAST(mic->iev_dst);
+                       wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+                               &event);
+                       break;
+               }
+               break;
+       case RTM_IFINFO:
+               ifm = (struct if_msghdr *) rtm;
+               if (ifm->ifm_index != drv->ifindex)
+                       break;
+               if ((rtm->rtm_flags & RTF_UP) == 0) {
+                       os_strlcpy(event.interface_status.ifname, drv->ifname,
+                                  sizeof(event.interface_status.ifname));
+                       event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+                       wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+                                  event.interface_status.ifname);
+                       wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+               }
+               break;
+       }
+}
+
+static void
+wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
+                             struct ieee80211req_scan_result *sr)
+{
+       struct wpa_scan_res *result, **tmp;
+       size_t extra_len;
+       u8 *pos;
+
+       extra_len = 2 + sr->isr_ssid_len;
+       extra_len += 2 + sr->isr_nrates;
+       extra_len += 3; /* ERP IE */
+       extra_len += sr->isr_ie_len;
+
+       result = os_zalloc(sizeof(*result) + extra_len);
+       if (result == NULL)
+               return;
+       os_memcpy(result->bssid, sr->isr_bssid, ETH_ALEN);
+       result->freq = sr->isr_freq;
+       result->beacon_int = sr->isr_intval;
+       result->caps = sr->isr_capinfo;
+       result->qual = sr->isr_rssi;
+       result->noise = sr->isr_noise;
+
+       pos = (u8 *)(result + 1);
+
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = sr->isr_ssid_len;
+       os_memcpy(pos, sr + 1, sr->isr_ssid_len);
+       pos += sr->isr_ssid_len;
+
+       /*
+        * Deal all rates as supported rate.
+        * Because net80211 doesn't report extended supported rate or not.
+        */
+       *pos++ = WLAN_EID_SUPP_RATES;
+       *pos++ = sr->isr_nrates;
+       os_memcpy(pos, sr->isr_rates, sr->isr_nrates);
+       pos += sr->isr_nrates;
+
+       *pos++ = WLAN_EID_ERP_INFO;
+       *pos++ = 1;
+       *pos++ = sr->isr_erp;
+
+       os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+       pos += sr->isr_ie_len;
+
+       result->ie_len = pos - (u8 *)(result + 1);
+
+       tmp = os_realloc(res->res,
+                        (res->num + 1) * sizeof(struct wpa_scan_res *));
+       if (tmp == NULL) {
+               os_free(result);
+               return;
+       }
+       tmp[res->num++] = result;
+       res->res = tmp;
+}
+
+struct wpa_scan_results *
+wpa_driver_bsd_get_scan_results2(void *priv)
+{
+       struct ieee80211req_scan_result *sr;
+       struct wpa_scan_results *res;
+       int len, rest;
+       uint8_t buf[24*1024], *pos;
+
+       len = get80211var(priv, IEEE80211_IOC_SCAN_RESULTS, buf, 24*1024);
+       if (len < 0)
+               return NULL;
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL)
+               return NULL;
+
+       pos = buf;
+       rest = len;
+       while (rest >= sizeof(struct ieee80211req_scan_result)) {
+               sr = (struct ieee80211req_scan_result *)pos;
+               wpa_driver_bsd_add_scan_entry(res, sr);
+               pos += sr->isr_len;
+               rest -= sr->isr_len;
+       }
+
+       wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%lu BSSes)",
+                  len, (unsigned long)res->num);
+
+       return res;
+}
+
+static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
+{
+#ifdef IEEE80211_IOC_DEVCAPS
+/* kernel definitions copied from net80211/ieee80211_var.h */
+#define IEEE80211_CIPHER_WEP            0
+#define IEEE80211_CIPHER_TKIP           1
+#define IEEE80211_CIPHER_AES_CCM        3
+#define IEEE80211_CRYPTO_WEP            (1<<IEEE80211_CIPHER_WEP)
+#define IEEE80211_CRYPTO_TKIP           (1<<IEEE80211_CIPHER_TKIP)
+#define IEEE80211_CRYPTO_AES_CCM        (1<<IEEE80211_CIPHER_AES_CCM)
+#define IEEE80211_C_HOSTAP      0x00000400      /* CAPABILITY: HOSTAP avail */
+#define IEEE80211_C_WPA1        0x00800000      /* CAPABILITY: WPA1 avail */
+#define IEEE80211_C_WPA2        0x01000000      /* CAPABILITY: WPA2 avail */
+       struct ieee80211_devcaps_req devcaps;
+
+       if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps,
+                       sizeof(devcaps)) < 0) {
+               wpa_printf(MSG_ERROR, "failed to IEEE80211_IOC_DEVCAPS: %s",
+                          strerror(errno));
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: drivercaps=0x%08x,cryptocaps=0x%08x",
+                  __func__, devcaps.dc_drivercaps, devcaps.dc_cryptocaps);
+
+       if (devcaps.dc_drivercaps & IEEE80211_C_WPA1)
+               drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                       WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+       if (devcaps.dc_drivercaps & IEEE80211_C_WPA2)
+               drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                       WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+
+       if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_WEP)
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+                       WPA_DRIVER_CAPA_ENC_WEP104;
+       if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_TKIP)
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+       if (devcaps.dc_cryptocaps & IEEE80211_CRYPTO_AES_CCM)
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+
+       if (devcaps.dc_drivercaps & IEEE80211_C_HOSTAP)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#undef IEEE80211_CIPHER_WEP
+#undef IEEE80211_CIPHER_TKIP
+#undef IEEE80211_CIPHER_AES_CCM
+#undef IEEE80211_CRYPTO_WEP
+#undef IEEE80211_CRYPTO_TKIP
+#undef IEEE80211_CRYPTO_AES_CCM
+#undef IEEE80211_C_HOSTAP
+#undef IEEE80211_C_WPA1
+#undef IEEE80211_C_WPA2
+#else /* IEEE80211_IOC_DEVCAPS */
+       /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+       drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+       drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+               WPA_DRIVER_CAPA_ENC_WEP104 |
+               WPA_DRIVER_CAPA_ENC_TKIP |
+               WPA_DRIVER_CAPA_ENC_CCMP;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+#endif /* IEEE80211_IOC_DEVCAPS */
+#ifdef IEEE80211_IOC_SCAN_MAX_SSID
+       drv->capa.max_scan_ssids = IEEE80211_IOC_SCAN_MAX_SSID;
+#else /* IEEE80211_IOC_SCAN_MAX_SSID */
+       drv->capa.max_scan_ssids = 1;
+#endif /* IEEE80211_IOC_SCAN_MAX_SSID */
+       drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+               WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
+       return 0;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname)
+{
+#define        GETPARAM(drv, param, v) \
+       (((v) = get80211param(drv, param)) != -1)
+       struct bsd_driver_data *drv;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       /*
+        * NB: We require the interface name be mappable to an index.
+        *     This implies we do not support having wpa_supplicant
+        *     wait for an interface to appear.  This seems ok; that
+        *     doesn't belong here; it's really the job of devd.
+        */
+       drv->ifindex = if_nametoindex(ifname);
+       if (drv->ifindex == 0) {
+               wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+                          __func__, ifname);
+               goto fail1;
+       }
+       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->sock < 0)
+               goto fail1;
+       drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+       if (drv->route < 0)
+               goto fail;
+       eloop_register_read_sock(drv->route,
+               wpa_driver_bsd_event_receive, ctx, drv);
+
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+       /* Down interface during setup. */
+       if (bsd_ctrl_iface(drv, 0) < 0)
+               goto fail;
+
+       if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+               wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+                       __func__, strerror(errno));
+               goto fail;
+       }
+       if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+               wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+                       __func__, strerror(errno));
+               goto fail;
+       }
+       if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+               wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+                       __func__, strerror(errno));
+               goto fail;
+       }
+
+       if (wpa_driver_bsd_capa(drv))
+               goto fail;
+
+       return drv;
+fail:
+       close(drv->sock);
+fail1:
+       os_free(drv);
+       return NULL;
+#undef GETPARAM
+}
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+       struct bsd_driver_data *drv = priv;
+
+       wpa_driver_bsd_set_wpa(drv, 0);
+       eloop_unregister_read_sock(drv->route);
+
+       /* NB: mark interface down */
+       bsd_ctrl_iface(drv, 0);
+
+       wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+       if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0)
+               wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state",
+                       __func__);
+
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       (void) close(drv->route);               /* ioctl socket */
+       (void) close(drv->sock);                /* event socket */
+       os_free(drv);
+}
+
+static int
+wpa_driver_bsd_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       struct bsd_driver_data *drv = priv;
+
+       os_memcpy(capa, &drv->capa, sizeof(*capa));
+       return 0;
+}
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+       .name                   = "bsd",
+       .desc                   = "BSD 802.11 support",
+#ifdef HOSTAPD
+       .hapd_init              = bsd_init,
+       .hapd_deinit            = bsd_deinit,
+       .set_privacy            = bsd_set_privacy,
+       .get_seqnum             = bsd_get_seqnum,
+       .flush                  = bsd_flush,
+       .read_sta_data          = bsd_read_sta_driver_data,
+       .sta_disassoc           = bsd_sta_disassoc,
+       .sta_deauth             = bsd_sta_deauth,
+       .set_freq               = hostapd_bsd_set_freq,
+#else /* HOSTAPD */
+       .init                   = wpa_driver_bsd_init,
+       .deinit                 = wpa_driver_bsd_deinit,
+       .get_bssid              = wpa_driver_bsd_get_bssid,
+       .get_ssid               = wpa_driver_bsd_get_ssid,
+       .set_countermeasures    = wpa_driver_bsd_set_countermeasures,
+       .scan2                  = wpa_driver_bsd_scan,
+       .get_scan_results2      = wpa_driver_bsd_get_scan_results2,
+       .deauthenticate         = wpa_driver_bsd_deauthenticate,
+       .disassociate           = wpa_driver_bsd_disassociate,
+       .associate              = wpa_driver_bsd_associate,
+       .get_capa               = wpa_driver_bsd_get_capa,
+#endif /* HOSTAPD */
+       .set_key                = bsd_set_key,
+       .set_ieee8021x          = bsd_set_ieee8021x,
+       .hapd_set_ssid          = bsd_set_ssid,
+       .hapd_get_ssid          = bsd_get_ssid,
+       .hapd_send_eapol        = bsd_send_eapol,
+       .sta_set_flags          = bsd_set_sta_authorized,
+       .set_generic_elem       = bsd_set_opt_ie,
+};
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
new file mode 100644 (file)
index 0000000..952f389
--- /dev/null
@@ -0,0 +1,1635 @@
+/*
+ * Driver interaction with Linux Host AP driver
+ * 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.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "driver_hostap.h"
+
+
+#ifdef HOSTAPD
+
+#include <net/if_arp.h>
+#include <netpacket/packet.h>
+
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "common/ieee802_11_defs.h"
+
+
+/* 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
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+struct hostap_driver_data {
+       struct hostapd_data *hapd;
+
+       char iface[IFNAMSIZ + 1];
+       int sock; /* raw packet socket for driver access */
+       int ioctl_sock; /* socket for ioctl() use */
+       struct netlink_data *netlink;
+
+       int we_version;
+
+       u8 *generic_ie;
+       size_t generic_ie_len;
+       u8 *wps_ie;
+       size_t wps_ie_len;
+};
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+                        int len);
+static int hostap_set_iface_flags(void *priv, int dev_up);
+
+static void handle_data(struct hostap_driver_data *drv, u8 *buf, size_t len,
+                       u16 stype)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc, ethertype;
+       u8 *pos, *sa;
+       size_t left;
+       union wpa_event_data event;
+
+       if (len < sizeof(struct ieee80211_hdr))
+               return;
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+
+       if ((fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) != WLAN_FC_TODS) {
+               printf("Not ToDS data frame (fc=0x%04x)\n", fc);
+               return;
+       }
+
+       sa = hdr->addr2;
+       os_memset(&event, 0, sizeof(event));
+       event.rx_from_unknown.frame = buf;
+       event.rx_from_unknown.len = len;
+       wpa_supplicant_event(drv->hapd, EVENT_RX_FROM_UNKNOWN, &event);
+
+       pos = (u8 *) (hdr + 1);
+       left = len - sizeof(*hdr);
+
+       if (left < sizeof(rfc1042_header)) {
+               printf("Too short data frame\n");
+               return;
+       }
+
+       if (memcmp(pos, rfc1042_header, sizeof(rfc1042_header)) != 0) {
+               printf("Data frame with no RFC1042 header\n");
+               return;
+       }
+       pos += sizeof(rfc1042_header);
+       left -= sizeof(rfc1042_header);
+
+       if (left < 2) {
+               printf("No ethertype in data frame\n");
+               return;
+       }
+
+       ethertype = WPA_GET_BE16(pos);
+       pos += 2;
+       left -= 2;
+       switch (ethertype) {
+       case ETH_P_PAE:
+               drv_event_eapol_rx(drv->hapd, sa, pos, left);
+               break;
+
+       default:
+               printf("Unknown ethertype 0x%04x in data frame\n", ethertype);
+               break;
+       }
+}
+
+
+static void handle_tx_callback(struct hostap_driver_data *drv, u8 *buf,
+                              size_t len, int ok)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       union wpa_event_data event;
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = buf;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ok;
+       wpa_supplicant_event(drv->hapd, EVENT_TX_STATUS, &event);
+}
+
+
+static void handle_frame(struct hostap_driver_data *drv, u8 *buf, size_t len)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc, extra_len, type, stype;
+       unsigned char *extra = NULL;
+       size_t data_len = len;
+       int ver;
+       union wpa_event_data event;
+
+       /* PSPOLL is only 16 bytes, but driver does not (at least yet) pass
+        * these to user space */
+       if (len < 24) {
+               wpa_printf(MSG_MSGDUMP, "handle_frame: too short (%lu)",
+                          (unsigned long) len);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+       type = WLAN_FC_GET_TYPE(fc);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       if (type != WLAN_FC_TYPE_MGMT || stype != WLAN_FC_STYPE_BEACON) {
+               wpa_hexdump(MSG_MSGDUMP, "Received management frame",
+                           buf, len);
+       }
+
+       ver = fc & WLAN_FC_PVER;
+
+       /* protocol version 3 is reserved for indicating extra data after the
+        * payload, version 2 for indicating ACKed frame (TX callbacks), and
+        * version 1 for indicating failed frame (no ACK, TX callbacks) */
+       if (ver == 3) {
+               u8 *pos = buf + len - 2;
+               extra_len = WPA_GET_LE16(pos);
+               printf("extra data in frame (elen=%d)\n", extra_len);
+               if ((size_t) extra_len + 2 > len) {
+                       printf("  extra data overflow\n");
+                       return;
+               }
+               len -= extra_len + 2;
+               extra = buf + len;
+       } else if (ver == 1 || ver == 2) {
+               handle_tx_callback(drv, buf, data_len, ver == 2 ? 1 : 0);
+               return;
+       } else if (ver != 0) {
+               printf("unknown protocol version %d\n", ver);
+               return;
+       }
+
+       switch (type) {
+       case WLAN_FC_TYPE_MGMT:
+               os_memset(&event, 0, sizeof(event));
+               event.rx_mgmt.frame = buf;
+               event.rx_mgmt.frame_len = data_len;
+               wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
+               break;
+       case WLAN_FC_TYPE_CTRL:
+               wpa_printf(MSG_DEBUG, "CTRL");
+               break;
+       case WLAN_FC_TYPE_DATA:
+               wpa_printf(MSG_DEBUG, "DATA");
+               handle_data(drv, buf, data_len, stype);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "unknown frame type %d", type);
+               break;
+       }
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct hostap_driver_data *drv = eloop_ctx;
+       int len;
+       unsigned char buf[3000];
+
+       len = recv(sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       handle_frame(drv, buf, len);
+}
+
+
+static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
+{
+       struct ifreq ifr;
+       struct sockaddr_ll addr;
+
+       drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (drv->sock < 0) {
+               perror("socket[PF_PACKET,SOCK_RAW]");
+               return -1;
+       }
+
+       if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
+               printf("Could not register read socket\n");
+               return -1;
+       }
+
+        memset(&ifr, 0, sizeof(ifr));
+        snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface);
+        if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+               perror("ioctl(SIOCGIFINDEX)");
+               return -1;
+        }
+
+       if (hostap_set_iface_flags(drv, 1)) {
+               return -1;
+       }
+
+       memset(&addr, 0, sizeof(addr));
+       addr.sll_family = AF_PACKET;
+       addr.sll_ifindex = ifr.ifr_ifindex;
+       wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+                  addr.sll_ifindex);
+
+       if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind");
+               return -1;
+       }
+
+       return linux_get_ifhwaddr(drv->sock, drv->iface, own_addr);
+}
+
+
+static int hostap_send_mlme(void *priv, const u8 *msg, size_t len)
+{
+       struct hostap_driver_data *drv = priv;
+       struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) msg;
+       int res;
+
+       /* Request TX callback */
+       hdr->frame_control |= host_to_le16(BIT(1));
+       res = send(drv->sock, msg, len, 0);
+       hdr->frame_control &= ~host_to_le16(BIT(1));
+
+       return res;
+}
+
+
+static int hostap_send_eapol(void *priv, const u8 *addr, const u8 *data,
+                            size_t data_len, int encrypt, const u8 *own_addr)
+{
+       struct hostap_driver_data *drv = priv;
+       struct ieee80211_hdr *hdr;
+       size_t len;
+       u8 *pos;
+       int res;
+
+       len = sizeof(*hdr) + sizeof(rfc1042_header) + 2 + data_len;
+       hdr = os_zalloc(len);
+       if (hdr == NULL) {
+               printf("malloc() failed for hostapd_send_data(len=%lu)\n",
+                      (unsigned long) len);
+               return -1;
+       }
+
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+       hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+       if (encrypt)
+               hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+       memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+       memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+       memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+       pos = (u8 *) (hdr + 1);
+       memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+       pos += sizeof(rfc1042_header);
+       *((u16 *) pos) = htons(ETH_P_PAE);
+       pos += 2;
+       memcpy(pos, data, data_len);
+
+       res = hostap_send_mlme(drv, (u8 *) hdr, len);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "hostap_send_eapol - packet len: %lu - "
+                          "failed: %d (%s)",
+                          (unsigned long) len, errno, strerror(errno));
+       }
+       free(hdr);
+
+       return res;
+}
+
+
+static int hostap_sta_set_flags(void *priv, const u8 *addr,
+                               int total_flags, int flags_or, int flags_and)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param param;
+
+       if (flags_or & WPA_STA_AUTHORIZED)
+               flags_or = BIT(5); /* WLAN_STA_AUTHORIZED */
+       if (!(flags_and & WPA_STA_AUTHORIZED))
+               flags_and = ~BIT(5);
+       else
+               flags_and = ~0;
+       memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_SET_FLAGS_STA;
+       memcpy(param.sta_addr, addr, ETH_ALEN);
+       param.u.set_flags_sta.flags_or = flags_or;
+       param.u.set_flags_sta.flags_and = flags_and;
+       return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_set_iface_flags(void *priv, int dev_up)
+{
+       struct hostap_driver_data *drv = priv;
+       struct ifreq ifr;
+       char ifname[IFNAMSIZ];
+
+       os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface);
+       if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0)
+               return -1;
+
+       if (dev_up) {
+               memset(&ifr, 0, sizeof(ifr));
+               os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+               ifr.ifr_mtu = HOSTAPD_MTU;
+               if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
+                       perror("ioctl[SIOCSIFMTU]");
+                       printf("Setting MTU failed - trying to survive with "
+                              "current value\n");
+               }
+       }
+
+       return 0;
+}
+
+
+static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
+                        int len)
+{
+       struct hostap_driver_data *drv = priv;
+       struct iwreq iwr;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) param;
+       iwr.u.data.length = len;
+
+       if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+               perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
+                                    enum wpa_alg alg, const u8 *addr,
+                                    int key_idx, int set_tx,
+                                    const u8 *seq, size_t seq_len,
+                                    const u8 *key, size_t key_len)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param *param;
+       u8 *buf;
+       size_t blen;
+       int ret = 0;
+
+       blen = sizeof(*param) + key_len;
+       buf = os_zalloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       param = (struct prism2_hostapd_param *) buf;
+       param->cmd = PRISM2_SET_ENCRYPTION;
+       if (addr == NULL)
+               memset(param->sta_addr, 0xff, ETH_ALEN);
+       else
+               memcpy(param->sta_addr, addr, ETH_ALEN);
+       switch (alg) {
+       case WPA_ALG_NONE:
+               os_strlcpy((char *) param->u.crypt.alg, "NONE",
+                          HOSTAP_CRYPT_ALG_NAME_LEN);
+               break;
+       case WPA_ALG_WEP:
+               os_strlcpy((char *) param->u.crypt.alg, "WEP",
+                          HOSTAP_CRYPT_ALG_NAME_LEN);
+               break;
+       case WPA_ALG_TKIP:
+               os_strlcpy((char *) param->u.crypt.alg, "TKIP",
+                          HOSTAP_CRYPT_ALG_NAME_LEN);
+               break;
+       case WPA_ALG_CCMP:
+               os_strlcpy((char *) param->u.crypt.alg, "CCMP",
+                          HOSTAP_CRYPT_ALG_NAME_LEN);
+               break;
+       default:
+               os_free(buf);
+               return -1;
+       }
+       param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
+       param->u.crypt.idx = key_idx;
+       param->u.crypt.key_len = key_len;
+       memcpy((u8 *) (param + 1), key, key_len);
+
+       if (hostapd_ioctl(drv, param, blen)) {
+               printf("Failed to set encryption.\n");
+               ret = -1;
+       }
+       free(buf);
+
+       return ret;
+}
+
+
+static int hostap_get_seqnum(const char *ifname, void *priv, const u8 *addr,
+                            int idx, u8 *seq)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param *param;
+       u8 *buf;
+       size_t blen;
+       int ret = 0;
+
+       blen = sizeof(*param) + 32;
+       buf = os_zalloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       param = (struct prism2_hostapd_param *) buf;
+       param->cmd = PRISM2_GET_ENCRYPTION;
+       if (addr == NULL)
+               memset(param->sta_addr, 0xff, ETH_ALEN);
+       else
+               memcpy(param->sta_addr, addr, ETH_ALEN);
+       param->u.crypt.idx = idx;
+
+       if (hostapd_ioctl(drv, param, blen)) {
+               printf("Failed to get encryption.\n");
+               ret = -1;
+       } else {
+               memcpy(seq, param->u.crypt.seq, 8);
+       }
+       free(buf);
+
+       return ret;
+}
+
+
+static int hostap_ioctl_prism2param(void *priv, int param, int value)
+{
+       struct hostap_driver_data *drv = priv;
+       struct iwreq iwr;
+       int *i;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       i = (int *) iwr.u.name;
+       *i++ = param;
+       *i++ = value;
+
+       if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+               perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int hostap_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+       struct hostap_driver_data *drv = priv;
+       int enabled = params->enabled;
+
+       /* enable kernel driver support for IEEE 802.1X */
+       if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_IEEE_802_1X, enabled)) {
+               printf("Could not setup IEEE 802.1X support in kernel driver."
+                      "\n");
+               return -1;
+       }
+
+       if (!enabled)
+               return 0;
+
+       /* use host driver implementation of encryption to allow
+        * individual keys and passing plaintext EAPOL frames */
+       if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_DECRYPT, 1) ||
+           hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOST_ENCRYPT, 1)) {
+               printf("Could not setup host-based encryption in kernel "
+                      "driver.\n");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int hostap_set_privacy(void *priv, int enabled)
+{
+       struct hostap_drvier_data *drv = priv;
+
+       return hostap_ioctl_prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+                                       enabled);
+}
+
+
+static int hostap_set_ssid(void *priv, const u8 *buf, int len)
+{
+       struct hostap_driver_data *drv = priv;
+       struct iwreq iwr;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.essid.flags = 1; /* SSID active */
+       iwr.u.essid.pointer = (caddr_t) buf;
+       iwr.u.essid.length = len + 1;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCSIWESSID]");
+               printf("len=%d\n", len);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int hostap_flush(void *priv)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param param;
+
+       memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_FLUSH;
+       return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_read_sta_data(void *priv,
+                               struct hostap_sta_driver_data *data,
+                               const u8 *addr)
+{
+       struct hostap_driver_data *drv = priv;
+       char buf[1024], line[128], *pos;
+       FILE *f;
+       unsigned long val;
+
+       memset(data, 0, sizeof(*data));
+       snprintf(buf, sizeof(buf), "/proc/net/hostap/%s/" MACSTR,
+                drv->iface, MAC2STR(addr));
+
+       f = fopen(buf, "r");
+       if (!f)
+               return -1;
+       /* Need to read proc file with in one piece, so use large enough
+        * buffer. */
+       setbuffer(f, buf, sizeof(buf));
+
+       while (fgets(line, sizeof(line), f)) {
+               pos = strchr(line, '=');
+               if (!pos)
+                       continue;
+               *pos++ = '\0';
+               val = strtoul(pos, NULL, 10);
+               if (strcmp(line, "rx_packets") == 0)
+                       data->rx_packets = val;
+               else if (strcmp(line, "tx_packets") == 0)
+                       data->tx_packets = val;
+               else if (strcmp(line, "rx_bytes") == 0)
+                       data->rx_bytes = val;
+               else if (strcmp(line, "tx_bytes") == 0)
+                       data->tx_bytes = val;
+       }
+
+       fclose(f);
+
+       return 0;
+}
+
+
+static int hostap_sta_add(void *priv, struct hostapd_sta_add_params *params)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param param;
+       int tx_supp_rates = 0;
+       size_t i;
+
+#define WLAN_RATE_1M BIT(0)
+#define WLAN_RATE_2M BIT(1)
+#define WLAN_RATE_5M5 BIT(2)
+#define WLAN_RATE_11M BIT(3)
+
+       for (i = 0; i < params->supp_rates_len; i++) {
+               if ((params->supp_rates[i] & 0x7f) == 2)
+                       tx_supp_rates |= WLAN_RATE_1M;
+               if ((params->supp_rates[i] & 0x7f) == 4)
+                       tx_supp_rates |= WLAN_RATE_2M;
+               if ((params->supp_rates[i] & 0x7f) == 11)
+                       tx_supp_rates |= WLAN_RATE_5M5;
+               if ((params->supp_rates[i] & 0x7f) == 22)
+                       tx_supp_rates |= WLAN_RATE_11M;
+       }
+
+       memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_ADD_STA;
+       memcpy(param.sta_addr, params->addr, ETH_ALEN);
+       param.u.add_sta.aid = params->aid;
+       param.u.add_sta.capability = params->capability;
+       param.u.add_sta.tx_supp_rates = tx_supp_rates;
+       return hostapd_ioctl(drv, &param, sizeof(param));
+}
+
+
+static int hostap_sta_remove(void *priv, const u8 *addr)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param param;
+
+       hostap_sta_set_flags(drv, addr, 0, 0, ~WPA_STA_AUTHORIZED);
+
+       memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_REMOVE_STA;
+       memcpy(param.sta_addr, addr, ETH_ALEN);
+       if (hostapd_ioctl(drv, &param, sizeof(param))) {
+               printf("Could not remove station from kernel driver.\n");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int hostap_get_inact_sec(void *priv, const u8 *addr)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param param;
+
+       memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_GET_INFO_STA;
+       memcpy(param.sta_addr, addr, ETH_ALEN);
+       if (hostapd_ioctl(drv, &param, sizeof(param))) {
+               return -1;
+       }
+
+       return param.u.get_info_sta.inactive_sec;
+}
+
+
+static int hostap_sta_clear_stats(void *priv, const u8 *addr)
+{
+       struct hostap_driver_data *drv = priv;
+       struct prism2_hostapd_param param;
+
+       memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_STA_CLEAR_STATS;
+       memcpy(param.sta_addr, addr, ETH_ALEN);
+       if (hostapd_ioctl(drv, &param, sizeof(param))) {
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int hostapd_ioctl_set_generic_elem(struct hostap_driver_data *drv)
+{
+       struct prism2_hostapd_param *param;
+       int res;
+       size_t blen, elem_len;
+
+       elem_len = drv->generic_ie_len + drv->wps_ie_len;
+       blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + elem_len;
+       if (blen < sizeof(*param))
+               blen = sizeof(*param);
+
+       param = os_zalloc(blen);
+       if (param == NULL)
+               return -1;
+
+       param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
+       param->u.generic_elem.len = elem_len;
+       if (drv->generic_ie) {
+               os_memcpy(param->u.generic_elem.data, drv->generic_ie,
+                         drv->generic_ie_len);
+       }
+       if (drv->wps_ie) {
+               os_memcpy(&param->u.generic_elem.data[drv->generic_ie_len],
+                         drv->wps_ie, drv->wps_ie_len);
+       }
+       wpa_hexdump(MSG_DEBUG, "hostap: Set generic IE",
+                   param->u.generic_elem.data, elem_len);
+       res = hostapd_ioctl(drv, param, blen);
+
+       os_free(param);
+
+       return res;
+}
+
+
+static int hostap_set_generic_elem(void *priv,
+                                  const u8 *elem, size_t elem_len)
+{
+       struct hostap_driver_data *drv = priv;
+
+       os_free(drv->generic_ie);
+       drv->generic_ie = NULL;
+       drv->generic_ie_len = 0;
+       if (elem) {
+               drv->generic_ie = os_malloc(elem_len);
+               if (drv->generic_ie == NULL)
+                       return -1;
+               os_memcpy(drv->generic_ie, elem, elem_len);
+               drv->generic_ie_len = elem_len;
+       }
+
+       return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static int hostap_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+                               const struct wpabuf *proberesp)
+{
+       struct hostap_driver_data *drv = priv;
+
+       /*
+        * Host AP driver supports only one set of extra IEs, so we need to
+        * use the Probe Response IEs also for Beacon frames since they include
+        * more information.
+        */
+
+       os_free(drv->wps_ie);
+       drv->wps_ie = NULL;
+       drv->wps_ie_len = 0;
+       if (proberesp) {
+               drv->wps_ie = os_malloc(wpabuf_len(proberesp));
+               if (drv->wps_ie == NULL)
+                       return -1;
+               os_memcpy(drv->wps_ie, wpabuf_head(proberesp),
+                         wpabuf_len(proberesp));
+               drv->wps_ie_len = wpabuf_len(proberesp);
+       }
+
+       return hostapd_ioctl_set_generic_elem(drv);
+}
+
+
+static void
+hostapd_wireless_event_wireless_custom(struct hostap_driver_data *drv,
+                                      char *custom)
+{
+       wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+       if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+               char *pos;
+               u8 addr[ETH_ALEN];
+               pos = strstr(custom, "addr=");
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLME-MICHAELMICFAILURE.indication "
+                                  "without sender address ignored");
+                       return;
+               }
+               pos += 5;
+               if (hwaddr_aton(pos, addr) == 0) {
+                       union wpa_event_data data;
+                       os_memset(&data, 0, sizeof(data));
+                       data.michael_mic_failure.unicast = 1;
+                       data.michael_mic_failure.src = addr;
+                       wpa_supplicant_event(drv->hapd,
+                                            EVENT_MICHAEL_MIC_FAILURE, &data);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLME-MICHAELMICFAILURE.indication "
+                                  "with invalid MAC address");
+               }
+       }
+}
+
+
+static void hostapd_wireless_event_wireless(struct hostap_driver_data *drv,
+                                           char *data, int len)
+{
+       struct iw_event iwe_buf, *iwe = &iwe_buf;
+       char *pos, *end, *custom, *buf;
+
+       pos = data;
+       end = data + len;
+
+       while (pos + IW_EV_LCP_LEN <= end) {
+               /* Event data may be unaligned, so make a local, aligned copy
+                * before processing. */
+               memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+               wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+                          iwe->cmd, iwe->len);
+               if (iwe->len <= IW_EV_LCP_LEN)
+                       return;
+
+               custom = pos + IW_EV_POINT_LEN;
+               if (drv->we_version > 18 &&
+                   (iwe->cmd == IWEVMICHAELMICFAILURE ||
+                    iwe->cmd == IWEVCUSTOM)) {
+                       /* WE-19 removed the pointer from struct iw_point */
+                       char *dpos = (char *) &iwe_buf.u.data.length;
+                       int dlen = dpos - (char *) &iwe_buf;
+                       memcpy(dpos, pos + IW_EV_LCP_LEN,
+                              sizeof(struct iw_event) - dlen);
+               } else {
+                       memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+                       custom += IW_EV_POINT_OFF;
+               }
+
+               switch (iwe->cmd) {
+               case IWEVCUSTOM:
+                       if (custom + iwe->u.data.length > end)
+                               return;
+                       buf = malloc(iwe->u.data.length + 1);
+                       if (buf == NULL)
+                               return;
+                       memcpy(buf, custom, iwe->u.data.length);
+                       buf[iwe->u.data.length] = '\0';
+                       hostapd_wireless_event_wireless_custom(drv, buf);
+                       free(buf);
+                       break;
+               }
+
+               pos += iwe->len;
+       }
+}
+
+
+static void hostapd_wireless_event_rtm_newlink(void *ctx,
+                                              struct ifinfomsg *ifi,
+                                              u8 *buf, size_t len)
+{
+       struct hostap_driver_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       /* TODO: use ifi->ifi_index to filter out wireless events from other
+        * interfaces */
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_WIRELESS) {
+                       hostapd_wireless_event_wireless(
+                               drv, ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len);
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+}
+
+
+static int hostap_get_we_version(struct hostap_driver_data *drv)
+{
+       struct iw_range *range;
+       struct iwreq iwr;
+       int minlen;
+       size_t buflen;
+
+       drv->we_version = 0;
+
+       /*
+        * Use larger buffer than struct iw_range in order to allow the
+        * structure to grow in the future.
+        */
+       buflen = sizeof(struct iw_range) + 500;
+       range = os_zalloc(buflen);
+       if (range == NULL)
+               return -1;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) range;
+       iwr.u.data.length = buflen;
+
+       minlen = ((char *) &range->enc_capa) - (char *) range +
+               sizeof(range->enc_capa);
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWRANGE]");
+               free(range);
+               return -1;
+       } else if (iwr.u.data.length >= minlen &&
+                  range->we_version_compiled >= 18) {
+               wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+                          "WE(source)=%d enc_capa=0x%x",
+                          range->we_version_compiled,
+                          range->we_version_source,
+                          range->enc_capa);
+               drv->we_version = range->we_version_compiled;
+       }
+
+       free(range);
+       return 0;
+}
+
+
+static int hostap_wireless_event_init(struct hostap_driver_data *drv)
+{
+       struct netlink_config *cfg;
+
+       hostap_get_we_version(drv);
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               return -1;
+       cfg->ctx = drv;
+       cfg->newlink_cb = hostapd_wireless_event_rtm_newlink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void * hostap_init(struct hostapd_data *hapd,
+                         struct wpa_init_params *params)
+{
+       struct hostap_driver_data *drv;
+
+       drv = os_zalloc(sizeof(struct hostap_driver_data));
+       if (drv == NULL) {
+               printf("Could not allocate memory for hostapd driver data\n");
+               return NULL;
+       }
+
+       drv->hapd = hapd;
+       drv->ioctl_sock = drv->sock = -1;
+       memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->ioctl_sock < 0) {
+               perror("socket[PF_INET,SOCK_DGRAM]");
+               free(drv);
+               return NULL;
+       }
+
+       if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
+               printf("Could not enable hostapd mode for interface %s\n",
+                      drv->iface);
+               close(drv->ioctl_sock);
+               free(drv);
+               return NULL;
+       }
+
+       if (hostap_init_sockets(drv, params->own_addr) ||
+           hostap_wireless_event_init(drv)) {
+               close(drv->ioctl_sock);
+               free(drv);
+               return NULL;
+       }
+
+       return drv;
+}
+
+
+static void hostap_driver_deinit(void *priv)
+{
+       struct hostap_driver_data *drv = priv;
+
+       netlink_deinit(drv->netlink);
+       (void) hostap_set_iface_flags(drv, 0);
+       (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 0);
+       (void) hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD_STA, 0);
+
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+
+       if (drv->sock >= 0)
+               close(drv->sock);
+
+       os_free(drv->generic_ie);
+       os_free(drv->wps_ie);
+
+       free(drv);
+}
+
+
+static int hostap_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                            int reason)
+{
+       struct hostap_driver_data *drv = priv;
+       struct ieee80211_mgmt mgmt;
+
+       memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_DEAUTH);
+       memcpy(mgmt.da, addr, ETH_ALEN);
+       memcpy(mgmt.sa, own_addr, ETH_ALEN);
+       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+       mgmt.u.deauth.reason_code = host_to_le16(reason);
+       return hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+                               sizeof(mgmt.u.deauth));
+}
+
+
+static int hostap_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                              int reason)
+{
+       struct hostap_driver_data *drv = priv;
+       struct ieee80211_mgmt mgmt;
+
+       memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_DISASSOC);
+       memcpy(mgmt.da, addr, ETH_ALEN);
+       memcpy(mgmt.sa, own_addr, ETH_ALEN);
+       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+       mgmt.u.disassoc.reason_code = host_to_le16(reason);
+       return  hostap_send_mlme(drv, (u8 *) &mgmt, IEEE80211_HDRLEN +
+                                sizeof(mgmt.u.disassoc));
+}
+
+
+static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
+                                                           u16 *num_modes,
+                                                           u16 *flags)
+{
+       struct hostapd_hw_modes *mode;
+       int i, clen, rlen;
+       const short chan2freq[14] = {
+               2412, 2417, 2422, 2427, 2432, 2437, 2442,
+               2447, 2452, 2457, 2462, 2467, 2472, 2484
+       };
+
+       mode = os_zalloc(sizeof(struct hostapd_hw_modes));
+       if (mode == NULL)
+               return NULL;
+
+       *num_modes = 1;
+       *flags = 0;
+
+       mode->mode = HOSTAPD_MODE_IEEE80211B;
+       mode->num_channels = 14;
+       mode->num_rates = 4;
+
+       clen = mode->num_channels * sizeof(struct hostapd_channel_data);
+       rlen = mode->num_rates * sizeof(int);
+
+       mode->channels = os_zalloc(clen);
+       mode->rates = os_zalloc(rlen);
+       if (mode->channels == NULL || mode->rates == NULL) {
+               os_free(mode->channels);
+               os_free(mode->rates);
+               os_free(mode);
+               return NULL;
+       }
+
+       for (i = 0; i < 14; i++) {
+               mode->channels[i].chan = i + 1;
+               mode->channels[i].freq = chan2freq[i];
+               /* TODO: Get allowed channel list from the driver */
+               if (i >= 11)
+                       mode->channels[i].flag = HOSTAPD_CHAN_DISABLED;
+       }
+
+       mode->rates[0] = 10;
+       mode->rates[1] = 20;
+       mode->rates[2] = 55;
+       mode->rates[3] = 110;
+
+       return mode;
+}
+
+#else /* HOSTAPD */
+
+struct wpa_driver_hostap_data {
+       void *wext; /* private data for driver_wext */
+       void *ctx;
+       char ifname[IFNAMSIZ + 1];
+       int sock;
+       int current_mode; /* infra/adhoc */
+};
+
+
+static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg);
+
+
+static int hostapd_ioctl(struct wpa_driver_hostap_data *drv,
+                        struct prism2_hostapd_param *param,
+                        int len, int show_err)
+{
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) param;
+       iwr.u.data.length = len;
+
+       if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
+               int ret = errno;
+               if (show_err)
+                       perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+               return ret;
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv,
+                                       const u8 *wpa_ie, size_t wpa_ie_len)
+{
+       struct prism2_hostapd_param *param;
+       int res;
+       size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len;
+       if (blen < sizeof(*param))
+               blen = sizeof(*param);
+
+       param = os_zalloc(blen);
+       if (param == NULL)
+               return -1;
+
+       param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT;
+       param->u.generic_elem.len = wpa_ie_len;
+       os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len);
+       res = hostapd_ioctl(drv, param, blen, 1);
+
+       os_free(param);
+
+       return res;
+}
+
+
+static int prism2param(struct wpa_driver_hostap_data *drv, int param,
+                      int value)
+{
+       struct iwreq iwr;
+       int *i, ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       i = (int *) iwr.u.name;
+       *i++ = param;
+       *i++ = value;
+
+       if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
+               perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+               ret = -1;
+       }
+       return ret;
+}
+
+
+static int wpa_driver_hostap_set_wpa(void *priv, int enabled)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+       if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0)
+               ret = -1;
+       if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0)
+               ret = -1;
+       if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+
+static void show_set_key_error(struct prism2_hostapd_param *param)
+{
+       switch (param->u.crypt.err) {
+       case HOSTAP_CRYPT_ERR_UNKNOWN_ALG:
+               wpa_printf(MSG_INFO, "Unknown algorithm '%s'.",
+                          param->u.crypt.alg);
+               wpa_printf(MSG_INFO, "You may need to load kernel module to "
+                          "register that algorithm.");
+               wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for "
+                          "WEP.");
+               break;
+       case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR:
+               wpa_printf(MSG_INFO, "Unknown address " MACSTR ".",
+                          MAC2STR(param->sta_addr));
+               break;
+       case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED:
+               wpa_printf(MSG_INFO, "Crypt algorithm initialization failed.");
+               break;
+       case HOSTAP_CRYPT_ERR_KEY_SET_FAILED:
+               wpa_printf(MSG_INFO, "Key setting failed.");
+               break;
+       case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED:
+               wpa_printf(MSG_INFO, "TX key index setting failed.");
+               break;
+       case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED:
+               wpa_printf(MSG_INFO, "Card configuration failed.");
+               break;
+       }
+}
+
+
+static int wpa_driver_hostap_set_key(const char *ifname, void *priv,
+                                    enum wpa_alg alg, const u8 *addr,
+                                    int key_idx, int set_tx,
+                                    const u8 *seq, size_t seq_len,
+                                    const u8 *key, size_t key_len)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       struct prism2_hostapd_param *param;
+       u8 *buf;
+       size_t blen;
+       int ret = 0;
+       char *alg_name;
+
+       switch (alg) {
+       case WPA_ALG_NONE:
+               alg_name = "none";
+               break;
+       case WPA_ALG_WEP:
+               alg_name = "WEP";
+               break;
+       case WPA_ALG_TKIP:
+               alg_name = "TKIP";
+               break;
+       case WPA_ALG_CCMP:
+               alg_name = "CCMP";
+               break;
+       default:
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
+                  "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       if (seq_len > 8)
+               return -2;
+
+       blen = sizeof(*param) + key_len;
+       buf = os_zalloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       param = (struct prism2_hostapd_param *) buf;
+       param->cmd = PRISM2_SET_ENCRYPTION;
+       /* TODO: In theory, STA in client mode can use five keys; four default
+        * keys for receiving (with keyidx 0..3) and one individual key for
+        * both transmitting and receiving (keyidx 0) _unicast_ packets. Now,
+        * keyidx 0 is reserved for this unicast use and default keys can only
+        * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported).
+        * This should be fine for more or less all cases, but for completeness
+        * sake, the driver could be enhanced to support the missing key. */
+#if 0
+       if (addr == NULL)
+               os_memset(param->sta_addr, 0xff, ETH_ALEN);
+       else
+               os_memcpy(param->sta_addr, addr, ETH_ALEN);
+#else
+       os_memset(param->sta_addr, 0xff, ETH_ALEN);
+#endif
+       os_strlcpy((char *) param->u.crypt.alg, alg_name,
+                  HOSTAP_CRYPT_ALG_NAME_LEN);
+       param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0;
+       param->u.crypt.idx = key_idx;
+       os_memcpy(param->u.crypt.seq, seq, seq_len);
+       param->u.crypt.key_len = key_len;
+       os_memcpy((u8 *) (param + 1), key, key_len);
+
+       if (hostapd_ioctl(drv, param, blen, 1)) {
+               wpa_printf(MSG_WARNING, "Failed to set encryption.");
+               show_set_key_error(param);
+               ret = -1;
+       }
+       os_free(buf);
+
+       return ret;
+}
+
+
+static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled);
+}
+
+
+static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv,
+                                  int type)
+{
+       struct iwreq iwr;
+       int *i, ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type);
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       i = (int *) iwr.u.name;
+       *i++ = type;
+
+       if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) {
+               perror("ioctl[PRISM2_IOCTL_RESET]");
+               ret = -1;
+       }
+       return ret;
+}
+
+
+static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv,
+                                 const u8 *addr, int cmd, int reason_code)
+{
+       struct prism2_hostapd_param param;
+       int ret;
+
+       /* There does not seem to be a better way of deauthenticating or
+        * disassociating with Prism2/2.5/3 than sending the management frame
+        * and then resetting the Port0 to make sure both the AP and the STA
+        * end up in disconnected state. */
+       os_memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_MLME;
+       os_memcpy(param.sta_addr, addr, ETH_ALEN);
+       param.u.mlme.cmd = cmd;
+       param.u.mlme.reason_code = reason_code;
+       ret = hostapd_ioctl(drv, &param, sizeof(param), 1);
+       if (ret == 0) {
+               os_sleep(0, 100000);
+               ret = wpa_driver_hostap_reset(drv, 2);
+       }
+       return ret;
+}
+
+
+static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr,
+                                           int reason_code)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH,
+                                     reason_code);
+}
+
+
+static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC,
+                                     reason_code);
+}
+
+
+static int
+wpa_driver_hostap_associate(void *priv,
+                           struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       int ret = 0;
+       int allow_unencrypted_eapol;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED,
+                       params->drop_unencrypted) < 0)
+               ret = -1;
+       if (wpa_driver_hostap_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+       if (params->mode != drv->current_mode) {
+               /* At the moment, Host AP driver requires host_roaming=2 for
+                * infrastructure mode and host_roaming=0 for adhoc. */
+               if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING,
+                               params->mode == IEEE80211_MODE_IBSS ? 0 : 2) <
+                   0) {
+                       wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming",
+                                  __func__);
+               }
+               drv->current_mode = params->mode;
+       }
+
+       if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED,
+                       params->key_mgmt_suite != KEY_MGMT_NONE) < 0)
+               ret = -1;
+       if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie,
+                                        params->wpa_ie_len) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0)
+               ret = -1;
+       if (params->freq &&
+           wpa_driver_wext_set_freq(drv->wext, params->freq) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len)
+           < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0)
+               ret = -1;
+
+       /* Allow unencrypted EAPOL messages even if pairwise keys are set when
+        * not using WPA. IEEE 802.1X specifies that these frames are not
+        * encrypted, but WPA encrypts them when pairwise keys are in use. */
+       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+           params->key_mgmt_suite == KEY_MGMT_PSK)
+               allow_unencrypted_eapol = 0;
+       else
+               allow_unencrypted_eapol = 1;
+       
+       if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X,
+                       allow_unencrypted_eapol) < 0) {
+               wpa_printf(MSG_DEBUG, "hostap: Failed to configure "
+                          "ieee_802_1x param");
+               /* Ignore this error.. driver_hostap.c can also be used with
+                * other drivers that do not support this prism2_param. */
+       }
+
+       return ret;
+}
+
+
+static int wpa_driver_hostap_scan(void *priv,
+                                 struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       struct prism2_hostapd_param param;
+       int ret;
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+
+       if (ssid == NULL) {
+               /* Use standard Linux Wireless Extensions ioctl if possible
+                * because some drivers using hostap code in wpa_supplicant
+                * might not support Host AP specific scan request (with SSID
+                * info). */
+               return wpa_driver_wext_scan(drv->wext, params);
+       }
+
+       if (ssid_len > 32)
+               ssid_len = 32;
+
+       os_memset(&param, 0, sizeof(param));
+       param.cmd = PRISM2_HOSTAPD_SCAN_REQ;
+       param.u.scan_req.ssid_len = ssid_len;
+       os_memcpy(param.u.scan_req.ssid, ssid, ssid_len);
+       ret = hostapd_ioctl(drv, &param, sizeof(param), 1);
+
+       /* Not all drivers generate "scan completed" wireless event, so try to
+        * read results after a timeout. */
+       eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext,
+                            drv->ctx);
+       eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext,
+                              drv->ctx);
+
+       return ret;
+}
+
+
+static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       int algs = 0;
+
+       if (auth_alg & WPA_AUTH_ALG_OPEN)
+               algs |= 1;
+       if (auth_alg & WPA_AUTH_ALG_SHARED)
+               algs |= 2;
+       if (auth_alg & WPA_AUTH_ALG_LEAP)
+               algs |= 4;
+       if (algs == 0)
+               algs = 1; /* at least one algorithm should be set */
+
+       return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs);
+}
+
+
+static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_hostap_set_operstate(void *priv, int state)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static void * wpa_driver_hostap_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_hostap_data *drv;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->wext = wpa_driver_wext_init(ctx, ifname);
+       if (drv->wext == NULL) {
+               os_free(drv);
+               return NULL;
+       }
+
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->sock < 0) {
+               perror("socket");
+               wpa_driver_wext_deinit(drv->wext);
+               os_free(drv);
+               return NULL;
+       }
+
+       if (os_strncmp(ifname, "wlan", 4) == 0) {
+               /*
+                * Host AP driver may use both wlan# and wifi# interface in
+                * wireless events.
+                */
+               char ifname2[IFNAMSIZ + 1];
+               os_strlcpy(ifname2, ifname, sizeof(ifname2));
+               os_memcpy(ifname2, "wifi", 4);
+               wpa_driver_wext_alternative_ifindex(drv->wext, ifname2);
+       }
+
+       wpa_driver_hostap_set_wpa(drv, 1);
+
+       return drv;
+}
+
+
+static void wpa_driver_hostap_deinit(void *priv)
+{
+       struct wpa_driver_hostap_data *drv = priv;
+       wpa_driver_hostap_set_wpa(drv, 0);
+       wpa_driver_wext_deinit(drv->wext);
+       close(drv->sock);
+       os_free(drv);
+}
+
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_hostap_ops = {
+       .name = "hostap",
+       .desc = "Host AP driver (Intersil Prism2/2.5/3)",
+       .set_key = wpa_driver_hostap_set_key,
+#ifdef HOSTAPD
+       .hapd_init = hostap_init,
+       .hapd_deinit = hostap_driver_deinit,
+       .set_ieee8021x = hostap_set_ieee8021x,
+       .set_privacy = hostap_set_privacy,
+       .get_seqnum = hostap_get_seqnum,
+       .flush = hostap_flush,
+       .set_generic_elem = hostap_set_generic_elem,
+       .read_sta_data = hostap_read_sta_data,
+       .hapd_send_eapol = hostap_send_eapol,
+       .sta_set_flags = hostap_sta_set_flags,
+       .sta_deauth = hostap_sta_deauth,
+       .sta_disassoc = hostap_sta_disassoc,
+       .sta_remove = hostap_sta_remove,
+       .hapd_set_ssid = hostap_set_ssid,
+       .send_mlme = hostap_send_mlme,
+       .sta_add = hostap_sta_add,
+       .get_inact_sec = hostap_get_inact_sec,
+       .sta_clear_stats = hostap_sta_clear_stats,
+       .get_hw_feature_data = hostap_get_hw_feature_data,
+       .set_ap_wps_ie = hostap_set_ap_wps_ie,
+#else /* HOSTAPD */
+       .get_bssid = wpa_driver_hostap_get_bssid,
+       .get_ssid = wpa_driver_hostap_get_ssid,
+       .set_countermeasures = wpa_driver_hostap_set_countermeasures,
+       .scan2 = wpa_driver_hostap_scan,
+       .get_scan_results2 = wpa_driver_hostap_get_scan_results,
+       .deauthenticate = wpa_driver_hostap_deauthenticate,
+       .disassociate = wpa_driver_hostap_disassociate,
+       .associate = wpa_driver_hostap_associate,
+       .init = wpa_driver_hostap_init,
+       .deinit = wpa_driver_hostap_deinit,
+       .set_operstate = wpa_driver_hostap_set_operstate,
+#endif /* HOSTAPD */
+};
diff --git a/src/drivers/driver_hostap.h b/src/drivers/driver_hostap.h
new file mode 100644 (file)
index 0000000..66b2bb3
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Driver interaction 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_DRIVER_H
+#define HOSTAP_DRIVER_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_DRIVER_H */
diff --git a/src/drivers/driver_iphone.m b/src/drivers/driver_iphone.m
new file mode 100644 (file)
index 0000000..8213fda
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface
+ * 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"
+#define Boolean __DummyBoolean
+#include <CoreFoundation/CoreFoundation.h>
+#undef Boolean
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include "MobileApple80211.h"
+
+struct wpa_driver_iphone_data {
+       void *ctx;
+       Apple80211Ref wireless_ctx;
+       CFArrayRef scan_results;
+       int ctrl_power;
+};
+
+
+static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key)
+{
+       const void *res;
+       CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key,
+                                                   kCFStringEncodingMacRoman);
+       if (str == NULL)
+               return NULL;
+
+       res = CFDictionaryGetValue(dict, str);
+       CFRelease(str);
+       return res;
+}
+
+
+static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_iphone_data *drv = priv;
+       CFDataRef data;
+       int err, len;
+
+       err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0,
+                                 &data);
+       if (err != 0) {
+               wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) "
+                          "failed: %d", err);
+               return -1;
+       }
+
+       len = CFDataGetLength(data);
+       if (len > 32) {
+               CFRelease(data);
+               return -1;
+       }
+       os_memcpy(ssid, CFDataGetBytePtr(data), len);
+       CFRelease(data);
+
+       return len;
+}
+
+
+static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_iphone_data *drv = priv;
+       CFStringRef data;
+       int err;
+       int a1, a2, a3, a4, a5, a6;
+
+       err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0,
+                                 &data);
+       if (err != 0) {
+               wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) "
+                          "failed: %d", err);
+               return -1;
+       }
+
+       sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman),
+              "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6);
+       bssid[0] = a1;
+       bssid[1] = a2;
+       bssid[2] = a3;
+       bssid[3] = a4;
+       bssid[4] = a5;
+       bssid[5] = a6;
+
+       CFRelease(data);
+
+       return 0;
+}
+
+
+static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len)
+{
+       struct wpa_driver_iphone_data *drv = priv;
+       int err;
+
+       if (drv->scan_results) {
+               CFRelease(drv->scan_results);
+               drv->scan_results = NULL;
+       }
+
+       err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d",
+                          err);
+               return -1;
+       }
+
+       eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv,
+                              drv->ctx);
+       return 0;
+}
+
+
+static int wpa_driver_iphone_get_scan_results(void *priv,
+                                             struct wpa_scan_result *results,
+                                             size_t max_size)
+{
+       struct wpa_driver_iphone_data *drv = priv;
+       size_t i, num;
+
+       if (drv->scan_results == NULL)
+               return 0;
+
+       num = CFArrayGetCount(drv->scan_results);
+       if (num > max_size)
+               num = max_size;
+       os_memset(results, 0, num * sizeof(struct wpa_scan_result));
+
+       for (i = 0; i < num; i++) {
+               struct wpa_scan_result *res = &results[i];
+               CFDictionaryRef dict =
+                       CFArrayGetValueAtIndex(drv->scan_results, i);
+               CFDataRef data;
+               CFStringRef str;
+               CFNumberRef num;
+               int val;
+
+               data = cfdict_get_key_str(dict, "SSID");
+               if (data) {
+                       res->ssid_len = CFDataGetLength(data);
+                       if (res->ssid_len > 32)
+                               res->ssid_len = 32;
+                       os_memcpy(res->ssid, CFDataGetBytePtr(data),
+                                 res->ssid_len);
+               }
+
+               str = cfdict_get_key_str(dict, "BSSID");
+               if (str) {
+                       int a1, a2, a3, a4, a5, a6;
+                       sscanf(CFStringGetCStringPtr(
+                                      str, kCFStringEncodingMacRoman),
+                              "%x:%x:%x:%x:%x:%x",
+                              &a1, &a2, &a3, &a4, &a5, &a6);
+                       res->bssid[0] = a1;
+                       res->bssid[1] = a2;
+                       res->bssid[2] = a3;
+                       res->bssid[3] = a4;
+                       res->bssid[4] = a5;
+                       res->bssid[5] = a6;
+               }
+
+               num = cfdict_get_key_str(dict, "CAPABILITIES");
+               if (num) {
+                       if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+                               res->caps = val;
+               }
+
+               num = cfdict_get_key_str(dict, "CHANNEL");
+               if (num) {
+                       if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+                               res->freq = 2407 + val * 5;
+               }
+
+               num = cfdict_get_key_str(dict, "RSSI");
+               if (num) {
+                       if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+                               res->level = val;
+               }
+
+               num = cfdict_get_key_str(dict, "NOISE");
+               if (num) {
+                       if (CFNumberGetValue(num, kCFNumberSInt32Type, &val))
+                               res->noise = val;
+               }
+
+               data = cfdict_get_key_str(dict, "IE");
+               if (data) {
+                       u8 *ptr = (u8 *) CFDataGetBytePtr(data);
+                       int len = CFDataGetLength(data);
+                       u8 *pos = ptr, *end = ptr + len;
+
+                       while (pos + 2 < end) {
+                               if (pos + 2 + pos[1] > end)
+                                       break;
+                               if (pos[0] == WLAN_EID_RSN &&
+                                   pos[1] <= SSID_MAX_WPA_IE_LEN) {
+                                       os_memcpy(res->rsn_ie, pos,
+                                                 2 + pos[1]);
+                                       res->rsn_ie_len = 2 + pos[1];
+                               }
+                               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC &&
+                                   pos[1] > 4 && pos[2] == 0x00 &&
+                                   pos[3] == 0x50 && pos[4] == 0xf2 &&
+                                   pos[5] == 0x01) {
+                                       os_memcpy(res->wpa_ie, pos,
+                                                 2 + pos[1]);
+                                       res->wpa_ie_len = 2 + pos[1];
+                               }
+
+                               pos = pos + 2 + pos[1];
+                       }
+               }
+       }
+
+       return num;
+}
+
+
+static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_iphone_data *drv = eloop_ctx;
+       u8 bssid[ETH_ALEN];
+
+       if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) {
+               eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout,
+                                      drv, drv->ctx);
+               return;
+       }
+
+       wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
+}
+
+
+static int wpa_driver_iphone_associate(
+       void *priv, struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_iphone_data *drv = priv;
+       int i, num, err;
+       size_t ssid_len;
+       CFDictionaryRef bss = NULL;
+
+       /*
+        * TODO: Consider generating parameters instead of just using an entry
+        * from scan results in order to support ap_scan=2.
+        */
+
+       if (drv->scan_results == NULL) {
+               wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot "
+                          "associate");
+               return -1;
+       }
+
+       num = CFArrayGetCount(drv->scan_results);
+
+       for (i = 0; i < num; i++) {
+               CFDictionaryRef dict =
+                       CFArrayGetValueAtIndex(drv->scan_results, i);
+               CFDataRef data;
+
+               data = cfdict_get_key_str(dict, "SSID");
+               if (data == NULL)
+                       continue;
+
+               ssid_len = CFDataGetLength(data);
+               if (ssid_len != params->ssid_len ||
+                   os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len)
+                   != 0)
+                       continue;
+
+               bss = dict;
+               break;
+       }
+
+       if (bss == NULL) {
+               wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan "
+                          "results - cannot associate");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found "
+                  "from scan results");
+
+       err = Apple80211Associate(drv->wireless_ctx, bss, NULL);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: "
+                          "%d", err);
+               return -1;
+       }
+
+       /*
+        * Driver is actually already associated; report association from an
+        * eloop callback.
+        */
+       eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
+       eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv,
+                              drv->ctx);
+
+       return 0;
+}
+
+
+static int wpa_driver_iphone_set_key(void *priv, wpa_alg alg, const u8 *addr,
+                                    int key_idx, int set_tx, const u8 *seq,
+                                    size_t seq_len, const u8 *key,
+                                    size_t key_len)
+{
+       /*
+        * TODO: Need to either support configuring PMK for 4-way handshake or
+        * PTK for TKIP/CCMP.
+        */
+       return -1;
+}
+
+
+static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       os_memset(capa, 0, sizeof(*capa));
+
+       capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+       capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
+               WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
+       capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
+       capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+       return 0;
+}
+
+
+static void * wpa_driver_iphone_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_iphone_data *drv;
+       int err;
+       char power;
+       CFStringRef name;
+       CFDictionaryRef dict;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       err = Apple80211Open(&drv->wireless_ctx);
+       if (err) {
+               wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d",
+                          err);
+               os_free(drv);
+               return NULL;
+       }
+
+       name = CFStringCreateWithCString(kCFAllocatorDefault, ifname,
+                                        kCFStringEncodingISOLatin1);
+       if (name == NULL) {
+               wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed");
+               Apple80211Close(drv->wireless_ctx);
+               os_free(drv);
+               return NULL;
+       }
+
+       err = Apple80211BindToInterface(drv->wireless_ctx, name);
+       CFRelease(name);
+
+       if (err) {
+               wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface "
+                          "failed: %d", err);
+               Apple80211Close(drv->wireless_ctx);
+               os_free(drv);
+               return NULL;
+       }
+
+       err = Apple80211GetPower(drv->wireless_ctx, &power);
+       if (err)
+               wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d",
+                          err);
+
+       wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power);
+
+       if (!power) {
+               drv->ctrl_power = 1;
+               err = Apple80211SetPower(drv->wireless_ctx, 1);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower "
+                                  "failed: %d", err);
+                       Apple80211Close(drv->wireless_ctx);
+                       os_free(drv);
+                       return NULL;
+               }
+       }
+
+       err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict);
+       if (err == 0) {
+               CFShow(dict);
+               CFRelease(dict);
+       } else {
+               printf("Apple80211GetInfoCopy: %d\n", err);
+       }
+
+       return drv;
+}
+
+
+static void wpa_driver_iphone_deinit(void *priv)
+{
+       struct wpa_driver_iphone_data *drv = priv;
+       int err;
+
+       eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx);
+       eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx);
+
+       if (drv->ctrl_power) {
+               wpa_printf(MSG_DEBUG, "iPhone: Power down the interface");
+               err = Apple80211SetPower(drv->wireless_ctx, 0);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) "
+                                  "failed: %d", err);
+               }
+       }
+
+       err = Apple80211Close(drv->wireless_ctx);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d",
+                          err);
+       }
+
+       if (drv->scan_results)
+               CFRelease(drv->scan_results);
+
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_iphone_ops = {
+       .name = "iphone",
+       .desc = "iPhone/iPod touch Apple80211 driver",
+       .get_ssid = wpa_driver_iphone_get_ssid,
+       .get_bssid = wpa_driver_iphone_get_bssid,
+       .init = wpa_driver_iphone_init,
+       .deinit = wpa_driver_iphone_deinit,
+       .scan = wpa_driver_iphone_scan,
+       .get_scan_results = wpa_driver_iphone_get_scan_results,
+       .associate = wpa_driver_iphone_associate,
+       .set_key = wpa_driver_iphone_set_key,
+       .get_capa = wpa_driver_iphone_get_capa,
+};
diff --git a/src/drivers/driver_ipw.c b/src/drivers/driver_ipw.c
new file mode 100644 (file)
index 0000000..77984f9
--- /dev/null
@@ -0,0 +1,472 @@
+/*
+ * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers
+ * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com>
+ * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk>
+ * Copyright (c) 2003-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.
+ *
+ * Please note that ipw2100/2200 drivers change to use generic Linux wireless
+ * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13
+ * or newer). driver_wext.c should be used in those cases.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+struct wpa_driver_ipw_data {
+       void *wext; /* private data for driver_wext */
+       void *ctx;
+       char ifname[IFNAMSIZ + 1];
+       int sock;
+};
+
+/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */
+
+#define IPW_IOCTL_WPA_SUPPLICANT               SIOCIWFIRSTPRIV+30
+
+#define IPW_CMD_SET_WPA_PARAM                  1
+#define        IPW_CMD_SET_WPA_IE                      2
+#define IPW_CMD_SET_ENCRYPTION                 3
+#define IPW_CMD_MLME                           4
+
+#define IPW_PARAM_WPA_ENABLED                  1
+#define IPW_PARAM_TKIP_COUNTERMEASURES         2
+#define IPW_PARAM_DROP_UNENCRYPTED             3
+#define IPW_PARAM_PRIVACY_INVOKED              4
+#define IPW_PARAM_AUTH_ALGS                    5
+#define IPW_PARAM_IEEE_802_1X                  6
+
+#define IPW_MLME_STA_DEAUTH                    1
+#define IPW_MLME_STA_DISASSOC                  2
+
+#define IPW_CRYPT_ERR_UNKNOWN_ALG              2
+#define IPW_CRYPT_ERR_UNKNOWN_ADDR             3
+#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED                4
+#define IPW_CRYPT_ERR_KEY_SET_FAILED           5
+#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED                6
+#define IPW_CRYPT_ERR_CARD_CONF_FAILED         7
+
+#define        IPW_CRYPT_ALG_NAME_LEN                  16
+
+struct ipw_param {
+       u32 cmd;
+       u8 sta_addr[ETH_ALEN];
+        union {
+               struct {
+                       u8 name;
+                       u32 value;
+               } wpa_param;
+               struct {
+                       u32 len;
+                       u8 reserved[32];
+                       u8 data[0];
+               } wpa_ie;
+               struct{
+                       u32 command;
+                       u32 reason_code;
+               } mlme;
+               struct {
+                       u8 alg[IPW_CRYPT_ALG_NAME_LEN];
+                       u8 set_tx;
+                       u32 err;
+                       u8 idx;
+                       u8 seq[8];
+                       u16 key_len;
+                       u8 key[0];
+               } crypt;
+
+       } u;
+};
+
+/* end of ipw2100.c and ipw2200.c code */
+
+static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg);
+
+static int ipw_ioctl(struct wpa_driver_ipw_data *drv,
+                    struct ipw_param *param, int len, int show_err)
+{
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) param;
+       iwr.u.data.length = len;
+
+       if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) {
+               int ret = errno;
+               if (show_err) 
+                       perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]");
+               return ret;
+       }
+
+       return 0;
+}
+
+
+static void ipw_show_set_key_error(struct ipw_param *param)
+{
+       switch (param->u.crypt.err) {
+       case IPW_CRYPT_ERR_UNKNOWN_ALG:
+               wpa_printf(MSG_INFO, "Unknown algorithm '%s'.",
+                          param->u.crypt.alg);
+               wpa_printf(MSG_INFO, "You may need to load kernel module to "
+                          "register that algorithm.");
+               wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for"
+                          " WEP.");
+               break;
+       case IPW_CRYPT_ERR_UNKNOWN_ADDR:
+               wpa_printf(MSG_INFO, "Unknown address " MACSTR ".",
+                          MAC2STR(param->sta_addr));
+               break;
+       case IPW_CRYPT_ERR_CRYPT_INIT_FAILED:
+               wpa_printf(MSG_INFO, "Crypt algorithm initialization failed.");
+               break;
+       case IPW_CRYPT_ERR_KEY_SET_FAILED:
+               wpa_printf(MSG_INFO, "Key setting failed.");
+               break;
+       case IPW_CRYPT_ERR_TX_KEY_SET_FAILED:
+               wpa_printf(MSG_INFO, "TX key index setting failed.");
+               break;
+       case IPW_CRYPT_ERR_CARD_CONF_FAILED:
+               wpa_printf(MSG_INFO, "Card configuration failed.");
+               break;
+       }
+}
+
+
+static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv,
+                         const u8 *wpa_ie, size_t wpa_ie_len)
+{
+       struct ipw_param *param;
+       int ret;
+       size_t blen = sizeof(*param) + wpa_ie_len;
+
+       param = os_zalloc(blen);
+       if (param == NULL)
+               return -1;
+
+       param->cmd = IPW_CMD_SET_WPA_IE;
+       param->u.wpa_ie.len = wpa_ie_len;
+       os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len);
+       
+       ret = ipw_ioctl(drv, param, blen, 1);
+
+       os_free(param);
+       return ret;
+}
+
+
+static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name,
+                            u32 value)
+{
+       struct ipw_param param;
+
+       os_memset(&param, 0, sizeof(param));
+       param.cmd = IPW_CMD_SET_WPA_PARAM;
+       param.u.wpa_param.name = name;
+       param.u.wpa_param.value = value;
+
+       return ipw_ioctl(drv, &param, sizeof(param), 1);
+}
+
+
+static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr,
+                   int cmd, int reason)
+{
+       struct ipw_param param;
+
+       os_memset(&param, 0, sizeof(param));
+       os_memcpy(param.sta_addr, addr, ETH_ALEN);      
+       param.cmd = IPW_CMD_MLME;
+       param.u.mlme.command = cmd;
+       param.u.mlme.reason_code = reason;
+
+       return ipw_ioctl(drv, &param, sizeof(param), 1);
+}
+
+
+static int wpa_driver_ipw_set_wpa(void *priv, int enabled)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+       if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0)
+               ret = -1;
+
+       if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+
+static int wpa_driver_ipw_set_key(const char *ifname, void *priv,
+                                 enum wpa_alg alg, const u8 *addr,
+                                 int key_idx, int set_tx,
+                                 const u8 *seq, size_t seq_len,
+                                 const u8 *key, size_t key_len)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       struct ipw_param *param;
+       u8 *buf;
+       size_t blen;
+       int ret = 0;
+       char *alg_name;
+
+       switch (alg) {
+       case WPA_ALG_NONE:
+               alg_name = "none";
+               break;
+       case WPA_ALG_WEP:
+               alg_name = "WEP";
+               break;
+       case WPA_ALG_TKIP:
+               alg_name = "TKIP";
+               break;
+       case WPA_ALG_CCMP:
+               alg_name = "CCMP";
+               break;
+       default:
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
+                  "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       if (seq_len > 8)
+               return -2;
+
+       blen = sizeof(*param) + key_len;
+       buf = os_zalloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       param = (struct ipw_param *) buf;
+       param->cmd = IPW_CMD_SET_ENCRYPTION;
+       os_memset(param->sta_addr, 0xff, ETH_ALEN);
+       os_strlcpy((char *) param->u.crypt.alg, alg_name,
+                  IPW_CRYPT_ALG_NAME_LEN);
+       param->u.crypt.set_tx = set_tx ? 1 : 0;
+       param->u.crypt.idx = key_idx;
+       os_memcpy(param->u.crypt.seq, seq, seq_len);
+       param->u.crypt.key_len = key_len;
+       os_memcpy((u8 *) (param + 1), key, key_len);
+
+       if (ipw_ioctl(drv, param, blen, 1)) {
+               wpa_printf(MSG_WARNING, "Failed to set encryption.");
+               ipw_show_set_key_error(param);
+               ret = -1;
+       }
+       os_free(buf);
+
+       return ret;
+}
+
+
+static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES,
+                                    enabled);
+
+}
+
+
+static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED,
+                                    enabled);
+}
+
+
+static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr,
+                                        int reason_code)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code);
+}
+
+
+static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr,
+                                      int reason_code)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code);
+}
+
+
+static int
+wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       int ret = 0;
+       int unencrypted_eapol;
+
+       if (wpa_driver_ipw_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+       if (wpa_driver_ipw_set_drop_unencrypted(drv, params->drop_unencrypted)
+           < 0)
+               ret = -1;
+       if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+                                    params->ssid_len) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0)
+               ret = -1;
+
+       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+           params->key_mgmt_suite == KEY_MGMT_PSK)
+               unencrypted_eapol = 0;
+       else
+               unencrypted_eapol = 1;
+       
+       if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X,
+                             unencrypted_eapol) < 0) {
+               wpa_printf(MSG_DEBUG, "ipw: Failed to configure "
+                          "ieee_802_1x param");
+       }
+
+       return ret;
+}
+
+
+static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       int algs = 0;
+
+       if (auth_alg & WPA_AUTH_ALG_OPEN)
+               algs |= 1;
+       if (auth_alg & WPA_AUTH_ALG_SHARED)
+               algs |= 2;
+       if (auth_alg & WPA_AUTH_ALG_LEAP)
+               algs |= 4;
+       if (algs == 0)
+               algs = 1; /* at least one algorithm should be set */
+
+       wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs);
+       return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs);
+}
+
+
+static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static int wpa_driver_ipw_scan(void *priv,
+                              struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return wpa_driver_wext_scan(drv->wext, params);
+}
+
+
+static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_ipw_set_operstate(void *priv, int state)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static void * wpa_driver_ipw_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_ipw_data *drv;
+       int ver;
+
+       wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__);
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->wext = wpa_driver_wext_init(ctx, ifname);
+       if (drv->wext == NULL) {
+               os_free(drv);
+               return NULL;
+       }
+
+       ver = wpa_driver_wext_get_version(drv->wext);
+       if (ver >= 18) {
+               wpa_printf(MSG_WARNING, "Linux wireless extensions version %d "
+                          "detected.", ver);
+               wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext "
+                          "(-Dwext) instead of driver_ipw.");
+       }
+
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->sock < 0) {
+               wpa_driver_wext_deinit(drv->wext);
+               os_free(drv);
+               return NULL;
+       }
+
+       wpa_driver_ipw_set_wpa(drv, 1);
+
+       return drv;
+}
+
+
+static void wpa_driver_ipw_deinit(void *priv)
+{
+       struct wpa_driver_ipw_data *drv = priv;
+       wpa_driver_ipw_set_wpa(drv, 0);
+       wpa_driver_wext_deinit(drv->wext);
+       close(drv->sock);
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_ipw_ops = {
+       .name = "ipw",
+       .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 "
+       "or newer)",
+       .get_bssid = wpa_driver_ipw_get_bssid,
+       .get_ssid = wpa_driver_ipw_get_ssid,
+       .set_key = wpa_driver_ipw_set_key,
+       .set_countermeasures = wpa_driver_ipw_set_countermeasures,
+       .scan2 = wpa_driver_ipw_scan,
+       .get_scan_results2 = wpa_driver_ipw_get_scan_results,
+       .deauthenticate = wpa_driver_ipw_deauthenticate,
+       .disassociate = wpa_driver_ipw_disassociate,
+       .associate = wpa_driver_ipw_associate,
+       .init = wpa_driver_ipw_init,
+       .deinit = wpa_driver_ipw_deinit,
+       .set_operstate = wpa_driver_ipw_set_operstate,
+};
diff --git a/src/drivers/driver_madwifi.c b/src/drivers/driver_madwifi.c
new file mode 100644 (file)
index 0000000..8687404
--- /dev/null
@@ -0,0 +1,1854 @@
+/*
+ * WPA Supplicant - driver interaction with MADWIFI 802.11 driver
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, Video54 Technologies
+ * 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.
+ *
+ * While this driver wrapper supports both AP (hostapd) and station
+ * (wpa_supplicant) operations, the station side is deprecated and
+ * driver_wext.c should be used instead. This driver wrapper should only be
+ * used with hostapd for AP mode functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "wireless_copy.h"
+
+/*
+ * Avoid conflicts with wpa_supplicant definitions by undefining a definition.
+ */
+#undef WME_OUI_TYPE
+
+#include <include/compat.h>
+#include <net80211/ieee80211.h>
+#ifdef WME_NUM_AC
+/* Assume this is built against BSD branch of madwifi driver. */
+#define MADWIFI_BSD
+#include <net80211/_ieee80211.h>
+#endif /* WME_NUM_AC */
+#include <net80211/ieee80211_crypto.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+#include <netpacket/packet.h>
+
+#ifndef ETH_P_80211_RAW
+#define ETH_P_80211_RAW 0x0019
+#endif
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+
+/*
+ * Avoid conflicts with hostapd definitions by undefining couple of defines
+ * from madwifi header files.
+ */
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+#undef WME_OUI_TYPE
+
+
+#ifdef IEEE80211_IOCTL_SETWMMPARAMS
+/* Assume this is built against madwifi-ng */
+#define MADWIFI_NG
+#endif /* IEEE80211_IOCTL_SETWMMPARAMS */
+
+
+#ifdef HOSTAPD
+
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "l2_packet/l2_packet.h"
+
+
+struct madwifi_driver_data {
+       struct hostapd_data *hapd;              /* back pointer */
+
+       char    iface[IFNAMSIZ + 1];
+       int     ifindex;
+       struct l2_packet_data *sock_xmit;       /* raw packet xmit socket */
+       struct l2_packet_data *sock_recv;       /* raw packet recv socket */
+       int     ioctl_sock;                     /* socket for ioctl() use */
+       struct netlink_data *netlink;
+       int     we_version;
+       u8      acct_mac[ETH_ALEN];
+       struct hostap_sta_driver_data acct_data;
+
+       struct l2_packet_data *sock_raw; /* raw 802.11 management frames */
+};
+
+static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                             int reason_code);
+
+static int
+set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
+{
+       struct iwreq iwr;
+       int do_inline = len < IFNAMSIZ;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+       /* FILTERFRAME must be NOT inline, regardless of size. */
+       if (op == IEEE80211_IOCTL_FILTERFRAME)
+               do_inline = 0;
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+       if (op == IEEE80211_IOCTL_SET_APPIEBUF)
+               do_inline = 0;
+       if (do_inline) {
+               /*
+                * Argument data fits inline; put it there.
+                */
+               memcpy(iwr.u.name, data, len);
+       } else {
+               /*
+                * Argument data too big for inline transfer; setup a
+                * parameter block instead; the kernel will transfer
+                * the data for the driver.
+                */
+               iwr.u.data.pointer = data;
+               iwr.u.data.length = len;
+       }
+
+       if (ioctl(drv->ioctl_sock, op, &iwr) < 0) {
+#ifdef MADWIFI_NG
+               int first = IEEE80211_IOCTL_SETPARAM;
+               static const char *opnames[] = {
+                       "ioctl[IEEE80211_IOCTL_SETPARAM]",
+                       "ioctl[IEEE80211_IOCTL_GETPARAM]",
+                       "ioctl[IEEE80211_IOCTL_SETMODE]",
+                       "ioctl[IEEE80211_IOCTL_GETMODE]",
+                       "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]",
+                       "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]",
+                       "ioctl[IEEE80211_IOCTL_SETCHANLIST]",
+                       "ioctl[IEEE80211_IOCTL_GETCHANLIST]",
+                       "ioctl[IEEE80211_IOCTL_CHANSWITCH]",
+                       "ioctl[IEEE80211_IOCTL_GET_APPIEBUF]",
+                       "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
+                       "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
+                       "ioctl[IEEE80211_IOCTL_FILTERFRAME]",
+                       "ioctl[IEEE80211_IOCTL_GETCHANINFO]",
+                       "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+                       "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+                       "ioctl[IEEE80211_IOCTL_SETMLME]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_SETKEY]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_DELKEY]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_ADDMAC]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_DELMAC]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_WDSMAC]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_WDSDELMAC]",
+                       NULL,
+                       "ioctl[IEEE80211_IOCTL_KICKMAC]",
+               };
+#else /* MADWIFI_NG */
+               int first = IEEE80211_IOCTL_SETPARAM;
+               static const char *opnames[] = {
+                       "ioctl[IEEE80211_IOCTL_SETPARAM]",
+                       "ioctl[IEEE80211_IOCTL_GETPARAM]",
+                       "ioctl[IEEE80211_IOCTL_SETKEY]",
+                       "ioctl[SIOCIWFIRSTPRIV+3]",
+                       "ioctl[IEEE80211_IOCTL_DELKEY]",
+                       "ioctl[SIOCIWFIRSTPRIV+5]",
+                       "ioctl[IEEE80211_IOCTL_SETMLME]",
+                       "ioctl[SIOCIWFIRSTPRIV+7]",
+                       "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+                       "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+                       "ioctl[IEEE80211_IOCTL_ADDMAC]",
+                       "ioctl[SIOCIWFIRSTPRIV+11]",
+                       "ioctl[IEEE80211_IOCTL_DELMAC]",
+                       "ioctl[SIOCIWFIRSTPRIV+13]",
+                       "ioctl[IEEE80211_IOCTL_CHANLIST]",
+                       "ioctl[SIOCIWFIRSTPRIV+15]",
+                       "ioctl[IEEE80211_IOCTL_GETRSN]",
+                       "ioctl[SIOCIWFIRSTPRIV+17]",
+                       "ioctl[IEEE80211_IOCTL_GETKEY]",
+               };
+#endif /* MADWIFI_NG */
+               int idx = op - first;
+               if (first <= op &&
+                   idx < (int) (sizeof(opnames) / sizeof(opnames[0])) &&
+                   opnames[idx])
+                       perror(opnames[idx]);
+               else
+                       perror("ioctl[unknown???]");
+               return -1;
+       }
+       return 0;
+}
+
+static int
+set80211param(struct madwifi_driver_data *drv, int op, int arg)
+{
+       struct iwreq iwr;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.mode = op;
+       memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
+
+       if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+               perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
+               wpa_printf(MSG_DEBUG, "%s: Failed to set parameter (op %d "
+                          "arg %d)", __func__, op, arg);
+               return -1;
+       }
+       return 0;
+}
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char *
+ether_sprintf(const u8 *addr)
+{
+       static char buf[sizeof(MACSTR)];
+
+       if (addr != NULL)
+               snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+       else
+               snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+       return buf;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+/*
+ * Configure WPA parameters.
+ */
+static int
+madwifi_configure_wpa(struct madwifi_driver_data *drv,
+                     struct wpa_bss_params *params)
+{
+       int v;
+
+       switch (params->wpa_group) {
+       case WPA_CIPHER_CCMP:
+               v = IEEE80211_CIPHER_AES_CCM;
+               break;
+       case WPA_CIPHER_TKIP:
+               v = IEEE80211_CIPHER_TKIP;
+               break;
+       case WPA_CIPHER_WEP104:
+               v = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_CIPHER_WEP40:
+               v = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_CIPHER_NONE:
+               v = IEEE80211_CIPHER_NONE;
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "Unknown group key cipher %u",
+                          params->wpa_group);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
+       if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
+               printf("Unable to set group key cipher to %u\n", v);
+               return -1;
+       }
+       if (v == IEEE80211_CIPHER_WEP) {
+               /* key length is done only for specific ciphers */
+               v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
+               if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
+                       printf("Unable to set group key length to %u\n", v);
+                       return -1;
+               }
+       }
+
+       v = 0;
+       if (params->wpa_pairwise & WPA_CIPHER_CCMP)
+               v |= 1<<IEEE80211_CIPHER_AES_CCM;
+       if (params->wpa_pairwise & WPA_CIPHER_TKIP)
+               v |= 1<<IEEE80211_CIPHER_TKIP;
+       if (params->wpa_pairwise & WPA_CIPHER_NONE)
+               v |= 1<<IEEE80211_CIPHER_NONE;
+       wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
+       if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
+               printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x",
+                  __func__, params->wpa_key_mgmt);
+       if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
+                         params->wpa_key_mgmt)) {
+               printf("Unable to set key management algorithms to 0x%x\n",
+                       params->wpa_key_mgmt);
+               return -1;
+       }
+
+       v = 0;
+       if (params->rsn_preauth)
+               v |= BIT(0);
+       wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
+                  __func__, params->rsn_preauth);
+       if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
+               printf("Unable to set RSN capabilities to 0x%x\n", v);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
+       if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
+               printf("Unable to set WPA to %u\n", params->wpa);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
+{
+       struct madwifi_driver_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled);
+
+       if (!params->enabled) {
+               /* XXX restore state */
+               return set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+                       IEEE80211_AUTH_AUTO);
+       }
+       if (!params->wpa && !params->ieee802_1x) {
+               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+                       HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+               return -1;
+       }
+       if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
+               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+                       HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+               return -1;
+       }
+       if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
+               (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
+                       HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+               return -1;
+       }
+
+       return 0;
+}
+
+static int
+madwifi_set_privacy(void *priv, int enabled)
+{
+       struct madwifi_driver_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+       return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled);
+}
+
+static int
+madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d",
+                  __func__, ether_sprintf(addr), authorized);
+
+       if (authorized)
+               mlme.im_op = IEEE80211_MLME_AUTHORIZE;
+       else
+               mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
+       mlme.im_reason = 0;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR,
+                          __func__, authorized ? "" : "un", MAC2STR(addr));
+       }
+
+       return ret;
+}
+
+static int
+madwifi_sta_set_flags(void *priv, const u8 *addr,
+                     int total_flags, int flags_or, int flags_and)
+{
+       /* For now, only support setting Authorized flag */
+       if (flags_or & WPA_STA_AUTHORIZED)
+               return madwifi_set_sta_authorized(priv, addr, 1);
+       if (!(flags_and & WPA_STA_AUTHORIZED))
+               return madwifi_set_sta_authorized(priv, addr, 0);
+       return 0;
+}
+
+static int
+madwifi_del_key(void *priv, const u8 *addr, int key_idx)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_del_key wk;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d",
+                  __func__, ether_sprintf(addr), key_idx);
+
+       memset(&wk, 0, sizeof(wk));
+       if (addr != NULL) {
+               memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+               wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE;
+       } else {
+               wk.idk_keyix = key_idx;
+       }
+
+       ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s"
+                          " key_idx %d)", __func__, ether_sprintf(addr),
+                          key_idx);
+       }
+
+       return ret;
+}
+
+static int
+wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+                          const u8 *addr, int key_idx, int set_tx,
+                          const u8 *seq, size_t seq_len,
+                          const u8 *key, size_t key_len)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_key wk;
+       u_int8_t cipher;
+       int ret;
+
+       if (alg == WPA_ALG_NONE)
+               return madwifi_del_key(drv, addr, key_idx);
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d",
+                  __func__, alg, ether_sprintf(addr), key_idx);
+
+       if (alg == WPA_ALG_WEP)
+               cipher = IEEE80211_CIPHER_WEP;
+       else if (alg == WPA_ALG_TKIP)
+               cipher = IEEE80211_CIPHER_TKIP;
+       else if (alg == WPA_ALG_CCMP)
+               cipher = IEEE80211_CIPHER_AES_CCM;
+       else {
+               printf("%s: unknown/unsupported algorithm %d\n",
+                       __func__, alg);
+               return -1;
+       }
+
+       if (key_len > sizeof(wk.ik_keydata)) {
+               printf("%s: key length %lu too big\n", __func__,
+                      (unsigned long) key_len);
+               return -3;
+       }
+
+       memset(&wk, 0, sizeof(wk));
+       wk.ik_type = cipher;
+       wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
+       if (addr == NULL) {
+               memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+               wk.ik_keyix = key_idx;
+               wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+       } else {
+               memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+               wk.ik_keyix = IEEE80211_KEYIX_NONE;
+       }
+       wk.ik_keylen = key_len;
+       memcpy(wk.ik_keydata, key, key_len);
+
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s"
+                          " key_idx %d alg %d key_len %lu set_tx %d)",
+                          __func__, ether_sprintf(wk.ik_macaddr), key_idx,
+                          alg, (unsigned long) key_len, set_tx);
+       }
+
+       return ret;
+}
+
+
+static int
+madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+                  u8 *seq)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_key wk;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d",
+                  __func__, ether_sprintf(addr), idx);
+
+       memset(&wk, 0, sizeof(wk));
+       if (addr == NULL)
+               memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+       else
+               memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+       wk.ik_keyix = idx;
+
+       if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data "
+                          "(addr " MACSTR " key_idx %d)",
+                          __func__, MAC2STR(wk.ik_macaddr), idx);
+               return -1;
+       }
+
+#ifdef WORDS_BIGENDIAN
+       {
+               /*
+                * wk.ik_keytsc is in host byte order (big endian), need to
+                * swap it to match with the byte order used in WPA.
+                */
+               int i;
+               u8 tmp[WPA_KEY_RSC_LEN];
+               memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+               for (i = 0; i < WPA_KEY_RSC_LEN; i++) {
+                       seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1];
+               }
+       }
+#else /* WORDS_BIGENDIAN */
+       memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+#endif /* WORDS_BIGENDIAN */
+       return 0;
+}
+
+
+static int 
+madwifi_flush(void *priv)
+{
+#ifdef MADWIFI_BSD
+       u8 allsta[IEEE80211_ADDR_LEN];
+       memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+       return madwifi_sta_deauth(priv, NULL, allsta,
+                                 IEEE80211_REASON_AUTH_LEAVE);
+#else /* MADWIFI_BSD */
+       return 0;               /* XXX */
+#endif /* MADWIFI_BSD */
+}
+
+
+static int
+madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+                            const u8 *addr)
+{
+       struct madwifi_driver_data *drv = priv;
+
+#ifdef MADWIFI_BSD
+       struct ieee80211req_sta_stats stats;
+
+       memset(data, 0, sizeof(*data));
+
+       /*
+        * Fetch statistics for station from the system.
+        */
+       memset(&stats, 0, sizeof(stats));
+       memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+       if (set80211priv(drv,
+#ifdef MADWIFI_NG
+                        IEEE80211_IOCTL_STA_STATS,
+#else /* MADWIFI_NG */
+                        IEEE80211_IOCTL_GETSTASTATS,
+#endif /* MADWIFI_NG */
+                        &stats, sizeof(stats))) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr "
+                          MACSTR ")", __func__, MAC2STR(addr));
+               if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+                       memcpy(data, &drv->acct_data, sizeof(*data));
+                       return 0;
+               }
+
+               printf("Failed to get station stats information element.\n");
+               return -1;
+       }
+
+       data->rx_packets = stats.is_stats.ns_rx_data;
+       data->rx_bytes = stats.is_stats.ns_rx_bytes;
+       data->tx_packets = stats.is_stats.ns_tx_data;
+       data->tx_bytes = stats.is_stats.ns_tx_bytes;
+       return 0;
+
+#else /* MADWIFI_BSD */
+
+       char buf[1024], line[128], *pos;
+       FILE *f;
+       unsigned long val;
+
+       memset(data, 0, sizeof(*data));
+       snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR,
+                drv->iface, MAC2STR(addr));
+
+       f = fopen(buf, "r");
+       if (!f) {
+               if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0)
+                       return -1;
+               memcpy(data, &drv->acct_data, sizeof(*data));
+               return 0;
+       }
+       /* Need to read proc file with in one piece, so use large enough
+        * buffer. */
+       setbuffer(f, buf, sizeof(buf));
+
+       while (fgets(line, sizeof(line), f)) {
+               pos = strchr(line, '=');
+               if (!pos)
+                       continue;
+               *pos++ = '\0';
+               val = strtoul(pos, NULL, 10);
+               if (strcmp(line, "rx_packets") == 0)
+                       data->rx_packets = val;
+               else if (strcmp(line, "tx_packets") == 0)
+                       data->tx_packets = val;
+               else if (strcmp(line, "rx_bytes") == 0)
+                       data->rx_bytes = val;
+               else if (strcmp(line, "tx_bytes") == 0)
+                       data->tx_bytes = val;
+       }
+
+       fclose(f);
+
+       return 0;
+#endif /* MADWIFI_BSD */
+}
+
+
+static int
+madwifi_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if defined(MADWIFI_BSD) && defined(IEEE80211_MLME_CLEAR_STATS)
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr));
+
+       mlme.im_op = IEEE80211_MLME_CLEAR_STATS;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+                          sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr "
+                          MACSTR ")", __func__, MAC2STR(addr));
+       }
+
+       return ret;
+#else /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */
+       return 0; /* FIX */
+#endif /* MADWIFI_BSD && IEEE80211_MLME_CLEAR_STATS */
+}
+
+
+static int
+madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
+{
+       /*
+        * Do nothing; we setup parameters at startup that define the
+        * contents of the beacon information element.
+        */
+       return 0;
+}
+
+static int
+madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                  int reason_code)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+                  __func__, ether_sprintf(addr), reason_code);
+
+       mlme.im_op = IEEE80211_MLME_DEAUTH;
+       mlme.im_reason = reason_code;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR
+                          " reason %d)",
+                          __func__, MAC2STR(addr), reason_code);
+       }
+
+       return ret;
+}
+
+static int
+madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                    int reason_code)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d",
+                  __func__, ether_sprintf(addr), reason_code);
+
+       mlme.im_op = IEEE80211_MLME_DISASSOC;
+       mlme.im_reason = reason_code;
+       memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme));
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr "
+                          MACSTR " reason %d)",
+                          __func__, MAC2STR(addr), reason_code);
+       }
+
+       return ret;
+}
+
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+                               size_t len)
+{
+       struct madwifi_driver_data *drv = ctx;
+       const struct ieee80211_mgmt *mgmt;
+       u16 fc;
+       union wpa_event_data event;
+
+       /* Send Probe Request information to WPS processing */
+
+       if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+               return;
+       mgmt = (const struct ieee80211_mgmt *) buf;
+
+       fc = le_to_host16(mgmt->frame_control);
+       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
+           WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ)
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_probe_req.sa = mgmt->sa;
+       event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+       event.rx_probe_req.ie_len =
+               len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+       wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+}
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+
+static int madwifi_receive_probe_req(struct madwifi_driver_data *drv)
+{
+       int ret = 0;
+#ifdef CONFIG_WPS
+#ifdef IEEE80211_IOCTL_FILTERFRAME
+       struct ieee80211req_set_filter filt;
+
+       wpa_printf(MSG_DEBUG, "%s Enter", __func__);
+       filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ;
+
+       ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt,
+                          sizeof(struct ieee80211req_set_filter));
+       if (ret)
+               return ret;
+
+       drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW,
+                                      madwifi_raw_receive, drv, 1);
+       if (drv->sock_raw == NULL)
+               return -1;
+#endif /* IEEE80211_IOCTL_FILTERFRAME */
+#endif /* CONFIG_WPS */
+       return ret;
+}
+
+#ifdef CONFIG_WPS
+static int
+madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype)
+{
+       struct madwifi_driver_data *drv = priv;
+       u8 buf[256];
+       struct ieee80211req_getset_appiebuf *beac_ie;
+
+       wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__,
+                  (unsigned long) len);
+
+       beac_ie = (struct ieee80211req_getset_appiebuf *) buf;
+       beac_ie->app_frmtype = frametype;
+       beac_ie->app_buflen = len;
+       memcpy(&(beac_ie->app_buf[0]), ie, len);
+
+       return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie,
+                           sizeof(struct ieee80211req_getset_appiebuf) + len);
+}
+
+static int
+madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+                     const struct wpabuf *proberesp)
+{
+       if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL,
+                              beacon ? wpabuf_len(beacon) : 0,
+                              IEEE80211_APPIE_FRAME_BEACON) < 0)
+               return -1;
+       return madwifi_set_wps_ie(priv,
+                                 proberesp ? wpabuf_head(proberesp) : NULL,
+                                 proberesp ? wpabuf_len(proberesp) : 0,
+                                 IEEE80211_APPIE_FRAME_PROBE_RESP);
+}
+#else /* CONFIG_WPS */
+#define madwifi_set_ap_wps_ie NULL
+#endif /* CONFIG_WPS */
+
+static void
+madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+{
+       struct hostapd_data *hapd = drv->hapd;
+       struct ieee80211req_wpaie ie;
+       int ielen = 0;
+       u8 *iebuf = NULL;
+
+       /*
+        * Fetch negotiated WPA/RSN parameters from the system.
+        */
+       memset(&ie, 0, sizeof(ie));
+       memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+       if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE",
+                          __func__);
+               goto no_ie;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE",
+                   ie.wpa_ie, IEEE80211_MAX_OPT_IE);
+       iebuf = ie.wpa_ie;
+       /* madwifi seems to return some random data if WPA/RSN IE is not set.
+        * Assume the IE was not included if the IE type is unknown. */
+       if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC)
+               iebuf[1] = 0;
+#ifdef MADWIFI_NG
+       wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE",
+                   ie.rsn_ie, IEEE80211_MAX_OPT_IE);
+       if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) {
+               /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not
+                * set. This is needed for WPA2. */
+               iebuf = ie.rsn_ie;
+               if (iebuf[0] != WLAN_EID_RSN)
+                       iebuf[1] = 0;
+       }
+#endif /* MADWIFI_NG */
+
+       ielen = iebuf[1];
+       if (ielen == 0)
+               iebuf = NULL;
+       else
+               ielen += 2;
+
+no_ie:
+       drv_event_assoc(hapd, addr, iebuf, ielen);
+
+       if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) {
+               /* Cached accounting data is not valid anymore. */
+               memset(drv->acct_mac, 0, ETH_ALEN);
+               memset(&drv->acct_data, 0, sizeof(drv->acct_data));
+       }
+}
+
+static void
+madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv,
+                                      char *custom)
+{
+       wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+       if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+               char *pos;
+               u8 addr[ETH_ALEN];
+               pos = strstr(custom, "addr=");
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLME-MICHAELMICFAILURE.indication "
+                                  "without sender address ignored");
+                       return;
+               }
+               pos += 5;
+               if (hwaddr_aton(pos, addr) == 0) {
+                       union wpa_event_data data;
+                       os_memset(&data, 0, sizeof(data));
+                       data.michael_mic_failure.unicast = 1;
+                       data.michael_mic_failure.src = addr;
+                       wpa_supplicant_event(drv->hapd,
+                                            EVENT_MICHAEL_MIC_FAILURE, &data);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "MLME-MICHAELMICFAILURE.indication "
+                                  "with invalid MAC address");
+               }
+       } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) {
+               char *key, *value;
+               u32 val;
+               key = custom;
+               while ((key = strchr(key, '\n')) != NULL) {
+                       key++;
+                       value = strchr(key, '=');
+                       if (value == NULL)
+                               continue;
+                       *value++ = '\0';
+                       val = strtoul(value, NULL, 10);
+                       if (strcmp(key, "mac") == 0)
+                               hwaddr_aton(value, drv->acct_mac);
+                       else if (strcmp(key, "rx_packets") == 0)
+                               drv->acct_data.rx_packets = val;
+                       else if (strcmp(key, "tx_packets") == 0)
+                               drv->acct_data.tx_packets = val;
+                       else if (strcmp(key, "rx_bytes") == 0)
+                               drv->acct_data.rx_bytes = val;
+                       else if (strcmp(key, "tx_bytes") == 0)
+                               drv->acct_data.tx_bytes = val;
+                       key = value;
+               }
+       }
+}
+
+static void
+madwifi_wireless_event_wireless(struct madwifi_driver_data *drv,
+                                           char *data, int len)
+{
+       struct iw_event iwe_buf, *iwe = &iwe_buf;
+       char *pos, *end, *custom, *buf;
+
+       pos = data;
+       end = data + len;
+
+       while (pos + IW_EV_LCP_LEN <= end) {
+               /* Event data may be unaligned, so make a local, aligned copy
+                * before processing. */
+               memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+               wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d",
+                          iwe->cmd, iwe->len);
+               if (iwe->len <= IW_EV_LCP_LEN)
+                       return;
+
+               custom = pos + IW_EV_POINT_LEN;
+               if (drv->we_version > 18 &&
+                   (iwe->cmd == IWEVMICHAELMICFAILURE ||
+                    iwe->cmd == IWEVCUSTOM)) {
+                       /* WE-19 removed the pointer from struct iw_point */
+                       char *dpos = (char *) &iwe_buf.u.data.length;
+                       int dlen = dpos - (char *) &iwe_buf;
+                       memcpy(dpos, pos + IW_EV_LCP_LEN,
+                              sizeof(struct iw_event) - dlen);
+               } else {
+                       memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+                       custom += IW_EV_POINT_OFF;
+               }
+
+               switch (iwe->cmd) {
+               case IWEVEXPIRED:
+                       drv_event_disassoc(drv->hapd,
+                                          (u8 *) iwe->u.addr.sa_data);
+                       break;
+               case IWEVREGISTERED:
+                       madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data);
+                       break;
+               case IWEVCUSTOM:
+                       if (custom + iwe->u.data.length > end)
+                               return;
+                       buf = malloc(iwe->u.data.length + 1);
+                       if (buf == NULL)
+                               return;         /* XXX */
+                       memcpy(buf, custom, iwe->u.data.length);
+                       buf[iwe->u.data.length] = '\0';
+                       madwifi_wireless_event_wireless_custom(drv, buf);
+                       free(buf);
+                       break;
+               }
+
+               pos += iwe->len;
+       }
+}
+
+
+static void
+madwifi_wireless_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+                                  u8 *buf, size_t len)
+{
+       struct madwifi_driver_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       if (ifi->ifi_index != drv->ifindex)
+               return;
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_WIRELESS) {
+                       madwifi_wireless_event_wireless(
+                               drv, ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len);
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+}
+
+
+static int
+madwifi_get_we_version(struct madwifi_driver_data *drv)
+{
+       struct iw_range *range;
+       struct iwreq iwr;
+       int minlen;
+       size_t buflen;
+
+       drv->we_version = 0;
+
+       /*
+        * Use larger buffer than struct iw_range in order to allow the
+        * structure to grow in the future.
+        */
+       buflen = sizeof(struct iw_range) + 500;
+       range = os_zalloc(buflen);
+       if (range == NULL)
+               return -1;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) range;
+       iwr.u.data.length = buflen;
+
+       minlen = ((char *) &range->enc_capa) - (char *) range +
+               sizeof(range->enc_capa);
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWRANGE]");
+               free(range);
+               return -1;
+       } else if (iwr.u.data.length >= minlen &&
+                  range->we_version_compiled >= 18) {
+               wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+                          "WE(source)=%d enc_capa=0x%x",
+                          range->we_version_compiled,
+                          range->we_version_source,
+                          range->enc_capa);
+               drv->we_version = range->we_version_compiled;
+       }
+
+       free(range);
+       return 0;
+}
+
+
+static int
+madwifi_wireless_event_init(struct madwifi_driver_data *drv)
+{
+       struct netlink_config *cfg;
+
+       madwifi_get_we_version(drv);
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               return -1;
+       cfg->ctx = drv;
+       cfg->newlink_cb = madwifi_wireless_event_rtm_newlink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int
+madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+                  int encrypt, const u8 *own_addr)
+{
+       struct madwifi_driver_data *drv = priv;
+       unsigned char buf[3000];
+       unsigned char *bp = buf;
+       struct l2_ethhdr *eth;
+       size_t len;
+       int status;
+
+       /*
+        * Prepend the Ethernet header.  If the caller left us
+        * space at the front we could just insert it but since
+        * we don't know we copy to a local buffer.  Given the frequency
+        * and size of frames this probably doesn't matter.
+        */
+       len = data_len + sizeof(struct l2_ethhdr);
+       if (len > sizeof(buf)) {
+               bp = malloc(len);
+               if (bp == NULL) {
+                       printf("EAPOL frame discarded, cannot malloc temp "
+                              "buffer of size %lu!\n", (unsigned long) len);
+                       return -1;
+               }
+       }
+       eth = (struct l2_ethhdr *) bp;
+       memcpy(eth->h_dest, addr, ETH_ALEN);
+       memcpy(eth->h_source, own_addr, ETH_ALEN);
+       eth->h_proto = host_to_be16(ETH_P_EAPOL);
+       memcpy(eth+1, data, data_len);
+
+       wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
+
+       status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
+
+       if (bp != buf)
+               free(bp);
+       return status;
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+       struct madwifi_driver_data *drv = ctx;
+       drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr),
+                          len - sizeof(struct l2_ethhdr));
+}
+
+static void *
+madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params)
+{
+       struct madwifi_driver_data *drv;
+       struct ifreq ifr;
+       struct iwreq iwr;
+       char brname[IFNAMSIZ];
+
+       drv = os_zalloc(sizeof(struct madwifi_driver_data));
+       if (drv == NULL) {
+               printf("Could not allocate memory for madwifi driver data\n");
+               return NULL;
+       }
+
+       drv->hapd = hapd;
+       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->ioctl_sock < 0) {
+               perror("socket[PF_INET,SOCK_DGRAM]");
+               goto bad;
+       }
+       memcpy(drv->iface, params->ifname, sizeof(drv->iface));
+
+       memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
+       if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
+               perror("ioctl(SIOCGIFINDEX)");
+               goto bad;
+       }
+       drv->ifindex = ifr.ifr_ifindex;
+
+       drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
+                                       handle_read, drv, 1);
+       if (drv->sock_xmit == NULL)
+               goto bad;
+       if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr))
+               goto bad;
+       if (params->bridge[0]) {
+               wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.",
+                          params->bridge[0]);
+               drv->sock_recv = l2_packet_init(params->bridge[0], NULL,
+                                               ETH_P_EAPOL, handle_read, drv,
+                                               1);
+               if (drv->sock_recv == NULL)
+                       goto bad;
+       } else if (linux_br_get(brname, drv->iface) == 0) {
+               wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for "
+                          "EAPOL receive", brname);
+               drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL,
+                                               handle_read, drv, 1);
+               if (drv->sock_recv == NULL)
+                       goto bad;
+       } else
+               drv->sock_recv = drv->sock_xmit;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+
+       iwr.u.mode = IW_MODE_MASTER;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
+               perror("ioctl[SIOCSIWMODE]");
+               printf("Could not set interface to master mode!\n");
+               goto bad;
+       }
+
+       /* mark down during setup */
+       linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+       madwifi_set_privacy(drv, 0); /* default to no privacy */
+
+       madwifi_receive_probe_req(drv);
+
+       if (madwifi_wireless_event_init(drv))
+               goto bad;
+
+       return drv;
+bad:
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+       if (drv != NULL)
+               free(drv);
+       return NULL;
+}
+
+
+static void
+madwifi_deinit(void *priv)
+{
+       struct madwifi_driver_data *drv = priv;
+
+       netlink_deinit(drv->netlink);
+       (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0);
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+       if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit)
+               l2_packet_deinit(drv->sock_recv);
+       if (drv->sock_xmit != NULL)
+               l2_packet_deinit(drv->sock_xmit);
+       if (drv->sock_raw)
+               l2_packet_deinit(drv->sock_raw);
+       free(drv);
+}
+
+static int
+madwifi_set_ssid(void *priv, const u8 *buf, int len)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct iwreq iwr;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.essid.flags = 1; /* SSID active */
+       iwr.u.essid.pointer = (caddr_t) buf;
+       iwr.u.essid.length = len + 1;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCSIWESSID]");
+               printf("len=%d\n", len);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+madwifi_get_ssid(void *priv, u8 *buf, int len)
+{
+       struct madwifi_driver_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+       iwr.u.essid.pointer = (caddr_t) buf;
+       iwr.u.essid.length = len;
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCGIWESSID]");
+               ret = -1;
+       } else
+               ret = iwr.u.essid.length;
+
+       return ret;
+}
+
+static int
+madwifi_set_countermeasures(void *priv, int enabled)
+{
+       struct madwifi_driver_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled);
+}
+
+static int
+madwifi_commit(void *priv)
+{
+       struct madwifi_driver_data *drv = priv;
+       return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1);
+}
+
+#else /* HOSTAPD */
+
+struct wpa_driver_madwifi_data {
+       void *wext; /* private data for driver_wext */
+       void *ctx;
+       char ifname[IFNAMSIZ + 1];
+       int sock;
+};
+
+static int wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg);
+static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies,
+                                              size_t ies_len);
+
+
+static int
+set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len,
+            int show_err)
+{
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       if (len < IFNAMSIZ &&
+           op != IEEE80211_IOCTL_SET_APPIEBUF) {
+               /*
+                * Argument data fits inline; put it there.
+                */
+               os_memcpy(iwr.u.name, data, len);
+       } else {
+               /*
+                * Argument data too big for inline transfer; setup a
+                * parameter block instead; the kernel will transfer
+                * the data for the driver.
+                */
+               iwr.u.data.pointer = data;
+               iwr.u.data.length = len;
+       }
+
+       if (ioctl(drv->sock, op, &iwr) < 0) {
+               if (show_err) {
+#ifdef MADWIFI_NG
+                       int first = IEEE80211_IOCTL_SETPARAM;
+                       int last = IEEE80211_IOCTL_KICKMAC;
+                       static const char *opnames[] = {
+                               "ioctl[IEEE80211_IOCTL_SETPARAM]",
+                               "ioctl[IEEE80211_IOCTL_GETPARAM]",
+                               "ioctl[IEEE80211_IOCTL_SETMODE]",
+                               "ioctl[IEEE80211_IOCTL_GETMODE]",
+                               "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]",
+                               "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]",
+                               "ioctl[IEEE80211_IOCTL_SETCHANLIST]",
+                               "ioctl[IEEE80211_IOCTL_GETCHANLIST]",
+                               "ioctl[IEEE80211_IOCTL_CHANSWITCH]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]",
+                               "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_GETCHANINFO]",
+                               "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+                               "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+                               "ioctl[IEEE80211_IOCTL_SETMLME]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_SETKEY]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_DELKEY]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_ADDMAC]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_DELMAC]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_WDSMAC]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_WDSDELMAC]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_KICKMAC]",
+                       };
+#else /* MADWIFI_NG */
+                       int first = IEEE80211_IOCTL_SETPARAM;
+                       int last = IEEE80211_IOCTL_CHANLIST;
+                       static const char *opnames[] = {
+                               "ioctl[IEEE80211_IOCTL_SETPARAM]",
+                               "ioctl[IEEE80211_IOCTL_GETPARAM]",
+                               "ioctl[IEEE80211_IOCTL_SETKEY]",
+                               "ioctl[IEEE80211_IOCTL_GETKEY]",
+                               "ioctl[IEEE80211_IOCTL_DELKEY]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_SETMLME]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_SETOPTIE]",
+                               "ioctl[IEEE80211_IOCTL_GETOPTIE]",
+                               "ioctl[IEEE80211_IOCTL_ADDMAC]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_DELMAC]",
+                               NULL,
+                               "ioctl[IEEE80211_IOCTL_CHANLIST]",
+                       };
+#endif /* MADWIFI_NG */
+                       int idx = op - first;
+                       if (first <= op && op <= last &&
+                           idx < (int) (sizeof(opnames) / sizeof(opnames[0]))
+                           && opnames[idx])
+                               perror(opnames[idx]);
+                       else
+                               perror("ioctl[unknown???]");
+               }
+               return -1;
+       }
+       return 0;
+}
+
+static int
+set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg,
+             int show_err)
+{
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.mode = op;
+       os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg));
+
+       if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
+               if (show_err) 
+                       perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
+               return -1;
+       }
+       return 0;
+}
+
+static int
+wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv,
+                             const u8 *wpa_ie, size_t wpa_ie_len)
+{
+       struct iwreq iwr;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       /* NB: SETOPTIE is not fixed-size so must not be inlined */
+       iwr.u.data.pointer = (void *) wpa_ie;
+       iwr.u.data.length = wpa_ie_len;
+
+       if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) {
+               perror("ioctl[IEEE80211_IOCTL_SETOPTIE]");
+               return -1;
+       }
+       return 0;
+}
+
+static int
+wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx,
+                          const u8 *addr)
+{
+       struct ieee80211req_del_key wk;
+
+       wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx);
+       os_memset(&wk, 0, sizeof(wk));
+       wk.idk_keyix = key_idx;
+       if (addr != NULL)
+               os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+
+       return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1);
+}
+
+static int
+wpa_driver_madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+                          const u8 *addr, int key_idx, int set_tx,
+                          const u8 *seq, size_t seq_len,
+                          const u8 *key, size_t key_len)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       struct ieee80211req_key wk;
+       char *alg_name;
+       u_int8_t cipher;
+
+       if (alg == WPA_ALG_NONE)
+               return wpa_driver_madwifi_del_key(drv, key_idx, addr);
+
+       switch (alg) {
+       case WPA_ALG_WEP:
+               if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+                                             ETH_ALEN) == 0) {
+                       /*
+                        * madwifi did not seem to like static WEP key
+                        * configuration with IEEE80211_IOCTL_SETKEY, so use
+                        * Linux wireless extensions ioctl for this.
+                        */
+                       return wpa_driver_wext_set_key(ifname, drv->wext, alg,
+                                                      addr, key_idx, set_tx,
+                                                      seq, seq_len,
+                                                      key, key_len);
+               }
+               alg_name = "WEP";
+               cipher = IEEE80211_CIPHER_WEP;
+               break;
+       case WPA_ALG_TKIP:
+               alg_name = "TKIP";
+               cipher = IEEE80211_CIPHER_TKIP;
+               break;
+       case WPA_ALG_CCMP:
+               alg_name = "CCMP";
+               cipher = IEEE80211_CIPHER_AES_CCM;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d",
+                       __FUNCTION__, alg);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu "
+                  "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       if (seq_len > sizeof(u_int64_t)) {
+               wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big",
+                          __FUNCTION__, (unsigned long) seq_len);
+               return -2;
+       }
+       if (key_len > sizeof(wk.ik_keydata)) {
+               wpa_printf(MSG_DEBUG, "%s: key length %lu too big",
+                          __FUNCTION__, (unsigned long) key_len);
+               return -3;
+       }
+
+       os_memset(&wk, 0, sizeof(wk));
+       wk.ik_type = cipher;
+       wk.ik_flags = IEEE80211_KEY_RECV;
+       if (addr == NULL ||
+           os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
+               wk.ik_flags |= IEEE80211_KEY_GROUP;
+       if (set_tx) {
+               wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT;
+               os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+       } else
+               os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN);
+       wk.ik_keyix = key_idx;
+       wk.ik_keylen = key_len;
+#ifdef WORDS_BIGENDIAN
+#define WPA_KEY_RSC_LEN 8
+       {
+               size_t i;
+               u8 tmp[WPA_KEY_RSC_LEN];
+               os_memset(tmp, 0, sizeof(tmp));
+               for (i = 0; i < seq_len; i++)
+                       tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i];
+               os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN);
+       }
+#else /* WORDS_BIGENDIAN */
+       os_memcpy(&wk.ik_keyrsc, seq, seq_len);
+#endif /* WORDS_BIGENDIAN */
+       os_memcpy(wk.ik_keydata, key, key_len);
+
+       return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1);
+}
+
+static int
+wpa_driver_madwifi_set_countermeasures(void *priv, int enabled)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1);
+}
+
+static int
+wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       mlme.im_op = IEEE80211_MLME_DEAUTH;
+       mlme.im_reason = reason_code;
+       os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1);
+}
+
+static int
+wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       mlme.im_op = IEEE80211_MLME_DISASSOC;
+       mlme.im_reason = reason_code;
+       os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+       return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1);
+}
+
+static int
+wpa_driver_madwifi_associate(void *priv,
+                            struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       struct ieee80211req_mlme mlme;
+       int ret = 0, privacy = 1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED,
+                         params->drop_unencrypted, 1) < 0)
+               ret = -1;
+       if (wpa_driver_madwifi_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+
+       /*
+        * NB: Don't need to set the freq or cipher-related state as
+        *     this is implied by the bssid which is used to locate
+        *     the scanned node state which holds it.  The ssid is
+        *     needed to disambiguate an AP that broadcasts multiple
+        *     ssid's but uses the same bssid.
+        */
+       /* XXX error handling is wrong but unclear what to do... */
+       if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie,
+                                         params->wpa_ie_len) < 0)
+               ret = -1;
+
+       if (params->pairwise_suite == CIPHER_NONE &&
+           params->group_suite == CIPHER_NONE &&
+           params->key_mgmt_suite == KEY_MGMT_NONE &&
+           params->wpa_ie_len == 0)
+               privacy = 0;
+
+       if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0)
+               ret = -1;
+
+       if (params->wpa_ie_len &&
+           set80211param(drv, IEEE80211_PARAM_WPA,
+                         params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0)
+               ret = -1;
+
+       if (params->bssid == NULL) {
+               /* ap_scan=2 mode - driver takes care of AP selection and
+                * roaming */
+               /* FIX: this does not seem to work; would probably need to
+                * change something in the driver */
+               if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0)
+                       ret = -1;
+
+               if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+                                            params->ssid_len) < 0)
+                       ret = -1;
+       } else {
+               if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0)
+                       ret = -1;
+               if (wpa_driver_wext_set_ssid(drv->wext, params->ssid,
+                                            params->ssid_len) < 0)
+                       ret = -1;
+               os_memset(&mlme, 0, sizeof(mlme));
+               mlme.im_op = IEEE80211_MLME_ASSOC;
+               os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+               if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme,
+                                sizeof(mlme), 1) < 0) {
+                       wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed",
+                                  __func__);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+static int
+wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       int authmode;
+
+       if ((auth_alg & WPA_AUTH_ALG_OPEN) &&
+           (auth_alg & WPA_AUTH_ALG_SHARED))
+               authmode = IEEE80211_AUTH_AUTO;
+       else if (auth_alg & WPA_AUTH_ALG_SHARED)
+               authmode = IEEE80211_AUTH_SHARED;
+       else
+               authmode = IEEE80211_AUTH_OPEN;
+
+       return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1);
+}
+
+static int
+wpa_driver_madwifi_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+
+       wpa_driver_madwifi_set_probe_req_ie(drv, params->extra_ies,
+                                           params->extra_ies_len);
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       /* set desired ssid before scan */
+       /* FIX: scan should not break the current association, so using
+        * set_ssid may not be the best way of doing this.. */
+       if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0)
+               ret = -1;
+
+       if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) {
+               perror("ioctl[SIOCSIWSCAN]");
+               ret = -1;
+       }
+
+       /*
+        * madwifi delivers a scan complete event so no need to poll, but
+        * register a backup timeout anyway to make sure that we recover even
+        * if the driver does not send this event for any reason. This timeout
+        * will only be used if the event is not delivered (event handler will
+        * cancel the timeout).
+        */
+       eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext,
+                            drv->ctx);
+       eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext,
+                              drv->ctx);
+
+       return ret;
+}
+
+static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_madwifi_get_scan_results(void *priv)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_driver_madwifi_set_operstate(void *priv, int state)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+       return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies,
+                                              size_t ies_len)
+{
+       struct ieee80211req_getset_appiebuf *probe_req_ie;
+       int ret;
+
+       probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len);
+       if (probe_req_ie == NULL)
+               return -1;
+
+       probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ;
+       probe_req_ie->app_buflen = ies_len;
+       os_memcpy(probe_req_ie->app_buf, ies, ies_len);
+
+       ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie,
+                          sizeof(struct ieee80211req_getset_appiebuf) +
+                          ies_len, 1);
+
+       os_free(probe_req_ie);
+
+       return ret;
+}
+
+
+static void * wpa_driver_madwifi_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_madwifi_data *drv;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->wext = wpa_driver_wext_init(ctx, ifname);
+       if (drv->wext == NULL)
+               goto fail;
+
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->sock < 0)
+               goto fail2;
+
+       if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based "
+                          "roaming", __FUNCTION__);
+               goto fail3;
+       }
+
+       if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support",
+                          __FUNCTION__);
+               goto fail3;
+       }
+
+       return drv;
+
+fail3:
+       close(drv->sock);
+fail2:
+       wpa_driver_wext_deinit(drv->wext);
+fail:
+       os_free(drv);
+       return NULL;
+}
+
+
+static void wpa_driver_madwifi_deinit(void *priv)
+{
+       struct wpa_driver_madwifi_data *drv = priv;
+
+       if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE",
+                          __FUNCTION__);
+       }
+       if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based "
+                          "roaming", __FUNCTION__);
+       }
+       if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy "
+                          "flag", __FUNCTION__);
+       }
+       if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed to disable WPA",
+                          __FUNCTION__);
+       }
+
+       wpa_driver_wext_deinit(drv->wext);
+
+       close(drv->sock);
+       os_free(drv);
+}
+
+#endif /* HOSTAPD */
+
+
+const struct wpa_driver_ops wpa_driver_madwifi_ops = {
+       .name                   = "madwifi",
+       .desc                   = "MADWIFI 802.11 support (Atheros, etc.)",
+       .set_key                = wpa_driver_madwifi_set_key,
+#ifdef HOSTAPD
+       .hapd_init              = madwifi_init,
+       .hapd_deinit            = madwifi_deinit,
+       .set_ieee8021x          = madwifi_set_ieee8021x,
+       .set_privacy            = madwifi_set_privacy,
+       .get_seqnum             = madwifi_get_seqnum,
+       .flush                  = madwifi_flush,
+       .set_generic_elem       = madwifi_set_opt_ie,
+       .sta_set_flags          = madwifi_sta_set_flags,
+       .read_sta_data          = madwifi_read_sta_driver_data,
+       .hapd_send_eapol        = madwifi_send_eapol,
+       .sta_disassoc           = madwifi_sta_disassoc,
+       .sta_deauth             = madwifi_sta_deauth,
+       .hapd_set_ssid          = madwifi_set_ssid,
+       .hapd_get_ssid          = madwifi_get_ssid,
+       .hapd_set_countermeasures       = madwifi_set_countermeasures,
+       .sta_clear_stats        = madwifi_sta_clear_stats,
+       .commit                 = madwifi_commit,
+       .set_ap_wps_ie          = madwifi_set_ap_wps_ie,
+#else /* HOSTAPD */
+       .get_bssid              = wpa_driver_madwifi_get_bssid,
+       .get_ssid               = wpa_driver_madwifi_get_ssid,
+       .init                   = wpa_driver_madwifi_init,
+       .deinit                 = wpa_driver_madwifi_deinit,
+       .set_countermeasures    = wpa_driver_madwifi_set_countermeasures,
+       .scan2                  = wpa_driver_madwifi_scan,
+       .get_scan_results2      = wpa_driver_madwifi_get_scan_results,
+       .deauthenticate         = wpa_driver_madwifi_deauthenticate,
+       .disassociate           = wpa_driver_madwifi_disassociate,
+       .associate              = wpa_driver_madwifi_associate,
+       .set_operstate          = wpa_driver_madwifi_set_operstate,
+#endif /* HOSTAPD */
+};
diff --git a/src/drivers/driver_ndis.c b/src/drivers/driver_ndis.c
new file mode 100644 (file)
index 0000000..462dd81
--- /dev/null
@@ -0,0 +1,3278 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * 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.
+ */
+
+#ifdef __CYGWIN__
+/* Avoid some header file conflicts by not including standard headers for
+ * cygwin builds when Packet32.h is included. */
+#include "build_config.h"
+int close(int fd);
+#else /* __CYGWIN__ */
+#include "includes.h"
+#endif /* __CYGWIN__ */
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#else /* CONFIG_USE_NDISUIO */
+#include <Packet32.h>
+#endif /* CONFIG_USE_NDISUIO */
+#ifdef __MINGW32_VERSION
+#include <ddk/ntddndis.h>
+#else /* __MINGW32_VERSION */
+#include <ntddndis.h>
+#endif /* __MINGW32_VERSION */
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#include <devload.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_ndis.h"
+
+int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv);
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+static void wpa_driver_ndis_deinit(void *priv);
+static void wpa_driver_ndis_poll(void *drv);
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx);
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv);
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv);
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv);
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+/* FIX: to be removed once this can be compiled with the complete NDIS
+ * header files */
+#ifndef OID_802_11_BSSID
+#define OID_802_11_BSSID                       0x0d010101
+#define OID_802_11_SSID                        0x0d010102
+#define OID_802_11_INFRASTRUCTURE_MODE         0x0d010108
+#define OID_802_11_ADD_WEP                     0x0D010113
+#define OID_802_11_REMOVE_WEP                  0x0D010114
+#define OID_802_11_DISASSOCIATE                        0x0D010115
+#define OID_802_11_BSSID_LIST                  0x0d010217
+#define OID_802_11_AUTHENTICATION_MODE         0x0d010118
+#define OID_802_11_PRIVACY_FILTER              0x0d010119
+#define OID_802_11_BSSID_LIST_SCAN             0x0d01011A
+#define OID_802_11_WEP_STATUS                  0x0d01011B
+#define OID_802_11_ENCRYPTION_STATUS OID_802_11_WEP_STATUS
+#define OID_802_11_ADD_KEY                     0x0d01011D
+#define OID_802_11_REMOVE_KEY                  0x0d01011E
+#define OID_802_11_ASSOCIATION_INFORMATION     0x0d01011F
+#define OID_802_11_TEST                        0x0d010120
+#define OID_802_11_CAPABILITY                  0x0d010122
+#define OID_802_11_PMKID                       0x0d010123
+
+#define NDIS_802_11_LENGTH_SSID 32
+#define NDIS_802_11_LENGTH_RATES 8
+#define NDIS_802_11_LENGTH_RATES_EX 16
+
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_SSID {
+       ULONG SsidLength;
+       UCHAR Ssid[NDIS_802_11_LENGTH_SSID];
+} NDIS_802_11_SSID;
+
+typedef LONG NDIS_802_11_RSSI;
+
+typedef enum NDIS_802_11_NETWORK_TYPE {
+       Ndis802_11FH,
+       Ndis802_11DS,
+       Ndis802_11OFDM5,
+       Ndis802_11OFDM24,
+       Ndis802_11NetworkTypeMax
+} NDIS_802_11_NETWORK_TYPE;
+
+typedef struct NDIS_802_11_CONFIGURATION_FH {
+       ULONG Length;
+       ULONG HopPattern;
+       ULONG HopSet;
+       ULONG DwellTime;
+} NDIS_802_11_CONFIGURATION_FH;
+
+typedef struct NDIS_802_11_CONFIGURATION {
+       ULONG Length;
+       ULONG BeaconPeriod;
+       ULONG ATIMWindow;
+       ULONG DSConfig;
+       NDIS_802_11_CONFIGURATION_FH FHConfig;
+} NDIS_802_11_CONFIGURATION;
+
+typedef enum NDIS_802_11_NETWORK_INFRASTRUCTURE {
+       Ndis802_11IBSS,
+       Ndis802_11Infrastructure,
+       Ndis802_11AutoUnknown,
+       Ndis802_11InfrastructureMax
+} NDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+typedef enum NDIS_802_11_AUTHENTICATION_MODE {
+       Ndis802_11AuthModeOpen,
+       Ndis802_11AuthModeShared,
+       Ndis802_11AuthModeAutoSwitch,
+       Ndis802_11AuthModeWPA,
+       Ndis802_11AuthModeWPAPSK,
+       Ndis802_11AuthModeWPANone,
+       Ndis802_11AuthModeWPA2,
+       Ndis802_11AuthModeWPA2PSK,
+       Ndis802_11AuthModeMax
+} NDIS_802_11_AUTHENTICATION_MODE;
+
+typedef enum NDIS_802_11_WEP_STATUS {
+       Ndis802_11WEPEnabled,
+       Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+       Ndis802_11WEPDisabled,
+       Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+       Ndis802_11WEPKeyAbsent,
+       Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+       Ndis802_11WEPNotSupported,
+       Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+       Ndis802_11Encryption2Enabled,
+       Ndis802_11Encryption2KeyAbsent,
+       Ndis802_11Encryption3Enabled,
+       Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, NDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum NDIS_802_11_PRIVACY_FILTER {
+       Ndis802_11PrivFilterAcceptAll,
+       Ndis802_11PrivFilter8021xWEP
+} NDIS_802_11_PRIVACY_FILTER;
+
+typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];
+typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];
+
+typedef struct NDIS_WLAN_BSSID_EX {
+       ULONG Length;
+       NDIS_802_11_MAC_ADDRESS MacAddress; /* BSSID */
+       UCHAR Reserved[2];
+       NDIS_802_11_SSID Ssid;
+       ULONG Privacy;
+       NDIS_802_11_RSSI Rssi;
+       NDIS_802_11_NETWORK_TYPE NetworkTypeInUse;
+       NDIS_802_11_CONFIGURATION Configuration;
+       NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode;
+       NDIS_802_11_RATES_EX SupportedRates;
+       ULONG IELength;
+       UCHAR IEs[1];
+} NDIS_WLAN_BSSID_EX;
+
+typedef struct NDIS_802_11_BSSID_LIST_EX {
+       ULONG NumberOfItems;
+       NDIS_WLAN_BSSID_EX Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX;
+
+typedef struct NDIS_802_11_FIXED_IEs {
+       UCHAR Timestamp[8];
+       USHORT BeaconInterval;
+       USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs;
+
+typedef struct NDIS_802_11_WEP {
+       ULONG Length;
+       ULONG KeyIndex;
+       ULONG KeyLength;
+       UCHAR KeyMaterial[1];
+} NDIS_802_11_WEP;
+
+typedef ULONG NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+
+typedef struct NDIS_802_11_KEY {
+       ULONG Length;
+       ULONG KeyIndex;
+       ULONG KeyLength;
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       NDIS_802_11_KEY_RSC KeyRSC;
+       UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_REMOVE_KEY {
+       ULONG Length;
+       ULONG KeyIndex;
+       NDIS_802_11_MAC_ADDRESS BSSID;
+} NDIS_802_11_REMOVE_KEY;
+
+typedef struct NDIS_802_11_AI_REQFI {
+       USHORT Capabilities;
+       USHORT ListenInterval;
+       NDIS_802_11_MAC_ADDRESS CurrentAPAddress;
+} NDIS_802_11_AI_REQFI;
+
+typedef struct NDIS_802_11_AI_RESFI {
+       USHORT Capabilities;
+       USHORT StatusCode;
+       USHORT AssociationId;
+} NDIS_802_11_AI_RESFI;
+
+typedef struct NDIS_802_11_ASSOCIATION_INFORMATION {
+       ULONG Length;
+       USHORT AvailableRequestFixedIEs;
+       NDIS_802_11_AI_REQFI RequestFixedIEs;
+       ULONG RequestIELength;
+       ULONG OffsetRequestIEs;
+       USHORT AvailableResponseFixedIEs;
+       NDIS_802_11_AI_RESFI ResponseFixedIEs;
+       ULONG ResponseIELength;
+       ULONG OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION;
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+       NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+       NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+       ULONG Length;
+       ULONG Version;
+       ULONG NoOfPMKIDs;
+       ULONG NoOfAuthEncryptPairsSupported;
+       NDIS_802_11_AUTHENTICATION_ENCRYPTION
+               AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+       ULONG Length;
+       ULONG BSSIDInfoCount;
+       BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef enum NDIS_802_11_STATUS_TYPE {
+       Ndis802_11StatusType_Authentication,
+       Ndis802_11StatusType_PMKID_CandidateList = 2,
+       Ndis802_11StatusTypeMax
+} NDIS_802_11_STATUS_TYPE;
+
+typedef struct NDIS_802_11_STATUS_INDICATION {
+       NDIS_802_11_STATUS_TYPE StatusType;
+} NDIS_802_11_STATUS_INDICATION;
+
+typedef struct PMKID_CANDIDATE {
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+       ULONG Version;
+       ULONG NumCandidates;
+       PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+typedef struct NDIS_802_11_AUTHENTICATION_REQUEST {
+       ULONG Length;
+       NDIS_802_11_MAC_ADDRESS Bssid;
+       ULONG Flags;
+} NDIS_802_11_AUTHENTICATION_REQUEST;
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH                        0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE             0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR                0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR           0x0E
+
+#endif /* OID_802_11_BSSID */
+
+
+#ifndef OID_802_11_PMKID
+/* Platform SDK for XP did not include WPA2, so add needed definitions */
+
+#define OID_802_11_CAPABILITY                  0x0d010122
+#define OID_802_11_PMKID                       0x0d010123
+
+#define Ndis802_11AuthModeWPA2 6
+#define Ndis802_11AuthModeWPA2PSK 7
+
+#define Ndis802_11StatusType_PMKID_CandidateList 2
+
+typedef struct NDIS_802_11_AUTHENTICATION_ENCRYPTION {
+       NDIS_802_11_AUTHENTICATION_MODE AuthModeSupported;
+       NDIS_802_11_ENCRYPTION_STATUS EncryptStatusSupported;
+} NDIS_802_11_AUTHENTICATION_ENCRYPTION;
+
+typedef struct NDIS_802_11_CAPABILITY {
+       ULONG Length;
+       ULONG Version;
+       ULONG NoOfPMKIDs;
+       ULONG NoOfAuthEncryptPairsSupported;
+       NDIS_802_11_AUTHENTICATION_ENCRYPTION
+               AuthenticationEncryptionSupported[1];
+} NDIS_802_11_CAPABILITY;
+
+typedef UCHAR NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct BSSID_INFO {
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO;
+
+typedef struct NDIS_802_11_PMKID {
+       ULONG Length;
+       ULONG BSSIDInfoCount;
+       BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID;
+
+typedef struct PMKID_CANDIDATE {
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       ULONG Flags;
+} PMKID_CANDIDATE;
+
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01
+
+typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST {
+       ULONG Version;
+       ULONG NumCandidates;
+       PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST;
+
+#endif /* OID_802_11_CAPABILITY */
+
+
+#ifndef OID_DOT11_CURRENT_OPERATION_MODE
+/* Native 802.11 OIDs */
+#define OID_DOT11_NDIS_START 0x0D010300
+#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8)
+#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11)
+
+typedef enum _DOT11_BSS_TYPE {
+       dot11_BSS_type_infrastructure = 1,
+       dot11_BSS_type_independent = 2,
+       dot11_BSS_type_any = 3
+} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE;
+
+typedef UCHAR DOT11_MAC_ADDRESS[6];
+typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS;
+
+typedef enum _DOT11_SCAN_TYPE {
+       dot11_scan_type_active = 1,
+       dot11_scan_type_passive = 2,
+       dot11_scan_type_auto = 3,
+       dot11_scan_type_forced = 0x80000000
+} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE;
+
+typedef struct _DOT11_SCAN_REQUEST_V2 {
+       DOT11_BSS_TYPE dot11BSSType;
+       DOT11_MAC_ADDRESS dot11BSSID;
+       DOT11_SCAN_TYPE dot11ScanType;
+       BOOLEAN bRestrictedScan;
+       ULONG udot11SSIDsOffset;
+       ULONG uNumOfdot11SSIDs;
+       BOOLEAN bUseRequestIE;
+       ULONG uRequestIDsOffset;
+       ULONG uNumOfRequestIDs;
+       ULONG uPhyTypeInfosOffset;
+       ULONG uNumOfPhyTypeInfos;
+       ULONG uIEsOffset;
+       ULONG uIEsLength;
+       UCHAR ucBuffer[1];
+} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2;
+
+#endif /* OID_DOT11_CURRENT_OPERATION_MODE */
+
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#ifdef __MINGW32_VERSION
+typedef ULONG NDIS_OID;
+#endif /* __MINGW32_VERSION */
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+       CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_OPEN_DEVICE \
+       _NDISUIO_CTL_CODE(0x200, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_OID_VALUE \
+       _NDISUIO_CTL_CODE(0x201, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_OID_VALUE \
+       _NDISUIO_CTL_CODE(0x205, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+       _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+       _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+       _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_OID
+{
+    NDIS_OID Oid;
+    UCHAR Data[sizeof(ULONG)];
+} NDISUIO_QUERY_OID, *PNDISUIO_QUERY_OID;
+
+typedef struct _NDISUIO_SET_OID
+{
+    NDIS_OID Oid;
+    UCHAR Data[sizeof(ULONG)];
+} NDISUIO_SET_OID, *PNDISUIO_SET_OID;
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+       ULONG BindingIndex;
+       ULONG DeviceNameOffset;
+       ULONG DeviceNameLength;
+       ULONG DeviceDescrOffset;
+       ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int ndis_get_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+                       char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+       NDISUIO_QUERY_OID *o;
+       size_t buflen = sizeof(*o) + len;
+       DWORD written;
+       int ret;
+       size_t hdrlen;
+
+       o = os_zalloc(buflen);
+       if (o == NULL)
+               return -1;
+       o->Oid = oid;
+#ifdef _WIN32_WCE
+       o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_OID_VALUE,
+                            o, sizeof(NDISUIO_QUERY_OID), o, buflen, &written,
+                            NULL)) {
+               wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_QUERY_OID_VALUE "
+                          "failed (oid=%08x): %d", oid, (int) GetLastError());
+               os_free(o);
+               return -1;
+       }
+       hdrlen = sizeof(NDISUIO_QUERY_OID) - sizeof(o->Data);
+       if (written < hdrlen) {
+               wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d); "
+                          "too short", oid, (unsigned int) written);
+               os_free(o);
+               return -1;
+       }
+       written -= hdrlen;
+       if (written > len) {
+               wpa_printf(MSG_DEBUG, "NDIS: query oid=%08x written (%d) > "
+                          "len (%d)",oid, (unsigned int) written, len);
+               os_free(o);
+               return -1;
+       }
+       os_memcpy(data, o->Data, written);
+       ret = written;
+       os_free(o);
+       return ret;
+#else /* CONFIG_USE_NDISUIO */
+       char *buf;
+       PACKET_OID_DATA *o;
+       int ret;
+
+       buf = os_zalloc(sizeof(*o) + len);
+       if (buf == NULL)
+               return -1;
+       o = (PACKET_OID_DATA *) buf;
+       o->Oid = oid;
+       o->Length = len;
+
+       if (!PacketRequest(drv->adapter, FALSE, o)) {
+               wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+                          __func__, oid, len);
+               os_free(buf);
+               return -1;
+       }
+       if (o->Length > len) {
+               wpa_printf(MSG_DEBUG, "%s: oid=0x%x Length (%d) > len (%d)",
+                          __func__, oid, (unsigned int) o->Length, len);
+               os_free(buf);
+               return -1;
+       }
+       os_memcpy(data, o->Data, o->Length);
+       ret = o->Length;
+       os_free(buf);
+       return ret;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_oid(struct wpa_driver_ndis_data *drv, unsigned int oid,
+                       const char *data, size_t len)
+{
+#ifdef CONFIG_USE_NDISUIO
+       NDISUIO_SET_OID *o;
+       size_t buflen, reallen;
+       DWORD written;
+       char txt[50];
+
+       os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+       wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+       buflen = sizeof(*o) + len;
+       reallen = buflen - sizeof(o->Data);
+       o = os_zalloc(buflen);
+       if (o == NULL)
+               return -1;
+       o->Oid = oid;
+#ifdef _WIN32_WCE
+       o->ptcDeviceName = drv->adapter_name;
+#endif /* _WIN32_WCE */
+       if (data)
+               os_memcpy(o->Data, data, len);
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_SET_OID_VALUE,
+                            o, reallen, NULL, 0, &written, NULL)) {
+               wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDISUIO_SET_OID_VALUE "
+                          "(oid=%08x) failed: %d", oid, (int) GetLastError());
+               os_free(o);
+               return -1;
+       }
+       os_free(o);
+       return 0;
+#else /* CONFIG_USE_NDISUIO */
+       char *buf;
+       PACKET_OID_DATA *o;
+       char txt[50];
+
+       os_snprintf(txt, sizeof(txt), "NDIS: Set OID %08x", oid);
+       wpa_hexdump_key(MSG_MSGDUMP, txt, (const u8 *) data, len);
+
+       buf = os_zalloc(sizeof(*o) + len);
+       if (buf == NULL)
+               return -1;
+       o = (PACKET_OID_DATA *) buf;
+       o->Oid = oid;
+       o->Length = len;
+       if (data)
+               os_memcpy(o->Data, data, len);
+
+       if (!PacketRequest(drv->adapter, TRUE, o)) {
+               wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+                          __func__, oid, len);
+               os_free(buf);
+               return -1;
+       }
+       os_free(buf);
+       return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_set_auth_mode(struct wpa_driver_ndis_data *drv, int mode)
+{
+       u32 auth_mode = mode;
+       if (ndis_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+                        (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+                          "OID_802_11_AUTHENTICATION_MODE (%d)",
+                          (int) auth_mode);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int ndis_get_auth_mode(struct wpa_driver_ndis_data *drv)
+{
+       u32 auth_mode;
+       int res;
+       res = ndis_get_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+                          (char *) &auth_mode, sizeof(auth_mode));
+       if (res != sizeof(auth_mode)) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+                          "OID_802_11_AUTHENTICATION_MODE");
+               return -1;
+       }
+       return auth_mode;
+}
+
+
+static int ndis_set_encr_status(struct wpa_driver_ndis_data *drv, int encr)
+{
+       u32 encr_status = encr;
+       if (ndis_set_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+                        (char *) &encr_status, sizeof(encr_status)) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+                          "OID_802_11_ENCRYPTION_STATUS (%d)", encr);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int ndis_get_encr_status(struct wpa_driver_ndis_data *drv)
+{
+       u32 encr;
+       int res;
+       res = ndis_get_oid(drv, OID_802_11_ENCRYPTION_STATUS,
+                          (char *) &encr, sizeof(encr));
+       if (res != sizeof(encr)) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to get "
+                          "OID_802_11_ENCRYPTION_STATUS");
+               return -1;
+       }
+       return encr;
+}
+
+
+static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+
+       if (drv->wired) {
+               /*
+                * Report PAE group address as the "BSSID" for wired
+                * connection.
+                */
+               os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+               return 0;
+       }
+
+       return ndis_get_oid(drv, OID_802_11_BSSID, (char *) bssid, ETH_ALEN) <
+               0 ? -1 : 0;
+}
+
+
+static int wpa_driver_ndis_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       NDIS_802_11_SSID buf;
+       int res;
+
+       res = ndis_get_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+       if (res < 4) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to get SSID");
+               if (drv->wired) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Allow get_ssid failure "
+                                  "with a wired interface");
+                       return 0;
+               }
+               return -1;
+       }
+       os_memcpy(ssid, buf.Ssid, buf.SsidLength);
+       return buf.SsidLength;
+}
+
+
+static int wpa_driver_ndis_set_ssid(struct wpa_driver_ndis_data *drv,
+                                   const u8 *ssid, size_t ssid_len)
+{
+       NDIS_802_11_SSID buf;
+
+       os_memset(&buf, 0, sizeof(buf));
+       buf.SsidLength = ssid_len;
+       os_memcpy(buf.Ssid, ssid, ssid_len);
+       /*
+        * Make sure radio is marked enabled here so that scan request will not
+        * force SSID to be changed to a random one in order to enable radio at
+        * that point.
+        */
+       drv->radio_enabled = 1;
+       return ndis_set_oid(drv, OID_802_11_SSID, (char *) &buf, sizeof(buf));
+}
+
+
+/* Disconnect using OID_802_11_DISASSOCIATE. This will also turn the radio off.
+ */
+static int wpa_driver_ndis_radio_off(struct wpa_driver_ndis_data *drv)
+{
+       drv->radio_enabled = 0;
+       return ndis_set_oid(drv, OID_802_11_DISASSOCIATE, "    ", 4);
+}
+
+
+/* Disconnect by setting SSID to random (i.e., likely not used). */
+static int wpa_driver_ndis_disconnect(struct wpa_driver_ndis_data *drv)
+{
+       char ssid[32];
+       int i;
+       for (i = 0; i < 32; i++)
+               ssid[i] = rand() & 0xff;
+       return wpa_driver_ndis_set_ssid(drv, (u8 *) ssid, 32);
+}
+
+
+static int wpa_driver_ndis_deauthenticate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr,
+                                       int reason_code)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       return wpa_driver_ndis_disconnect(drv);
+}
+
+
+static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_ndis_scan_native80211(
+       struct wpa_driver_ndis_data *drv,
+       struct wpa_driver_scan_params *params)
+{
+       DOT11_SCAN_REQUEST_V2 req;
+       int res;
+
+       os_memset(&req, 0, sizeof(req));
+       req.dot11BSSType = dot11_BSS_type_any;
+       os_memset(req.dot11BSSID, 0xff, ETH_ALEN);
+       req.dot11ScanType = dot11_scan_type_auto;
+       res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req,
+                          sizeof(req));
+       eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+                              drv->ctx);
+       return res;
+}
+
+
+static int wpa_driver_ndis_scan(void *priv,
+                               struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       int res;
+
+       if (drv->native80211)
+               return wpa_driver_ndis_scan_native80211(drv, params);
+
+       if (!drv->radio_enabled) {
+               wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first"
+                          " scan");
+               if (wpa_driver_ndis_disconnect(drv) < 0) {
+                       wpa_printf(MSG_DEBUG, "NDIS: failed to enable radio");
+               }
+               drv->radio_enabled = 1;
+       }
+
+       res = ndis_set_oid(drv, OID_802_11_BSSID_LIST_SCAN, "    ", 4);
+       eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv,
+                              drv->ctx);
+       return res;
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+       const u8 *end, *pos;
+
+       pos = (const u8 *) (res + 1);
+       end = pos + res->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid(
+       struct wpa_scan_res *r, NDIS_802_11_SSID *ssid)
+{
+       struct wpa_scan_res *nr;
+       u8 *pos;
+
+       if (wpa_scan_get_ie(r, WLAN_EID_SSID))
+               return r; /* SSID IE already present */
+
+       if (ssid->SsidLength == 0 || ssid->SsidLength > 32)
+               return r; /* No valid SSID inside scan data */
+
+       nr = os_realloc(r, sizeof(*r) + r->ie_len + 2 + ssid->SsidLength);
+       if (nr == NULL)
+               return r;
+
+       pos = ((u8 *) (nr + 1)) + nr->ie_len;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ssid->SsidLength;
+       os_memcpy(pos, ssid->Ssid, ssid->SsidLength);
+       nr->ie_len += 2 + ssid->SsidLength;
+
+       return nr;
+}
+
+
+static struct wpa_scan_results * wpa_driver_ndis_get_scan_results(void *priv)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       NDIS_802_11_BSSID_LIST_EX *b;
+       size_t blen, count, i;
+       int len;
+       char *pos;
+       struct wpa_scan_results *results;
+       struct wpa_scan_res *r;
+
+       blen = 65535;
+       b = os_zalloc(blen);
+       if (b == NULL)
+               return NULL;
+       len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+       if (len < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+               os_free(b);
+               return NULL;
+       }
+       count = b->NumberOfItems;
+
+       results = os_zalloc(sizeof(*results));
+       if (results == NULL) {
+               os_free(b);
+               return NULL;
+       }
+       results->res = os_zalloc(count * sizeof(struct wpa_scan_res *));
+       if (results->res == NULL) {
+               os_free(results);
+               os_free(b);
+               return NULL;
+       }
+
+       pos = (char *) &b->Bssid[0];
+       for (i = 0; i < count; i++) {
+               NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+               NDIS_802_11_FIXED_IEs *fixed;
+
+               if (bss->IELength < sizeof(NDIS_802_11_FIXED_IEs)) {
+                       wpa_printf(MSG_DEBUG, "NDIS: too small IELength=%d",
+                                  (int) bss->IELength);
+                       break;
+               }
+               if (((char *) bss->IEs) + bss->IELength  > (char *) b + blen) {
+                       /*
+                        * Some NDIS drivers have been reported to include an
+                        * entry with an invalid IELength in scan results and
+                        * this has crashed wpa_supplicant, so validate the
+                        * returned value before using it.
+                        */
+                       wpa_printf(MSG_DEBUG, "NDIS: skipped invalid scan "
+                                  "result IE (BSSID=" MACSTR ") IELength=%d",
+                                  MAC2STR(bss->MacAddress),
+                                  (int) bss->IELength);
+                       break;
+               }
+
+               r = os_zalloc(sizeof(*r) + bss->IELength -
+                             sizeof(NDIS_802_11_FIXED_IEs));
+               if (r == NULL)
+                       break;
+
+               os_memcpy(r->bssid, bss->MacAddress, ETH_ALEN);
+               r->level = (int) bss->Rssi;
+               r->freq = bss->Configuration.DSConfig / 1000;
+               fixed = (NDIS_802_11_FIXED_IEs *) bss->IEs;
+               r->beacon_int = WPA_GET_LE16((u8 *) &fixed->BeaconInterval);
+               r->caps = WPA_GET_LE16((u8 *) &fixed->Capabilities);
+               r->tsf = WPA_GET_LE64(fixed->Timestamp);
+               os_memcpy(r + 1, bss->IEs + sizeof(NDIS_802_11_FIXED_IEs),
+                         bss->IELength - sizeof(NDIS_802_11_FIXED_IEs));
+               r->ie_len = bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+               r = wpa_driver_ndis_add_scan_ssid(r, &bss->Ssid);
+
+               results->res[results->num++] = r;
+
+               pos += bss->Length;
+               if (pos > (char *) b + blen)
+                       break;
+       }
+
+       os_free(b);
+
+       return results;
+}
+
+
+static int wpa_driver_ndis_remove_key(struct wpa_driver_ndis_data *drv,
+                                     int key_idx, const u8 *addr,
+                                     const u8 *bssid, int pairwise)
+{
+       NDIS_802_11_REMOVE_KEY rkey;
+       NDIS_802_11_KEY_INDEX index;
+       int res, res2;
+
+       os_memset(&rkey, 0, sizeof(rkey));
+
+       rkey.Length = sizeof(rkey);
+       rkey.KeyIndex = key_idx;
+       if (pairwise)
+               rkey.KeyIndex |= 1 << 30;
+       os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+       res = ndis_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+                          sizeof(rkey));
+       if (!pairwise) {
+               index = key_idx;
+               res2 = ndis_set_oid(drv, OID_802_11_REMOVE_WEP,
+                                   (char *) &index, sizeof(index));
+       } else
+               res2 = 0;
+
+       if (res < 0 && res2 < 0)
+               return -1;
+       return 0;
+}
+
+
+static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv,
+                                  int pairwise, int key_idx, int set_tx,
+                                  const u8 *key, size_t key_len)
+{
+       NDIS_802_11_WEP *wep;
+       size_t len;
+       int res;
+
+       len = 12 + key_len;
+       wep = os_zalloc(len);
+       if (wep == NULL)
+               return -1;
+       wep->Length = len;
+       wep->KeyIndex = key_idx;
+       if (set_tx)
+               wep->KeyIndex |= 1 << 31;
+#if 0 /* Setting bit30 does not seem to work with some NDIS drivers */
+       if (pairwise)
+               wep->KeyIndex |= 1 << 30;
+#endif
+       wep->KeyLength = key_len;
+       os_memcpy(wep->KeyMaterial, key, key_len);
+
+       wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_WEP",
+                       (u8 *) wep, len);
+       res = ndis_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+       os_free(wep);
+
+       return res;
+}
+
+
+static int wpa_driver_ndis_set_key(const char *ifname, void *priv,
+                                  enum wpa_alg alg, const u8 *addr,
+                                  int key_idx, int set_tx,
+                                  const u8 *seq, size_t seq_len,
+                                  const u8 *key, size_t key_len)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       size_t len, i;
+       NDIS_802_11_KEY *nkey;
+       int res, pairwise;
+       u8 bssid[ETH_ALEN];
+
+       if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+                                     ETH_ALEN) == 0) {
+               /* Group Key */
+               pairwise = 0;
+               if (wpa_driver_ndis_get_bssid(drv, bssid) < 0)
+                       os_memset(bssid, 0xff, ETH_ALEN);
+       } else {
+               /* Pairwise Key */
+               pairwise = 1;
+               os_memcpy(bssid, addr, ETH_ALEN);
+       }
+
+       if (alg == WPA_ALG_NONE || key_len == 0) {
+               return wpa_driver_ndis_remove_key(drv, key_idx, addr, bssid,
+                                                 pairwise);
+       }
+
+       if (alg == WPA_ALG_WEP) {
+               return wpa_driver_ndis_add_wep(drv, pairwise, key_idx, set_tx,
+                                              key, key_len);
+       }
+
+       len = 12 + 6 + 6 + 8 + key_len;
+
+       nkey = os_zalloc(len);
+       if (nkey == NULL)
+               return -1;
+
+       nkey->Length = len;
+       nkey->KeyIndex = key_idx;
+       if (set_tx)
+               nkey->KeyIndex |= 1 << 31;
+       if (pairwise)
+               nkey->KeyIndex |= 1 << 30;
+       if (seq && seq_len)
+               nkey->KeyIndex |= 1 << 29;
+       nkey->KeyLength = key_len;
+       os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+       if (seq && seq_len) {
+               for (i = 0; i < seq_len; i++)
+                       nkey->KeyRSC |= (ULONGLONG) seq[i] << (i * 8);
+       }
+       if (alg == WPA_ALG_TKIP && key_len == 32) {
+               os_memcpy(nkey->KeyMaterial, key, 16);
+               os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+               os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+       } else {
+               os_memcpy(nkey->KeyMaterial, key, key_len);
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, "NDIS: OID_802_11_ADD_KEY",
+                       (u8 *) nkey, len);
+       res = ndis_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+       os_free(nkey);
+
+       return res;
+}
+
+
+static int
+wpa_driver_ndis_associate(void *priv,
+                         struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       u32 auth_mode, encr, priv_mode, mode;
+
+       drv->mode = params->mode;
+
+       /* Note: Setting OID_802_11_INFRASTRUCTURE_MODE clears current keys,
+        * so static WEP keys needs to be set again after this. */
+       if (params->mode == IEEE80211_MODE_IBSS) {
+               mode = Ndis802_11IBSS;
+               /* Need to make sure that BSSID polling is enabled for
+                * IBSS mode. */
+               eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+               eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+                                      drv, NULL);
+       } else
+               mode = Ndis802_11Infrastructure;
+       if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+                        (char *) &mode, sizeof(mode)) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+                          "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+                          (int) mode);
+               /* Try to continue anyway */
+       }
+
+       if (params->key_mgmt_suite == KEY_MGMT_NONE ||
+           params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+               /* Re-set WEP keys if static WEP configuration is used. */
+               u8 bcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+               int i;
+               for (i = 0; i < 4; i++) {
+                       if (!params->wep_key[i])
+                               continue;
+                       wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP "
+                                  "key %d", i);
+                       wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP,
+                                               bcast, i,
+                                               i == params->wep_tx_keyidx,
+                                               NULL, 0, params->wep_key[i],
+                                               params->wep_key_len[i]);
+               }
+       }
+
+       if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+               if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
+                       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+                               auth_mode = Ndis802_11AuthModeAutoSwitch;
+                       else
+                               auth_mode = Ndis802_11AuthModeShared;
+               } else
+                       auth_mode = Ndis802_11AuthModeOpen;
+               priv_mode = Ndis802_11PrivFilterAcceptAll;
+       } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+               priv_mode = Ndis802_11PrivFilter8021xWEP;
+               if (params->key_mgmt_suite == KEY_MGMT_PSK)
+                       auth_mode = Ndis802_11AuthModeWPA2PSK;
+               else
+                       auth_mode = Ndis802_11AuthModeWPA2;
+#ifdef CONFIG_WPS
+       } else if (params->key_mgmt_suite == KEY_MGMT_WPS) {
+               auth_mode = Ndis802_11AuthModeOpen;
+               priv_mode = Ndis802_11PrivFilterAcceptAll;
+#endif /* CONFIG_WPS */
+       } else {
+               priv_mode = Ndis802_11PrivFilter8021xWEP;
+               if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE)
+                       auth_mode = Ndis802_11AuthModeWPANone;
+               else if (params->key_mgmt_suite == KEY_MGMT_PSK)
+                       auth_mode = Ndis802_11AuthModeWPAPSK;
+               else
+                       auth_mode = Ndis802_11AuthModeWPA;
+       }
+
+       switch (params->pairwise_suite) {
+       case CIPHER_CCMP:
+               encr = Ndis802_11Encryption3Enabled;
+               break;
+       case CIPHER_TKIP:
+               encr = Ndis802_11Encryption2Enabled;
+               break;
+       case CIPHER_WEP40:
+       case CIPHER_WEP104:
+               encr = Ndis802_11Encryption1Enabled;
+               break;
+       case CIPHER_NONE:
+               if (params->group_suite == CIPHER_CCMP)
+                       encr = Ndis802_11Encryption3Enabled;
+               else if (params->group_suite == CIPHER_TKIP)
+                       encr = Ndis802_11Encryption2Enabled;
+               else
+                       encr = Ndis802_11EncryptionDisabled;
+               break;
+       default:
+               encr = Ndis802_11EncryptionDisabled;
+       };
+
+       if (ndis_set_oid(drv, OID_802_11_PRIVACY_FILTER,
+                        (char *) &priv_mode, sizeof(priv_mode)) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+                          "OID_802_11_PRIVACY_FILTER (%d)",
+                          (int) priv_mode);
+               /* Try to continue anyway */
+       }
+
+       ndis_set_auth_mode(drv, auth_mode);
+       ndis_set_encr_status(drv, encr);
+
+       if (params->bssid) {
+               ndis_set_oid(drv, OID_802_11_BSSID, (char *) params->bssid,
+                            ETH_ALEN);
+               drv->oid_bssid_set = 1;
+       } else if (drv->oid_bssid_set) {
+               ndis_set_oid(drv, OID_802_11_BSSID, "\xff\xff\xff\xff\xff\xff",
+                            ETH_ALEN);
+               drv->oid_bssid_set = 0;
+       }
+
+       return wpa_driver_ndis_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+
+static int wpa_driver_ndis_set_pmkid(struct wpa_driver_ndis_data *drv)
+{
+       int len, count, i, ret;
+       struct ndis_pmkid_entry *entry;
+       NDIS_802_11_PMKID *p;
+
+       count = 0;
+       entry = drv->pmkid;
+       while (entry) {
+               count++;
+               if (count >= drv->no_of_pmkid)
+                       break;
+               entry = entry->next;
+       }
+       len = 8 + count * sizeof(BSSID_INFO);
+       p = os_zalloc(len);
+       if (p == NULL)
+               return -1;
+
+       p->Length = len;
+       p->BSSIDInfoCount = count;
+       entry = drv->pmkid;
+       for (i = 0; i < count; i++) {
+               os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+               os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+               entry = entry->next;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", (u8 *) p, len);
+       ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+       os_free(p);
+       return ret;
+}
+
+
+static int wpa_driver_ndis_add_pmkid(void *priv, const u8 *bssid,
+                                    const u8 *pmkid)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       struct ndis_pmkid_entry *entry, *prev;
+
+       if (drv->no_of_pmkid == 0)
+               return 0;
+
+       prev = NULL;
+       entry = drv->pmkid;
+       while (entry) {
+               if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+                       break;
+               prev = entry;
+               entry = entry->next;
+       }
+
+       if (entry) {
+               /* Replace existing entry for this BSSID and move it into the
+                * beginning of the list. */
+               os_memcpy(entry->pmkid, pmkid, 16);
+               if (prev) {
+                       prev->next = entry->next;
+                       entry->next = drv->pmkid;
+                       drv->pmkid = entry;
+               }
+       } else {
+               entry = os_malloc(sizeof(*entry));
+               if (entry) {
+                       os_memcpy(entry->bssid, bssid, ETH_ALEN);
+                       os_memcpy(entry->pmkid, pmkid, 16);
+                       entry->next = drv->pmkid;
+                       drv->pmkid = entry;
+               }
+       }
+
+       return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_remove_pmkid(void *priv, const u8 *bssid,
+                                       const u8 *pmkid)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       struct ndis_pmkid_entry *entry, *prev;
+
+       if (drv->no_of_pmkid == 0)
+               return 0;
+
+       entry = drv->pmkid;
+       prev = NULL;
+       while (entry) {
+               if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+                   os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               drv->pmkid = entry->next;
+                       os_free(entry);
+                       break;
+               }
+               prev = entry;
+               entry = entry->next;
+       }
+       return wpa_driver_ndis_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ndis_flush_pmkid(void *priv)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       NDIS_802_11_PMKID p;
+       struct ndis_pmkid_entry *pmkid, *prev;
+       int prev_authmode, ret;
+
+       if (drv->no_of_pmkid == 0)
+               return 0;
+
+       pmkid = drv->pmkid;
+       drv->pmkid = NULL;
+       while (pmkid) {
+               prev = pmkid;
+               pmkid = pmkid->next;
+               os_free(prev);
+       }
+
+       /*
+        * Some drivers may refuse OID_802_11_PMKID if authMode is not set to
+        * WPA2, so change authMode temporarily, if needed.
+        */
+       prev_authmode = ndis_get_auth_mode(drv);
+       if (prev_authmode != Ndis802_11AuthModeWPA2)
+               ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA2);
+
+       os_memset(&p, 0, sizeof(p));
+       p.Length = 8;
+       p.BSSIDInfoCount = 0;
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+                   (u8 *) &p, 8);
+       ret = ndis_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+
+       if (prev_authmode != Ndis802_11AuthModeWPA2)
+               ndis_set_auth_mode(drv, prev_authmode);
+
+       return ret;
+}
+
+
+static int wpa_driver_ndis_get_associnfo(struct wpa_driver_ndis_data *drv)
+{
+       char buf[512], *pos;
+       NDIS_802_11_ASSOCIATION_INFORMATION *ai;
+       int len;
+       union wpa_event_data data;
+       NDIS_802_11_BSSID_LIST_EX *b;
+       size_t blen, i;
+
+       len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION, buf,
+                          sizeof(buf));
+       if (len < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: failed to get association "
+                          "information");
+               return -1;
+       }
+       if (len > sizeof(buf)) {
+               /* Some drivers seem to be producing incorrect length for this
+                * data. Limit the length to the current buffer size to avoid
+                * crashing in hexdump. The data seems to be otherwise valid,
+                * so better try to use it. */
+               wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association "
+                          "information length %d", len);
+               len = ndis_get_oid(drv, OID_802_11_ASSOCIATION_INFORMATION,
+                                  buf, sizeof(buf));
+               if (len < -1) {
+                       wpa_printf(MSG_DEBUG, "NDIS: re-reading association "
+                                  "information failed");
+                       return -1;
+               }
+               if (len > sizeof(buf)) {
+                       wpa_printf(MSG_DEBUG, "NDIS: ignored bogus association"
+                                  " information length %d (re-read)", len);
+                       len = sizeof(buf);
+               }
+       }
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: association information",
+                   (u8 *) buf, len);
+       if (len < sizeof(*ai)) {
+               wpa_printf(MSG_DEBUG, "NDIS: too short association "
+                          "information");
+               return -1;
+       }
+       ai = (NDIS_802_11_ASSOCIATION_INFORMATION *) buf;
+       wpa_printf(MSG_DEBUG, "NDIS: ReqFixed=0x%x RespFixed=0x%x off_req=%d "
+                  "off_resp=%d len_req=%d len_resp=%d",
+                  ai->AvailableRequestFixedIEs, ai->AvailableResponseFixedIEs,
+                  (int) ai->OffsetRequestIEs, (int) ai->OffsetResponseIEs,
+                  (int) ai->RequestIELength, (int) ai->ResponseIELength);
+
+       if (ai->OffsetRequestIEs + ai->RequestIELength > (unsigned) len ||
+           ai->OffsetResponseIEs + ai->ResponseIELength > (unsigned) len) {
+               wpa_printf(MSG_DEBUG, "NDIS: association information - "
+                          "IE overflow");
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: Request IEs",
+                   (u8 *) buf + ai->OffsetRequestIEs, ai->RequestIELength);
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: Response IEs",
+                   (u8 *) buf + ai->OffsetResponseIEs, ai->ResponseIELength);
+
+       os_memset(&data, 0, sizeof(data));
+       data.assoc_info.req_ies = (u8 *) buf + ai->OffsetRequestIEs;
+       data.assoc_info.req_ies_len = ai->RequestIELength;
+       data.assoc_info.resp_ies = (u8 *) buf + ai->OffsetResponseIEs;
+       data.assoc_info.resp_ies_len = ai->ResponseIELength;
+
+       blen = 65535;
+       b = os_zalloc(blen);
+       if (b == NULL)
+               goto skip_scan_results;
+       len = ndis_get_oid(drv, OID_802_11_BSSID_LIST, (char *) b, blen);
+       if (len < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: failed to get scan results");
+               os_free(b);
+               b = NULL;
+               goto skip_scan_results;
+       }
+       wpa_printf(MSG_DEBUG, "NDIS: %d BSSID items to process for AssocInfo",
+                  (unsigned int) b->NumberOfItems);
+
+       pos = (char *) &b->Bssid[0];
+       for (i = 0; i < b->NumberOfItems; i++) {
+               NDIS_WLAN_BSSID_EX *bss = (NDIS_WLAN_BSSID_EX *) pos;
+               if (os_memcmp(drv->bssid, bss->MacAddress, ETH_ALEN) == 0 &&
+                   bss->IELength > sizeof(NDIS_802_11_FIXED_IEs)) {
+                       data.assoc_info.beacon_ies =
+                               ((u8 *) bss->IEs) +
+                               sizeof(NDIS_802_11_FIXED_IEs);
+                       data.assoc_info.beacon_ies_len =
+                               bss->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+                       wpa_hexdump(MSG_MSGDUMP, "NDIS: Beacon IEs",
+                                   data.assoc_info.beacon_ies,
+                                   data.assoc_info.beacon_ies_len);
+                       break;
+               }
+               pos += bss->Length;
+               if (pos > (char *) b + blen)
+                       break;
+       }
+
+skip_scan_results:
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+       os_free(b);
+
+       return 0;
+}
+
+
+static void wpa_driver_ndis_poll_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_ndis_data *drv = eloop_ctx;
+       u8 bssid[ETH_ALEN];
+       int poll;
+
+       if (drv->wired)
+               return;
+
+       if (wpa_driver_ndis_get_bssid(drv, bssid)) {
+               /* Disconnected */
+               if (!is_zero_ether_addr(drv->bssid)) {
+                       os_memset(drv->bssid, 0, ETH_ALEN);
+                       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+               }
+       } else {
+               /* Connected */
+               if (os_memcmp(drv->bssid, bssid, ETH_ALEN) != 0) {
+                       os_memcpy(drv->bssid, bssid, ETH_ALEN);
+                       wpa_driver_ndis_get_associnfo(drv);
+                       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+               }
+       }
+
+       /* When using integrated NDIS event receiver, we can skip BSSID
+        * polling when using infrastructure network. However, when using
+        * IBSS mode, many driver do not seem to generate connection event,
+        * so we need to enable BSSID polling to figure out when IBSS network
+        * has been formed.
+        */
+       poll = drv->mode == IEEE80211_MODE_IBSS;
+#ifndef CONFIG_NDIS_EVENTS_INTEGRATED
+#ifndef _WIN32_WCE
+       poll = 1;
+#endif /* _WIN32_WCE */
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+       if (poll) {
+               eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout,
+                                       drv, NULL);
+       }
+}
+
+
+static void wpa_driver_ndis_poll(void *priv)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+       wpa_driver_ndis_poll_timeout(drv, NULL);
+}
+
+
+/* Called when driver generates Media Connect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_CONNECT */
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv)
+{
+       wpa_printf(MSG_DEBUG, "NDIS: Media Connect Event");
+       if (wpa_driver_ndis_get_bssid(drv, drv->bssid) == 0) {
+               wpa_driver_ndis_get_associnfo(drv);
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+       }
+}
+
+
+/* Called when driver generates Media Disconnect Event by calling
+ * NdisMIndicateStatus() with NDIS_STATUS_MEDIA_DISCONNECT */
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv)
+{
+       wpa_printf(MSG_DEBUG, "NDIS: Media Disconnect Event");
+       os_memset(drv->bssid, 0, ETH_ALEN);
+       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_ndis_event_auth(struct wpa_driver_ndis_data *drv,
+                                      const u8 *data, size_t data_len)
+{
+       NDIS_802_11_AUTHENTICATION_REQUEST *req;
+       int pairwise = 0, group = 0;
+       union wpa_event_data event;
+
+       if (data_len < sizeof(*req)) {
+               wpa_printf(MSG_DEBUG, "NDIS: Too short Authentication Request "
+                          "Event (len=%d)", data_len);
+               return;
+       }
+       req = (NDIS_802_11_AUTHENTICATION_REQUEST *) data;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Authentication Request Event: "
+                  "Bssid " MACSTR " Flags 0x%x",
+                  MAC2STR(req->Bssid), (int) req->Flags);
+
+       if ((req->Flags & NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR) ==
+           NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR)
+               pairwise = 1;
+       else if ((req->Flags & NDIS_802_11_AUTH_REQUEST_GROUP_ERROR) ==
+           NDIS_802_11_AUTH_REQUEST_GROUP_ERROR)
+               group = 1;
+
+       if (pairwise || group) {
+               os_memset(&event, 0, sizeof(event));
+               event.michael_mic_failure.unicast = pairwise;
+               wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE,
+                                    &event);
+       }
+}
+
+
+static void wpa_driver_ndis_event_pmkid(struct wpa_driver_ndis_data *drv,
+                                       const u8 *data, size_t data_len)
+{
+       NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+       size_t i;
+       union wpa_event_data event;
+
+       if (data_len < 8) {
+               wpa_printf(MSG_DEBUG, "NDIS: Too short PMKID Candidate List "
+                          "Event (len=%d)", data_len);
+               return;
+       }
+       pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+       wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List Event - Version %d "
+                  "NumCandidates %d",
+                  (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+       if (pmkid->Version != 1) {
+               wpa_printf(MSG_DEBUG, "NDIS: Unsupported PMKID Candidate List "
+                          "Version %d", (int) pmkid->Version);
+               return;
+       }
+
+       if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+               wpa_printf(MSG_DEBUG, "NDIS: PMKID Candidate List underflow");
+               return;
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       for (i = 0; i < pmkid->NumCandidates; i++) {
+               PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+               wpa_printf(MSG_DEBUG, "NDIS: %d: " MACSTR " Flags 0x%x",
+                          i, MAC2STR(p->BSSID), (int) p->Flags);
+               os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+               event.pmkid_candidate.index = i;
+               event.pmkid_candidate.preauth =
+                       p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+               wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+                                    &event);
+       }
+}
+
+
+/* Called when driver calls NdisMIndicateStatus() with
+ * NDIS_STATUS_MEDIA_SPECIFIC_INDICATION */
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+                                         const u8 *data, size_t data_len)
+{
+       NDIS_802_11_STATUS_INDICATION *status;
+
+       if (data == NULL || data_len < sizeof(*status))
+               return;
+
+       wpa_hexdump(MSG_DEBUG, "NDIS: Media Specific Indication",
+                   data, data_len);
+
+       status = (NDIS_802_11_STATUS_INDICATION *) data;
+       data += sizeof(status);
+       data_len -= sizeof(status);
+
+       switch (status->StatusType) {
+       case Ndis802_11StatusType_Authentication:
+               wpa_driver_ndis_event_auth(drv, data, data_len);
+               break;
+       case Ndis802_11StatusType_PMKID_CandidateList:
+               wpa_driver_ndis_event_pmkid(drv, data, data_len);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "NDIS: Unknown StatusType %d",
+                          (int) status->StatusType);
+               break;
+       }
+}
+
+
+/* Called when an adapter is added */
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv)
+{
+       union wpa_event_data event;
+       int i;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Arrival");
+
+       for (i = 0; i < 30; i++) {
+               /* Re-open Packet32/NDISUIO connection */
+               wpa_driver_ndis_adapter_close(drv);
+               if (wpa_driver_ndis_adapter_init(drv) < 0 ||
+                   wpa_driver_ndis_adapter_open(drv) < 0) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialization "
+                                  "(%d) failed", i);
+                       os_sleep(1, 0);
+               } else {
+                       wpa_printf(MSG_DEBUG, "NDIS: Driver re-initialized");
+                       break;
+               }
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       os_strlcpy(event.interface_status.ifname, drv->ifname,
+                  sizeof(event.interface_status.ifname));
+       event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+/* Called when an adapter is removed */
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv)
+{
+       union wpa_event_data event;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Notify Adapter Removal");
+       os_memset(&event, 0, sizeof(event));
+       os_strlcpy(event.interface_status.ifname, drv->ifname,
+                  sizeof(event.interface_status.ifname));
+       event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void
+wpa_driver_ndis_get_wpa_capability(struct wpa_driver_ndis_data *drv)
+{
+       wpa_printf(MSG_DEBUG, "NDIS: verifying driver WPA capability");
+
+       if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPA) == 0 &&
+           ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPA) {
+               wpa_printf(MSG_DEBUG, "NDIS: WPA key management supported");
+               drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+       }
+
+       if (ndis_set_auth_mode(drv, Ndis802_11AuthModeWPAPSK) == 0 &&
+           ndis_get_auth_mode(drv) == Ndis802_11AuthModeWPAPSK) {
+               wpa_printf(MSG_DEBUG, "NDIS: WPA-PSK key management "
+                          "supported");
+               drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+       }
+
+       if (ndis_set_encr_status(drv, Ndis802_11Encryption3Enabled) == 0 &&
+           ndis_get_encr_status(drv) == Ndis802_11Encryption3KeyAbsent) {
+               wpa_printf(MSG_DEBUG, "NDIS: CCMP encryption supported");
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+       }
+
+       if (ndis_set_encr_status(drv, Ndis802_11Encryption2Enabled) == 0 &&
+           ndis_get_encr_status(drv) == Ndis802_11Encryption2KeyAbsent) {
+               wpa_printf(MSG_DEBUG, "NDIS: TKIP encryption supported");
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+       }
+
+       if (ndis_set_encr_status(drv, Ndis802_11Encryption1Enabled) == 0 &&
+           ndis_get_encr_status(drv) == Ndis802_11Encryption1KeyAbsent) {
+               wpa_printf(MSG_DEBUG, "NDIS: WEP encryption supported");
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+                       WPA_DRIVER_CAPA_ENC_WEP104;
+       }
+
+       if (ndis_set_auth_mode(drv, Ndis802_11AuthModeShared) == 0 &&
+           ndis_get_auth_mode(drv) == Ndis802_11AuthModeShared) {
+               drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+       }
+
+       if (ndis_set_auth_mode(drv, Ndis802_11AuthModeOpen) == 0 &&
+           ndis_get_auth_mode(drv) == Ndis802_11AuthModeOpen) {
+               drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+       }
+
+       ndis_set_encr_status(drv, Ndis802_11EncryptionDisabled);
+
+       /* Could also verify OID_802_11_ADD_KEY error reporting and
+        * support for OID_802_11_ASSOCIATION_INFORMATION. */
+
+       if (drv->capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA &&
+           drv->capa.enc & (WPA_DRIVER_CAPA_ENC_TKIP |
+                            WPA_DRIVER_CAPA_ENC_CCMP)) {
+               wpa_printf(MSG_DEBUG, "NDIS: driver supports WPA");
+               drv->has_capability = 1;
+       } else {
+               wpa_printf(MSG_DEBUG, "NDIS: no WPA support found");
+       }
+
+       wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+                  "enc 0x%x auth 0x%x",
+                  drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static void wpa_driver_ndis_get_capability(struct wpa_driver_ndis_data *drv)
+{
+       char buf[512];
+       int len;
+       size_t i;
+       NDIS_802_11_CAPABILITY *c;
+
+       drv->capa.flags = WPA_DRIVER_FLAGS_DRIVER_IE;
+
+       len = ndis_get_oid(drv, OID_802_11_CAPABILITY, buf, sizeof(buf));
+       if (len < 0) {
+               wpa_driver_ndis_get_wpa_capability(drv);
+               return;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "OID_802_11_CAPABILITY", (u8 *) buf, len);
+       c = (NDIS_802_11_CAPABILITY *) buf;
+       if (len < sizeof(*c) || c->Version != 2) {
+               wpa_printf(MSG_DEBUG, "NDIS: unsupported "
+                          "OID_802_11_CAPABILITY data");
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "NDIS: Driver supports OID_802_11_CAPABILITY - "
+                  "NoOfPMKIDs %d NoOfAuthEncrPairs %d",
+                  (int) c->NoOfPMKIDs,
+                  (int) c->NoOfAuthEncryptPairsSupported);
+       drv->has_capability = 1;
+       drv->no_of_pmkid = c->NoOfPMKIDs;
+       for (i = 0; i < c->NoOfAuthEncryptPairsSupported; i++) {
+               NDIS_802_11_AUTHENTICATION_ENCRYPTION *ae;
+               ae = &c->AuthenticationEncryptionSupported[i];
+               if ((char *) (ae + 1) > buf + len) {
+                       wpa_printf(MSG_DEBUG, "NDIS: auth/encr pair list "
+                                  "overflow");
+                       break;
+               }
+               wpa_printf(MSG_MSGDUMP, "NDIS: %d - auth %d encr %d",
+                          i, (int) ae->AuthModeSupported,
+                          (int) ae->EncryptStatusSupported);
+               switch (ae->AuthModeSupported) {
+               case Ndis802_11AuthModeOpen:
+                       drv->capa.auth |= WPA_DRIVER_AUTH_OPEN;
+                       break;
+               case Ndis802_11AuthModeShared:
+                       drv->capa.auth |= WPA_DRIVER_AUTH_SHARED;
+                       break;
+               case Ndis802_11AuthModeWPA:
+                       drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA;
+                       break;
+               case Ndis802_11AuthModeWPAPSK:
+                       drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+                       break;
+               case Ndis802_11AuthModeWPA2:
+                       drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+                       break;
+               case Ndis802_11AuthModeWPA2PSK:
+                       drv->capa.key_mgmt |=
+                               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+                       break;
+               case Ndis802_11AuthModeWPANone:
+                       drv->capa.key_mgmt |=
+                               WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE;
+                       break;
+               default:
+                       break;
+               }
+               switch (ae->EncryptStatusSupported) {
+               case Ndis802_11Encryption1Enabled:
+                       drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+                       drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+                       break;
+               case Ndis802_11Encryption2Enabled:
+                       drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+                       break;
+               case Ndis802_11Encryption3Enabled:
+                       drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "NDIS: driver capabilities: key_mgmt 0x%x "
+                  "enc 0x%x auth 0x%x",
+                  drv->capa.key_mgmt, drv->capa.enc, drv->capa.auth);
+}
+
+
+static int wpa_driver_ndis_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       if (!drv->has_capability)
+               return -1;
+       os_memcpy(capa, &drv->capa, sizeof(*capa));
+       return 0;
+}
+
+
+static const char * wpa_driver_ndis_get_ifname(void *priv)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       return drv->ifname;
+}
+
+
+static const u8 * wpa_driver_ndis_get_mac_addr(void *priv)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+       return drv->own_addr;
+}
+
+
+#ifdef _WIN32_WCE
+
+#define NDISUIO_MSG_SIZE (sizeof(NDISUIO_DEVICE_NOTIFICATION) + 512)
+
+static void ndisuio_notification_receive(void *eloop_data, void *user_ctx)
+{
+       struct wpa_driver_ndis_data *drv = eloop_data;
+       NDISUIO_DEVICE_NOTIFICATION *hdr;
+       u8 buf[NDISUIO_MSG_SIZE];
+       DWORD len, flags;
+
+       if (!ReadMsgQueue(drv->event_queue, buf, NDISUIO_MSG_SIZE, &len, 0,
+                         &flags)) {
+               wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+                          "ReadMsgQueue failed: %d", (int) GetLastError());
+               return;
+       }
+
+       if (len < sizeof(NDISUIO_DEVICE_NOTIFICATION)) {
+               wpa_printf(MSG_DEBUG, "ndisuio_notification_receive: "
+                          "Too short message (len=%d)", (int) len);
+               return;
+       }
+
+       hdr = (NDISUIO_DEVICE_NOTIFICATION *) buf;
+       wpa_printf(MSG_DEBUG, "NDIS: Notification received: len=%d type=0x%x",
+                  (int) len, hdr->dwNotificationType);
+
+       switch (hdr->dwNotificationType) {
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+       case NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL:
+               wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_ARRIVAL");
+               wpa_driver_ndis_event_adapter_arrival(drv);
+               break;
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+       case NDISUIO_NOTIFICATION_ADAPTER_REMOVAL:
+               wpa_printf(MSG_DEBUG, "NDIS: ADAPTER_REMOVAL");
+               wpa_driver_ndis_event_adapter_removal(drv);
+               break;
+#endif
+       case NDISUIO_NOTIFICATION_MEDIA_CONNECT:
+               wpa_printf(MSG_DEBUG, "NDIS: MEDIA_CONNECT");
+               SetEvent(drv->connected_event);
+               wpa_driver_ndis_event_connect(drv);
+               break;
+       case NDISUIO_NOTIFICATION_MEDIA_DISCONNECT:
+               ResetEvent(drv->connected_event);
+               wpa_printf(MSG_DEBUG, "NDIS: MEDIA_DISCONNECT");
+               wpa_driver_ndis_event_disconnect(drv);
+               break;
+       case NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION:
+               wpa_printf(MSG_DEBUG, "NDIS: MEDIA_SPECIFIC_NOTIFICATION");
+#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420
+               wpa_driver_ndis_event_media_specific(
+                       drv, hdr->pvStatusBuffer, hdr->uiStatusBufferSize);
+#else
+               wpa_driver_ndis_event_media_specific(
+                       drv, ((const u8 *) hdr) + hdr->uiOffsetToStatusBuffer,
+                       (size_t) hdr->uiStatusBufferSize);
+#endif
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "NDIS: Unknown notification type 0x%x",
+                          hdr->dwNotificationType);
+               break;
+       }
+}
+
+
+static void ndisuio_notification_deinit(struct wpa_driver_ndis_data *drv)
+{
+       NDISUIO_REQUEST_NOTIFICATION req;
+
+       memset(&req, 0, sizeof(req));
+       req.hMsgQueue = drv->event_queue;
+       req.dwNotificationTypes = 0;
+
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+                            &req, sizeof(req), NULL, 0, NULL, NULL)) {
+               wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+                          "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+                          (int) GetLastError());
+       }
+
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_CANCEL_NOTIFICATION,
+                            NULL, 0, NULL, 0, NULL, NULL)) {
+               wpa_printf(MSG_INFO, "ndisuio_notification_deinit: "
+                          "IOCTL_NDISUIO_CANCEL_NOTIFICATION failed: %d",
+                          (int) GetLastError());
+       }
+
+       if (drv->event_queue) {
+               eloop_unregister_event(drv->event_queue,
+                                      sizeof(drv->event_queue));
+               CloseHandle(drv->event_queue);
+               drv->event_queue = NULL;
+       }
+
+       if (drv->connected_event) {
+               CloseHandle(drv->connected_event);
+               drv->connected_event = NULL;
+       }
+}
+
+
+static int ndisuio_notification_init(struct wpa_driver_ndis_data *drv)
+{
+       MSGQUEUEOPTIONS opt;
+       NDISUIO_REQUEST_NOTIFICATION req;
+
+       drv->connected_event =
+               CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+       if (drv->connected_event == NULL) {
+               wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+                          "CreateEvent failed: %d",
+                          (int) GetLastError());
+               return -1;
+       }
+
+       memset(&opt, 0, sizeof(opt));
+       opt.dwSize = sizeof(opt);
+       opt.dwMaxMessages = 5;
+       opt.cbMaxMessage = NDISUIO_MSG_SIZE;
+       opt.bReadAccess = TRUE;
+
+       drv->event_queue = CreateMsgQueue(NULL, &opt);
+       if (drv->event_queue == NULL) {
+               wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+                          "CreateMsgQueue failed: %d",
+                          (int) GetLastError());
+               ndisuio_notification_deinit(drv);
+               return -1;
+       }
+
+       memset(&req, 0, sizeof(req));
+       req.hMsgQueue = drv->event_queue;
+       req.dwNotificationTypes =
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL
+               NDISUIO_NOTIFICATION_ADAPTER_ARRIVAL |
+#endif
+#ifdef NDISUIO_NOTIFICATION_ADAPTER_REMOVAL
+               NDISUIO_NOTIFICATION_ADAPTER_REMOVAL |
+#endif
+               NDISUIO_NOTIFICATION_MEDIA_CONNECT |
+               NDISUIO_NOTIFICATION_MEDIA_DISCONNECT |
+               NDISUIO_NOTIFICATION_MEDIA_SPECIFIC_NOTIFICATION;
+
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_REQUEST_NOTIFICATION,
+                            &req, sizeof(req), NULL, 0, NULL, NULL)) {
+               wpa_printf(MSG_INFO, "ndisuio_notification_init: "
+                          "IOCTL_NDISUIO_REQUEST_NOTIFICATION failed: %d",
+                          (int) GetLastError());
+               ndisuio_notification_deinit(drv);
+               return -1;
+       }
+
+       eloop_register_event(drv->event_queue, sizeof(drv->event_queue),
+                            ndisuio_notification_receive, drv, NULL);
+
+       return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_get_names(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+       NDISUIO_QUERY_BINDING *b;
+       size_t blen = sizeof(*b) + 1024;
+       int i, error, found = 0;
+       DWORD written;
+       char name[256], desc[256], *dpos;
+       WCHAR *pos;
+       size_t j, len, dlen;
+
+       b = os_malloc(blen);
+       if (b == NULL)
+               return -1;
+
+       for (i = 0; ; i++) {
+               os_memset(b, 0, blen);
+               b->BindingIndex = i;
+               if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+                                    b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+                                    &written, NULL)) {
+                       error = (int) GetLastError();
+                       if (error == ERROR_NO_MORE_ITEMS)
+                               break;
+                       wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+                                  "failed: %d", error);
+                       break;
+               }
+
+               pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+               len = b->DeviceNameLength;
+               if (len >= sizeof(name))
+                       len = sizeof(name) - 1;
+               for (j = 0; j < len; j++)
+                       name[j] = (char) pos[j];
+               name[len] = '\0';
+
+               pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+               len = b->DeviceDescrLength;
+               if (len >= sizeof(desc))
+                       len = sizeof(desc) - 1;
+               for (j = 0; j < len; j++)
+                       desc[j] = (char) pos[j];
+               desc[len] = '\0';
+
+               wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+               if (os_strstr(name, drv->ifname)) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Interface name match");
+                       found = 1;
+                       break;
+               }
+
+               if (os_strncmp(desc, drv->ifname, os_strlen(drv->ifname)) == 0)
+               {
+                       wpa_printf(MSG_DEBUG, "NDIS: Interface description "
+                                  "match");
+                       found = 1;
+                       break;
+               }
+       }
+
+       if (!found) {
+               wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+                          drv->ifname);
+               os_free(b);
+               return -1;
+       }
+
+       os_strlcpy(drv->ifname,
+                  os_strncmp(name, "\\DEVICE\\", 8) == 0 ? name + 8 : name,
+                  sizeof(drv->ifname));
+#ifdef _WIN32_WCE
+       drv->adapter_name = wpa_strdup_tchar(drv->ifname);
+       if (drv->adapter_name == NULL) {
+               wpa_printf(MSG_ERROR, "NDIS: Failed to allocate memory for "
+                          "adapter name");
+               os_free(b);
+               return -1;
+       }
+#endif /* _WIN32_WCE */
+
+       dpos = os_strstr(desc, " - ");
+       if (dpos)
+               dlen = dpos - desc;
+       else
+               dlen = os_strlen(desc);
+       drv->adapter_desc = os_malloc(dlen + 1);
+       if (drv->adapter_desc) {
+               os_memcpy(drv->adapter_desc, desc, dlen);
+               drv->adapter_desc[dlen] = '\0';
+       }
+
+       os_free(b);
+
+       if (drv->adapter_desc == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+                  drv->adapter_desc);
+
+       return 0;
+#else /* CONFIG_USE_NDISUIO */
+       PTSTR _names;
+       char *names, *pos, *pos2;
+       ULONG len;
+       BOOLEAN res;
+#define MAX_ADAPTERS 32
+       char *name[MAX_ADAPTERS];
+       char *desc[MAX_ADAPTERS];
+       int num_name, num_desc, i, found_name, found_desc;
+       size_t dlen;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+                  PacketGetVersion());
+
+       len = 8192;
+       _names = os_zalloc(len);
+       if (_names == NULL)
+               return -1;
+
+       res = PacketGetAdapterNames(_names, &len);
+       if (!res && len > 8192) {
+               os_free(_names);
+               _names = os_zalloc(len);
+               if (_names == NULL)
+                       return -1;
+               res = PacketGetAdapterNames(_names, &len);
+       }
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+                          "(PacketGetAdapterNames)");
+               os_free(_names);
+               return -1;
+       }
+
+       names = (char *) _names;
+       if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+               wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+                          "UNICODE");
+               /* Convert to ASCII */
+               pos2 = pos = names;
+               while (pos2 < names + len) {
+                       if (pos2[0] == '\0' && pos2[1] == '\0' &&
+                           pos2[2] == '\0' && pos2[3] == '\0') {
+                               pos2 += 4;
+                               break;
+                       }
+                       *pos++ = pos2[0];
+                       pos2 += 2;
+               }
+               os_memcpy(pos + 2, names, pos - names);
+               pos += 2;
+       } else
+               pos = names;
+
+       num_name = 0;
+       while (pos < names + len) {
+               name[num_name] = pos;
+               while (*pos && pos < names + len)
+                       pos++;
+               if (pos + 1 >= names + len) {
+                       os_free(names);
+                       return -1;
+               }
+               pos++;
+               num_name++;
+               if (num_name >= MAX_ADAPTERS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+                       os_free(names);
+                       return -1;
+               }
+               if (*pos == '\0') {
+                       wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+                                  num_name);
+                       pos++;
+                       break;
+               }
+       }
+
+       num_desc = 0;
+       while (pos < names + len) {
+               desc[num_desc] = pos;
+               while (*pos && pos < names + len)
+                       pos++;
+               if (pos + 1 >= names + len) {
+                       os_free(names);
+                       return -1;
+               }
+               pos++;
+               num_desc++;
+               if (num_desc >= MAX_ADAPTERS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+                                  "descriptions");
+                       os_free(names);
+                       return -1;
+               }
+               if (*pos == '\0') {
+                       wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+                                  "found", num_name);
+                       pos++;
+                       break;
+               }
+       }
+
+       /*
+        * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+        * descriptions. Fill in dummy descriptors to work around this.
+        */
+       while (num_desc < num_name)
+               desc[num_desc++] = "dummy description";
+
+       if (num_name != num_desc) {
+               wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+                          "description counts (%d != %d)",
+                          num_name, num_desc);
+               os_free(names);
+               return -1;
+       }
+
+       found_name = found_desc = -1;
+       for (i = 0; i < num_name; i++) {
+               wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s",
+                          i, name[i], desc[i]);
+               if (found_name == -1 && os_strstr(name[i], drv->ifname))
+                       found_name = i;
+               if (found_desc == -1 &&
+                   os_strncmp(desc[i], drv->ifname, os_strlen(drv->ifname)) ==
+                   0)
+                       found_desc = i;
+       }
+
+       if (found_name < 0 && found_desc >= 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Matched interface '%s' based on "
+                          "description '%s'",
+                          name[found_desc], desc[found_desc]);
+               found_name = found_desc;
+               os_strlcpy(drv->ifname,
+                          os_strncmp(name[found_desc], "\\Device\\NPF_", 12)
+                          == 0 ? name[found_desc] + 12 : name[found_desc],
+                          sizeof(drv->ifname));
+       }
+
+       if (found_name < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Could not find interface '%s'",
+                          drv->ifname);
+               os_free(names);
+               return -1;
+       }
+
+       i = found_name;
+       pos = os_strrchr(desc[i], '(');
+       if (pos) {
+               dlen = pos - desc[i];
+               pos--;
+               if (pos > desc[i] && *pos == ' ')
+                       dlen--;
+       } else {
+               dlen = os_strlen(desc[i]);
+       }
+       drv->adapter_desc = os_malloc(dlen + 1);
+       if (drv->adapter_desc) {
+               os_memcpy(drv->adapter_desc, desc[i], dlen);
+               drv->adapter_desc[dlen] = '\0';
+       }
+
+       os_free(names);
+
+       if (drv->adapter_desc == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Adapter description prefix '%s'",
+                  drv->adapter_desc);
+
+       return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(__CYGWIN__)
+#ifndef _WIN32_WCE
+/*
+ * These structures are undocumented for WinXP; only WinCE version is
+ * documented. These would be included wzcsapi.h if it were available. Some
+ * changes here have been needed to make the structures match with WinXP SP2.
+ * It is unclear whether these work with any other version.
+ */
+
+typedef struct {
+       LPWSTR wszGuid;
+} INTF_KEY_ENTRY, *PINTF_KEY_ENTRY;
+
+typedef struct {
+       DWORD dwNumIntfs;
+       PINTF_KEY_ENTRY pIntfs;
+} INTFS_KEY_TABLE, *PINTFS_KEY_TABLE;
+
+typedef struct {
+       DWORD dwDataLen;
+       LPBYTE pData;
+} RAW_DATA, *PRAW_DATA;
+
+typedef struct {
+       LPWSTR wszGuid;
+       LPWSTR wszDescr;
+       ULONG ulMediaState;
+       ULONG ulMediaType;
+       ULONG ulPhysicalMediaType;
+       INT nInfraMode;
+       INT nAuthMode;
+       INT nWepStatus;
+#ifndef _WIN32_WCE
+       u8 pad[2]; /* why is this needed? */
+#endif /* _WIN32_WCE */
+       DWORD dwCtlFlags;
+       DWORD dwCapabilities; /* something added for WinXP SP2(?) */
+       RAW_DATA rdSSID;
+       RAW_DATA rdBSSID;
+       RAW_DATA rdBSSIDList;
+       RAW_DATA rdStSSIDList;
+       RAW_DATA rdCtrlData;
+#ifdef UNDER_CE
+       BOOL bInitialized;
+#endif
+       DWORD nWPAMCastCipher;
+       /* add some extra buffer for later additions since this interface is
+        * far from stable */
+       u8 later_additions[100];
+} INTF_ENTRY, *PINTF_ENTRY;
+
+#define INTF_ALL 0xffffffff
+#define INTF_ALL_FLAGS 0x0000ffff
+#define INTF_CTLFLAGS 0x00000010
+#define INTFCTL_ENABLED 0x8000
+#endif /* _WIN32_WCE */
+
+
+#ifdef _WIN32_WCE
+static int wpa_driver_ndis_rebind_adapter(struct wpa_driver_ndis_data *drv)
+{
+       HANDLE ndis;
+       TCHAR multi[100];
+       int len;
+
+       len = _tcslen(drv->adapter_name);
+       if (len > 80)
+               return -1;
+
+       ndis = CreateFile(DD_NDIS_DEVICE_NAME, GENERIC_READ | GENERIC_WRITE,
+                         0, NULL, OPEN_EXISTING, 0, NULL);
+       if (ndis == INVALID_HANDLE_VALUE) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to open file to NDIS "
+                          "device: %d", (int) GetLastError());
+               return -1;
+       }
+
+       len++;
+       memcpy(multi, drv->adapter_name, len * sizeof(TCHAR));
+       memcpy(&multi[len], TEXT("NDISUIO\0"), 9 * sizeof(TCHAR));
+       len += 9;
+
+       if (!DeviceIoControl(ndis, IOCTL_NDIS_REBIND_ADAPTER,
+                            multi, len * sizeof(TCHAR), NULL, 0, NULL, NULL))
+       {
+               wpa_printf(MSG_DEBUG, "NDIS: IOCTL_NDIS_REBIND_ADAPTER "
+                          "failed: 0x%x", (int) GetLastError());
+               wpa_hexdump_ascii(MSG_DEBUG, "NDIS: rebind multi_sz",
+                                 (u8 *) multi, len * sizeof(TCHAR));
+               CloseHandle(ndis);
+               return -1;
+       }
+
+       CloseHandle(ndis);
+
+       wpa_printf(MSG_DEBUG, "NDIS: Requested NDIS rebind of NDISUIO "
+                  "protocol");
+
+       return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+                                  int enable)
+{
+#ifdef _WIN32_WCE
+       HKEY hk, hk2;
+       LONG ret;
+       DWORD i, hnd, len;
+       TCHAR keyname[256], devname[256];
+
+#define WZC_DRIVER TEXT("Drivers\\BuiltIn\\ZeroConfig")
+
+       if (enable) {
+               HANDLE h;
+               h = ActivateDeviceEx(WZC_DRIVER, NULL, 0, NULL);
+               if (h == INVALID_HANDLE_VALUE || h == 0) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Failed to re-enable WZC "
+                                  "- ActivateDeviceEx failed: %d",
+                                  (int) GetLastError());
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "NDIS: WZC re-enabled");
+               return wpa_driver_ndis_rebind_adapter(drv);
+       }
+
+       /*
+        * Unfortunately, just disabling the WZC for an interface is not enough
+        * to free NDISUIO for us, so need to disable and unload WZC completely
+        * for now when using WinCE with NDISUIO. In addition, must request
+        * NDISUIO protocol to be rebound to the adapter in order to free the
+        * NDISUIO binding that WZC hold before us.
+        */
+
+       /* Enumerate HKLM\Drivers\Active\* to find a handle to WZC. */
+       ret = RegOpenKeyEx(HKEY_LOCAL_MACHINE, DEVLOAD_ACTIVE_KEY, 0, 0, &hk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(DEVLOAD_ACTIVE_KEY) "
+                          "failed: %d %d", (int) ret, (int) GetLastError());
+               return -1;
+       }
+
+       for (i = 0; ; i++) {
+               len = sizeof(keyname);
+               ret = RegEnumKeyEx(hk, i, keyname, &len, NULL, NULL, NULL,
+                                  NULL);
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Could not find active "
+                                  "WZC - assuming it is not running.");
+                       RegCloseKey(hk);
+                       return -1;
+               }
+
+               ret = RegOpenKeyEx(hk, keyname, 0, 0, &hk2);
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: RegOpenKeyEx(active dev) "
+                                  "failed: %d %d",
+                                  (int) ret, (int) GetLastError());
+                       continue;
+               }
+
+               len = sizeof(devname);
+               ret = RegQueryValueEx(hk2, DEVLOAD_DEVKEY_VALNAME, NULL, NULL,
+                                     (LPBYTE) devname, &len);
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx("
+                                  "DEVKEY_VALNAME) failed: %d %d",
+                                  (int) ret, (int) GetLastError());
+                       RegCloseKey(hk2);
+                       continue;
+               }
+
+               if (_tcscmp(devname, WZC_DRIVER) == 0)
+                       break;
+
+               RegCloseKey(hk2);
+       }
+
+       RegCloseKey(hk);
+
+       /* Found WZC - get handle to it. */
+       len = sizeof(hnd);
+       ret = RegQueryValueEx(hk2, DEVLOAD_HANDLE_VALNAME, NULL, NULL,
+                             (PUCHAR) &hnd, &len);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "NDIS: RegQueryValueEx(HANDLE_VALNAME) "
+                          "failed: %d %d", (int) ret, (int) GetLastError());
+               RegCloseKey(hk2);
+               return -1;
+       }
+
+       RegCloseKey(hk2);
+
+       /* Deactivate WZC */
+       if (!DeactivateDevice((HANDLE) hnd)) {
+               wpa_printf(MSG_DEBUG, "NDIS: DeactivateDevice failed: %d",
+                          (int) GetLastError());
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily");
+       drv->wzc_disabled = 1;
+       return wpa_driver_ndis_rebind_adapter(drv);
+
+#else /* _WIN32_WCE */
+
+       HMODULE hm;
+       DWORD (WINAPI *wzc_enum_interf)(LPWSTR pSrvAddr,
+                                       PINTFS_KEY_TABLE pIntfs);
+       DWORD (WINAPI *wzc_query_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+                                        PINTF_ENTRY pIntf,
+                                        LPDWORD pdwOutFlags);
+       DWORD (WINAPI *wzc_set_interf)(LPWSTR pSrvAddr, DWORD dwInFlags,
+                                      PINTF_ENTRY pIntf, LPDWORD pdwOutFlags);
+       int ret = -1, j;
+       DWORD res;
+       INTFS_KEY_TABLE guids;
+       INTF_ENTRY intf;
+       char guid[128];
+       WCHAR *pos;
+       DWORD flags, i;
+
+       hm = LoadLibrary(TEXT("wzcsapi.dll"));
+       if (hm == NULL) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to load wzcsapi.dll (%u) "
+                          "- WZC probably not running",
+                          (unsigned int) GetLastError());
+               return -1;
+       }
+
+#ifdef _WIN32_WCE
+       wzc_enum_interf = (void *) GetProcAddressA(hm, "WZCEnumInterfaces");
+       wzc_query_interf = (void *) GetProcAddressA(hm, "WZCQueryInterface");
+       wzc_set_interf = (void *) GetProcAddressA(hm, "WZCSetInterface");
+#else /* _WIN32_WCE */
+       wzc_enum_interf = (void *) GetProcAddress(hm, "WZCEnumInterfaces");
+       wzc_query_interf = (void *) GetProcAddress(hm, "WZCQueryInterface");
+       wzc_set_interf = (void *) GetProcAddress(hm, "WZCSetInterface");
+#endif /* _WIN32_WCE */
+
+       if (wzc_enum_interf == NULL || wzc_query_interf == NULL ||
+           wzc_set_interf == NULL) {
+               wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces, "
+                          "WZCQueryInterface, or WZCSetInterface not found "
+                          "in wzcsapi.dll");
+               goto fail;
+       }
+
+       os_memset(&guids, 0, sizeof(guids));
+       res = wzc_enum_interf(NULL, &guids);
+       if (res != 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces failed: %d; "
+                          "WZC service is apparently not running",
+                          (int) res);
+               goto fail;
+       }
+
+       wpa_printf(MSG_DEBUG, "NDIS: WZCEnumInterfaces: %d interfaces",
+                  (int) guids.dwNumIntfs);
+
+       for (i = 0; i < guids.dwNumIntfs; i++) {
+               pos = guids.pIntfs[i].wszGuid;
+               for (j = 0; j < sizeof(guid); j++) {
+                       guid[j] = (char) *pos;
+                       if (*pos == 0)
+                               break;
+                       pos++;
+               }
+               guid[sizeof(guid) - 1] = '\0';
+               wpa_printf(MSG_DEBUG, "NDIS: intfs %d GUID '%s'",
+                          (int) i, guid);
+               if (os_strstr(drv->ifname, guid) == NULL)
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "NDIS: Current interface found from "
+                          "WZC");
+               break;
+       }
+
+       if (i >= guids.dwNumIntfs) {
+               wpa_printf(MSG_DEBUG, "NDIS: Current interface not found from "
+                          "WZC");
+               goto fail;
+       }
+
+       os_memset(&intf, 0, sizeof(intf));
+       intf.wszGuid = guids.pIntfs[i].wszGuid;
+       /* Set flags to verify that the structure has not changed. */
+       intf.dwCtlFlags = -1;
+       flags = 0;
+       res = wzc_query_interf(NULL, INTFCTL_ENABLED, &intf, &flags);
+       if (res != 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Could not query flags for the "
+                          "WZC interface: %d (0x%x)",
+                          (int) res, (int) res);
+               wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+                          (unsigned int) GetLastError());
+               goto fail;
+       }
+
+       wpa_printf(MSG_DEBUG, "NDIS: WZC interface flags 0x%x dwCtlFlags 0x%x",
+                  (int) flags, (int) intf.dwCtlFlags);
+
+       if (intf.dwCtlFlags == -1) {
+               wpa_printf(MSG_DEBUG, "NDIS: Looks like wzcsapi has changed "
+                          "again - could not disable WZC");
+               wpa_hexdump(MSG_MSGDUMP, "NDIS: intf",
+                           (u8 *) &intf, sizeof(intf));
+               goto fail;
+       }
+
+       if (enable) {
+               if (!(intf.dwCtlFlags & INTFCTL_ENABLED)) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Enabling WZC for this "
+                                  "interface");
+                       intf.dwCtlFlags |= INTFCTL_ENABLED;
+                       res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+                                            &flags);
+                       if (res != 0) {
+                               wpa_printf(MSG_DEBUG, "NDIS: Failed to enable "
+                                          "WZC: %d (0x%x)",
+                                          (int) res, (int) res);
+                               wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+                                          (unsigned int) GetLastError());
+                               goto fail;
+                       }
+                       wpa_printf(MSG_DEBUG, "NDIS: Re-enabled WZC for this "
+                                  "interface");
+                       drv->wzc_disabled = 0;
+               }
+       } else {
+               if (intf.dwCtlFlags & INTFCTL_ENABLED) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Disabling WZC for this "
+                                  "interface");
+                       intf.dwCtlFlags &= ~INTFCTL_ENABLED;
+                       res = wzc_set_interf(NULL, INTFCTL_ENABLED, &intf,
+                                            &flags);
+                       if (res != 0) {
+                               wpa_printf(MSG_DEBUG, "NDIS: Failed to "
+                                          "disable WZC: %d (0x%x)",
+                                          (int) res, (int) res);
+                               wpa_printf(MSG_DEBUG, "NDIS: GetLastError: %u",
+                                          (unsigned int) GetLastError());
+                               goto fail;
+                       }
+                       wpa_printf(MSG_DEBUG, "NDIS: Disabled WZC temporarily "
+                                  "for this interface");
+                       drv->wzc_disabled = 1;
+               } else {
+                       wpa_printf(MSG_DEBUG, "NDIS: WZC was not enabled for "
+                                  "this interface");
+               }
+       }
+
+       ret = 0;
+
+fail:
+       FreeLibrary(hm);
+
+       return ret;
+#endif /* _WIN32_WCE */
+}
+
+#else /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+static int wpa_driver_ndis_set_wzc(struct wpa_driver_ndis_data *drv,
+                                  int enable)
+{
+       return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS || __CYGWIN__ */
+
+
+#ifdef CONFIG_USE_NDISUIO
+/*
+ * l2_packet_ndis.c is sharing the same handle to NDISUIO, so we must be able
+ * to export this handle. This is somewhat ugly, but there is no better
+ * mechanism available to pass data from driver interface to l2_packet wrapper.
+ */
+static HANDLE driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+
+HANDLE driver_ndis_get_ndisuio_handle(void)
+{
+       return driver_ndis_ndisuio_handle;
+}
+#endif /* CONFIG_USE_NDISUIO */
+
+
+static int wpa_driver_ndis_adapter_init(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+#ifndef _WIN32_WCE
+#define NDISUIO_DEVICE_NAME TEXT("\\\\.\\\\Ndisuio")
+       DWORD written;
+#endif /* _WIN32_WCE */
+       drv->ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+                                 GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                                 OPEN_EXISTING,
+                                 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+                                 INVALID_HANDLE_VALUE);
+       if (drv->ndisuio == INVALID_HANDLE_VALUE) {
+               wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+                          "NDISUIO: %d", (int) GetLastError());
+               return -1;
+       }
+       driver_ndis_ndisuio_handle = drv->ndisuio;
+
+#ifndef _WIN32_WCE
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+                            NULL, 0, &written, NULL)) {
+               wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+                          "%d", (int) GetLastError());
+               CloseHandle(drv->ndisuio);
+               drv->ndisuio = INVALID_HANDLE_VALUE;
+               return -1;
+       }
+#endif /* _WIN32_WCE */
+
+       return 0;
+#else /* CONFIG_USE_NDISUIO */
+       return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+       DWORD written;
+#define MAX_NDIS_DEVICE_NAME_LEN 256
+       WCHAR ifname[MAX_NDIS_DEVICE_NAME_LEN];
+       size_t len, i, pos;
+       const char *prefix = "\\DEVICE\\";
+
+#ifdef _WIN32_WCE
+       pos = 0;
+#else /* _WIN32_WCE */
+       pos = 8;
+#endif /* _WIN32_WCE */
+       len = pos + os_strlen(drv->ifname);
+       if (len >= MAX_NDIS_DEVICE_NAME_LEN)
+               return -1;
+       for (i = 0; i < pos; i++)
+               ifname[i] = (WCHAR) prefix[i];
+       for (i = pos; i < len; i++)
+               ifname[i] = (WCHAR) drv->ifname[i - pos];
+       ifname[i] = L'\0';
+
+       if (!DeviceIoControl(drv->ndisuio, IOCTL_NDISUIO_OPEN_DEVICE,
+                            ifname, len * sizeof(WCHAR), NULL, 0, &written,
+                            NULL)) {
+               wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_OPEN_DEVICE "
+                          "failed: %d", (int) GetLastError());
+               wpa_hexdump_ascii(MSG_DEBUG, "NDIS: ifname",
+                                 (const u8 *) ifname, len * sizeof(WCHAR));
+               CloseHandle(drv->ndisuio);
+               drv->ndisuio = INVALID_HANDLE_VALUE;
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "NDIS: Opened NDISUIO device successfully");
+
+       return 0;
+#else /* CONFIG_USE_NDISUIO */
+       char ifname[128];
+       os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", drv->ifname);
+       drv->adapter = PacketOpenAdapter(ifname);
+       if (drv->adapter == NULL) {
+               wpa_printf(MSG_DEBUG, "NDIS: PacketOpenAdapter failed for "
+                          "'%s'", ifname);
+               return -1;
+       }
+       return 0;
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv)
+{
+#ifdef CONFIG_USE_NDISUIO
+       driver_ndis_ndisuio_handle = INVALID_HANDLE_VALUE;
+       if (drv->ndisuio != INVALID_HANDLE_VALUE)
+               CloseHandle(drv->ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+       if (drv->adapter)
+               PacketCloseAdapter(drv->adapter);
+#endif /* CONFIG_USE_NDISUIO */
+}
+
+
+static int ndis_add_multicast(struct wpa_driver_ndis_data *drv)
+{
+       if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST,
+                        (const char *) pae_group_addr, ETH_ALEN) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address "
+                          "to the multicast list");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void * wpa_driver_ndis_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_ndis_data *drv;
+       u32 mode;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       /*
+        * Compatibility code to strip possible prefix from the GUID. Previous
+        * versions include \Device\NPF_ prefix for all names, but the internal
+        * interface name is now only the GUI. Both Packet32 and NDISUIO
+        * prefixes are supported.
+        */
+       if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+               ifname += 12;
+       else if (os_strncmp(ifname, "\\DEVICE\\", 8) == 0)
+               ifname += 8;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+       if (wpa_driver_ndis_adapter_init(drv) < 0) {
+               os_free(drv);
+               return NULL;
+       }
+
+       if (wpa_driver_ndis_get_names(drv) < 0) {
+               wpa_driver_ndis_adapter_close(drv);
+               os_free(drv);
+               return NULL;
+       }
+
+       wpa_driver_ndis_set_wzc(drv, 0);
+
+       if (wpa_driver_ndis_adapter_open(drv) < 0) {
+               wpa_driver_ndis_adapter_close(drv);
+               os_free(drv);
+               return NULL;
+       }
+
+       if (ndis_get_oid(drv, OID_802_3_CURRENT_ADDRESS,
+                        (char *) drv->own_addr, ETH_ALEN) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: Get OID_802_3_CURRENT_ADDRESS "
+                          "failed");
+               wpa_driver_ndis_adapter_close(drv);
+               os_free(drv);
+               return NULL;
+       }
+       wpa_driver_ndis_get_capability(drv);
+
+       /* Make sure that the driver does not have any obsolete PMKID entries.
+        */
+       wpa_driver_ndis_flush_pmkid(drv);
+
+       /*
+        * Disconnect to make sure that driver re-associates if it was
+        * connected.
+        */
+       wpa_driver_ndis_disconnect(drv);
+
+       eloop_register_timeout(1, 0, wpa_driver_ndis_poll_timeout, drv, NULL);
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+       drv->events = ndis_events_init(&drv->events_pipe, &drv->event_avail,
+                                      drv->ifname, drv->adapter_desc);
+       if (drv->events == NULL) {
+               wpa_driver_ndis_deinit(drv);
+               return NULL;
+       }
+       eloop_register_event(drv->event_avail, sizeof(drv->event_avail),
+                            wpa_driver_ndis_event_pipe_cb, drv, NULL);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+       if (ndisuio_notification_init(drv) < 0) {
+               wpa_driver_ndis_deinit(drv);
+               return NULL;
+       }
+#endif /* _WIN32_WCE */
+
+       /* Set mode here in case card was configured for ad-hoc mode
+        * previously. */
+       mode = Ndis802_11Infrastructure;
+       if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+                        (char *) &mode, sizeof(mode)) < 0) {
+               char buf[8];
+               int res;
+               wpa_printf(MSG_DEBUG, "NDIS: Failed to set "
+                          "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+                          (int) mode);
+               /* Try to continue anyway */
+
+               res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf,
+                                  sizeof(buf));
+               if (res > 0) {
+                       wpa_printf(MSG_INFO, "NDIS: The driver seems to use "
+                                  "Native 802.11 OIDs. These are not yet "
+                                  "fully supported.");
+                       drv->native80211 = 1;
+               } else if (!drv->has_capability || drv->capa.enc == 0) {
+                       /*
+                        * Note: This will also happen with NDIS 6 drivers with
+                        * Vista.
+                        */
+                       wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide "
+                                  "any wireless capabilities - assume it is "
+                                  "a wired interface");
+                       drv->wired = 1;
+                       drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED;
+                       drv->has_capability = 1;
+                       ndis_add_multicast(drv);
+               }
+       }
+
+       return drv;
+}
+
+
+static void wpa_driver_ndis_deinit(void *priv)
+{
+       struct wpa_driver_ndis_data *drv = priv;
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+       if (drv->events) {
+               eloop_unregister_event(drv->event_avail,
+                                      sizeof(drv->event_avail));
+               ndis_events_deinit(drv->events);
+       }
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+#ifdef _WIN32_WCE
+       ndisuio_notification_deinit(drv);
+#endif /* _WIN32_WCE */
+
+       eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx);
+       eloop_cancel_timeout(wpa_driver_ndis_poll_timeout, drv, NULL);
+       wpa_driver_ndis_flush_pmkid(drv);
+       wpa_driver_ndis_disconnect(drv);
+       if (wpa_driver_ndis_radio_off(drv) < 0) {
+               wpa_printf(MSG_DEBUG, "NDIS: failed to disassociate and turn "
+                          "radio off");
+       }
+
+       wpa_driver_ndis_adapter_close(drv);
+
+       if (drv->wzc_disabled)
+               wpa_driver_ndis_set_wzc(drv, 1);
+
+#ifdef _WIN32_WCE
+       os_free(drv->adapter_name);
+#endif /* _WIN32_WCE */
+       os_free(drv->adapter_desc);
+       os_free(drv);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_ndis_get_interfaces(void *global_priv)
+{
+       struct wpa_interface_info *iface = NULL, *niface;
+
+#ifdef CONFIG_USE_NDISUIO
+       NDISUIO_QUERY_BINDING *b;
+       size_t blen = sizeof(*b) + 1024;
+       int i, error;
+       DWORD written;
+       char name[256], desc[256];
+       WCHAR *pos;
+       size_t j, len;
+       HANDLE ndisuio;
+
+       ndisuio = CreateFile(NDISUIO_DEVICE_NAME,
+                            GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                            OPEN_EXISTING,
+                            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,
+                            INVALID_HANDLE_VALUE);
+       if (ndisuio == INVALID_HANDLE_VALUE) {
+               wpa_printf(MSG_ERROR, "NDIS: Failed to open connection to "
+                          "NDISUIO: %d", (int) GetLastError());
+               return NULL;
+       }
+
+#ifndef _WIN32_WCE
+       if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_BIND_WAIT, NULL, 0,
+                            NULL, 0, &written, NULL)) {
+               wpa_printf(MSG_ERROR, "NDIS: IOCTL_NDISUIO_BIND_WAIT failed: "
+                          "%d", (int) GetLastError());
+               CloseHandle(ndisuio);
+               return NULL;
+       }
+#endif /* _WIN32_WCE */
+
+       b = os_malloc(blen);
+       if (b == NULL) {
+               CloseHandle(ndisuio);
+               return NULL;
+       }
+
+       for (i = 0; ; i++) {
+               os_memset(b, 0, blen);
+               b->BindingIndex = i;
+               if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+                                    b, sizeof(NDISUIO_QUERY_BINDING), b, blen,
+                                    &written, NULL)) {
+                       error = (int) GetLastError();
+                       if (error == ERROR_NO_MORE_ITEMS)
+                               break;
+                       wpa_printf(MSG_DEBUG, "IOCTL_NDISUIO_QUERY_BINDING "
+                                  "failed: %d", error);
+                       break;
+               }
+
+               pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+               len = b->DeviceNameLength;
+               if (len >= sizeof(name))
+                       len = sizeof(name) - 1;
+               for (j = 0; j < len; j++)
+                       name[j] = (char) pos[j];
+               name[len] = '\0';
+
+               pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+               len = b->DeviceDescrLength;
+               if (len >= sizeof(desc))
+                       len = sizeof(desc) - 1;
+               for (j = 0; j < len; j++)
+                       desc[j] = (char) pos[j];
+               desc[len] = '\0';
+
+               wpa_printf(MSG_DEBUG, "NDIS: %d - %s - %s", i, name, desc);
+
+               niface = os_zalloc(sizeof(*niface));
+               if (niface == NULL)
+                       break;
+               niface->drv_name = "ndis";
+               if (os_strncmp(name, "\\DEVICE\\", 8) == 0)
+                       niface->ifname = os_strdup(name + 8);
+               else
+                       niface->ifname = os_strdup(name);
+               if (niface->ifname == NULL) {
+                       os_free(niface);
+                       break;
+               }
+               niface->desc = os_strdup(desc);
+               niface->next = iface;
+               iface = niface;
+       }
+
+       os_free(b);
+       CloseHandle(ndisuio);
+#else /* CONFIG_USE_NDISUIO */
+       PTSTR _names;
+       char *names, *pos, *pos2;
+       ULONG len;
+       BOOLEAN res;
+       char *name[MAX_ADAPTERS];
+       char *desc[MAX_ADAPTERS];
+       int num_name, num_desc, i;
+
+       wpa_printf(MSG_DEBUG, "NDIS: Packet.dll version: %s",
+                  PacketGetVersion());
+
+       len = 8192;
+       _names = os_zalloc(len);
+       if (_names == NULL)
+               return NULL;
+
+       res = PacketGetAdapterNames(_names, &len);
+       if (!res && len > 8192) {
+               os_free(_names);
+               _names = os_zalloc(len);
+               if (_names == NULL)
+                       return NULL;
+               res = PacketGetAdapterNames(_names, &len);
+       }
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "NDIS: Failed to get adapter list "
+                          "(PacketGetAdapterNames)");
+               os_free(_names);
+               return NULL;
+       }
+
+       names = (char *) _names;
+       if (names[0] && names[1] == '\0' && names[2] && names[3] == '\0') {
+               wpa_printf(MSG_DEBUG, "NDIS: Looks like adapter names are in "
+                          "UNICODE");
+               /* Convert to ASCII */
+               pos2 = pos = names;
+               while (pos2 < names + len) {
+                       if (pos2[0] == '\0' && pos2[1] == '\0' &&
+                           pos2[2] == '\0' && pos2[3] == '\0') {
+                               pos2 += 4;
+                               break;
+                       }
+                       *pos++ = pos2[0];
+                       pos2 += 2;
+               }
+               os_memcpy(pos + 2, names, pos - names);
+               pos += 2;
+       } else
+               pos = names;
+
+       num_name = 0;
+       while (pos < names + len) {
+               name[num_name] = pos;
+               while (*pos && pos < names + len)
+                       pos++;
+               if (pos + 1 >= names + len) {
+                       os_free(names);
+                       return NULL;
+               }
+               pos++;
+               num_name++;
+               if (num_name >= MAX_ADAPTERS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Too many adapters");
+                       os_free(names);
+                       return NULL;
+               }
+               if (*pos == '\0') {
+                       wpa_printf(MSG_DEBUG, "NDIS: %d adapter names found",
+                                  num_name);
+                       pos++;
+                       break;
+               }
+       }
+
+       num_desc = 0;
+       while (pos < names + len) {
+               desc[num_desc] = pos;
+               while (*pos && pos < names + len)
+                       pos++;
+               if (pos + 1 >= names + len) {
+                       os_free(names);
+                       return NULL;
+               }
+               pos++;
+               num_desc++;
+               if (num_desc >= MAX_ADAPTERS) {
+                       wpa_printf(MSG_DEBUG, "NDIS: Too many adapter "
+                                  "descriptions");
+                       os_free(names);
+                       return NULL;
+               }
+               if (*pos == '\0') {
+                       wpa_printf(MSG_DEBUG, "NDIS: %d adapter descriptions "
+                                  "found", num_name);
+                       pos++;
+                       break;
+               }
+       }
+
+       /*
+        * Windows 98 with Packet.dll 3.0 alpha3 does not include adapter
+        * descriptions. Fill in dummy descriptors to work around this.
+        */
+       while (num_desc < num_name)
+               desc[num_desc++] = "dummy description";
+
+       if (num_name != num_desc) {
+               wpa_printf(MSG_DEBUG, "NDIS: mismatch in adapter name and "
+                          "description counts (%d != %d)",
+                          num_name, num_desc);
+               os_free(names);
+               return NULL;
+       }
+
+       for (i = 0; i < num_name; i++) {
+               niface = os_zalloc(sizeof(*niface));
+               if (niface == NULL)
+                       break;
+               niface->drv_name = "ndis";
+               if (os_strncmp(name[i], "\\Device\\NPF_", 12) == 0)
+                       niface->ifname = os_strdup(name[i] + 12);
+               else
+                       niface->ifname = os_strdup(name[i]);
+               if (niface->ifname == NULL) {
+                       os_free(niface);
+                       break;
+               }
+               niface->desc = os_strdup(desc[i]);
+               niface->next = iface;
+               iface = niface;
+       }
+
+#endif /* CONFIG_USE_NDISUIO */
+
+       return iface;
+}
+
+
+const struct wpa_driver_ops wpa_driver_ndis_ops = {
+       "ndis",
+       "Windows NDIS driver",
+       wpa_driver_ndis_get_bssid,
+       wpa_driver_ndis_get_ssid,
+       wpa_driver_ndis_set_key,
+       wpa_driver_ndis_init,
+       wpa_driver_ndis_deinit,
+       NULL /* set_param */,
+       NULL /* set_countermeasures */,
+       wpa_driver_ndis_deauthenticate,
+       wpa_driver_ndis_disassociate,
+       wpa_driver_ndis_associate,
+       wpa_driver_ndis_add_pmkid,
+       wpa_driver_ndis_remove_pmkid,
+       wpa_driver_ndis_flush_pmkid,
+       wpa_driver_ndis_get_capa,
+       wpa_driver_ndis_poll,
+       wpa_driver_ndis_get_ifname,
+       wpa_driver_ndis_get_mac_addr,
+       NULL /* send_eapol */,
+       NULL /* set_operstate */,
+       NULL /* mlme_setprotection */,
+       NULL /* get_hw_feature_data */,
+       NULL /* set_channel */,
+       NULL /* set_ssid */,
+       NULL /* set_bssid */,
+       NULL /* send_mlme */,
+       NULL /* mlme_add_sta */,
+       NULL /* mlme_remove_sta */,
+       NULL /* update_ft_ies */,
+       NULL /* send_ft_action */,
+       wpa_driver_ndis_get_scan_results,
+       NULL /* set_country */,
+       NULL /* global_init */,
+       NULL /* global_deinit */,
+       NULL /* init2 */,
+       wpa_driver_ndis_get_interfaces,
+       wpa_driver_ndis_scan,
+       NULL /* authenticate */,
+       NULL /* set_beacon */,
+       NULL /* hapd_init */,
+       NULL /* hapd_deinit */,
+       NULL /* set_ieee8021x */,
+       NULL /* set_privacy */,
+       NULL /* get_seqnum */,
+       NULL /* flush */,
+       NULL /* set_generic_elem */,
+       NULL /* read_sta_data */,
+       NULL /* hapd_send_eapol */,
+       NULL /* sta_deauth */,
+       NULL /* sta_disassoc */,
+       NULL /* sta_remove */,
+       NULL /* hapd_get_ssid */,
+       NULL /* hapd_set_ssid */,
+       NULL /* hapd_set_countermeasures */,
+       NULL /* sta_add */,
+       NULL /* get_inact_sec */,
+       NULL /* sta_clear_stats */,
+       NULL /* set_freq */,
+       NULL /* set_rts */,
+       NULL /* set_frag */,
+       NULL /* sta_set_flags */,
+       NULL /* set_rate_sets */,
+       NULL /* set_cts_protect */,
+       NULL /* set_preamble */,
+       NULL /* set_short_slot_time */,
+       NULL /* set_tx_queue_params */,
+       NULL /* valid_bss_mask */,
+       NULL /* if_add */,
+       NULL /* if_remove */,
+       NULL /* set_sta_vlan */,
+       NULL /* commit */,
+       NULL /* send_ether */,
+       NULL /* set_radius_acl_auth */,
+       NULL /* set_radius_acl_expire */,
+       NULL /* set_ht_params */,
+       NULL /* set_ap_wps_ie */,
+       NULL /* set_supp_port */,
+       NULL /* set_wds_sta */,
+       NULL /* send_action */,
+       NULL /* remain_on_channel */,
+       NULL /* cancel_remain_on_channel */,
+       NULL /* probe_req_report */,
+       NULL /* disable_11b_rates */,
+       NULL /* deinit_ap */,
+       NULL /* suspend */,
+       NULL /* resume */,
+       NULL /* signal_monitor */,
+       NULL /* send_frame */
+};
diff --git a/src/drivers/driver_ndis.h b/src/drivers/driver_ndis.h
new file mode 100644 (file)
index 0000000..f263f0e
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface
+ * Copyright (c) 2004-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 DRIVER_NDIS_H
+#define DRIVER_NDIS_H
+
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+struct ndis_events_data;
+struct ndis_events_data * ndis_events_init(HANDLE *read_pipe, HANDLE *event,
+                                          const char *ifname,
+                                          const char *desc);
+void ndis_events_deinit(struct ndis_events_data *events);
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+
+struct ndis_pmkid_entry {
+       struct ndis_pmkid_entry *next;
+       u8 bssid[ETH_ALEN];
+       u8 pmkid[16];
+};
+
+struct wpa_driver_ndis_data {
+       void *ctx;
+       char ifname[100]; /* GUID: {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D} */
+#ifdef _WIN32_WCE
+       TCHAR *adapter_name;
+       HANDLE event_queue; /* NDISUIO notifier MsgQueue */
+       HANDLE connected_event; /* WpaSupplicantConnected event */
+#endif /* _WIN32_WCE */
+       u8 own_addr[ETH_ALEN];
+#ifdef CONFIG_USE_NDISUIO
+       HANDLE ndisuio;
+#else /* CONFIG_USE_NDISUIO */
+       LPADAPTER adapter;
+#endif /* CONFIG_USE_NDISUIO */
+       u8 bssid[ETH_ALEN];
+
+       int has_capability;
+       int no_of_pmkid;
+       int radio_enabled;
+       struct wpa_driver_capa capa;
+       struct ndis_pmkid_entry *pmkid;
+       char *adapter_desc;
+       int wired;
+       int native80211;
+       int mode;
+       int wzc_disabled;
+       int oid_bssid_set;
+#ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+       HANDLE events_pipe, event_avail;
+       struct ndis_events_data *events;
+#endif /* CONFIG_NDIS_EVENTS_INTEGRATED */
+};
+
+#endif /* DRIVER_NDIS_H */
diff --git a/src/drivers/driver_ndis_.c b/src/drivers/driver_ndis_.c
new file mode 100644 (file)
index 0000000..4bee9aa
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * WPA Supplicant - Windows/NDIS driver interface - event processing
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+
+/* Keep this event processing in a separate file and without WinPcap headers to
+ * avoid conflicts with some of the header files. */
+struct _ADAPTER;
+typedef struct _ADAPTER * LPADAPTER;
+#include "driver_ndis.h"
+
+
+void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv,
+                                         const u8 *data, size_t data_len);
+void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv);
+void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv);
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT,
+                  EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL,
+                  EVENT_ADAPTER_REMOVAL };
+
+/* Event data:
+ * enum event_types (as int, i.e., 4 octets)
+ * data length (2 octets (big endian), optional)
+ * data (variable len, optional)
+ */
+
+
+static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv,
+                                         u8 *buf, size_t len)
+{
+       u8 *pos, *data = NULL;
+       enum event_types type;
+       size_t data_len = 0;
+
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len);
+       if (len < sizeof(int))
+               return;
+       type = *((int *) buf);
+       pos = buf + sizeof(int);
+       wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type);
+
+       if (buf + len - pos > 2) {
+               data_len = (int) *pos++ << 8;
+               data_len += *pos++;
+               if (data_len > (size_t) (buf + len - pos)) {
+                       wpa_printf(MSG_DEBUG, "NDIS: event data overflow");
+                       return;
+               }
+               data = pos;
+               wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len);
+       }
+
+       switch (type) {
+       case EVENT_CONNECT:
+               wpa_driver_ndis_event_connect(drv);
+               break;
+       case EVENT_DISCONNECT:
+               wpa_driver_ndis_event_disconnect(drv);
+               break;
+       case EVENT_MEDIA_SPECIFIC:
+               wpa_driver_ndis_event_media_specific(drv, data, data_len);
+               break;
+       case EVENT_ADAPTER_ARRIVAL:
+               wpa_driver_ndis_event_adapter_arrival(drv);
+               break;
+       case EVENT_ADAPTER_REMOVAL:
+               wpa_driver_ndis_event_adapter_removal(drv);
+               break;
+       }
+}
+
+
+void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data)
+{
+       struct wpa_driver_ndis_data *drv = eloop_data;
+       u8 buf[512];
+       DWORD len;
+
+       ResetEvent(drv->event_avail);
+       if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL))
+               wpa_driver_ndis_event_process(drv, buf, len);
+       else {
+               wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__,
+                          (int) GetLastError());
+       }
+}
diff --git a/src/drivers/driver_ndiswrapper.c b/src/drivers/driver_ndiswrapper.c
new file mode 100644 (file)
index 0000000..cd2f61e
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * WPA Supplicant - driver interaction with Linux ndiswrapper
+ * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu>
+ * Copyright (c) 2004-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.
+ *
+ * Please note that ndiswrapper supports WPA configuration via Linux wireless
+ * extensions and if the kernel includes support for this, driver_wext.c should
+ * be used instead of this driver wrapper.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+struct wpa_driver_ndiswrapper_data {
+       void *wext; /* private data for driver_wext */
+       void *ctx;
+       char ifname[IFNAMSIZ + 1];
+       int sock;
+};
+
+
+struct wpa_key {
+       enum wpa_alg alg;
+       const u8 *addr;
+       int key_index;
+       int set_tx;
+       const u8 *seq;
+       size_t seq_len;
+       const u8 *key;
+       size_t key_len;
+};
+
+struct wpa_assoc_info {
+       const u8 *bssid;
+       const u8 *ssid;
+       size_t ssid_len;
+       int freq;
+       const u8 *wpa_ie;
+       size_t wpa_ie_len;
+       enum wpa_cipher pairwise_suite;
+       enum wpa_cipher group_suite;
+       enum wpa_key_mgmt key_mgmt_suite;
+       int auth_alg;
+       int mode;
+};
+
+#define PRIV_RESET                     SIOCIWFIRSTPRIV+0
+#define WPA_SET_WPA                    SIOCIWFIRSTPRIV+1
+#define WPA_SET_KEY                    SIOCIWFIRSTPRIV+2
+#define WPA_ASSOCIATE                  SIOCIWFIRSTPRIV+3
+#define WPA_DISASSOCIATE               SIOCIWFIRSTPRIV+4
+#define WPA_DROP_UNENCRYPTED           SIOCIWFIRSTPRIV+5
+#define WPA_SET_COUNTERMEASURES        SIOCIWFIRSTPRIV+6
+#define WPA_DEAUTHENTICATE             SIOCIWFIRSTPRIV+7
+#define WPA_SET_AUTH_ALG               SIOCIWFIRSTPRIV+8
+#define WPA_INIT                       SIOCIWFIRSTPRIV+9
+#define WPA_DEINIT                     SIOCIWFIRSTPRIV+10
+#define WPA_GET_CAPA                   SIOCIWFIRSTPRIV+11
+
+static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg);
+
+static int get_socket(void)
+{
+       static const int families[] = {
+               AF_INET, AF_IPX, AF_AX25, AF_APPLETALK
+       };
+       unsigned int i;
+       int sock;
+
+       for (i = 0; i < sizeof(families) / sizeof(int); ++i) {
+               sock = socket(families[i], SOCK_DGRAM, 0);
+               if (sock >= 0)
+                       return sock;
+       }
+
+       return -1;
+}
+
+static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request,
+                     struct iwreq *pwrq)
+{
+       os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ);
+       return ioctl(drv->sock, request, pwrq);
+}
+
+static int wpa_ndiswrapper_set_wpa(void *priv, int enabled)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       struct iwreq priv_req;
+       int ret = 0;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       priv_req.u.data.flags = enabled;
+       if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int wpa_ndiswrapper_set_key(const char *ifname, void *priv,
+                                  enum wpa_alg alg, const u8 *addr,
+                                  int key_idx, int set_tx,
+                                  const u8 *seq, size_t seq_len,
+                                  const u8 *key, size_t key_len)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       struct wpa_key wpa_key;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       wpa_key.alg = alg;
+       wpa_key.addr = addr;
+       wpa_key.key_index = key_idx;
+       wpa_key.set_tx = set_tx;
+       wpa_key.seq = seq;
+       wpa_key.seq_len = seq_len;
+       wpa_key.key = key;
+       wpa_key.key_len = key_len;
+
+       priv_req.u.data.pointer = (void *)&wpa_key;
+       priv_req.u.data.length = sizeof(wpa_key);
+
+       if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0)
+               ret = -1;
+
+       if (alg == WPA_ALG_NONE) {
+               /*
+                * ndiswrapper did not seem to be clearing keys properly in
+                * some cases with WPA_SET_KEY. For example, roaming from WPA
+                * enabled AP to plaintext one seemed to fail since the driver
+                * did not associate. Try to make sure the keys are cleared so
+                * that plaintext APs can be used in all cases.
+                */
+               wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx,
+                                       set_tx, seq, seq_len, key, key_len);
+       }
+
+       return ret;
+}
+
+static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       priv_req.u.param.value = enabled;
+       if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+static int wpa_ndiswrapper_set_drop_unencrypted(void *priv,
+                                               int enabled)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       priv_req.u.param.value = enabled;
+       if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       priv_req.u.param.value = reason_code;
+       os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
+       if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr,
+                                       int reason_code)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN);
+       if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int
+wpa_ndiswrapper_associate(void *priv,
+                         struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct wpa_assoc_info wpa_assoc_info;
+       struct iwreq priv_req;
+
+       if (wpa_ndiswrapper_set_drop_unencrypted(drv,
+                                                params->drop_unencrypted) < 0)
+               ret = -1;
+       if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+       os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info));
+
+       wpa_assoc_info.bssid = params->bssid;
+       wpa_assoc_info.ssid = params->ssid;
+       wpa_assoc_info.ssid_len = params->ssid_len;
+       wpa_assoc_info.freq = params->freq;
+       wpa_assoc_info.wpa_ie = params->wpa_ie;
+       wpa_assoc_info.wpa_ie_len = params->wpa_ie_len;
+       wpa_assoc_info.pairwise_suite = params->pairwise_suite;
+       wpa_assoc_info.group_suite = params->group_suite;
+       wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite;
+       wpa_assoc_info.auth_alg = params->auth_alg;
+       wpa_assoc_info.mode = params->mode;
+
+       priv_req.u.data.pointer = (void *)&wpa_assoc_info;
+       priv_req.u.data.length = sizeof(wpa_assoc_info);
+
+       if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       priv_req.u.param.value = auth_alg;
+       if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0)
+               ret = -1;
+       return ret;
+}
+
+static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       return wpa_driver_wext_get_bssid(drv->wext, bssid);
+}
+
+
+static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       return wpa_driver_wext_get_ssid(drv->wext, ssid);
+}
+
+
+static int wpa_ndiswrapper_scan(void *priv,
+                               struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       return wpa_driver_wext_scan(drv->wext, params);
+}
+
+
+static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       return wpa_driver_wext_get_scan_results(drv->wext);
+}
+
+
+static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       int ret = 0;
+       struct iwreq priv_req;
+
+       os_memset(&priv_req, 0, sizeof(priv_req));
+
+       priv_req.u.data.pointer = (void *) capa;
+       priv_req.u.data.length = sizeof(*capa);
+       if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0)
+               ret = -1;
+       return ret;
+       
+}
+
+
+static int wpa_ndiswrapper_set_operstate(void *priv, int state)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       return wpa_driver_wext_set_operstate(drv->wext, state);
+}
+
+
+static void * wpa_ndiswrapper_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_ndiswrapper_data *drv;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->wext = wpa_driver_wext_init(ctx, ifname);
+       if (drv->wext == NULL) {
+               os_free(drv);
+               return NULL;
+       }
+
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->sock = get_socket();
+       if (drv->sock < 0) {
+               wpa_driver_wext_deinit(drv->wext);
+               os_free(drv);
+               return NULL;
+       }
+
+       wpa_ndiswrapper_set_wpa(drv, 1);
+
+       return drv;
+}
+
+
+static void wpa_ndiswrapper_deinit(void *priv)
+{
+       struct wpa_driver_ndiswrapper_data *drv = priv;
+       wpa_ndiswrapper_set_wpa(drv, 0);
+       wpa_driver_wext_deinit(drv->wext);
+       close(drv->sock);
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = {
+       .name = "ndiswrapper",
+       .desc = "Linux ndiswrapper (deprecated; use wext)",
+       .set_key = wpa_ndiswrapper_set_key,
+       .set_countermeasures = wpa_ndiswrapper_set_countermeasures,
+       .deauthenticate = wpa_ndiswrapper_deauthenticate,
+       .disassociate = wpa_ndiswrapper_disassociate,
+       .associate = wpa_ndiswrapper_associate,
+
+       .get_bssid = wpa_ndiswrapper_get_bssid,
+       .get_ssid = wpa_ndiswrapper_get_ssid,
+       .scan2 = wpa_ndiswrapper_scan,
+       .get_scan_results2 = wpa_ndiswrapper_get_scan_results,
+       .init = wpa_ndiswrapper_init,
+       .deinit = wpa_ndiswrapper_deinit,
+       .get_capa = wpa_ndiswrapper_get_capa,
+       .set_operstate = wpa_ndiswrapper_set_operstate,
+};
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
new file mode 100644 (file)
index 0000000..364158c
--- /dev/null
@@ -0,0 +1,5390 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211
+ * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * 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 <sys/ioctl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+#include "nl80211_copy.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "radiotap.h"
+#include "radiotap_iter.h"
+#include "driver.h"
+
+#ifdef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
+#define nl_handle_alloc_cb nl_socket_alloc_cb
+#define nl_handle_destroy nl_socket_free
+#endif /* CONFIG_LIBNL20 */
+
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+struct i802_bss {
+       struct wpa_driver_nl80211_data *drv;
+       struct i802_bss *next;
+       int ifindex;
+       char ifname[IFNAMSIZ + 1];
+       unsigned int beacon_set:1;
+};
+
+struct wpa_driver_nl80211_data {
+       void *ctx;
+       struct netlink_data *netlink;
+       int ioctl_sock; /* socket for ioctl() use */
+       char brname[IFNAMSIZ];
+       int ifindex;
+       int if_removed;
+       struct wpa_driver_capa capa;
+       int has_capability;
+
+       int operstate;
+
+       int scan_complete_events;
+
+       struct nl_handle *nl_handle;
+       struct nl_handle *nl_handle_event;
+       struct nl_cache *nl_cache;
+       struct nl_cache *nl_cache_event;
+       struct nl_cb *nl_cb;
+       struct genl_family *nl80211;
+
+       u8 auth_bssid[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+       int associated;
+       u8 ssid[32];
+       size_t ssid_len;
+       int nlmode;
+       int ap_scan_as_station;
+       unsigned int assoc_freq;
+
+       int monitor_sock;
+       int monitor_ifidx;
+       int probe_req_report;
+       int disable_11b_rates;
+
+       unsigned int pending_remain_on_chan:1;
+       unsigned int added_bridge:1;
+       unsigned int added_if_into_bridge:1;
+
+       u64 remain_on_chan_cookie;
+       u64 send_action_cookie;
+
+       struct wpa_driver_scan_filter *filter_ssids;
+       size_t num_filter_ssids;
+
+       struct i802_bss first_bss;
+
+#ifdef HOSTAPD
+       int eapol_sock; /* socket for EAPOL frames */
+
+       int default_if_indices[16];
+       int *if_indices;
+       int num_if_indices;
+
+       int last_freq;
+       int last_freq_ht;
+#endif /* HOSTAPD */
+};
+
+
+static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
+                                           void *timeout_ctx);
+static int wpa_driver_nl80211_set_mode(void *priv, int mode);
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
+static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *addr, int cmd, u16 reason_code,
+                                  int local_state_change);
+static void nl80211_remove_monitor_interface(
+       struct wpa_driver_nl80211_data *drv);
+
+#ifdef HOSTAPD
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
+static int wpa_driver_nl80211_if_remove(void *priv,
+                                       enum wpa_driver_if_type type,
+                                       const char *ifname);
+#else /* HOSTAPD */
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       return 0;
+}
+#endif /* HOSTAPD */
+
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq);
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled);
+
+
+/* nl80211 code */
+static int ack_handler(struct nl_msg *msg, void *arg)
+{
+       int *err = arg;
+       *err = 0;
+       return NL_STOP;
+}
+
+static int finish_handler(struct nl_msg *msg, void *arg)
+{
+       int *ret = arg;
+       *ret = 0;
+       return NL_SKIP;
+}
+
+static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err,
+                        void *arg)
+{
+       int *ret = arg;
+       *ret = err->error;
+       return NL_SKIP;
+}
+
+
+static int no_seq_check(struct nl_msg *msg, void *arg)
+{
+       return NL_OK;
+}
+
+
+static int send_and_recv(struct wpa_driver_nl80211_data *drv,
+                        struct nl_handle *nl_handle, struct nl_msg *msg,
+                        int (*valid_handler)(struct nl_msg *, void *),
+                        void *valid_data)
+{
+       struct nl_cb *cb;
+       int err = -ENOMEM;
+
+       cb = nl_cb_clone(drv->nl_cb);
+       if (!cb)
+               goto out;
+
+       err = nl_send_auto_complete(nl_handle, msg);
+       if (err < 0)
+               goto out;
+
+       err = 1;
+
+       nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err);
+       nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err);
+       nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err);
+
+       if (valid_handler)
+               nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
+                         valid_handler, valid_data);
+
+       while (err > 0)
+               nl_recvmsgs(nl_handle, cb);
+ out:
+       nl_cb_put(cb);
+       nlmsg_free(msg);
+       return err;
+}
+
+
+static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+                             struct nl_msg *msg,
+                             int (*valid_handler)(struct nl_msg *, void *),
+                             void *valid_data)
+{
+       return send_and_recv(drv, drv->nl_handle, msg, valid_handler,
+                            valid_data);
+}
+
+
+struct family_data {
+       const char *group;
+       int id;
+};
+
+
+static int family_handler(struct nl_msg *msg, void *arg)
+{
+       struct family_data *res = arg;
+       struct nlattr *tb[CTRL_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *mcgrp;
+       int i;
+
+       nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[CTRL_ATTR_MCAST_GROUPS])
+               return NL_SKIP;
+
+       nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) {
+               struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1];
+               nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp),
+                         nla_len(mcgrp), NULL);
+               if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] ||
+                   !tb2[CTRL_ATTR_MCAST_GRP_ID] ||
+                   os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]),
+                              res->group,
+                              nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0)
+                       continue;
+               res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]);
+               break;
+       };
+
+       return NL_SKIP;
+}
+
+
+static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv,
+                              const char *family, const char *group)
+{
+       struct nl_msg *msg;
+       int ret = -1;
+       struct family_data res = { group, -ENOENT };
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+       genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"),
+                   0, 0, CTRL_CMD_GETFAMILY, 0);
+       NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+
+       ret = send_and_recv_msgs(drv, msg, family_handler, &res);
+       msg = NULL;
+       if (ret == 0)
+               ret = res.id;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (!drv->associated)
+               return -1;
+       os_memcpy(bssid, drv->bssid, ETH_ALEN);
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (!drv->associated)
+               return -1;
+       os_memcpy(ssid, drv->ssid, drv->ssid_len);
+       return drv->ssid_len;
+}
+
+
+static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
+                                         char *buf, size_t len, int del)
+{
+       union wpa_event_data event;
+
+       os_memset(&event, 0, sizeof(event));
+       if (len > sizeof(event.interface_status.ifname))
+               len = sizeof(event.interface_status.ifname) - 1;
+       os_memcpy(event.interface_status.ifname, buf, len);
+       event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
+               EVENT_INTERFACE_ADDED;
+
+       wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
+                  del ? "DEL" : "NEW",
+                  event.interface_status.ifname,
+                  del ? "removed" : "added");
+
+       if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
+               if (del)
+                       drv->if_removed = 1;
+               else
+                       drv->if_removed = 0;
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
+                                        u8 *buf, size_t len)
+{
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_IFNAME) {
+                       if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname)
+                           == 0)
+                               return 1;
+                       else
+                               break;
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
+                                         int ifindex, u8 *buf, size_t len)
+{
+       if (drv->ifindex == ifindex)
+               return 1;
+
+       if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
+               drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname);
+               wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
+                          "interface");
+               wpa_driver_nl80211_finish_drv_init(drv);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
+                                                struct ifinfomsg *ifi,
+                                                u8 *buf, size_t len)
+{
+       struct wpa_driver_nl80211_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+       u32 brid = 0;
+
+       if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, buf, len) &&
+           !have_ifidx(drv, ifi->ifi_index)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
+                          "ifindex %d", ifi->ifi_index);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
+                  "(%s%s%s%s)",
+                  drv->operstate, ifi->ifi_flags,
+                  (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+                  (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+                  (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+                  (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+       /*
+        * Some drivers send the association event before the operup event--in
+        * this case, lifting operstate in wpa_driver_nl80211_set_operstate()
+        * fails. This will hit us when wpa_supplicant does not need to do
+        * IEEE 802.1X authentication
+        */
+       if (drv->operstate == 1 &&
+           (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
+           !(ifi->ifi_flags & IFF_RUNNING))
+               netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                                      -1, IF_OPER_UP);
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_IFNAME) {
+                       wpa_driver_nl80211_event_link(
+                               drv,
+                               ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len, 0);
+               } else if (attr->rta_type == IFLA_MASTER)
+                       brid = nla_get_u32((struct nlattr *) attr);
+               attr = RTA_NEXT(attr, attrlen);
+       }
+
+#ifdef HOSTAPD
+       if (ifi->ifi_family == AF_BRIDGE && brid) {
+               /* device has been added to bridge */
+               char namebuf[IFNAMSIZ];
+               if_indextoname(brid, namebuf);
+               wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
+                          brid, namebuf);
+               add_ifidx(drv, brid);
+       }
+#endif /* HOSTAPD */
+}
+
+
+static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
+                                                struct ifinfomsg *ifi,
+                                                u8 *buf, size_t len)
+{
+       struct wpa_driver_nl80211_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+       u32 brid = 0;
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_IFNAME) {
+                       wpa_driver_nl80211_event_link(
+                               drv,
+                               ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len, 1);
+               } else if (attr->rta_type == IFLA_MASTER)
+                       brid = nla_get_u32((struct nlattr *) attr);
+               attr = RTA_NEXT(attr, attrlen);
+       }
+
+#ifdef HOSTAPD
+       if (ifi->ifi_family == AF_BRIDGE && brid) {
+               /* device has been removed from bridge */
+               char namebuf[IFNAMSIZ];
+               if_indextoname(brid, namebuf);
+               wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge "
+                          "%s", brid, namebuf);
+               del_ifidx(drv, brid);
+       }
+#endif /* HOSTAPD */
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+                           const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len < 24 + sizeof(mgmt->u.auth)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+                          "frame");
+               return;
+       }
+
+       os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+       os_memset(&event, 0, sizeof(event));
+       os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+       event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+       event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+       if (len > 24 + sizeof(mgmt->u.auth)) {
+               event.auth.ies = mgmt->u.auth.variable;
+               event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+                           const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 status;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+                          "frame");
+               return;
+       }
+
+       status = le_to_host16(mgmt->u.assoc_resp.status_code);
+       if (status != WLAN_STATUS_SUCCESS) {
+               os_memset(&event, 0, sizeof(event));
+               if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+                       event.assoc_reject.resp_ies =
+                               (u8 *) mgmt->u.assoc_resp.variable;
+                       event.assoc_reject.resp_ies_len =
+                               len - 24 - sizeof(mgmt->u.assoc_resp);
+               }
+               event.assoc_reject.status_code = status;
+
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+               return;
+       }
+
+       drv->associated = 1;
+       os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+
+       os_memset(&event, 0, sizeof(event));
+       if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+               event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+               event.assoc_info.resp_ies_len =
+                       len - 24 - sizeof(mgmt->u.assoc_resp);
+       }
+
+       event.assoc_info.freq = drv->assoc_freq;
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+                              enum nl80211_commands cmd, struct nlattr *status,
+                              struct nlattr *addr, struct nlattr *req_ie,
+                              struct nlattr *resp_ie)
+{
+       union wpa_event_data event;
+
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+               /*
+                * Avoid reporting two association events that would confuse
+                * the core code.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+                          "when using userspace SME", cmd);
+               return;
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       if (cmd == NL80211_CMD_CONNECT &&
+           nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
+               if (resp_ie) {
+                       event.assoc_reject.resp_ies = nla_data(resp_ie);
+                       event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+               }
+               event.assoc_reject.status_code = nla_get_u16(status);
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+               return;
+       }
+
+       drv->associated = 1;
+       if (addr)
+               os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+
+       if (req_ie) {
+               event.assoc_info.req_ies = nla_data(req_ie);
+               event.assoc_info.req_ies_len = nla_len(req_ie);
+       }
+       if (resp_ie) {
+               event.assoc_info.resp_ies = nla_data(resp_ie);
+               event.assoc_info.resp_ies_len = nla_len(resp_ie);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+                              enum nl80211_commands cmd, struct nlattr *addr)
+{
+       union wpa_event_data event;
+       enum wpa_event_type ev;
+
+       if (nla_len(addr) != ETH_ALEN)
+               return;
+
+       wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+                  cmd, MAC2STR((u8 *) nla_data(addr)));
+
+       if (cmd == NL80211_CMD_AUTHENTICATE)
+               ev = EVENT_AUTH_TIMED_OUT;
+       else if (cmd == NL80211_CMD_ASSOCIATE)
+               ev = EVENT_ASSOC_TIMED_OUT;
+       else
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+       wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+
+static void mlme_event_action(struct wpa_driver_nl80211_data *drv,
+                             struct nlattr *freq, const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 fc, stype;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len < 24) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short action frame");
+               return;
+       }
+
+       fc = le_to_host16(mgmt->frame_control);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_action.da = mgmt->da;
+       event.rx_action.sa = mgmt->sa;
+       event.rx_action.bssid = mgmt->bssid;
+       event.rx_action.category = mgmt->u.action.category;
+       event.rx_action.data = &mgmt->u.action.category + 1;
+       event.rx_action.len = frame + len - event.rx_action.data;
+       if (freq)
+               event.rx_action.freq = nla_get_u32(freq);
+       wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
+}
+
+
+static void mlme_event_action_tx_status(struct wpa_driver_nl80211_data *drv,
+                                       struct nlattr *cookie, const u8 *frame,
+                                       size_t len, struct nlattr *ack)
+{
+       union wpa_event_data event;
+       const struct ieee80211_hdr *hdr;
+       u16 fc;
+       u64 cookie_val;
+
+       if (!cookie)
+               return;
+
+       cookie_val = nla_get_u64(cookie);
+       wpa_printf(MSG_DEBUG, "nl80211: Action TX status: cookie=0%llx%s",
+                  (long long unsigned int) cookie_val,
+                  cookie_val == drv->send_action_cookie ?
+                  " (match)" : " (unknown)");
+       if (cookie_val != drv->send_action_cookie)
+               return;
+
+       hdr = (const struct ieee80211_hdr *) frame;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = frame;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ack != NULL;
+       wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+                                      enum wpa_event_type type,
+                                      const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       const u8 *bssid = NULL;
+       u16 reason_code = 0;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len >= 24) {
+               bssid = mgmt->bssid;
+
+               if (drv->associated != 0 &&
+                   os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+                   os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+                       /*
+                        * We have presumably received this deauth as a
+                        * response to a clear_state_mismatch() outgoing
+                        * deauth.  Don't let it take us offline!
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+                                  "from Unknown BSSID " MACSTR " -- ignoring",
+                                  MAC2STR(bssid));
+                       return;
+               }
+       }
+
+       drv->associated = 0;
+       os_memset(&event, 0, sizeof(event));
+
+       /* Note: Same offset for Reason Code in both frame subtypes */
+       if (len >= 24 + sizeof(mgmt->u.deauth))
+               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+       if (type == EVENT_DISASSOC) {
+               event.disassoc_info.addr = bssid;
+               event.disassoc_info.reason_code = reason_code;
+       } else {
+               event.deauth_info.addr = bssid;
+               event.deauth_info.reason_code = reason_code;
+       }
+
+       wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event(struct wpa_driver_nl80211_data *drv,
+                      enum nl80211_commands cmd, struct nlattr *frame,
+                      struct nlattr *addr, struct nlattr *timed_out,
+                      struct nlattr *freq, struct nlattr *ack,
+                      struct nlattr *cookie)
+{
+       if (timed_out && addr) {
+               mlme_timeout_event(drv, cmd, addr);
+               return;
+       }
+
+       if (frame == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: MLME event %d without frame "
+                          "data", cmd);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: MLME event %d", cmd);
+       wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+                   nla_data(frame), nla_len(frame));
+
+       switch (cmd) {
+       case NL80211_CMD_AUTHENTICATE:
+               mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_ASSOCIATE:
+               mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_DEAUTHENTICATE:
+               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+                                          nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_DISASSOCIATE:
+               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+                                          nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_ACTION:
+               mlme_event_action(drv, freq, nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_ACTION_TX_STATUS:
+               mlme_event_action_tx_status(drv, cookie, nla_data(frame),
+                                           nla_len(frame), ack);
+               break;
+       default:
+               break;
+       }
+}
+
+
+static void mlme_event_michael_mic_failure(struct wpa_driver_nl80211_data *drv,
+                                          struct nlattr *tb[])
+{
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
+       os_memset(&data, 0, sizeof(data));
+       if (tb[NL80211_ATTR_MAC]) {
+               wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
+                           nla_data(tb[NL80211_ATTR_MAC]),
+                           nla_len(tb[NL80211_ATTR_MAC]));
+               data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
+       }
+       if (tb[NL80211_ATTR_KEY_SEQ]) {
+               wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
+                           nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+                           nla_len(tb[NL80211_ATTR_KEY_SEQ]));
+       }
+       if (tb[NL80211_ATTR_KEY_TYPE]) {
+               enum nl80211_key_type key_type =
+                       nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
+               wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
+               if (key_type == NL80211_KEYTYPE_PAIRWISE)
+                       data.michael_mic_failure.unicast = 1;
+       } else
+               data.michael_mic_failure.unicast = 1;
+
+       if (tb[NL80211_ATTR_KEY_IDX]) {
+               u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
+               wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+                                struct nlattr *tb[])
+{
+       if (tb[NL80211_ATTR_MAC] == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+                          "event");
+               return;
+       }
+       os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+       drv->associated = 1;
+       wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
+                  MAC2STR(drv->bssid));
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+                                        int cancel_event, struct nlattr *tb[])
+{
+       unsigned int freq, chan_type, duration;
+       union wpa_event_data data;
+       u64 cookie;
+
+       if (tb[NL80211_ATTR_WIPHY_FREQ])
+               freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+       else
+               freq = 0;
+
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+               chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+       else
+               chan_type = 0;
+
+       if (tb[NL80211_ATTR_DURATION])
+               duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+       else
+               duration = 0;
+
+       if (tb[NL80211_ATTR_COOKIE])
+               cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       else
+               cookie = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+                  "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+                  cancel_event, freq, chan_type, duration,
+                  (long long unsigned int) cookie,
+                  cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+       if (cookie != drv->remain_on_chan_cookie)
+               return; /* not for us */
+
+       drv->pending_remain_on_chan = !cancel_event;
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = freq;
+       data.remain_on_channel.duration = duration;
+       wpa_supplicant_event(drv->ctx, cancel_event ?
+                            EVENT_CANCEL_REMAIN_ON_CHANNEL :
+                            EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+                           struct nlattr *tb[])
+{
+       union wpa_event_data event;
+       struct nlattr *nl;
+       int rem;
+       struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+       int freqs[MAX_REPORT_FREQS];
+       int num_freqs = 0;
+
+       os_memset(&event, 0, sizeof(event));
+       info = &event.scan_info;
+       info->aborted = aborted;
+
+       if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+                       struct wpa_driver_scan_ssid *s =
+                               &info->ssids[info->num_ssids];
+                       s->ssid = nla_data(nl);
+                       s->ssid_len = nla_len(nl);
+                       info->num_ssids++;
+                       if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+                               break;
+               }
+       }
+       if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+               {
+                       freqs[num_freqs] = nla_get_u32(nl);
+                       num_freqs++;
+                       if (num_freqs == MAX_REPORT_FREQS - 1)
+                               break;
+               }
+               info->freqs = freqs;
+               info->num_freqs = num_freqs;
+       }
+       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+                             struct nlattr *tb[])
+{
+       static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+               [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+               [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+               [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+       };
+       struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+       enum nl80211_cqm_rssi_threshold_event event;
+       union wpa_event_data ed;
+
+       if (tb[NL80211_ATTR_CQM] == NULL ||
+           nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+                            cqm_policy)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+               return;
+       }
+
+       if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+               return;
+       event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+       os_memset(&ed, 0, sizeof(ed));
+
+       if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: RSSI high");
+               ed.signal_change.above_threshold = 1;
+       } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: RSSI low");
+               ed.signal_change.above_threshold = 0;
+       } else
+               return;
+
+       wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static int process_event(struct nl_msg *msg, void *arg)
+{
+       struct wpa_driver_nl80211_data *drv = arg;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       union wpa_event_data data;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_IFINDEX]) {
+               int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+               if (ifindex != drv->ifindex && !have_ifidx(drv, ifindex)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)"
+                                  " for foreign interface (ifindex %d)",
+                                  gnlh->cmd, ifindex);
+                       return NL_SKIP;
+               }
+       }
+
+       if (drv->ap_scan_as_station &&
+           (gnlh->cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+            gnlh->cmd == NL80211_CMD_SCAN_ABORTED)) {
+               wpa_driver_nl80211_set_mode(&drv->first_bss,
+                                           IEEE80211_MODE_AP);
+               drv->ap_scan_as_station = 0;
+       }
+
+       switch (gnlh->cmd) {
+       case NL80211_CMD_TRIGGER_SCAN:
+               wpa_printf(MSG_DEBUG, "nl80211: Scan trigger");
+               break;
+       case NL80211_CMD_NEW_SCAN_RESULTS:
+               wpa_printf(MSG_DEBUG, "nl80211: New scan results available");
+               drv->scan_complete_events = 1;
+               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+                                    drv->ctx);
+               send_scan_event(drv, 0, tb);
+               break;
+       case NL80211_CMD_SCAN_ABORTED:
+               wpa_printf(MSG_DEBUG, "nl80211: Scan aborted");
+               /*
+                * Need to indicate that scan results are available in order
+                * not to make wpa_supplicant stop its scanning.
+                */
+               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+                                    drv->ctx);
+               send_scan_event(drv, 1, tb);
+               break;
+       case NL80211_CMD_AUTHENTICATE:
+       case NL80211_CMD_ASSOCIATE:
+       case NL80211_CMD_DEAUTHENTICATE:
+       case NL80211_CMD_DISASSOCIATE:
+       case NL80211_CMD_ACTION:
+       case NL80211_CMD_ACTION_TX_STATUS:
+               mlme_event(drv, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+                          tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+                          tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+                          tb[NL80211_ATTR_COOKIE]);
+               break;
+       case NL80211_CMD_CONNECT:
+       case NL80211_CMD_ROAM:
+               mlme_event_connect(drv, gnlh->cmd,
+                                  tb[NL80211_ATTR_STATUS_CODE],
+                                  tb[NL80211_ATTR_MAC],
+                                  tb[NL80211_ATTR_REQ_IE],
+                                  tb[NL80211_ATTR_RESP_IE]);
+               break;
+       case NL80211_CMD_DISCONNECT:
+               if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+                       /*
+                        * Avoid reporting two disassociation events that could
+                        * confuse the core code.
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+                                  "event when using userspace SME");
+                       break;
+               }
+               drv->associated = 0;
+               os_memset(&data, 0, sizeof(data));
+               if (tb[NL80211_ATTR_REASON_CODE])
+                       data.disassoc_info.reason_code =
+                               nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, &data);
+               break;
+       case NL80211_CMD_MICHAEL_MIC_FAILURE:
+               mlme_event_michael_mic_failure(drv, tb);
+               break;
+       case NL80211_CMD_JOIN_IBSS:
+               mlme_event_join_ibss(drv, tb);
+               break;
+       case NL80211_CMD_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 0, tb);
+               break;
+       case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 1, tb);
+               break;
+       case NL80211_CMD_NOTIFY_CQM:
+               nl80211_cqm_event(drv, tb);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+                          "(cmd=%d)", gnlh->cmd);
+               break;
+       }
+
+       return NL_SKIP;
+}
+
+
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+                                            void *sock_ctx)
+{
+       struct nl_cb *cb;
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Event message available");
+
+       cb = nl_cb_clone(drv->nl_cb);
+       if (!cb)
+               return;
+       nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL);
+       nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv);
+       nl_recvmsgs(drv->nl_handle_event, cb);
+       nl_cb_put(cb);
+}
+
+
+/**
+ * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+ * @priv: driver_nl80211 private data
+ * @alpha2_arg: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This asks nl80211 to set the regulatory domain for given
+ * country ISO / IEC alpha2.
+ */
+static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       char alpha2[3];
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       alpha2[0] = alpha2_arg[0];
+       alpha2[1] = alpha2_arg[1];
+       alpha2[2] = '\0';
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_REQ_SET_REG, 0);
+
+       NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
+       if (send_and_recv_msgs(drv, msg, NULL, NULL))
+               return -EINVAL;
+       return 0;
+nla_put_failure:
+       return -EINVAL;
+}
+
+
+#ifndef HOSTAPD
+struct wiphy_info_data {
+       int max_scan_ssids;
+       int ap_supported;
+       int auth_supported;
+       int connect_supported;
+};
+
+
+static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wiphy_info_data *info = arg;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+               info->max_scan_ssids =
+                       nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+       if (tb[NL80211_ATTR_SUPPORTED_IFTYPES]) {
+               struct nlattr *nl_mode;
+               int i;
+               nla_for_each_nested(nl_mode,
+                                   tb[NL80211_ATTR_SUPPORTED_IFTYPES], i) {
+                       if (nl_mode->nla_type == NL80211_IFTYPE_AP) {
+                               info->ap_supported = 1;
+                               break;
+                       }
+               }
+       }
+
+       if (tb[NL80211_ATTR_SUPPORTED_COMMANDS]) {
+               struct nlattr *nl_cmd;
+               int i;
+
+               nla_for_each_nested(nl_cmd,
+                                   tb[NL80211_ATTR_SUPPORTED_COMMANDS], i) {
+                       u32 cmd = nla_get_u32(nl_cmd);
+                       if (cmd == NL80211_CMD_AUTHENTICATE)
+                               info->auth_supported = 1;
+                       else if (cmd == NL80211_CMD_CONNECT)
+                               info->connect_supported = 1;
+               }
+       }
+
+       return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+                                      struct wiphy_info_data *info)
+{
+       struct nl_msg *msg;
+
+       os_memset(info, 0, sizeof(*info));
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_GET_WIPHY, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->first_bss.ifindex);
+
+       if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info) == 0)
+               return 0;
+       msg = NULL;
+nla_put_failure:
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+       struct wiphy_info_data info;
+       if (wpa_driver_nl80211_get_info(drv, &info))
+               return -1;
+       drv->has_capability = 1;
+       /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
+       drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+       drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+               WPA_DRIVER_CAPA_ENC_WEP104 |
+               WPA_DRIVER_CAPA_ENC_TKIP |
+               WPA_DRIVER_CAPA_ENC_CCMP;
+       drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+               WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
+
+       drv->capa.max_scan_ssids = info.max_scan_ssids;
+       if (info.ap_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_AP;
+
+       if (info.auth_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+       else if (!info.connect_supported) {
+               wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+                          "authentication/association or connect commands");
+               return -1;
+       }
+
+       drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+       drv->capa.max_remain_on_chan = 5000;
+
+       return 0;
+}
+#endif /* HOSTAPD */
+
+
+static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv,
+                                     void *ctx)
+{
+       int ret;
+
+       /* Initialize generic netlink and nl80211 */
+
+       drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (drv->nl_cb == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+                          "callbacks");
+               goto err1;
+       }
+
+       drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb);
+       if (drv->nl_handle == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+                          "callbacks");
+               goto err2;
+       }
+
+       drv->nl_handle_event = nl_handle_alloc_cb(drv->nl_cb);
+       if (drv->nl_handle_event == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+                          "callbacks (event)");
+               goto err2b;
+       }
+
+       if (genl_connect(drv->nl_handle)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+                          "netlink");
+               goto err3;
+       }
+
+       if (genl_connect(drv->nl_handle_event)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic "
+                          "netlink (event)");
+               goto err3;
+       }
+
+#ifdef CONFIG_LIBNL20
+       if (genl_ctrl_alloc_cache(drv->nl_handle, &drv->nl_cache) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache");
+               goto err3;
+       }
+       if (genl_ctrl_alloc_cache(drv->nl_handle_event, &drv->nl_cache_event) <
+           0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache (event)");
+               goto err3b;
+       }
+#else /* CONFIG_LIBNL20 */
+       drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle);
+       if (drv->nl_cache == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache");
+               goto err3;
+       }
+       drv->nl_cache_event = genl_ctrl_alloc_cache(drv->nl_handle_event);
+       if (drv->nl_cache_event == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache (event)");
+               goto err3b;
+       }
+#endif /* CONFIG_LIBNL20 */
+
+       drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211");
+       if (drv->nl80211 == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+                          "found");
+               goto err4;
+       }
+
+       ret = nl_get_multicast_id(drv, "nl80211", "scan");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+                          "membership for scan events: %d (%s)",
+                          ret, strerror(-ret));
+               goto err4;
+       }
+
+       ret = nl_get_multicast_id(drv, "nl80211", "mlme");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(drv->nl_handle_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+                          "membership for mlme events: %d (%s)",
+                          ret, strerror(-ret));
+               goto err4;
+       }
+
+       eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle_event),
+                                wpa_driver_nl80211_event_receive, drv, ctx);
+
+       return 0;
+
+err4:
+       nl_cache_free(drv->nl_cache_event);
+err3b:
+       nl_cache_free(drv->nl_cache);
+err3:
+       nl_handle_destroy(drv->nl_handle_event);
+err2b:
+       nl_handle_destroy(drv->nl_handle);
+err2:
+       nl_cb_put(drv->nl_cb);
+err1:
+       return -1;
+}
+
+
+/**
+ * wpa_driver_nl80211_init - Initialize nl80211 driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * Returns: Pointer to private data, %NULL on failure
+ */
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_nl80211_data *drv;
+       struct netlink_config *cfg;
+       struct i802_bss *bss;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       bss = &drv->first_bss;
+       bss->drv = drv;
+       os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+       drv->monitor_ifidx = -1;
+       drv->monitor_sock = -1;
+       drv->ioctl_sock = -1;
+
+       if (wpa_driver_nl80211_init_nl(drv, ctx)) {
+               os_free(drv);
+               return NULL;
+       }
+
+       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->ioctl_sock < 0) {
+               perror("socket(PF_INET,SOCK_DGRAM)");
+               goto failed;
+       }
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               goto failed;
+       cfg->ctx = drv;
+       cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+       cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               goto failed;
+       }
+       if (wpa_driver_nl80211_finish_drv_init(drv))
+               goto failed;
+
+       return bss;
+
+failed:
+       netlink_deinit(drv->netlink);
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+
+       genl_family_put(drv->nl80211);
+       nl_cache_free(drv->nl_cache);
+       nl_handle_destroy(drv->nl_handle);
+       nl_cb_put(drv->nl_cb);
+       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+
+       os_free(drv);
+       return NULL;
+}
+
+
+static int nl80211_register_action_frame(struct wpa_driver_nl80211_data *drv,
+                                        const u8 *match, size_t match_len)
+{
+       struct nl_msg *msg;
+       int ret = -1;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_REGISTER_ACTION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
+
+       ret = send_and_recv(drv, drv->nl_handle_event, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Register Action command "
+                          "failed: ret=%d (%s)", ret, strerror(-ret));
+               wpa_hexdump(MSG_DEBUG, "nl80211: Register Action match",
+                           match, match_len);
+               goto nla_put_failure;
+       }
+       ret = 0;
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int nl80211_register_action_frames(struct wpa_driver_nl80211_data *drv)
+{
+       /* FT Action frames */
+       if (nl80211_register_action_frame(drv, (u8 *) "\x06", 1) < 0)
+               return -1;
+       else
+               drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+                       WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+
+       return 0;
+}
+
+
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
+{
+       struct i802_bss *bss = &drv->first_bss;
+
+       drv->ifindex = if_nametoindex(bss->ifname);
+       drv->first_bss.ifindex = drv->ifindex;
+
+#ifndef HOSTAPD
+       if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA) < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not configure driver to "
+                          "use managed mode");
+       }
+
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+               wpa_printf(MSG_ERROR, "Could not set interface '%s' UP",
+                          bss->ifname);
+               return -1;
+       }
+
+       if (wpa_driver_nl80211_capa(drv))
+               return -1;
+
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                              1, IF_OPER_DORMANT);
+#endif /* HOSTAPD */
+
+       if (nl80211_register_action_frames(drv) < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+                          "frame processing - ignore for now");
+               /*
+                * Older kernel versions did not support this, so ignore the
+                * error for now. Some functionality may not be available
+                * because of this.
+                */
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_DEL_BEACON, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_nl80211_init().
+ */
+static void wpa_driver_nl80211_deinit(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+
+       if (drv->added_if_into_bridge) {
+               if (linux_br_del_if(drv->ioctl_sock, drv->brname, bss->ifname)
+                   < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "interface %s from bridge %s: %s",
+                                  bss->ifname, drv->brname, strerror(errno));
+       }
+       if (drv->added_bridge) {
+               if (linux_br_del(drv->ioctl_sock, drv->brname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "bridge %s: %s",
+                                  drv->brname, strerror(errno));
+       }
+
+       nl80211_remove_monitor_interface(drv);
+
+       if (drv->nlmode == NL80211_IFTYPE_AP)
+               wpa_driver_nl80211_del_beacon(drv);
+
+#ifdef HOSTAPD
+       if (drv->last_freq_ht) {
+               /* Clear HT flags from the driver */
+               struct hostapd_freq_params freq;
+               os_memset(&freq, 0, sizeof(freq));
+               freq.freq = drv->last_freq;
+               i802_set_freq(priv, &freq);
+       }
+
+       if (drv->eapol_sock >= 0) {
+               eloop_unregister_read_sock(drv->eapol_sock);
+               close(drv->eapol_sock);
+       }
+
+       if (drv->if_indices != drv->default_if_indices)
+               os_free(drv->if_indices);
+#endif /* HOSTAPD */
+
+       if (drv->disable_11b_rates)
+               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+       netlink_deinit(drv->netlink);
+
+       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+
+       (void) linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0);
+       wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_INFRA);
+
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+
+       eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle_event));
+       genl_family_put(drv->nl80211);
+       nl_cache_free(drv->nl_cache);
+       nl_cache_free(drv->nl_cache_event);
+       nl_handle_destroy(drv->nl_handle);
+       nl_handle_destroy(drv->nl_handle_event);
+       nl_cb_put(drv->nl_cb);
+
+       eloop_cancel_timeout(wpa_driver_nl80211_probe_req_report_timeout,
+                            drv, NULL);
+
+       os_free(drv->filter_ssids);
+
+       os_free(drv);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Driver private data
+ * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->ap_scan_as_station) {
+               wpa_driver_nl80211_set_mode(&drv->first_bss,
+                                           IEEE80211_MODE_AP);
+               drv->ap_scan_as_station = 0;
+       }
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+static int wpa_driver_nl80211_scan(void *priv,
+                                  struct wpa_driver_scan_params *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = 0, timeout;
+       struct nl_msg *msg, *ssids, *freqs;
+       size_t i;
+
+       msg = nlmsg_alloc();
+       ssids = nlmsg_alloc();
+       freqs = nlmsg_alloc();
+       if (!msg || !ssids || !freqs) {
+               nlmsg_free(msg);
+               nlmsg_free(ssids);
+               nlmsg_free(freqs);
+               return -1;
+       }
+
+       os_free(drv->filter_ssids);
+       drv->filter_ssids = params->filter_ssids;
+       params->filter_ssids = NULL;
+       drv->num_filter_ssids = params->num_filter_ssids;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_TRIGGER_SCAN, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       for (i = 0; i < params->num_ssids; i++) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+                                 params->ssids[i].ssid,
+                                 params->ssids[i].ssid_len);
+               NLA_PUT(ssids, i + 1, params->ssids[i].ssid_len,
+                       params->ssids[i].ssid);
+       }
+       if (params->num_ssids)
+               nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids);
+
+       if (params->extra_ies) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+                                 params->extra_ies, params->extra_ies_len);
+               NLA_PUT(msg, NL80211_ATTR_IE, params->extra_ies_len,
+                       params->extra_ies);
+       }
+
+       if (params->freqs) {
+               for (i = 0; params->freqs[i]; i++) {
+                       wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+                                  "MHz", params->freqs[i]);
+                       NLA_PUT_U32(freqs, i + 1, params->freqs[i]);
+               }
+               nla_put_nested(msg, NL80211_ATTR_SCAN_FREQUENCIES, freqs);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+#ifdef HOSTAPD
+               if (drv->nlmode == NL80211_IFTYPE_AP) {
+                       /*
+                        * mac80211 does not allow scan requests in AP mode, so
+                        * try to do this in station mode.
+                        */
+                       if (wpa_driver_nl80211_set_mode(bss,
+                                                       IEEE80211_MODE_INFRA))
+                               goto nla_put_failure;
+
+                       if (wpa_driver_nl80211_scan(drv, params)) {
+                               wpa_driver_nl80211_set_mode(bss,
+                                                           IEEE80211_MODE_AP);
+                               goto nla_put_failure;
+                       }
+
+                       /* Restore AP mode when processing scan results */
+                       drv->ap_scan_as_station = 1;
+                       ret = 0;
+               } else
+                       goto nla_put_failure;
+#else /* HOSTAPD */
+               goto nla_put_failure;
+#endif /* HOSTAPD */
+       }
+
+       /* Not all drivers generate "scan completed" wireless event, so try to
+        * read results after a timeout. */
+       timeout = 10;
+       if (drv->scan_complete_events) {
+               /*
+                * The driver seems to deliver events to notify when scan is
+                * complete, so use longer timeout to avoid race conditions
+                * with scanning and following association request.
+                */
+               timeout = 30;
+       }
+       wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+                  "seconds", ret, timeout);
+       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+                              drv, drv->ctx);
+
+nla_put_failure:
+       nlmsg_free(ssids);
+       nlmsg_free(msg);
+       nlmsg_free(freqs);
+       return ret;
+}
+
+
+static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
+{
+       const u8 *end, *pos;
+
+       if (ies == NULL)
+               return NULL;
+
+       pos = ies;
+       end = ies + ies_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+                                const u8 *ie, size_t ie_len)
+{
+       const u8 *ssid;
+       size_t i;
+
+       if (drv->filter_ssids == NULL)
+               return 0;
+
+       ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+       if (ssid == NULL)
+               return 1;
+
+       for (i = 0; i < drv->num_filter_ssids; i++) {
+               if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+                   os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+                   0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+struct nl80211_bss_info_arg {
+       struct wpa_driver_nl80211_data *drv;
+       struct wpa_scan_results *res;
+};
+
+static int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *bss[NL80211_BSS_MAX + 1];
+       static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+               [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+               [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_BSS_TSF] = { .type = NLA_U64 },
+               [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+               [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+               [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+               [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+               [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+               [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+               [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+               [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+       };
+       struct nl80211_bss_info_arg *_arg = arg;
+       struct wpa_scan_results *res = _arg->res;
+       struct wpa_scan_res **tmp;
+       struct wpa_scan_res *r;
+       const u8 *ie, *beacon_ie;
+       size_t ie_len, beacon_ie_len;
+       u8 *pos;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_BSS])
+               return NL_SKIP;
+       if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+                            bss_policy))
+               return NL_SKIP;
+       if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+               ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+               ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+       } else {
+               ie = NULL;
+               ie_len = 0;
+       }
+       if (bss[NL80211_BSS_BEACON_IES]) {
+               beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+               beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+       } else {
+               beacon_ie = NULL;
+               beacon_ie_len = 0;
+       }
+
+       if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+                                 ie ? ie_len : beacon_ie_len))
+               return NL_SKIP;
+
+       r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+       if (r == NULL)
+               return NL_SKIP;
+       if (bss[NL80211_BSS_BSSID])
+               os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+                         ETH_ALEN);
+       if (bss[NL80211_BSS_FREQUENCY])
+               r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+       if (bss[NL80211_BSS_BEACON_INTERVAL])
+               r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+       if (bss[NL80211_BSS_CAPABILITY])
+               r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+       r->flags |= WPA_SCAN_NOISE_INVALID;
+       if (bss[NL80211_BSS_SIGNAL_MBM]) {
+               r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+               r->level /= 100; /* mBm to dBm */
+               r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
+       } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+               r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+               r->flags |= WPA_SCAN_LEVEL_INVALID;
+       } else
+               r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
+       if (bss[NL80211_BSS_TSF])
+               r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+       if (bss[NL80211_BSS_SEEN_MS_AGO])
+               r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+       r->ie_len = ie_len;
+       pos = (u8 *) (r + 1);
+       if (ie) {
+               os_memcpy(pos, ie, ie_len);
+               pos += ie_len;
+       }
+       r->beacon_ie_len = beacon_ie_len;
+       if (beacon_ie)
+               os_memcpy(pos, beacon_ie, beacon_ie_len);
+
+       if (bss[NL80211_BSS_STATUS]) {
+               enum nl80211_bss_status status;
+               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+               switch (status) {
+               case NL80211_BSS_STATUS_AUTHENTICATED:
+                       r->flags |= WPA_SCAN_AUTHENTICATED;
+                       break;
+               case NL80211_BSS_STATUS_ASSOCIATED:
+                       r->flags |= WPA_SCAN_ASSOCIATED;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       tmp = os_realloc(res->res,
+                        (res->num + 1) * sizeof(struct wpa_scan_res *));
+       if (tmp == NULL) {
+               os_free(r);
+               return NL_SKIP;
+       }
+       tmp[res->num++] = r;
+       res->res = tmp;
+
+       return NL_SKIP;
+}
+
+
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+                                const u8 *addr)
+{
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+               wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+                          "mismatch (" MACSTR ")", MAC2STR(addr));
+               wpa_driver_nl80211_mlme(drv, addr,
+                                       NL80211_CMD_DEAUTHENTICATE,
+                                       WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
+       }
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+       struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+       size_t i;
+
+       for (i = 0; i < res->num; i++) {
+               struct wpa_scan_res *r = res->res[i];
+               if (r->flags & WPA_SCAN_AUTHENTICATED) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+                                  "indicates BSS status with " MACSTR
+                                  " as authenticated",
+                                  MAC2STR(r->bssid));
+                       if (drv->nlmode == NL80211_IFTYPE_STATION &&
+                           os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
+                           os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
+                           0) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID"
+                                          " in local state (auth=" MACSTR
+                                          " assoc=" MACSTR ")",
+                                          MAC2STR(drv->auth_bssid),
+                                          MAC2STR(drv->bssid));
+                               clear_state_mismatch(drv, r->bssid);
+                       }
+               }
+
+               if (r->flags & WPA_SCAN_ASSOCIATED) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+                                  "indicate BSS status with " MACSTR
+                                  " as associated",
+                                  MAC2STR(r->bssid));
+                       if (drv->nlmode == NL80211_IFTYPE_STATION &&
+                           !drv->associated) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Local state "
+                                          "(not associated) does not match "
+                                          "with BSS state");
+                               clear_state_mismatch(drv, r->bssid);
+                       } else if (drv->nlmode == NL80211_IFTYPE_STATION &&
+                                  os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
+                                  0) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Local state "
+                                          "(associated with " MACSTR ") does "
+                                          "not match with BSS state",
+                                          MAC2STR(drv->bssid));
+                               clear_state_mismatch(drv, r->bssid);
+                               clear_state_mismatch(drv, drv->bssid);
+                       }
+               }
+       }
+}
+
+
+static void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
+
+
+static struct wpa_scan_results *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       struct wpa_scan_results *res;
+       int ret;
+       struct nl80211_bss_info_arg arg;
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL)
+               return NULL;
+       msg = nlmsg_alloc();
+       if (!msg)
+               goto nla_put_failure;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP,
+                   NL80211_CMD_GET_SCAN, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       arg.drv = drv;
+       arg.res = res;
+       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       msg = NULL;
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)",
+                          (unsigned long) res->num);
+               return res;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+                  "(%s)", ret, strerror(-ret));
+nla_put_failure:
+       nlmsg_free(msg);
+       wpa_scan_results_free(res);
+       return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+static struct wpa_scan_results *
+wpa_driver_nl80211_get_scan_results(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct wpa_scan_results *res;
+
+       res = nl80211_get_scan_results(drv);
+       if (res)
+               wpa_driver_nl80211_check_bss_status(drv, res);
+       return res;
+}
+
+
+static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+       struct wpa_scan_results *res;
+       size_t i;
+
+       res = nl80211_get_scan_results(drv);
+       if (res == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+       for (i = 0; i < res->num; i++) {
+               struct wpa_scan_res *r = res->res[i];
+               wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s",
+                          (int) i, (int) res->num, MAC2STR(r->bssid),
+                          r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "",
+                          r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+       }
+
+       wpa_scan_results_free(res);
+}
+
+
+static int wpa_driver_nl80211_set_key(const char *ifname, void *priv,
+                                     enum wpa_alg alg, const u8 *addr,
+                                     int key_idx, int set_tx,
+                                     const u8 *seq, size_t seq_len,
+                                     const u8 *key, size_t key_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ifindex = if_nametoindex(ifname);
+       struct nl_msg *msg;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s: ifindex=%d alg=%d addr=%p key_idx=%d "
+                  "set_tx=%d seq_len=%lu key_len=%lu",
+                  __func__, ifindex, alg, addr, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       if (alg == WPA_ALG_NONE) {
+               genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                           0, NL80211_CMD_DEL_KEY, 0);
+       } else {
+               genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                           0, NL80211_CMD_NEW_KEY, 0);
+               NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
+               switch (alg) {
+               case WPA_ALG_WEP:
+                       if (key_len == 5)
+                               NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                           WLAN_CIPHER_SUITE_WEP40);
+                       else
+                               NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                           WLAN_CIPHER_SUITE_WEP104);
+                       break;
+               case WPA_ALG_TKIP:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_TKIP);
+                       break;
+               case WPA_ALG_CCMP:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_CCMP);
+                       break;
+               case WPA_ALG_IGTK:
+                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_AES_CMAC);
+                       break;
+               default:
+                       wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
+                                  "algorithm %d", __func__, alg);
+                       nlmsg_free(msg);
+                       return -1;
+               }
+       }
+
+       if (seq && seq_len)
+               NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
+
+       if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
+       {
+               wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       }
+       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
+               ret = 0;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)",
+                          ret, strerror(-ret));
+
+       /*
+        * If we failed or don't need to set the default TX key (below),
+        * we're done here.
+        */
+       if (ret || !set_tx || alg == WPA_ALG_NONE)
+               return ret;
+#ifdef HOSTAPD
+       if (addr)
+               return ret;
+#else /* HOSTAPD */
+       if (drv->nlmode == NL80211_IFTYPE_AP && addr)
+               return ret;
+#endif /* HOSTAPD */
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_KEY, 0);
+       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+       if (alg == WPA_ALG_IGTK)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
+       else
+               NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == -ENOENT)
+               ret = 0;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; "
+                          "err=%d %s)", ret, strerror(-ret));
+       return ret;
+
+nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
+                     int key_idx, int defkey,
+                     const u8 *seq, size_t seq_len,
+                     const u8 *key, size_t key_len)
+{
+       struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+       if (!key_attr)
+               return -1;
+
+       if (defkey && alg == WPA_ALG_IGTK)
+               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT);
+       else if (defkey)
+               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+       NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
+
+       switch (alg) {
+       case WPA_ALG_WEP:
+               if (key_len == 5)
+                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_WEP40);
+               else
+                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_WEP104);
+               break;
+       case WPA_ALG_TKIP:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP);
+               break;
+       case WPA_ALG_CCMP:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
+               break;
+       case WPA_ALG_IGTK:
+               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                           WLAN_CIPHER_SUITE_AES_CMAC);
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
+                          "algorithm %d", __func__, alg);
+               return -1;
+       }
+
+       if (seq && seq_len)
+               NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
+
+       NLA_PUT(msg, NL80211_KEY_DATA, key_len, key);
+
+       nla_nest_end(msg, key_attr);
+
+       return 0;
+ nla_put_failure:
+       return -1;
+}
+
+
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+                                struct nl_msg *msg)
+{
+       int i, privacy = 0;
+       struct nlattr *nl_keys, *nl_key;
+
+       for (i = 0; i < 4; i++) {
+               if (!params->wep_key[i])
+                       continue;
+               privacy = 1;
+               break;
+       }
+       if (!privacy)
+               return 0;
+
+       NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
+
+       nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+       if (!nl_keys)
+               goto nla_put_failure;
+
+       for (i = 0; i < 4; i++) {
+               if (!params->wep_key[i])
+                       continue;
+
+               nl_key = nla_nest_start(msg, i);
+               if (!nl_key)
+                       goto nla_put_failure;
+
+               NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+                       params->wep_key[i]);
+               if (params->wep_key_len[i] == 5)
+                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_WEP40);
+               else
+                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
+                                   WLAN_CIPHER_SUITE_WEP104);
+
+               NLA_PUT_U8(msg, NL80211_KEY_IDX, i);
+
+               if (i == params->wep_tx_keyidx)
+                       NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
+
+               nla_nest_end(msg, nl_key);
+       }
+       nla_nest_end(msg, nl_keys);
+
+       return 0;
+
+nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *addr, int cmd, u16 reason_code,
+                                  int local_state_change)
+{
+       int ret = -1;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, cmd, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       if (local_state_change)
+               NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+       ret = 0;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+                                        const u8 *addr, int reason_code)
+{
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       drv->associated = 0;
+       return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISCONNECT,
+                                      reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+                                            int reason_code)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+               return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       drv->associated = 0;
+       return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+                                      reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr,
+                                          int reason_code)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+               return wpa_driver_nl80211_disconnect(drv, addr, reason_code);
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       drv->associated = 0;
+       return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DISASSOCIATE,
+                                      reason_code, 0);
+}
+
+
+static int wpa_driver_nl80211_authenticate(
+       void *priv, struct wpa_driver_auth_params *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1, i;
+       struct nl_msg *msg;
+       enum nl80211_auth_type type;
+       int count = 0;
+
+       drv->associated = 0;
+       os_memset(drv->auth_bssid, 0, ETH_ALEN);
+       /* FIX: IBSS mode */
+       if (drv->nlmode != NL80211_IFTYPE_STATION)
+               wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+
+       if (wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA) < 0)
+               return -1;
+
+retry:
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
+                  drv->ifindex);
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_AUTHENTICATE, 0);
+
+       for (i = 0; i < 4; i++) {
+               if (!params->wep_key[i])
+                       continue;
+               wpa_driver_nl80211_set_key(bss->ifname, priv, WPA_ALG_WEP,
+                                          NULL, i,
+                                          i == params->wep_tx_keyidx, NULL, 0,
+                                          params->wep_key[i],
+                                          params->wep_key_len[i]);
+               if (params->wep_tx_keyidx != i)
+                       continue;
+               if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
+                              params->wep_key[i], params->wep_key_len[i])) {
+                       nlmsg_free(msg);
+                       return -1;
+               }
+       }
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       if (params->bssid) {
+               wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+                          MAC2STR(params->bssid));
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+       }
+       if (params->freq) {
+               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+       }
+       if (params->ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+                                 params->ssid, params->ssid_len);
+               NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+                       params->ssid);
+       }
+       wpa_hexdump(MSG_DEBUG, "  * IEs", params->ie, params->ie_len);
+       if (params->ie)
+               NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie);
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+               type = NL80211_AUTHTYPE_SHARED_KEY;
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+               type = NL80211_AUTHTYPE_NETWORK_EAP;
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
+               type = NL80211_AUTHTYPE_FT;
+       else
+               goto nla_put_failure;
+       wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+       NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+       if (params->local_state_change) {
+               wpa_printf(MSG_DEBUG, "  * Local state change only");
+               NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               count++;
+               if (ret == -EALREADY && count == 1 && params->bssid &&
+                   !params->local_state_change) {
+                       /*
+                        * mac80211 does not currently accept new
+                        * authentication if we are already authenticated. As a
+                        * workaround, force deauthentication and try again.
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
+                                  "after forced deauthentication");
+                       wpa_driver_nl80211_deauthenticate(
+                               bss, params->bssid,
+                               WLAN_REASON_PREV_AUTH_NOT_VALID);
+                       nlmsg_free(msg);
+                       goto retry;
+               }
+               goto nla_put_failure;
+       }
+       ret = 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
+                  "successfully");
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+struct phy_info_arg {
+       u16 *num_modes;
+       struct hostapd_hw_modes *modes;
+};
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct phy_info_arg *phy_info = arg;
+
+       struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+
+       struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+       static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+               [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+               [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+       };
+
+       struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+       static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+               [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+               [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG },
+       };
+
+       struct nlattr *nl_band;
+       struct nlattr *nl_freq;
+       struct nlattr *nl_rate;
+       int rem_band, rem_freq, rem_rate;
+       struct hostapd_hw_modes *mode;
+       int idx, mode_is_set;
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+               return NL_SKIP;
+
+       nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) {
+               mode = os_realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode));
+               if (!mode)
+                       return NL_SKIP;
+               phy_info->modes = mode;
+
+               mode_is_set = 0;
+
+               mode = &phy_info->modes[*(phy_info->num_modes)];
+               memset(mode, 0, sizeof(*mode));
+               *(phy_info->num_modes) += 1;
+
+               nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+                         nla_len(nl_band), NULL);
+
+               if (tb_band[NL80211_BAND_ATTR_HT_CAPA]) {
+                       mode->ht_capab = nla_get_u16(
+                               tb_band[NL80211_BAND_ATTR_HT_CAPA]);
+               }
+
+               if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) {
+                       mode->a_mpdu_params |= nla_get_u8(
+                               tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR]) &
+                               0x03;
+               }
+
+               if (tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) {
+                       mode->a_mpdu_params |= nla_get_u8(
+                               tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY]) <<
+                               2;
+               }
+
+               if (tb_band[NL80211_BAND_ATTR_HT_MCS_SET] &&
+                   nla_len(tb_band[NL80211_BAND_ATTR_HT_MCS_SET])) {
+                       u8 *mcs;
+                       mcs = nla_data(tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+                       os_memcpy(mode->mcs_set, mcs, 16);
+               }
+
+               nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+                       nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+                                 nla_len(nl_freq), freq_policy);
+                       if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                               continue;
+                       mode->num_channels++;
+               }
+
+               mode->channels = os_zalloc(mode->num_channels * sizeof(struct hostapd_channel_data));
+               if (!mode->channels)
+                       return NL_SKIP;
+
+               idx = 0;
+
+               nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) {
+                       nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq),
+                                 nla_len(nl_freq), freq_policy);
+                       if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                               continue;
+
+                       mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+                       mode->channels[idx].flag = 0;
+
+                       if (!mode_is_set) {
+                               /* crude heuristic */
+                               if (mode->channels[idx].freq < 4000)
+                                       mode->mode = HOSTAPD_MODE_IEEE80211B;
+                               else
+                                       mode->mode = HOSTAPD_MODE_IEEE80211A;
+                               mode_is_set = 1;
+                       }
+
+                       /* crude heuristic */
+                       if (mode->channels[idx].freq < 4000)
+                               if (mode->channels[idx].freq == 2484)
+                                       mode->channels[idx].chan = 14;
+                               else
+                                       mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5;
+                       else
+                               mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000;
+
+                       if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+                               mode->channels[idx].flag |=
+                                       HOSTAPD_CHAN_DISABLED;
+                       if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
+                               mode->channels[idx].flag |=
+                                       HOSTAPD_CHAN_PASSIVE_SCAN;
+                       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
+                               mode->channels[idx].flag |=
+                                       HOSTAPD_CHAN_NO_IBSS;
+                       if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+                               mode->channels[idx].flag |=
+                                       HOSTAPD_CHAN_RADAR;
+
+                       if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
+                           !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+                               mode->channels[idx].max_tx_power =
+                                       nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
+
+                       idx++;
+               }
+
+               nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+                       nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+                                 nla_len(nl_rate), rate_policy);
+                       if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+                               continue;
+                       mode->num_rates++;
+               }
+
+               mode->rates = os_zalloc(mode->num_rates * sizeof(int));
+               if (!mode->rates)
+                       return NL_SKIP;
+
+               idx = 0;
+
+               nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) {
+                       nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate),
+                                 nla_len(nl_rate), rate_policy);
+                       if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+                               continue;
+                       mode->rates[idx] = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]);
+
+                       /* crude heuristic */
+                       if (mode->mode == HOSTAPD_MODE_IEEE80211B &&
+                           mode->rates[idx] > 200)
+                               mode->mode = HOSTAPD_MODE_IEEE80211G;
+
+                       idx++;
+               }
+       }
+
+       return NL_SKIP;
+}
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_add_11b(struct hostapd_hw_modes *modes, u16 *num_modes)
+{
+       u16 m;
+       struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+       int i, mode11g_idx = -1;
+
+       /* If only 802.11g mode is included, use it to construct matching
+        * 802.11b mode data. */
+
+       for (m = 0; m < *num_modes; m++) {
+               if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+                       return modes; /* 802.11b already included */
+               if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+                       mode11g_idx = m;
+       }
+
+       if (mode11g_idx < 0)
+               return modes; /* 2.4 GHz band not supported at all */
+
+       nmodes = os_realloc(modes, (*num_modes + 1) * sizeof(*nmodes));
+       if (nmodes == NULL)
+               return modes; /* Could not add 802.11b mode */
+
+       mode = &nmodes[*num_modes];
+       os_memset(mode, 0, sizeof(*mode));
+       (*num_modes)++;
+       modes = nmodes;
+
+       mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+       mode11g = &modes[mode11g_idx];
+       mode->num_channels = mode11g->num_channels;
+       mode->channels = os_malloc(mode11g->num_channels *
+                                  sizeof(struct hostapd_channel_data));
+       if (mode->channels == NULL) {
+               (*num_modes)--;
+               return modes; /* Could not add 802.11b mode */
+       }
+       os_memcpy(mode->channels, mode11g->channels,
+                 mode11g->num_channels * sizeof(struct hostapd_channel_data));
+
+       mode->num_rates = 0;
+       mode->rates = os_malloc(4 * sizeof(int));
+       if (mode->rates == NULL) {
+               os_free(mode->channels);
+               (*num_modes)--;
+               return modes; /* Could not add 802.11b mode */
+       }
+
+       for (i = 0; i < mode11g->num_rates; i++) {
+               if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+                   mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
+                       continue;
+               mode->rates[mode->num_rates] = mode11g->rates[i];
+               mode->num_rates++;
+               if (mode->num_rates == 4)
+                       break;
+       }
+
+       if (mode->num_rates == 0) {
+               os_free(mode->channels);
+               os_free(mode->rates);
+               (*num_modes)--;
+               return modes; /* No 802.11b rates */
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+                  "information");
+
+       return modes;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct phy_info_arg result = {
+               .num_modes = num_modes,
+               .modes = NULL,
+       };
+
+       *num_modes = 0;
+       *flags = 0;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return NULL;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_GET_WIPHY, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0)
+               return wpa_driver_nl80211_add_11b(result.modes, num_modes);
+ nla_put_failure:
+       return NULL;
+}
+
+
+static int wpa_driver_nl80211_send_frame(struct wpa_driver_nl80211_data *drv,
+                                        const void *data, size_t len,
+                                        int encrypt)
+{
+       __u8 rtap_hdr[] = {
+               0x00, 0x00, /* radiotap version */
+               0x0e, 0x00, /* radiotap length */
+               0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+               IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+               0x00,       /* padding */
+               0x00, 0x00, /* RX and TX flags to indicate that */
+               0x00, 0x00, /* this is the injected frame directly */
+       };
+       struct iovec iov[2] = {
+               {
+                       .iov_base = &rtap_hdr,
+                       .iov_len = sizeof(rtap_hdr),
+               },
+               {
+                       .iov_base = (void *) data,
+                       .iov_len = len,
+               }
+       };
+       struct msghdr msg = {
+               .msg_name = NULL,
+               .msg_namelen = 0,
+               .msg_iov = iov,
+               .msg_iovlen = 2,
+               .msg_control = NULL,
+               .msg_controllen = 0,
+               .msg_flags = 0,
+       };
+
+       if (encrypt)
+               rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+       return sendmsg(drv->monitor_sock, &msg, 0);
+}
+
+
+static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data,
+                                       size_t data_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ieee80211_mgmt *mgmt;
+       int encrypt = 1;
+       u16 fc;
+
+       mgmt = (struct ieee80211_mgmt *) data;
+       fc = le_to_host16(mgmt->frame_control);
+
+       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+               /*
+                * Only one of the authentication frame types is encrypted.
+                * In order for static WEP encryption to work properly (i.e.,
+                * to not encrypt the frame), we need to tell mac80211 about
+                * the frames that must not be encrypted.
+                */
+               u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+               u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
+               if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
+                       encrypt = 0;
+       }
+
+       return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+
+static int wpa_driver_nl80211_set_beacon(void *priv,
+                                        const u8 *head, size_t head_len,
+                                        const u8 *tail, size_t tail_len,
+                                        int dtim_period, int beacon_int)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       u8 cmd = NL80211_CMD_NEW_BEACON;
+       int ret;
+       int beacon_set;
+       int ifindex = if_nametoindex(bss->ifname);
+
+       beacon_set = bss->beacon_set;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+                  beacon_set);
+       if (beacon_set)
+               cmd = NL80211_CMD_SET_BEACON;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, cmd, 0);
+       NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head);
+       NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, beacon_int);
+       NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, dtim_period);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+                          ret, strerror(-ret));
+       } else {
+               bss->beacon_set = 1;
+       }
+       return ret;
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_set_freq(struct wpa_driver_nl80211_data *drv,
+                                      int freq, int ht_enabled,
+                                      int sec_channel_offset)
+{
+       struct nl_msg *msg;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_WIPHY, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       if (ht_enabled) {
+               switch (sec_channel_offset) {
+               case -1:
+                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                                   NL80211_CHAN_HT40MINUS);
+                       break;
+               case 1:
+                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                                   NL80211_CHAN_HT40PLUS);
+                       break;
+               default:
+                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                                   NL80211_CHAN_HT20);
+                       break;
+               }
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == 0)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
+                  "%d (%s)", freq, ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_sta_add(void *priv,
+                                     struct hostapd_sta_add_params *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_NEW_STATION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
+       NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
+       NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
+               params->supp_rates);
+       NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+                   params->listen_interval);
+       if (params->ht_capabilities) {
+               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
+                       sizeof(*params->ht_capabilities),
+                       params->ht_capabilities);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_NEW_STATION "
+                          "result: %d (%s)", ret, strerror(-ret));
+       if (ret == -EEXIST)
+               ret = 0;
+ nla_put_failure:
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_sta_remove(void *priv, const u8 *addr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_DEL_STATION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+                   if_nametoindex(bss->ifname));
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == -ENOENT)
+               return 0;
+       return ret;
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
+                                int ifidx)
+{
+       struct nl_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+
+#ifdef HOSTAPD
+       /* stop listening for EAPOL on this interface */
+       del_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               goto nla_put_failure;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_DEL_INTERFACE, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
+
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return;
+ nla_put_failure:
+       wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
+}
+
+
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+                                    const char *ifname,
+                                    enum nl80211_iftype iftype,
+                                    const u8 *addr, int wds)
+{
+       struct nl_msg *msg, *flags = NULL;
+       int ifidx;
+       int ret = -ENOBUFS;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_NEW_INTERFACE, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
+
+       if (iftype == NL80211_IFTYPE_MONITOR) {
+               int err;
+
+               flags = nlmsg_alloc();
+               if (!flags)
+                       goto nla_put_failure;
+
+               NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES);
+
+               err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags);
+
+               nlmsg_free(flags);
+
+               if (err)
+                       goto nla_put_failure;
+       } else if (wds) {
+               NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+ nla_put_failure:
+               wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+                          ifname, ret, strerror(-ret));
+               return ret;
+       }
+
+       ifidx = if_nametoindex(ifname);
+       wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+                  ifname, ifidx);
+
+       if (ifidx <= 0)
+               return -1;
+
+#ifdef HOSTAPD
+       /* start listening for EAPOL on this interface */
+       add_ifidx(drv, ifidx);
+#endif /* HOSTAPD */
+
+       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+           linux_set_ifhwaddr(drv->ioctl_sock, ifname, addr)) {
+               nl80211_remove_iface(drv, ifidx);
+               return -1;
+       }
+
+       return ifidx;
+}
+
+
+static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+                               const char *ifname, enum nl80211_iftype iftype,
+                               const u8 *addr, int wds)
+{
+       int ret;
+
+       ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds);
+
+       /* if error occured and interface exists already */
+       if (ret == -ENFILE && if_nametoindex(ifname)) {
+               wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
+
+               /* Try to remove the interface that was already there. */
+               nl80211_remove_iface(drv, if_nametoindex(ifname));
+
+               /* Try to create the interface again */
+               ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+                                               wds);
+       }
+
+       if (ret >= 0 && drv->disable_11b_rates)
+               nl80211_disable_11b_rates(drv, ret, 1);
+
+       return ret;
+}
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       union wpa_event_data event;
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = buf;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ok;
+       wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+                            u8 *buf, size_t len)
+{
+       union wpa_event_data event;
+       os_memset(&event, 0, sizeof(event));
+       event.rx_from_unknown.frame = buf;
+       event.rx_from_unknown.len = len;
+       wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+                        u8 *buf, size_t len, int datarate, int ssi_signal)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       union wpa_event_data event;
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+
+       switch (WLAN_FC_GET_TYPE(fc)) {
+       case WLAN_FC_TYPE_MGMT:
+               os_memset(&event, 0, sizeof(event));
+               event.rx_mgmt.frame = buf;
+               event.rx_mgmt.frame_len = len;
+               event.rx_mgmt.datarate = datarate;
+               event.rx_mgmt.ssi_signal = ssi_signal;
+               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+               break;
+       case WLAN_FC_TYPE_CTRL:
+               /* can only get here with PS-Poll frames */
+               wpa_printf(MSG_DEBUG, "CTRL");
+               from_unknown_sta(drv, buf, len);
+               break;
+       case WLAN_FC_TYPE_DATA:
+               from_unknown_sta(drv, buf, len);
+               break;
+       }
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       int len;
+       unsigned char buf[3000];
+       struct ieee80211_radiotap_iterator iter;
+       int ret;
+       int datarate = 0, ssi_signal = 0;
+       int injected = 0, failed = 0, rxflags = 0;
+
+       len = recv(sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       if (drv->nlmode == NL80211_IFTYPE_STATION && !drv->probe_req_report) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore monitor interface "
+                          "frame since Probe Request reporting is disabled");
+               return;
+       }
+
+       if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
+               printf("received invalid radiotap frame\n");
+               return;
+       }
+
+       while (1) {
+               ret = ieee80211_radiotap_iterator_next(&iter);
+               if (ret == -ENOENT)
+                       break;
+               if (ret) {
+                       printf("received invalid radiotap frame (%d)\n", ret);
+                       return;
+               }
+               switch (iter.this_arg_index) {
+               case IEEE80211_RADIOTAP_FLAGS:
+                       if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+                               len -= 4;
+                       break;
+               case IEEE80211_RADIOTAP_RX_FLAGS:
+                       rxflags = 1;
+                       break;
+               case IEEE80211_RADIOTAP_TX_FLAGS:
+                       injected = 1;
+                       failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+                                       IEEE80211_RADIOTAP_F_TX_FAIL;
+                       break;
+               case IEEE80211_RADIOTAP_DATA_RETRIES:
+                       break;
+               case IEEE80211_RADIOTAP_CHANNEL:
+                       /* TODO: convert from freq/flags to channel number */
+                       break;
+               case IEEE80211_RADIOTAP_RATE:
+                       datarate = *iter.this_arg * 5;
+                       break;
+               case IEEE80211_RADIOTAP_DB_ANTSIGNAL:
+                       ssi_signal = *iter.this_arg;
+                       break;
+               }
+       }
+
+       if (rxflags && injected)
+               return;
+
+       if (!injected)
+               handle_frame(drv, buf + iter.max_length,
+                            len - iter.max_length, datarate, ssi_signal);
+       else
+               handle_tx_callback(drv->ctx, buf + iter.max_length,
+                                  len - iter.max_length, !failed);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS   0xFF
+#define FAIL   0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+       /*
+        * do a little-endian load of the radiotap length field
+        */
+       /* load lower byte into A */
+       BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
+       /* put it into X (== index register) */
+       BPF_STMT(BPF_MISC| BPF_TAX, 0),
+       /* load upper byte into A */
+       BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
+       /* left-shift it by 8 */
+       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+       /* or with X */
+       BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+       /* put result into X */
+       BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+       /*
+        * Allow management frames through, this also gives us those
+        * management frames that we sent ourselves with status
+        */
+       /* load the lower byte of the IEEE 802.11 frame control field */
+       BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
+       /* mask off frame type and version */
+       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+       /* accept frame if it's both 0, fall through otherwise */
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+       /*
+        * TODO: add a bit to radiotap RX flags that indicates
+        * that the sending station is not associated, then
+        * add a filter here that filters on our DA and that flag
+        * to allow us to deauth frames to that bad station.
+        *
+        * Not a regression -- we didn't do it before either.
+        */
+
+#if 0
+       /*
+        * drop non-data frames
+        */
+       /* load the lower byte of the frame control field */
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+       /* mask off QoS bit */
+       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
+       /* drop non-data frames */
+       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
+       /* load the upper byte of the frame control field */
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
+       /* mask off toDS/fromDS */
+       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
+       /* accept WDS frames */
+       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
+
+       /*
+        * add header length to index
+        */
+       /* load the lower byte of the frame control field */
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+       /* mask off QoS bit */
+       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
+       /* right shift it by 6 to give 0 or 2 */
+       BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
+       /* add data frame header length */
+       BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
+       /* add index, was start of 802.11 header */
+       BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
+       /* move to index, now start of LL header */
+       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+
+       /*
+        * Accept empty data frames, we use those for
+        * polling activity.
+        */
+       BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+
+       /*
+        * Accept EAPOL frames
+        */
+       BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
+       BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
+
+       /* keep these last two statements or change the code below */
+       /* return 0 == "DROP" */
+       BPF_STMT(BPF_RET | BPF_K, 0),
+       /* return ~0 == "keep all" */
+       BPF_STMT(BPF_RET | BPF_K, ~0),
+};
+
+static struct sock_fprog msock_filter = {
+       .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]),
+       .filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+       int idx;
+
+       /* rewrite all PASS/FAIL jump offsets */
+       for (idx = 0; idx < msock_filter.len; idx++) {
+               struct sock_filter *insn = &msock_filter_insns[idx];
+
+               if (BPF_CLASS(insn->code) == BPF_JMP) {
+                       if (insn->code == (BPF_JMP|BPF_JA)) {
+                               if (insn->k == PASS)
+                                       insn->k = msock_filter.len - idx - 2;
+                               else if (insn->k == FAIL)
+                                       insn->k = msock_filter.len - idx - 3;
+                       }
+
+                       if (insn->jt == PASS)
+                               insn->jt = msock_filter.len - idx - 2;
+                       else if (insn->jt == FAIL)
+                               insn->jt = msock_filter.len - idx - 3;
+
+                       if (insn->jf == PASS)
+                               insn->jf = msock_filter.len - idx - 2;
+                       else if (insn->jf == FAIL)
+                               insn->jf = msock_filter.len - idx - 3;
+               }
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+                      &msock_filter, sizeof(msock_filter))) {
+               perror("SO_ATTACH_FILTER");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void nl80211_remove_monitor_interface(
+       struct wpa_driver_nl80211_data *drv)
+{
+       if (drv->monitor_ifidx >= 0) {
+               nl80211_remove_iface(drv, drv->monitor_ifidx);
+               drv->monitor_ifidx = -1;
+       }
+       if (drv->monitor_sock >= 0) {
+               eloop_unregister_read_sock(drv->monitor_sock);
+               close(drv->monitor_sock);
+               drv->monitor_sock = -1;
+       }
+}
+
+
+static int
+nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+       char buf[IFNAMSIZ];
+       struct sockaddr_ll ll;
+       int optval;
+       socklen_t optlen;
+
+       snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
+       buf[IFNAMSIZ - 1] = '\0';
+
+       drv->monitor_ifidx =
+               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+                                    0);
+
+       if (drv->monitor_ifidx < 0)
+               return -1;
+
+       if (linux_set_iface_flags(drv->ioctl_sock, buf, 1))
+               goto error;
+
+       memset(&ll, 0, sizeof(ll));
+       ll.sll_family = AF_PACKET;
+       ll.sll_ifindex = drv->monitor_ifidx;
+       drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (drv->monitor_sock < 0) {
+               perror("socket[PF_PACKET,SOCK_RAW]");
+               goto error;
+       }
+
+       if (add_monitor_filter(drv->monitor_sock)) {
+               wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+                          "interface; do filtering in user space");
+               /* This works, but will cost in performance. */
+       }
+
+       if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+               perror("monitor socket bind");
+               goto error;
+       }
+
+       optlen = sizeof(optval);
+       optval = 20;
+       if (setsockopt
+           (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+               perror("Failed to set socket priority");
+               goto error;
+       }
+
+       if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+                                    drv, NULL)) {
+               printf("Could not register monitor read socket\n");
+               goto error;
+       }
+
+       return 0;
+ error:
+       nl80211_remove_monitor_interface(drv);
+       return -1;
+}
+
+
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+static int wpa_driver_nl80211_hapd_send_eapol(
+       void *priv, const u8 *addr, const u8 *data,
+       size_t data_len, int encrypt, const u8 *own_addr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ieee80211_hdr *hdr;
+       size_t len;
+       u8 *pos;
+       int res;
+#if 0 /* FIX */
+       int qos = sta->flags & WPA_STA_WMM;
+#else
+       int qos = 0;
+#endif
+
+       len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+               data_len;
+       hdr = os_zalloc(len);
+       if (hdr == NULL) {
+               printf("malloc() failed for i802_send_data(len=%lu)\n",
+                      (unsigned long) len);
+               return -1;
+       }
+
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+       hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+       if (encrypt)
+               hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+#if 0 /* To be enabled if qos determination is added above */
+       if (qos) {
+               hdr->frame_control |=
+                       host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
+       }
+#endif
+
+       memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+       memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+       memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+       pos = (u8 *) (hdr + 1);
+
+#if 0 /* To be enabled if qos determination is added above */
+       if (qos) {
+               /* add an empty QoS header if needed */
+               pos[0] = 0;
+               pos[1] = 0;
+               pos += 2;
+       }
+#endif
+
+       memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+       pos += sizeof(rfc1042_header);
+       WPA_PUT_BE16(pos, ETH_P_PAE);
+       pos += 2;
+       memcpy(pos, data, data_len);
+
+       res = wpa_driver_nl80211_send_frame(drv, (u8 *) hdr, len, encrypt);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
+                          "failed: %d (%s)",
+                          (unsigned long) len, errno, strerror(errno));
+       }
+       os_free(hdr);
+
+       return res;
+}
+
+
+static u32 sta_flags_nl80211(int flags)
+{
+       u32 f = 0;
+
+       if (flags & WPA_STA_AUTHORIZED)
+               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (flags & WPA_STA_WMM)
+               f |= BIT(NL80211_STA_FLAG_WME);
+       if (flags & WPA_STA_SHORT_PREAMBLE)
+               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (flags & WPA_STA_MFP)
+               f |= BIT(NL80211_STA_FLAG_MFP);
+
+       return f;
+}
+
+
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+                                           int total_flags,
+                                           int flags_or, int flags_and)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg, *flags = NULL;
+       struct nl80211_sta_flag_update upd;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       flags = nlmsg_alloc();
+       if (!flags) {
+               nlmsg_free(msg);
+               return -ENOMEM;
+       }
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_STATION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+                   if_nametoindex(bss->ifname));
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+
+       /*
+        * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+        * can be removed eventually.
+        */
+       if (total_flags & WPA_STA_AUTHORIZED)
+               NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED);
+
+       if (total_flags & WPA_STA_WMM)
+               NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME);
+
+       if (total_flags & WPA_STA_SHORT_PREAMBLE)
+               NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE);
+
+       if (total_flags & WPA_STA_MFP)
+               NLA_PUT_FLAG(flags, NL80211_STA_FLAG_MFP);
+
+       if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags))
+               goto nla_put_failure;
+
+       os_memset(&upd, 0, sizeof(upd));
+       upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+       upd.set = sta_flags_nl80211(flags_or);
+       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+       nlmsg_free(flags);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       nlmsg_free(flags);
+       return -ENOBUFS;
+}
+
+
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+                                struct wpa_driver_associate_params *params)
+{
+       if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode) ||
+           wpa_driver_nl80211_set_freq(drv, params->freq, 0, 0)) {
+               nl80211_remove_monitor_interface(drv);
+               return -1;
+       }
+
+       /* TODO: setup monitor interface (and add code somewhere to remove this
+        * when AP mode is stopped; associate with mode != 2 or drv_deinit) */
+
+       return 0;
+}
+
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       int ret = -1;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_LEAVE_IBSS, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+
+       ret = 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully");
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+                                  struct wpa_driver_associate_params *params)
+{
+       struct nl_msg *msg;
+       int ret = -1;
+       int count = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
+
+       if (wpa_driver_nl80211_set_mode(&drv->first_bss, params->mode)) {
+               wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+                          "IBSS mode");
+               return -1;
+       }
+
+retry:
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_JOIN_IBSS, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+
+       if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+               goto nla_put_failure;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+                         params->ssid, params->ssid_len);
+       NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+               params->ssid);
+       os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+       drv->ssid_len = params->ssid_len;
+
+       wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+
+       ret = nl80211_set_conn_keys(params, msg);
+       if (ret)
+               goto nla_put_failure;
+
+       if (params->wpa_ie) {
+               wpa_hexdump(MSG_DEBUG,
+                           "  * Extra IEs for Beacon/Probe Response frames",
+                           params->wpa_ie, params->wpa_ie_len);
+               NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+                       params->wpa_ie);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               count++;
+               if (ret == -EALREADY && count == 1) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
+                                  "forced leave");
+                       nl80211_leave_ibss(drv);
+                       nlmsg_free(msg);
+                       goto retry;
+               }
+
+               goto nla_put_failure;
+       }
+       ret = 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully");
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_connect(
+       struct wpa_driver_nl80211_data *drv,
+       struct wpa_driver_associate_params *params)
+{
+       struct nl_msg *msg;
+       enum nl80211_auth_type type;
+       int ret = 0;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_CONNECT, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       if (params->bssid) {
+               wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+                          MAC2STR(params->bssid));
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+       }
+       if (params->freq) {
+               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+       }
+       if (params->ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+                                 params->ssid, params->ssid_len);
+               NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+                       params->ssid);
+               if (params->ssid_len > sizeof(drv->ssid))
+                       goto nla_put_failure;
+               os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+               drv->ssid_len = params->ssid_len;
+       }
+       wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
+       if (params->wpa_ie)
+               NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+                       params->wpa_ie);
+
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+               type = NL80211_AUTHTYPE_SHARED_KEY;
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+               type = NL80211_AUTHTYPE_NETWORK_EAP;
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
+               type = NL80211_AUTHTYPE_FT;
+       else
+               goto nla_put_failure;
+
+       wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+       NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+
+       if (params->wpa_ie && params->wpa_ie_len) {
+               enum nl80211_wpa_versions ver;
+
+               if (params->wpa_ie[0] == WLAN_EID_RSN)
+                       ver = NL80211_WPA_VERSION_2;
+               else
+                       ver = NL80211_WPA_VERSION_1;
+
+               wpa_printf(MSG_DEBUG, "  * WPA Version %d", ver);
+               NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+       }
+
+       if (params->pairwise_suite != CIPHER_NONE) {
+               int cipher;
+
+               switch (params->pairwise_suite) {
+               case CIPHER_WEP40:
+                       cipher = WLAN_CIPHER_SUITE_WEP40;
+                       break;
+               case CIPHER_WEP104:
+                       cipher = WLAN_CIPHER_SUITE_WEP104;
+                       break;
+               case CIPHER_CCMP:
+                       cipher = WLAN_CIPHER_SUITE_CCMP;
+                       break;
+               case CIPHER_TKIP:
+               default:
+                       cipher = WLAN_CIPHER_SUITE_TKIP;
+                       break;
+               }
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
+       }
+
+       if (params->group_suite != CIPHER_NONE) {
+               int cipher;
+
+               switch (params->group_suite) {
+               case CIPHER_WEP40:
+                       cipher = WLAN_CIPHER_SUITE_WEP40;
+                       break;
+               case CIPHER_WEP104:
+                       cipher = WLAN_CIPHER_SUITE_WEP104;
+                       break;
+               case CIPHER_CCMP:
+                       cipher = WLAN_CIPHER_SUITE_CCMP;
+                       break;
+               case CIPHER_TKIP:
+               default:
+                       cipher = WLAN_CIPHER_SUITE_TKIP;
+                       break;
+               }
+               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+       }
+
+       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+           params->key_mgmt_suite == KEY_MGMT_PSK) {
+               int mgmt = WLAN_AKM_SUITE_PSK;
+
+               switch (params->key_mgmt_suite) {
+               case KEY_MGMT_802_1X:
+                       mgmt = WLAN_AKM_SUITE_8021X;
+                       break;
+               case KEY_MGMT_PSK:
+               default:
+                       mgmt = WLAN_AKM_SUITE_PSK;
+                       break;
+               }
+               NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
+       }
+
+       ret = nl80211_set_conn_keys(params, msg);
+       if (ret)
+               goto nla_put_failure;
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+       ret = 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+
+}
+
+
+static int wpa_driver_nl80211_associate(
+       void *priv, struct wpa_driver_associate_params *params)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       struct nl_msg *msg;
+
+       if (params->mode == IEEE80211_MODE_AP)
+               return wpa_driver_nl80211_ap(drv, params);
+
+       if (params->mode == IEEE80211_MODE_IBSS)
+               return wpa_driver_nl80211_ibss(drv, params);
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+               if (wpa_driver_nl80211_set_mode(priv, params->mode) < 0)
+                       return -1;
+               return wpa_driver_nl80211_connect(drv, params);
+       }
+
+       drv->associated = 0;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+                  drv->ifindex);
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_ASSOCIATE, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       if (params->bssid) {
+               wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+                          MAC2STR(params->bssid));
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+       }
+       if (params->freq) {
+               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
+               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+               drv->assoc_freq = params->freq;
+       } else
+               drv->assoc_freq = 0;
+       if (params->ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+                                 params->ssid, params->ssid_len);
+               NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
+                       params->ssid);
+               if (params->ssid_len > sizeof(drv->ssid))
+                       goto nla_put_failure;
+               os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+               drv->ssid_len = params->ssid_len;
+       }
+       wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
+       if (params->wpa_ie)
+               NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+                       params->wpa_ie);
+
+#ifdef CONFIG_IEEE80211W
+       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
+               NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
+#endif /* CONFIG_IEEE80211W */
+
+       NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
+
+       if (params->prev_bssid) {
+               wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
+                          MAC2STR(params->prev_bssid));
+               NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+                       params->prev_bssid);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: MLME command failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               nl80211_dump_scan(drv);
+               goto nla_put_failure;
+       }
+       ret = 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Association request send "
+                  "successfully");
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+                           int ifindex, int mode)
+{
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_INTERFACE, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+nla_put_failure:
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+                  " %d (%s)", ifindex, mode, ret, strerror(-ret));
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_set_mode(void *priv, int mode)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       int nlmode;
+
+       switch (mode) {
+       case 0:
+               nlmode = NL80211_IFTYPE_STATION;
+               break;
+       case 1:
+               nlmode = NL80211_IFTYPE_ADHOC;
+               break;
+       case 2:
+               nlmode = NL80211_IFTYPE_AP;
+               break;
+       default:
+               return -1;
+       }
+
+       if (nl80211_set_mode(drv, drv->ifindex, nlmode) == 0) {
+               drv->nlmode = nlmode;
+               ret = 0;
+               goto done;
+       }
+
+       if (nlmode == drv->nlmode) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+                          "requested mode - ignore error");
+               ret = 0;
+               goto done; /* Already in the requested mode */
+       }
+
+       /* mac80211 doesn't allow mode changes while the device is up, so
+        * take the device down, try to set the mode again, and bring the
+        * device back up.
+        */
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0) == 0) {
+               /* Try to set the mode again while the interface is down */
+               ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
+               if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+                       ret = -1;
+       }
+
+       if (!ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+                          "interface is down");
+               drv->nlmode = nlmode;
+       }
+
+done:
+       if (!ret && nlmode == NL80211_IFTYPE_AP) {
+               /* Setup additional AP mode functionality if needed */
+               if (drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv))
+                       return -1;
+       } else if (!ret && nlmode != NL80211_IFTYPE_AP) {
+               /* Remove additional AP mode functionality */
+               nl80211_remove_monitor_interface(drv);
+               bss->beacon_set = 0;
+       }
+
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+                          "from %d failed", nlmode, drv->nlmode);
+
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_get_capa(void *priv,
+                                      struct wpa_driver_capa *capa)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (!drv->has_capability)
+               return -1;
+       os_memcpy(capa, &drv->capa, sizeof(*capa));
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_set_operstate(void *priv, int state)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+
+       wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
+                  __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+       drv->operstate = state;
+       return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nl80211_sta_flag_update upd;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_STATION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+                   if_nametoindex(bss->ifname));
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+
+       os_memset(&upd, 0, sizeof(upd));
+       upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (authorized)
+               upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
+       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+#ifdef HOSTAPD
+
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+       int *old;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+                  ifidx);
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == 0) {
+                       drv->if_indices[i] = ifidx;
+                       return;
+               }
+       }
+
+       if (drv->if_indices != drv->default_if_indices)
+               old = drv->if_indices;
+       else
+               old = NULL;
+
+       drv->if_indices = os_realloc(old,
+                                    sizeof(int) * (drv->num_if_indices + 1));
+       if (!drv->if_indices) {
+               if (!old)
+                       drv->if_indices = drv->default_if_indices;
+               else
+                       drv->if_indices = old;
+               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+                          "interfaces");
+               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+               return;
+       } else if (!old)
+               os_memcpy(drv->if_indices, drv->default_if_indices,
+                         sizeof(drv->default_if_indices));
+       drv->if_indices[drv->num_if_indices] = ifidx;
+       drv->num_if_indices++;
+}
+
+
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == ifidx) {
+                       drv->if_indices[i] = 0;
+                       break;
+               }
+       }
+}
+
+
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++)
+               if (drv->if_indices[i] == ifidx)
+                       return 1;
+
+       return 0;
+}
+
+
+static inline int min_int(int a, int b)
+{
+       if (a < b)
+               return a;
+       return b;
+}
+
+
+static int get_key_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       /*
+        * TODO: validate the key index and mac address!
+        * Otherwise, there's a race condition as soon as
+        * the kernel starts sending key notifications.
+        */
+
+       if (tb[NL80211_ATTR_KEY_SEQ])
+               memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+                      min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+       return NL_SKIP;
+}
+
+
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+                          int idx, u8 *seq)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_GET_KEY, 0);
+
+       if (addr)
+               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface));
+
+       memset(seq, 0, 6);
+
+       return send_and_recv_msgs(drv, msg, get_key_handler, seq);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates,
+                             int mode)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       u8 rates[NL80211_MAX_SUPP_RATES];
+       u8 rates_len = 0;
+       int i;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_BSS, 0);
+
+       for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+               rates[rates_len++] = basic_rates[i] / 5;
+
+       NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+#endif /* HOSTAPD */
+
+
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return wpa_driver_nl80211_set_freq(drv, freq->freq, freq->ht_enabled,
+                                          freq->sec_channel_offset);
+}
+
+
+#ifdef HOSTAPD
+
+static int i802_set_rts(void *priv, int rts)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
+       u32 val;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       if (rts >= 2347)
+               val = (u32) -1;
+       else
+               val = rts;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_WIPHY, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+nla_put_failure:
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+                  "%d (%s)", rts, ret, strerror(-ret));
+       return ret;
+}
+
+
+static int i802_set_frag(void *priv, int frag)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
+       u32 val;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       if (frag >= 2346)
+               val = (u32) -1;
+       else
+               val = frag;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_WIPHY, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+nla_put_failure:
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+                  "%d: %d (%s)", frag, ret, strerror(-ret));
+       return ret;
+}
+
+
+static int i802_flush(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_DEL_STATION, 0);
+
+       /*
+        * XXX: FIX! this needs to flush all VLANs too
+        */
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+                   if_nametoindex(bss->ifname));
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int get_sta_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct hostap_sta_driver_data *data = arg;
+       struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
+       static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+       };
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       /*
+        * TODO: validate the interface and mac address!
+        * Otherwise, there's a race condition as soon as
+        * the kernel starts sending station notifications.
+        */
+
+       if (!tb[NL80211_ATTR_STA_INFO]) {
+               wpa_printf(MSG_DEBUG, "sta stats missing!");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
+                            tb[NL80211_ATTR_STA_INFO],
+                            stats_policy)) {
+               wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
+               return NL_SKIP;
+       }
+
+       if (stats[NL80211_STA_INFO_INACTIVE_TIME])
+               data->inactive_msec =
+                       nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+       if (stats[NL80211_STA_INFO_RX_BYTES])
+               data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
+       if (stats[NL80211_STA_INFO_TX_BYTES])
+               data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+       if (stats[NL80211_STA_INFO_RX_PACKETS])
+               data->rx_packets =
+                       nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
+       if (stats[NL80211_STA_INFO_TX_PACKETS])
+               data->tx_packets =
+                       nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+
+       return NL_SKIP;
+}
+
+static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data,
+                             const u8 *addr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       os_memset(data, 0, sizeof(*data));
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_GET_STATION, 0);
+
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+       return send_and_recv_msgs(drv, msg, get_sta_handler, data);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+                                   int cw_min, int cw_max, int burst_time)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *txq, *params;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_WIPHY, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+       txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+       if (!txq)
+               goto nla_put_failure;
+
+       /* We are only sending parameters for a single TXQ at a time */
+       params = nla_nest_start(msg, 1);
+       if (!params)
+               goto nla_put_failure;
+
+       NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, queue);
+       /* Burst time is configured in units of 0.1 msec and TXOP parameter in
+        * 32 usec, so need to convert the value here. */
+       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32);
+       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min);
+       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
+       NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
+
+       nla_nest_end(msg, params);
+
+       nla_nest_end(msg, txq);
+
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+ nla_put_failure:
+       return -1;
+}
+
+
+static int i802_set_bss(void *priv, int cts, int preamble, int slot)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_BSS, 0);
+
+       if (cts >= 0)
+               NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
+       if (preamble >= 0)
+               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
+       if (slot >= 0)
+               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+ nla_put_failure:
+       return -ENOBUFS;
+}
+
+
+static int i802_set_cts_protect(void *priv, int value)
+{
+       return i802_set_bss(priv, value, -1, -1);
+}
+
+
+static int i802_set_preamble(void *priv, int value)
+{
+       return i802_set_bss(priv, -1, value, -1);
+}
+
+
+static int i802_set_short_slot_time(void *priv, int value)
+{
+       return i802_set_bss(priv, -1, -1, value);
+}
+
+
+static int i802_set_sta_vlan(void *priv, const u8 *addr,
+                            const char *ifname, int vlan_id)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_STATION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
+                   if_nametoindex(bss->ifname));
+       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
+                   if_nametoindex(ifname));
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+                          MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+                          MAC2STR(addr), ifname, vlan_id, ret,
+                          strerror(-ret));
+       }
+ nla_put_failure:
+       return ret;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       char name[IFNAMSIZ + 1];
+
+       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+       if (val) {
+               if (nl80211_create_iface(drv, name, NL80211_IFTYPE_AP_VLAN,
+                                        NULL, 1) < 0)
+                       return -1;
+               linux_set_iface_flags(drv->ioctl_sock, name, 1);
+               return i802_set_sta_vlan(priv, addr, name, 0);
+       } else {
+               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
+                                                   name);
+       }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       struct sockaddr_ll lladdr;
+       unsigned char buf[3000];
+       int len;
+       socklen_t fromlen = sizeof(lladdr);
+
+       len = recvfrom(sock, buf, sizeof(buf), 0,
+                      (struct sockaddr *)&lladdr, &fromlen);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
+
+
+static int i802_get_inact_sec(void *priv, const u8 *addr)
+{
+       struct hostap_sta_driver_data data;
+       int ret;
+
+       data.inactive_msec = (unsigned long) -1;
+       ret = i802_read_sta_data(priv, &data, addr);
+       if (ret || data.inactive_msec == (unsigned long) -1)
+               return -1;
+       return data.inactive_msec / 1000;
+}
+
+
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+       /* TODO */
+#endif
+       return 0;
+}
+
+
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                          int reason)
+{
+       struct i802_bss *bss = priv;
+       struct ieee80211_mgmt mgmt;
+
+       memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_DEAUTH);
+       memcpy(mgmt.da, addr, ETH_ALEN);
+       memcpy(mgmt.sa, own_addr, ETH_ALEN);
+       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+       mgmt.u.deauth.reason_code = host_to_le16(reason);
+       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+                                           IEEE80211_HDRLEN +
+                                           sizeof(mgmt.u.deauth));
+}
+
+
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                            int reason)
+{
+       struct i802_bss *bss = priv;
+       struct ieee80211_mgmt mgmt;
+
+       memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_DISASSOC);
+       memcpy(mgmt.da, addr, ETH_ALEN);
+       memcpy(mgmt.sa, own_addr, ETH_ALEN);
+       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+       mgmt.u.disassoc.reason_code = host_to_le16(reason);
+       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+                                           IEEE80211_HDRLEN +
+                                           sizeof(mgmt.u.disassoc));
+}
+
+
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+                            const char *brname, const char *ifname)
+{
+       int ifindex;
+       char in_br[IFNAMSIZ];
+
+       os_strlcpy(drv->brname, brname, IFNAMSIZ);
+       ifindex = if_nametoindex(brname);
+       if (ifindex == 0) {
+               /*
+                * Bridge was configured, but the bridge device does
+                * not exist. Try to add it now.
+                */
+               if (linux_br_add(drv->ioctl_sock, brname) < 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+                                  "bridge interface %s: %s",
+                                  brname, strerror(errno));
+                       return -1;
+               }
+               drv->added_bridge = 1;
+               add_ifidx(drv, if_nametoindex(brname));
+       }
+
+       if (linux_br_get(in_br, ifname) == 0) {
+               if (os_strcmp(in_br, brname) == 0)
+                       return 0; /* already in the bridge */
+
+               wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+                          "bridge %s", ifname, in_br);
+               if (linux_br_del_if(drv->ioctl_sock, in_br, ifname) < 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to "
+                                  "remove interface %s from bridge "
+                                  "%s: %s",
+                                  ifname, brname, strerror(errno));
+                       return -1;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+                  ifname, brname);
+       if (linux_br_add_if(drv->ioctl_sock, brname, ifname) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+                          "into bridge %s: %s",
+                          ifname, brname, strerror(errno));
+               return -1;
+       }
+       drv->added_if_into_bridge = 1;
+
+       return 0;
+}
+
+
+static void *i802_init(struct hostapd_data *hapd,
+                      struct wpa_init_params *params)
+{
+       struct wpa_driver_nl80211_data *drv;
+       struct i802_bss *bss;
+       size_t i;
+       char brname[IFNAMSIZ];
+       int ifindex, br_ifindex;
+       int br_added = 0;
+
+       bss = wpa_driver_nl80211_init(hapd, params->ifname);
+       if (bss == NULL)
+               return NULL;
+
+       drv = bss->drv;
+       if (linux_br_get(brname, params->ifname) == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+                          params->ifname, brname);
+               br_ifindex = if_nametoindex(brname);
+       } else {
+               brname[0] = '\0';
+               br_ifindex = 0;
+       }
+
+       drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
+       drv->if_indices = drv->default_if_indices;
+       for (i = 0; i < params->num_bridge; i++) {
+               if (params->bridge[i]) {
+                       ifindex = if_nametoindex(params->bridge[i]);
+                       if (ifindex)
+                               add_ifidx(drv, ifindex);
+                       if (ifindex == br_ifindex)
+                               br_added = 1;
+               }
+       }
+       if (!br_added && br_ifindex &&
+           (params->num_bridge == 0 || !params->bridge[0]))
+               add_ifidx(drv, br_ifindex);
+
+       /* start listening for EAPOL on the default AP interface */
+       add_ifidx(drv, drv->ifindex);
+
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 0))
+               goto failed;
+
+       if (params->bssid) {
+               if (linux_set_ifhwaddr(drv->ioctl_sock, bss->ifname,
+                                      params->bssid))
+                       goto failed;
+       }
+
+       if (wpa_driver_nl80211_set_mode(bss, IEEE80211_MODE_AP)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
+                          "into AP mode", bss->ifname);
+               goto failed;
+       }
+
+       if (params->num_bridge && params->bridge[0] &&
+           i802_check_bridge(drv, params->bridge[0], params->ifname) < 0)
+               goto failed;
+
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1))
+               goto failed;
+
+       drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+       if (drv->eapol_sock < 0) {
+               perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
+               goto failed;
+       }
+
+       if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+       {
+               printf("Could not register read socket for eapol\n");
+               goto failed;
+       }
+
+       if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, params->own_addr))
+               goto failed;
+
+       return bss;
+
+failed:
+       nl80211_remove_monitor_interface(drv);
+       if (drv->ioctl_sock >= 0)
+               close(drv->ioctl_sock);
+
+       genl_family_put(drv->nl80211);
+       nl_cache_free(drv->nl_cache);
+       nl_handle_destroy(drv->nl_handle);
+       nl_cb_put(drv->nl_cb);
+
+       os_free(drv);
+       return NULL;
+}
+
+
+static void i802_deinit(void *priv)
+{
+       wpa_driver_nl80211_deinit(priv);
+}
+
+#endif /* HOSTAPD */
+
+
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+       enum wpa_driver_if_type type)
+{
+       switch (type) {
+       case WPA_IF_STATION:
+               return NL80211_IFTYPE_STATION;
+       case WPA_IF_AP_VLAN:
+               return NL80211_IFTYPE_AP_VLAN;
+       case WPA_IF_AP_BSS:
+               return NL80211_IFTYPE_AP;
+       }
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+                                    const char *ifname, const u8 *addr,
+                                    void *bss_ctx, void **drv_priv,
+                                    char *force_ifname, u8 *if_addr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ifidx;
+#ifdef HOSTAPD
+       struct i802_bss *new_bss = NULL;
+
+       if (type == WPA_IF_AP_BSS) {
+               new_bss = os_zalloc(sizeof(*new_bss));
+               if (new_bss == NULL)
+                       return -1;
+       }
+#endif /* HOSTAPD */
+
+       if (addr)
+               os_memcpy(if_addr, addr, ETH_ALEN);
+       ifidx = nl80211_create_iface(drv, ifname,
+                                    wpa_driver_nl80211_if_type(type), addr,
+                                    0);
+       if (ifidx < 0) {
+#ifdef HOSTAPD
+               os_free(new_bss);
+#endif /* HOSTAPD */
+               return -1;
+       }
+
+       if (!addr &&
+           linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, if_addr) < 0)
+               return -1;
+
+#ifdef HOSTAPD
+       if (type == WPA_IF_AP_BSS) {
+               if (linux_set_iface_flags(drv->ioctl_sock, ifname, 1)) {
+                       nl80211_remove_iface(drv, ifidx);
+                       os_free(new_bss);
+                       return -1;
+               }
+               os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+               new_bss->ifindex = ifidx;
+               new_bss->drv = drv;
+               new_bss->next = drv->first_bss.next;
+               drv->first_bss.next = new_bss;
+               if (drv_priv)
+                       *drv_priv = new_bss;
+       }
+#endif /* HOSTAPD */
+
+       return 0;
+}
+
+
+static int wpa_driver_nl80211_if_remove(void *priv,
+                                       enum wpa_driver_if_type type,
+                                       const char *ifname)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ifindex = if_nametoindex(ifname);
+
+       wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d",
+                  __func__, type, ifname, ifindex);
+       if (ifindex <= 0)
+               return -1;
+       nl80211_remove_iface(drv, ifindex);
+
+#ifdef HOSTAPD
+       if (type != WPA_IF_AP_BSS)
+               return 0;
+
+       if (bss != &drv->first_bss) {
+               struct i802_bss *tbss = &drv->first_bss;
+
+               while (tbss) {
+                       if (tbss->next != bss)
+                               continue;
+
+                       tbss->next = bss->next;
+                       os_free(bss);
+                       break;
+               }
+       }
+#endif /* HOSTAPD */
+
+       return 0;
+}
+
+
+static int cookie_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       u64 *cookie = arg;
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (tb[NL80211_ATTR_COOKIE])
+               *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_send_action(void *priv, unsigned int freq,
+                                         const u8 *dst, const u8 *src,
+                                         const u8 *bssid,
+                                         const u8 *data, size_t data_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       struct nl_msg *msg;
+       u8 *buf;
+       struct ieee80211_hdr *hdr;
+       u64 cookie;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d)",
+                  drv->ifindex);
+
+       buf = os_zalloc(24 + data_len);
+       if (buf == NULL)
+               return ret;
+       os_memcpy(buf + 24, data, data_len);
+       hdr = (struct ieee80211_hdr *) buf;
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+       os_memcpy(hdr->addr1, dst, ETH_ALEN);
+       os_memcpy(hdr->addr2, src, ETH_ALEN);
+       os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+       if (drv->nlmode == NL80211_IFTYPE_AP) {
+               ret = wpa_driver_nl80211_send_mlme(priv, buf, 24 + data_len);
+               os_free(buf);
+               return ret;
+       }
+
+       msg = nlmsg_alloc();
+       if (!msg) {
+               os_free(buf);
+               return -1;
+       }
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_ACTION, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       NLA_PUT(msg, NL80211_ATTR_FRAME, 24 + data_len, buf);
+       os_free(buf);
+       buf = NULL;
+
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Action command failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               goto nla_put_failure;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Action TX command accepted; "
+                  "cookie 0x%llx", (long long unsigned int) cookie);
+       drv->send_action_cookie = cookie;
+       ret = 0;
+
+nla_put_failure:
+       os_free(buf);
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+                                               unsigned int duration)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+       u64 cookie;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_REMAIN_ON_CHANNEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
+       NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+                          "0x%llx for freq=%u MHz duration=%u",
+                          (long long unsigned int) cookie, freq, duration);
+               drv->remain_on_chan_cookie = cookie;
+               return 0;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+                  "(freq=%d): %d (%s)", freq, ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       if (!drv->pending_remain_on_chan) {
+               wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+                          "to cancel");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+                  "0x%llx",
+                  (long long unsigned int) drv->remain_on_chan_cookie);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == 0)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+                  "%d (%s)", ret, strerror(-ret));
+nla_put_failure:
+       return -1;
+}
+
+
+static void wpa_driver_nl80211_probe_req_report_timeout(void *eloop_ctx,
+                                                       void *timeout_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->monitor_ifidx < 0)
+               return; /* monitor interface already removed */
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION)
+               return; /* not in station mode anymore */
+
+       if (drv->probe_req_report)
+               return; /* reporting enabled */
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface due to no "
+                  "Probe Request reporting needed anymore");
+       nl80211_remove_monitor_interface(drv);
+}
+
+
+static int wpa_driver_nl80211_probe_req_report(void *priv, int report)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+
+       if (drv->nlmode != NL80211_IFTYPE_STATION) {
+               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+                          "allowed in station mode (iftype=%d)",
+                          drv->nlmode);
+               return -1;
+       }
+       drv->probe_req_report = report;
+
+       if (report) {
+               eloop_cancel_timeout(
+                       wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+               if (drv->monitor_ifidx < 0 &&
+                   nl80211_create_monitor_interface(drv))
+                       return -1;
+       } else {
+               /*
+                * It takes a while to remove the monitor interface, so try to
+                * avoid doing this if it is needed again shortly. Instead,
+                * schedule the interface to be removed later if no need for it
+                * is seen.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Scheduling monitor interface "
+                          "to be removed after 10 seconds of no use");
+               eloop_register_timeout(
+                       10, 0, wpa_driver_nl80211_probe_req_report_timeout,
+                       drv, NULL);
+       }
+
+       return 0;
+}
+
+
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled)
+{
+       struct nl_msg *msg;
+       struct nlattr *bands, *band;
+       int ret;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0,
+                   NL80211_CMD_SET_TX_BITRATE_MASK, 0);
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+
+       bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+       if (!bands)
+               goto nla_put_failure;
+
+       /*
+        * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+        * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+        * rates. All 5 GHz rates are left enabled.
+        */
+       band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+       if (!band)
+               goto nla_put_failure;
+       NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
+               "\x0c\x12\x18\x24\x30\x48\x60\x6c");
+       nla_nest_end(msg, band);
+
+       nla_nest_end(msg, bands);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+       }
+
+       return ret;
+
+nla_put_failure:
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int wpa_driver_nl80211_disable_11b_rates(void *priv, int disabled)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       drv->disable_11b_rates = disabled;
+       return nl80211_disable_11b_rates(drv, drv->ifindex, disabled);
+}
+
+
+static int wpa_driver_nl80211_deinit_ap(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (drv->nlmode != NL80211_IFTYPE_AP)
+               return -1;
+       wpa_driver_nl80211_del_beacon(drv);
+       return wpa_driver_nl80211_set_mode(priv, IEEE80211_MODE_INFRA);
+}
+
+
+static void wpa_driver_nl80211_resume(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (linux_set_iface_flags(drv->ioctl_sock, bss->ifname, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on "
+                          "resume event");
+       }
+}
+
+
+static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
+                                 const u8 *ies, size_t ies_len)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret;
+       u8 *data, *pos;
+       size_t data_len;
+       u8 own_addr[ETH_ALEN];
+
+       if (linux_get_ifhwaddr(drv->ioctl_sock, bss->ifname, own_addr) < 0)
+               return -1;
+
+       if (action != 1) {
+               wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
+                          "action %d", action);
+               return -1;
+       }
+
+       /*
+        * Action frame payload:
+        * Category[1] = 6 (Fast BSS Transition)
+        * Action[1] = 1 (Fast BSS Transition Request)
+        * STA Address
+        * Target AP Address
+        * FT IEs
+        */
+
+       data_len = 2 + 2 * ETH_ALEN + ies_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       pos = data;
+       *pos++ = 0x06; /* FT Action category */
+       *pos++ = action;
+       os_memcpy(pos, own_addr, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, target_ap, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, ies, ies_len);
+
+       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, drv->bssid,
+                                            own_addr, drv->bssid,
+                                            data, data_len);
+       os_free(data);
+
+       return ret;
+}
+
+
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg, *cqm = NULL;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+                  "hysteresis=%d", threshold, hysteresis);
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -1;
+
+       genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0,
+                   0, NL80211_CMD_SET_CQM, 0);
+
+       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+
+       cqm = nlmsg_alloc();
+       if (cqm == NULL)
+               return -1;
+
+       NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
+       NLA_PUT_U32(cqm, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
+       nla_put_nested(msg, NL80211_ATTR_CQM, cqm);
+
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+       msg = NULL;
+
+nla_put_failure:
+       if (cqm)
+               nlmsg_free(cqm);
+       nlmsg_free(msg);
+       return -1;
+}
+
+
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+                             int encrypt)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return wpa_driver_nl80211_send_frame(drv, data, data_len, encrypt);
+}
+
+
+const struct wpa_driver_ops wpa_driver_nl80211_ops = {
+       .name = "nl80211",
+       .desc = "Linux nl80211/cfg80211",
+       .get_bssid = wpa_driver_nl80211_get_bssid,
+       .get_ssid = wpa_driver_nl80211_get_ssid,
+       .set_key = wpa_driver_nl80211_set_key,
+       .scan2 = wpa_driver_nl80211_scan,
+       .get_scan_results2 = wpa_driver_nl80211_get_scan_results,
+       .deauthenticate = wpa_driver_nl80211_deauthenticate,
+       .disassociate = wpa_driver_nl80211_disassociate,
+       .authenticate = wpa_driver_nl80211_authenticate,
+       .associate = wpa_driver_nl80211_associate,
+       .init = wpa_driver_nl80211_init,
+       .deinit = wpa_driver_nl80211_deinit,
+       .get_capa = wpa_driver_nl80211_get_capa,
+       .set_operstate = wpa_driver_nl80211_set_operstate,
+       .set_supp_port = wpa_driver_nl80211_set_supp_port,
+       .set_country = wpa_driver_nl80211_set_country,
+       .set_beacon = wpa_driver_nl80211_set_beacon,
+       .if_add = wpa_driver_nl80211_if_add,
+       .if_remove = wpa_driver_nl80211_if_remove,
+       .send_mlme = wpa_driver_nl80211_send_mlme,
+       .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
+       .sta_add = wpa_driver_nl80211_sta_add,
+       .sta_remove = wpa_driver_nl80211_sta_remove,
+       .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
+       .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
+#ifdef HOSTAPD
+       .hapd_init = i802_init,
+       .hapd_deinit = i802_deinit,
+       .get_seqnum = i802_get_seqnum,
+       .flush = i802_flush,
+       .read_sta_data = i802_read_sta_data,
+       .sta_deauth = i802_sta_deauth,
+       .sta_disassoc = i802_sta_disassoc,
+       .get_inact_sec = i802_get_inact_sec,
+       .sta_clear_stats = i802_sta_clear_stats,
+       .set_rts = i802_set_rts,
+       .set_frag = i802_set_frag,
+       .set_rate_sets = i802_set_rate_sets,
+       .set_cts_protect = i802_set_cts_protect,
+       .set_preamble = i802_set_preamble,
+       .set_short_slot_time = i802_set_short_slot_time,
+       .set_tx_queue_params = i802_set_tx_queue_params,
+       .set_sta_vlan = i802_set_sta_vlan,
+       .set_wds_sta = i802_set_wds_sta,
+#endif /* HOSTAPD */
+       .set_freq = i802_set_freq,
+       .send_action = wpa_driver_nl80211_send_action,
+       .remain_on_channel = wpa_driver_nl80211_remain_on_channel,
+       .cancel_remain_on_channel =
+       wpa_driver_nl80211_cancel_remain_on_channel,
+       .probe_req_report = wpa_driver_nl80211_probe_req_report,
+       .disable_11b_rates = wpa_driver_nl80211_disable_11b_rates,
+       .deinit_ap = wpa_driver_nl80211_deinit_ap,
+       .resume = wpa_driver_nl80211_resume,
+       .send_ft_action = nl80211_send_ft_action,
+       .signal_monitor = nl80211_signal_monitor,
+       .send_frame = nl80211_send_frame,
+};
diff --git a/src/drivers/driver_none.c b/src/drivers/driver_none.c
new file mode 100644 (file)
index 0000000..aaeacd6
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Driver interface for RADIUS server or WPS ER only (no driver)
+ * Copyright (c) 2008, Atheros Communications
+ *
+ * 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 "driver.h"
+
+
+struct none_driver_data {
+       struct hostapd_data *hapd;
+       void *ctx;
+};
+
+
+static void * none_driver_hapd_init(struct hostapd_data *hapd,
+                                   struct wpa_init_params *params)
+{
+       struct none_driver_data *drv;
+
+       drv = os_zalloc(sizeof(struct none_driver_data));
+       if (drv == NULL) {
+               wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+                          "driver data");
+               return NULL;
+       }
+       drv->hapd = hapd;
+
+       return drv;
+}
+
+
+static void none_driver_hapd_deinit(void *priv)
+{
+       struct none_driver_data *drv = priv;
+
+       os_free(drv);
+}
+
+
+static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
+                                 u16 proto, const u8 *data, size_t data_len)
+{
+       return 0;
+}
+
+
+static void * none_driver_init(void *ctx, const char *ifname)
+{
+       struct none_driver_data *drv;
+
+       drv = os_zalloc(sizeof(struct none_driver_data));
+       if (drv == NULL) {
+               wpa_printf(MSG_ERROR, "Could not allocate memory for none "
+                          "driver data");
+               return NULL;
+       }
+       drv->ctx = ctx;
+
+       return drv;
+}
+
+
+static void none_driver_deinit(void *priv)
+{
+       struct none_driver_data *drv = priv;
+
+       os_free(drv);
+}
+
+
+static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto,
+                                 const u8 *data, size_t data_len)
+{
+       return -1;
+}
+
+
+const struct wpa_driver_ops wpa_driver_none_ops = {
+       .name = "none",
+       .desc = "no driver (RADIUS server/WPS ER)",
+       .hapd_init = none_driver_hapd_init,
+       .hapd_deinit = none_driver_hapd_deinit,
+       .send_ether = none_driver_send_ether,
+       .init = none_driver_init,
+       .deinit = none_driver_deinit,
+       .send_eapol = none_driver_send_eapol,
+};
diff --git a/src/drivers/driver_osx.m b/src/drivers/driver_osx.m
new file mode 100644 (file)
index 0000000..69ca4b5
--- /dev/null
@@ -0,0 +1,459 @@
+/*
+ * WPA Supplicant - Mac OS X Apple80211 driver interface
+ * 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"
+#define Boolean __DummyBoolean
+#include <CoreFoundation/CoreFoundation.h>
+#undef Boolean
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+
+#include "Apple80211.h"
+
+struct wpa_driver_osx_data {
+       void *ctx;
+       WirelessRef wireless_ctx;
+       CFArrayRef scan_results;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+extern int wpa_debug_level;
+
+static void dump_dict_cb(const void *key, const void *value, void *context)
+{
+        if (MSG_DEBUG < wpa_debug_level)
+                return;
+
+       wpa_printf(MSG_DEBUG, "Key:");
+       CFShow(key);
+       wpa_printf(MSG_DEBUG, "Value:");
+       CFShow(value);
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries",
+                  title, (unsigned int) CFDictionaryGetCount(dict));
+       CFDictionaryApplyFunction(dict, dump_dict_cb, NULL);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       WirelessError err;
+       WirelessInfo info;
+       int len;
+
+       err = WirelessGetInfo(drv->wireless_ctx, &info);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d",
+                          (int) err);
+               return -1;
+       }
+       if (!info.power) {
+               wpa_printf(MSG_DEBUG, "OSX: Wireless device power off");
+               return -1;
+       }
+
+       for (len = 0; len < 32; len++)
+               if (info.ssid[len] == 0)
+                       break;
+
+       os_memcpy(ssid, info.ssid, len);
+       return len;
+}
+
+
+static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       WirelessError err;
+       WirelessInfo info;
+
+       err = WirelessGetInfo(drv->wireless_ctx, &info);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d",
+                          (int) err);
+               return -1;
+       }
+       if (!info.power) {
+               wpa_printf(MSG_DEBUG, "OSX: Wireless device power off");
+               return -1;
+       }
+
+       os_memcpy(bssid, info.bssID, ETH_ALEN);
+       return 0;
+}
+
+
+static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static int wpa_driver_osx_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       WirelessError err;
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+
+       if (drv->scan_results) {
+               CFRelease(drv->scan_results);
+               drv->scan_results = NULL;
+       }
+
+       if (ssid) {
+               CFStringRef data;
+               data = CFStringCreateWithBytes(kCFAllocatorDefault,
+                                              ssid, ssid_len,
+                                              kCFStringEncodingISOLatin1,
+                                              FALSE);
+               if (data == NULL) {
+                       wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes "
+                                  "failed");
+                       return -1;
+               }
+
+               err = WirelessDirectedScan(drv->wireless_ctx,
+                                          &drv->scan_results, 0, data);
+               CFRelease(data);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan "
+                                  "failed: 0x%08x", (unsigned int) err);
+                       return -1;
+               }
+       } else {
+               err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: "
+                                  "0x%08x", (unsigned int) err);
+                       return -1;
+               }
+       }
+
+       eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv,
+                              drv->ctx);
+       return 0;
+}
+
+
+static void wpa_driver_osx_add_scan_entry(struct wpa_scan_results *res,
+                                         WirelessNetworkInfo *info)
+{
+       struct wpa_scan_res *result, **tmp;
+       size_t extra_len;
+       u8 *pos;
+
+       extra_len = 2 + info->ssid_len;
+
+       result = os_zalloc(sizeof(*result) + extra_len);
+       if (result == NULL)
+               return;
+       os_memcpy(result->bssid, info->bssid, ETH_ALEN);
+       result->freq = 2407 + info->channel * 5;
+       //result->beacon_int =;
+       result->caps = info->capability;
+       //result->qual = info->signal;
+       result->noise = info->noise;
+
+       pos = (u8 *)(result + 1);
+
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = info->ssid_len;
+       os_memcpy(pos, info->ssid, info->ssid_len);
+       pos += info->ssid_len;
+
+       result->ie_len = pos - (u8 *)(result + 1);
+
+       tmp = os_realloc(res->res,
+                        (res->num + 1) * sizeof(struct wpa_scan_res *));
+       if (tmp == NULL) {
+               os_free(result);
+               return;
+       }
+       tmp[res->num++] = result;
+       res->res = tmp;
+}
+
+
+static struct wpa_scan_results * wpa_driver_osx_get_scan_results(void *priv)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       struct wpa_scan_results *res;
+       size_t i, num;
+
+       if (drv->scan_results == NULL)
+               return 0;
+
+       num = CFArrayGetCount(drv->scan_results);
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL)
+               return NULL;
+
+       for (i = 0; i < num; i++)
+               wpa_driver_osx_add_scan_entry(res, (WirelessNetworkInfo *)
+                       CFDataGetBytePtr(CFArrayGetValueAtIndex(
+                               drv->scan_results, i)));
+
+       return res;
+}
+
+
+static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_osx_data *drv = eloop_ctx;
+       u8 bssid[ETH_ALEN];
+       CFDictionaryRef ai;
+
+       if (wpa_driver_osx_get_bssid(drv, bssid) != 0) {
+               eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout,
+                                      drv, drv->ctx);
+               return;
+       }
+
+       ai = WirelessGetAssociationInfo(drv->wireless_ctx);
+       if (ai) {
+               wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo");
+               CFRelease(ai);
+       } else {
+               wpa_printf(MSG_DEBUG, "OSX: Failed to get association info");
+       }
+
+       wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL);
+}
+
+
+static int wpa_driver_osx_associate(void *priv,
+                                   struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       WirelessError err;
+       CFDataRef ssid;
+       CFStringRef key;
+       int assoc_type;
+
+       ssid = CFDataCreate(kCFAllocatorDefault, params->ssid,
+                           params->ssid_len);
+       if (ssid == NULL)
+               return -1;
+
+       /* TODO: support for WEP */
+       if (params->key_mgmt_suite == KEY_MGMT_PSK) {
+               if (params->passphrase == NULL)
+                       return -1;
+               key = CFStringCreateWithCString(kCFAllocatorDefault,
+                                               params->passphrase,
+                                               kCFStringEncodingISOLatin1);
+               if (key == NULL) {
+                       CFRelease(ssid);
+                       return -1;
+               }
+       } else
+               key = NULL;
+
+       if (params->key_mgmt_suite == KEY_MGMT_NONE)
+               assoc_type = 0;
+       else
+               assoc_type = 4;
+
+       wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)",
+                  assoc_type, key);
+       err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key);
+       CFRelease(ssid);
+       if (key)
+               CFRelease(key);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x",
+                          (unsigned int) err);
+               return -1;
+       }
+
+       /*
+        * Driver is actually already associated; report association from an
+        * eloop callback.
+        */
+       eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx);
+       eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv,
+                              drv->ctx);
+
+       return 0;
+}
+
+
+static int wpa_driver_osx_set_key(const char *ifname, void *priv,
+                                 enum wpa_alg alg, const u8 *addr,
+                                 int key_idx, int set_tx, const u8 *seq,
+                                 size_t seq_len, const u8 *key,
+                                 size_t key_len)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       WirelessError err;
+
+       if (alg == WPA_ALG_WEP) {
+               err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len,
+                                    key);
+               if (err != 0) {
+                       wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: "
+                                  "0x%08x", (unsigned int) err);
+                       return -1;
+               }
+
+               return 0;
+       }
+
+       if (alg == WPA_ALG_PMK) {
+               err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key);
+               if (err != 0) {
+                       wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: "
+                                  "0x%08x", (unsigned int) err);
+                       return -1;
+               }
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg);
+       return -1;
+}
+
+
+static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       os_memset(capa, 0, sizeof(*capa));
+
+       capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+       capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 |
+               WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP;
+       capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
+       capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+
+       return 0;
+}
+
+
+static void * wpa_driver_osx_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_osx_data *drv;
+       WirelessError err;
+       u8 enabled, power;
+
+       if (!WirelessIsAvailable()) {
+               wpa_printf(MSG_ERROR, "OSX: No wireless interface available");
+               return NULL;
+       }
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       err = WirelessAttach(&drv->wireless_ctx, 0);
+       if (err) {
+               wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d",
+                          (int) err);
+               os_free(drv);
+               return NULL;
+       }
+
+       err = WirelessGetEnabled(drv->wireless_ctx, &enabled);
+       if (err)
+               wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x",
+                          (unsigned int) err);
+       err = WirelessGetPower(drv->wireless_ctx, &power);
+       if (err)
+               wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x",
+                          (unsigned int) err);
+
+       wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power);
+
+       if (!enabled) {
+               err = WirelessSetEnabled(drv->wireless_ctx, 1);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:"
+                                  " 0x%08x", (unsigned int) err);
+                       WirelessDetach(drv->wireless_ctx);
+                       os_free(drv);
+                       return NULL;
+               }
+       }
+
+       if (!power) {
+               err = WirelessSetPower(drv->wireless_ctx, 1);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: "
+                                  "0x%08x", (unsigned int) err);
+                       WirelessDetach(drv->wireless_ctx);
+                       os_free(drv);
+                       return NULL;
+               }
+       }
+
+       return drv;
+}
+
+
+static void wpa_driver_osx_deinit(void *priv)
+{
+       struct wpa_driver_osx_data *drv = priv;
+       WirelessError err;
+
+       eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx);
+       eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx);
+
+       err = WirelessSetPower(drv->wireless_ctx, 0);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: "
+                          "0x%08x", (unsigned int) err);
+       }
+
+       err = WirelessDetach(drv->wireless_ctx);
+       if (err) {
+               wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x",
+                          (unsigned int) err);
+       }
+
+       if (drv->scan_results)
+               CFRelease(drv->scan_results);
+
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_osx_ops = {
+       .name = "osx",
+       .desc = "Mac OS X Apple80211 driver",
+       .get_ssid = wpa_driver_osx_get_ssid,
+       .get_bssid = wpa_driver_osx_get_bssid,
+       .init = wpa_driver_osx_init,
+       .deinit = wpa_driver_osx_deinit,
+       .scan2 = wpa_driver_osx_scan,
+       .get_scan_results2 = wpa_driver_osx_get_scan_results,
+       .associate = wpa_driver_osx_associate,
+       .set_key = wpa_driver_osx_set_key,
+       .get_capa = wpa_driver_osx_get_capa,
+};
diff --git a/src/drivers/driver_privsep.c b/src/drivers/driver_privsep.c
new file mode 100644 (file)
index 0000000..2848521
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+ * WPA Supplicant - privilege separated driver interface
+ * Copyright (c) 2007-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 <sys/un.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "common/privsep_commands.h"
+
+
+struct wpa_driver_privsep_data {
+       void *ctx;
+       u8 own_addr[ETH_ALEN];
+       int priv_socket;
+       char *own_socket_path;
+       int cmd_socket;
+       char *own_cmd_path;
+       struct sockaddr_un priv_addr;
+       char ifname[16];
+};
+
+
+static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
+{
+       int res;
+
+       res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0,
+                    (struct sockaddr *) &drv->priv_addr,
+                    sizeof(drv->priv_addr));
+       if (res < 0)
+               perror("sendto");
+       return res < 0 ? -1 : 0;
+}
+
+
+static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
+                       const void *data, size_t data_len,
+                       void *reply, size_t *reply_len)
+{
+       struct msghdr msg;
+       struct iovec io[2];
+
+       io[0].iov_base = &cmd;
+       io[0].iov_len = sizeof(cmd);
+       io[1].iov_base = (u8 *) data;
+       io[1].iov_len = data_len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = data ? 2 : 1;
+       msg.msg_name = &drv->priv_addr;
+       msg.msg_namelen = sizeof(drv->priv_addr);
+
+       if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
+               perror("sendmsg(cmd_socket)");
+               return -1;
+       }
+
+       if (reply) {
+               fd_set rfds;
+               struct timeval tv;
+               int res;
+
+               FD_ZERO(&rfds);
+               FD_SET(drv->cmd_socket, &rfds);
+               tv.tv_sec = 5;
+               tv.tv_usec = 0;
+               res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
+               if (res < 0 && errno != EINTR) {
+                       perror("select");
+                       return -1;
+               }
+
+               if (FD_ISSET(drv->cmd_socket, &rfds)) {
+                       res = recv(drv->cmd_socket, reply, *reply_len, 0);
+                       if (res < 0) {
+                               perror("recv");
+                               return -1;
+                       }
+                       *reply_len = res;
+               } else {
+                       wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting "
+                                  "for reply (cmd=%d)", cmd);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+                            
+static int wpa_driver_privsep_scan(void *priv,
+                                  struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+       wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+       return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len,
+                           NULL, NULL);
+}
+
+
+static struct wpa_scan_results *
+wpa_driver_privsep_get_scan_results2(void *priv)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       int res, num;
+       u8 *buf, *pos, *end;
+       size_t reply_len = 60000;
+       struct wpa_scan_results *results;
+       struct wpa_scan_res *r;
+
+       buf = os_malloc(reply_len);
+       if (buf == NULL)
+               return NULL;
+       res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS,
+                          NULL, 0, buf, &reply_len);
+       if (res < 0) {
+               os_free(buf);
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results",
+                  (unsigned long) reply_len);
+       if (reply_len < sizeof(int)) {
+               wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu",
+                          (unsigned long) reply_len);
+               os_free(buf);
+               return NULL;
+       }
+
+       pos = buf;
+       end = buf + reply_len;
+       os_memcpy(&num, pos, sizeof(int));
+       if (num < 0 || num > 1000) {
+               os_free(buf);
+               return NULL;
+       }
+       pos += sizeof(int);
+
+       results = os_zalloc(sizeof(*results));
+       if (results == NULL) {
+               os_free(buf);
+               return NULL;
+       }
+
+       results->res = os_zalloc(num * sizeof(struct wpa_scan_res *));
+       if (results->res == NULL) {
+               os_free(results);
+               os_free(buf);
+               return NULL;
+       }
+
+       while (results->num < (size_t) num && pos + sizeof(int) < end) {
+               int len;
+               os_memcpy(&len, pos, sizeof(int));
+               pos += sizeof(int);
+               if (len < 0 || len > 10000 || pos + len > end)
+                       break;
+
+               r = os_malloc(len);
+               if (r == NULL)
+                       break;
+               os_memcpy(r, pos, len);
+               pos += len;
+               if (sizeof(*r) + r->ie_len > (size_t) len) {
+                       os_free(r);
+                       break;
+               }
+
+               results->res[results->num++] = r;
+       }
+
+       os_free(buf);
+       return results;
+}
+
+
+static int wpa_driver_privsep_set_key(const char *ifname, void *priv,
+                                     enum wpa_alg alg, const u8 *addr,
+                                     int key_idx, int set_tx,
+                                     const u8 *seq, size_t seq_len,
+                                     const u8 *key, size_t key_len)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       struct privsep_cmd_set_key cmd;
+
+       wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d",
+                  __func__, priv, alg, key_idx, set_tx);
+
+       os_memset(&cmd, 0, sizeof(cmd));
+       cmd.alg = alg;
+       if (addr)
+               os_memcpy(cmd.addr, addr, ETH_ALEN);
+       else
+               os_memset(cmd.addr, 0xff, ETH_ALEN);
+       cmd.key_idx = key_idx;
+       cmd.set_tx = set_tx;
+       if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) {
+               os_memcpy(cmd.seq, seq, seq_len);
+               cmd.seq_len = seq_len;
+       }
+       if (key && key_len > 0 && key_len < sizeof(cmd.key)) {
+               os_memcpy(cmd.key, key, key_len);
+               cmd.key_len = key_len;
+       }
+
+       return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd),
+                           NULL, NULL);
+}
+
+
+static int wpa_driver_privsep_associate(
+       void *priv, struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       struct privsep_cmd_associate *data;
+       int res;
+       size_t buflen;
+
+       wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+                  "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+                  __func__, priv, params->freq, params->pairwise_suite,
+                  params->group_suite, params->key_mgmt_suite,
+                  params->auth_alg, params->mode);
+
+       buflen = sizeof(*data) + params->wpa_ie_len;
+       data = os_zalloc(buflen);
+       if (data == NULL)
+               return -1;
+
+       if (params->bssid)
+               os_memcpy(data->bssid, params->bssid, ETH_ALEN);
+       os_memcpy(data->ssid, params->ssid, params->ssid_len);
+       data->ssid_len = params->ssid_len;
+       data->freq = params->freq;
+       data->pairwise_suite = params->pairwise_suite;
+       data->group_suite = params->group_suite;
+       data->key_mgmt_suite = params->key_mgmt_suite;
+       data->auth_alg = params->auth_alg;
+       data->mode = params->mode;
+       data->wpa_ie_len = params->wpa_ie_len;
+       if (params->wpa_ie)
+               os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len);
+       /* TODO: add support for other assoc parameters */
+
+       res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen,
+                          NULL, NULL);
+       os_free(data);
+
+       return res;
+}
+
+
+static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       int res;
+       size_t len = ETH_ALEN;
+
+       res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len);
+       if (res < 0 || len != ETH_ALEN)
+               return -1;
+       return 0;
+}
+
+
+static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       int res, ssid_len;
+       u8 reply[sizeof(int) + 32];
+       size_t len = sizeof(reply);
+
+       res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len);
+       if (res < 0 || len < sizeof(int))
+               return -1;
+       os_memcpy(&ssid_len, reply, sizeof(int));
+       if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) {
+               wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply");
+               return -1;
+       }
+       os_memcpy(ssid, &reply[sizeof(int)], ssid_len);
+       return ssid_len;
+}
+
+
+static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       //struct wpa_driver_privsep_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+                  __func__, MAC2STR(addr), reason_code);
+       wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+       return 0;
+}
+
+
+static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr,
+                                       int reason_code)
+{
+       //struct wpa_driver_privsep_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+                  __func__, MAC2STR(addr), reason_code);
+       wpa_printf(MSG_DEBUG, "%s - TODO", __func__);
+       return 0;
+}
+
+
+static void wpa_driver_privsep_event_assoc(void *ctx,
+                                          enum wpa_event_type event,
+                                          u8 *buf, size_t len)
+{
+       union wpa_event_data data;
+       int inc_data = 0;
+       u8 *pos, *end;
+       int ie_len;
+
+       os_memset(&data, 0, sizeof(data));
+
+       pos = buf;
+       end = buf + len;
+
+       if (end - pos < (int) sizeof(int))
+               return;
+       os_memcpy(&ie_len, pos, sizeof(int));
+       pos += sizeof(int);
+       if (ie_len < 0 || ie_len > end - pos)
+               return;
+       if (ie_len) {
+               data.assoc_info.req_ies = pos;
+               data.assoc_info.req_ies_len = ie_len;
+               pos += ie_len;
+               inc_data = 1;
+       }
+
+       wpa_supplicant_event(ctx, event, inc_data ? &data : NULL);
+}
+
+
+static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf,
+                                                     size_t len)
+{
+       union wpa_event_data data;
+       int ievent;
+
+       if (len < sizeof(int) ||
+           len - sizeof(int) > sizeof(data.interface_status.ifname))
+               return;
+
+       os_memcpy(&ievent, buf, sizeof(int));
+
+       os_memset(&data, 0, sizeof(data));
+       data.interface_status.ievent = ievent;
+       os_memcpy(data.interface_status.ifname, buf + sizeof(int),
+                 len - sizeof(int));
+       wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data);
+}
+
+
+static void wpa_driver_privsep_event_michael_mic_failure(
+       void *ctx, u8 *buf, size_t len)
+{
+       union wpa_event_data data;
+
+       if (len != sizeof(int))
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int));
+       wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf,
+                                                    size_t len)
+{
+       union wpa_event_data data;
+
+       if (len != sizeof(struct pmkid_candidate))
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(&data.pmkid_candidate, buf, len);
+       wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len)
+{
+       union wpa_event_data data;
+
+       if (len != ETH_ALEN)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.stkstart.peer, buf, ETH_ALEN);
+       wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+}
+
+
+static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf,
+                                                size_t len)
+{
+       union wpa_event_data data;
+
+       if (len < sizeof(int) + ETH_ALEN)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int));
+       os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN);
+       data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN;
+       data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN;
+       wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len)
+{
+       if (len < ETH_ALEN)
+               return;
+       drv_event_eapol_rx(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN);
+}
+
+
+static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
+                                      void *sock_ctx)
+{
+       struct wpa_driver_privsep_data *drv = eloop_ctx;
+       u8 *buf, *event_buf;
+       size_t event_len;
+       int res, event;
+       enum privsep_event e;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+       const size_t buflen = 2000;
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       res = recvfrom(sock, buf, buflen, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               perror("recvfrom(priv_socket)");
+               os_free(buf);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res);
+
+       if (res < (int) sizeof(int)) {
+               wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res);
+               return;
+       }
+
+       os_memcpy(&event, buf, sizeof(int));
+       event_buf = &buf[sizeof(int)];
+       event_len = res - sizeof(int);
+       wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)",
+                  event, (unsigned long) event_len);
+
+       e = event;
+       switch (e) {
+       case PRIVSEP_EVENT_SCAN_RESULTS:
+               wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL);
+               break;
+       case PRIVSEP_EVENT_ASSOC:
+               wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC,
+                                              event_buf, event_len);
+               break;
+       case PRIVSEP_EVENT_DISASSOC:
+               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+               break;
+       case PRIVSEP_EVENT_ASSOCINFO:
+               wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO,
+                                              event_buf, event_len);
+               break;
+       case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE:
+               wpa_driver_privsep_event_michael_mic_failure(
+                       drv->ctx, event_buf, event_len);
+               break;
+       case PRIVSEP_EVENT_INTERFACE_STATUS:
+               wpa_driver_privsep_event_interface_status(drv->ctx, event_buf,
+                                                         event_len);
+               break;
+       case PRIVSEP_EVENT_PMKID_CANDIDATE:
+               wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf,
+                                                        event_len);
+               break;
+       case PRIVSEP_EVENT_STKSTART:
+               wpa_driver_privsep_event_stkstart(drv->ctx, event_buf,
+                                                 event_len);
+               break;
+       case PRIVSEP_EVENT_FT_RESPONSE:
+               wpa_driver_privsep_event_ft_response(drv->ctx, event_buf,
+                                                    event_len);
+               break;
+       case PRIVSEP_EVENT_RX_EAPOL:
+               wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf,
+                                                 event_len);
+               break;
+       }
+
+       os_free(buf);
+}
+
+
+static void * wpa_driver_privsep_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_privsep_data *drv;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       drv->priv_socket = -1;
+       drv->cmd_socket = -1;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+       return drv;
+}
+
+
+static void wpa_driver_privsep_deinit(void *priv)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+
+       if (drv->priv_socket >= 0) {
+               wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER);
+               eloop_unregister_read_sock(drv->priv_socket);
+               close(drv->priv_socket);
+       }
+
+       if (drv->own_socket_path) {
+               unlink(drv->own_socket_path);
+               os_free(drv->own_socket_path);
+       }
+
+       if (drv->cmd_socket >= 0) {
+               eloop_unregister_read_sock(drv->cmd_socket);
+               close(drv->cmd_socket);
+       }
+
+       if (drv->own_cmd_path) {
+               unlink(drv->own_cmd_path);
+               os_free(drv->own_cmd_path);
+       }
+
+       os_free(drv);
+}
+
+
+static int wpa_driver_privsep_set_param(void *priv, const char *param)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       const char *pos;
+       char *own_dir, *priv_dir;
+       static unsigned int counter = 0;
+       size_t len;
+       struct sockaddr_un addr;
+
+       wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+       if (param == NULL)
+               pos = NULL;
+       else
+               pos = os_strstr(param, "own_dir=");
+       if (pos) {
+               char *end;
+               own_dir = os_strdup(pos + 8);
+               if (own_dir == NULL)
+                       return -1;
+               end = os_strchr(own_dir, ' ');
+               if (end)
+                       *end = '\0';
+       } else {
+               own_dir = os_strdup("/tmp");
+               if (own_dir == NULL)
+                       return -1;
+       }
+
+       if (param == NULL)
+               pos = NULL;
+       else
+               pos = os_strstr(param, "priv_dir=");
+       if (pos) {
+               char *end;
+               priv_dir = os_strdup(pos + 9);
+               if (priv_dir == NULL) {
+                       os_free(own_dir);
+                       return -1;
+               }
+               end = os_strchr(priv_dir, ' ');
+               if (end)
+                       *end = '\0';
+       } else {
+               priv_dir = os_strdup("/var/run/wpa_priv");
+               if (priv_dir == NULL) {
+                       os_free(own_dir);
+                       return -1;
+               }
+       }
+
+       len = os_strlen(own_dir) + 50;
+       drv->own_socket_path = os_malloc(len);
+       if (drv->own_socket_path == NULL) {
+               os_free(priv_dir);
+               os_free(own_dir);
+               return -1;
+       }
+       os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d",
+                   own_dir, getpid(), counter++);
+
+       len = os_strlen(own_dir) + 50;
+       drv->own_cmd_path = os_malloc(len);
+       if (drv->own_cmd_path == NULL) {
+               os_free(drv->own_socket_path);
+               drv->own_socket_path = NULL;
+               os_free(priv_dir);
+               os_free(own_dir);
+               return -1;
+       }
+       os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d",
+                   own_dir, getpid(), counter++);
+
+       os_free(own_dir);
+
+       drv->priv_addr.sun_family = AF_UNIX;
+       os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path),
+                   "%s/%s", priv_dir, drv->ifname);
+       os_free(priv_dir);
+
+       drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (drv->priv_socket < 0) {
+               perror("socket(PF_UNIX)");
+               os_free(drv->own_socket_path);
+               drv->own_socket_path = NULL;
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+       if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
+           0) {
+               perror("bind(PF_UNIX)");
+               close(drv->priv_socket);
+               drv->priv_socket = -1;
+               unlink(drv->own_socket_path);
+               os_free(drv->own_socket_path);
+               drv->own_socket_path = NULL;
+               return -1;
+       }
+
+       eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive,
+                                drv, NULL);
+
+       drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (drv->cmd_socket < 0) {
+               perror("socket(PF_UNIX)");
+               os_free(drv->own_cmd_path);
+               drv->own_cmd_path = NULL;
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
+       if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+       {
+               perror("bind(PF_UNIX)");
+               close(drv->cmd_socket);
+               drv->cmd_socket = -1;
+               unlink(drv->own_cmd_path);
+               os_free(drv->own_cmd_path);
+               drv->own_cmd_path = NULL;
+               return -1;
+       }
+
+       if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) {
+               wpa_printf(MSG_ERROR, "Failed to register with wpa_priv");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_privsep_get_capa(void *priv,
+                                      struct wpa_driver_capa *capa)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       int res;
+       size_t len = sizeof(*capa);
+
+       res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len);
+       if (res < 0 || len != sizeof(*capa))
+               return -1;
+       return 0;
+}
+
+
+static const u8 * wpa_driver_privsep_get_mac_addr(void *priv)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       return drv->own_addr;
+}
+
+
+static int wpa_driver_privsep_set_country(void *priv, const char *alpha2)
+{
+       struct wpa_driver_privsep_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2);
+       return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2,
+                           os_strlen(alpha2), NULL, NULL);
+}
+
+
+struct wpa_driver_ops wpa_driver_privsep_ops = {
+       "privsep",
+       "wpa_supplicant privilege separated driver",
+       .get_bssid = wpa_driver_privsep_get_bssid,
+       .get_ssid = wpa_driver_privsep_get_ssid,
+       .set_key = wpa_driver_privsep_set_key,
+       .init = wpa_driver_privsep_init,
+       .deinit = wpa_driver_privsep_deinit,
+       .set_param = wpa_driver_privsep_set_param,
+       .scan2 = wpa_driver_privsep_scan,
+       .deauthenticate = wpa_driver_privsep_deauthenticate,
+       .disassociate = wpa_driver_privsep_disassociate,
+       .associate = wpa_driver_privsep_associate,
+       .get_capa = wpa_driver_privsep_get_capa,
+       .get_mac_addr = wpa_driver_privsep_get_mac_addr,
+       .get_scan_results2 = wpa_driver_privsep_get_scan_results2,
+       .set_country = wpa_driver_privsep_set_country,
+};
+
+
+struct wpa_driver_ops *wpa_drivers[] =
+{
+       &wpa_driver_privsep_ops,
+       NULL
+};
diff --git a/src/drivers/driver_ralink.c b/src/drivers/driver_ralink.c
new file mode 100644 (file)
index 0000000..09d1ef5
--- /dev/null
@@ -0,0 +1,1499 @@
+/*
+ * WPA Supplicant - driver interaction with Ralink Wireless Client
+ * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw>
+ *
+ * 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 <sys/ioctl.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "driver_ralink.h"
+
+static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+#define MAX_SSID_LEN 32
+
+struct wpa_driver_ralink_data {
+       void *ctx;
+       int ioctl_sock;
+       struct netlink_data *netlink;
+       char ifname[IFNAMSIZ + 1];
+       u8 *assoc_req_ies;
+       size_t assoc_req_ies_len;
+       u8 *assoc_resp_ies;
+       size_t assoc_resp_ies_len;
+       int no_of_pmkid;
+       struct ndis_pmkid_entry *pmkid;
+       int we_version_compiled;
+       int ap_scan;
+       int scanning_done;
+       u8 g_driver_down;
+       BOOLEAN bAddWepKey;
+};
+
+static int ralink_set_oid(struct wpa_driver_ralink_data *drv,
+                         unsigned short oid, char *data, int len)
+{
+       char *buf;
+       struct iwreq iwr;
+
+       buf = os_zalloc(len);
+       if (buf == NULL)
+               return -1;
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.flags = oid;
+       iwr.u.data.flags |= OID_GET_SET_TOGGLE;
+
+       if (data)
+               os_memcpy(buf, data, len);
+
+       iwr.u.data.pointer = (caddr_t) buf;
+       iwr.u.data.length = len;
+
+       if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed",
+                          __func__, oid, len);
+               os_free(buf);
+               return -1;
+       }
+       os_free(buf);
+       return 0;
+}
+
+static int
+ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv)
+{
+       struct iwreq iwr;
+       UCHAR enabled = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (UCHAR*) &enabled;
+       iwr.u.data.flags = RT_OID_NEW_DRIVER;
+
+       if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed", __func__);
+               return 0;
+       }
+
+       return (enabled == 1) ? 1 : 0;
+}
+
+static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+               perror("ioctl[SIOCGIWAP]");
+               ret = -1;
+       }
+       os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+       return ret;
+}
+
+static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+#if 0
+       struct wpa_supplicant *wpa_s = drv->ctx;
+       struct wpa_ssid *entry;
+#endif
+       int ssid_len;
+       u8 bssid[ETH_ALEN];
+       u8 ssid_str[MAX_SSID_LEN];
+       struct iwreq iwr;
+#if 0
+       int result = 0;
+#endif
+       int ret = 0;
+#if 0
+       BOOLEAN ieee8021x_mode = FALSE;
+       BOOLEAN ieee8021x_required_key = FALSE;
+#endif
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.essid.pointer = (caddr_t) ssid;
+       iwr.u.essid.length = 32;
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCGIWESSID]");
+               ret = -1;
+       } else
+               ret = iwr.u.essid.length;
+
+       if (ret <= 0)
+               return ret;
+
+       ssid_len = ret;
+       os_memset(ssid_str, 0, MAX_SSID_LEN);
+       os_memcpy(ssid_str, ssid, ssid_len);
+
+       if (drv->ap_scan == 0) {
+               /* Read BSSID form driver */
+               if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) {
+                       wpa_printf(MSG_WARNING, "Could not read BSSID from "
+                                  "driver.");
+                       return ret;
+               }
+
+#if 0
+               entry = wpa_s->conf->ssid;
+               while (entry) {
+                       if (!entry->disabled && ssid_len == entry->ssid_len &&
+                           os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 &&
+                           (!entry->bssid_set ||
+                            os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) {
+                               /* match the config of driver */
+                               result = 1;
+                               break;
+                       }
+                       entry = entry->next;
+               }
+
+               if (result) {
+                       wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and "
+                                  "ieee_required_keys parameters to driver");
+
+                       /* set 802.1x mode and ieee_required_keys parameter */
+                       if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+                               if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST)))
+                                               ieee8021x_required_key = TRUE;
+                               ieee8021x_mode = TRUE;
+                       }
+
+                       if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0)
+                       {
+                               wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode);
+                       }
+                       else
+                       {
+                               wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE");
+                       }
+
+                       if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0)
+                       {
+                               wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key);
+                       }
+                       else
+                       {
+                               wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE",
+                                                                                                                                                                                               entry->eapol_flags);
+                       }
+               }
+#endif
+       }
+
+       return ret;
+}
+
+static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv,
+                                     const u8 *ssid, size_t ssid_len)
+{
+       NDIS_802_11_SSID *buf;
+       int ret = 0;
+       struct iwreq iwr;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       buf = os_zalloc(sizeof(NDIS_802_11_SSID));
+       if (buf == NULL)
+               return -1;
+       os_memset(buf, 0, sizeof(buf));
+       buf->SsidLength = ssid_len;
+       os_memcpy(buf->Ssid, ssid, ssid_len);
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       iwr.u.data.flags = OID_802_11_SSID;
+       iwr.u.data.flags |= OID_GET_SET_TOGGLE;
+       iwr.u.data.pointer = (caddr_t) buf;
+       iwr.u.data.length = sizeof(NDIS_802_11_SSID);
+
+       if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+               perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID");
+               ret = -1;
+       }
+       os_free(buf);
+       return ret;
+}
+
+static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv,
+                                         const u8 *data, size_t data_len)
+{
+       NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid;
+       size_t i;
+       union wpa_event_data event;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (data_len < 8) {
+               wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List "
+                          "Event (len=%lu)", (unsigned long) data_len);
+               return;
+       }
+       pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data;
+       wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d"
+                  " NumCandidates %d",
+                  (int) pmkid->Version, (int) pmkid->NumCandidates);
+
+       if (pmkid->Version != 1) {
+               wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate "
+                          "List Version %d", (int) pmkid->Version);
+               return;
+       }
+
+       if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) {
+               wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List "
+                          "underflow");
+
+               return;
+       }
+
+
+
+       os_memset(&event, 0, sizeof(event));
+       for (i = 0; i < pmkid->NumCandidates; i++) {
+               PMKID_CANDIDATE *p = &pmkid->CandidateList[i];
+               wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x",
+                          (unsigned long) i, MAC2STR(p->BSSID),
+                          (int) p->Flags);
+               os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN);
+               event.pmkid_candidate.index = i;
+               event.pmkid_candidate.preauth =
+                       p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED;
+               wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE,
+                                    &event);
+       }
+}
+
+static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv)
+{
+       int len, count, i, ret;
+       struct ndis_pmkid_entry *entry;
+       NDIS_802_11_PMKID *p;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       count = 0;
+       entry = drv->pmkid;
+       while (entry) {
+               count++;
+               if (count >= drv->no_of_pmkid)
+                       break;
+               entry = entry->next;
+       }
+       len = 8 + count * sizeof(BSSID_INFO);
+       p = os_zalloc(len);
+       if (p == NULL)
+               return -1;
+       p->Length = len;
+       p->BSSIDInfoCount = count;
+       entry = drv->pmkid;
+       for (i = 0; i < count; i++) {
+               os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN);
+               os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16);
+               entry = entry->next;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID",
+                   (const u8 *) p, len);
+       ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len);
+       os_free(p);
+       return ret;
+}
+
+static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid,
+                                      const u8 *pmkid)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       struct ndis_pmkid_entry *entry, *prev;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (drv->no_of_pmkid == 0)
+               return 0;
+
+       prev = NULL;
+       entry = drv->pmkid;
+       while (entry) {
+               if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0)
+                       break;
+               prev = entry;
+               entry = entry->next;
+       }
+
+       if (entry) {
+               /* Replace existing entry for this BSSID and move it into the
+                * beginning of the list. */
+               os_memcpy(entry->pmkid, pmkid, 16);
+               if (prev) {
+                       prev->next = entry->next;
+                       entry->next = drv->pmkid;
+                       drv->pmkid = entry;
+               }
+       } else {
+               entry = os_malloc(sizeof(*entry));
+               if (entry) {
+                       os_memcpy(entry->bssid, bssid, ETH_ALEN);
+                       os_memcpy(entry->pmkid, pmkid, 16);
+                       entry->next = drv->pmkid;
+                       drv->pmkid = entry;
+               }
+       }
+
+       return wpa_driver_ralink_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid,
+                                         const u8 *pmkid)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       struct ndis_pmkid_entry *entry, *prev;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (drv->no_of_pmkid == 0)
+               return 0;
+
+       entry = drv->pmkid;
+       prev = NULL;
+       drv->pmkid = NULL;
+       while (entry) {
+               if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 &&
+                   os_memcmp(entry->pmkid, pmkid, 16) == 0) {
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               drv->pmkid = entry->next;
+                       os_free(entry);
+                       break;
+               }
+               prev = entry;
+               entry = entry->next;
+       }
+       return wpa_driver_ralink_set_pmkid(drv);
+}
+
+
+static int wpa_driver_ralink_flush_pmkid(void *priv)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       NDIS_802_11_PMKID p;
+       struct ndis_pmkid_entry *pmkid, *prev;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (drv->no_of_pmkid == 0)
+               return 0;
+
+       pmkid = drv->pmkid;
+       drv->pmkid = NULL;
+       while (pmkid) {
+               prev = pmkid;
+               pmkid = pmkid->next;
+               os_free(prev);
+       }
+
+       os_memset(&p, 0, sizeof(p));
+       p.Length = 8;
+       p.BSSIDInfoCount = 0;
+       wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)",
+                   (const u8 *) &p, 8);
+       return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8);
+}
+
+static void
+wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv,
+                                       void *ctx, char *custom)
+{
+       union wpa_event_data data;
+       u8 *req_ies = NULL, *resp_ies = NULL;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom);
+
+       os_memset(&data, 0, sizeof(data));
+       /* Host AP driver */
+       if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+               /* receive a MICFAILURE report */
+               data.michael_mic_failure.unicast =
+                       os_strstr(custom, " unicast") != NULL;
+               /* TODO: parse parameters(?) */
+               wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+       } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) {
+               /* receive assoc. req. IEs */
+               char *spos;
+               int bytes;
+
+               spos = custom + 17;
+               /*get IE's length */
+               /*
+                * bytes = strlen(spos); ==> bug, bytes may less than original
+                * size by using this way to get size. snowpin 20070312
+                * if (!bytes)
+                *      return;
+                */
+               bytes = drv->assoc_req_ies_len;
+
+               req_ies = os_malloc(bytes);
+               if (req_ies == NULL)
+                       return;
+               os_memcpy(req_ies, spos, bytes);
+               data.assoc_info.req_ies = req_ies;
+               data.assoc_info.req_ies_len = bytes;
+
+               /* skip the '\0' byte */
+               spos += bytes + 1;
+
+               data.assoc_info.resp_ies = NULL;
+               data.assoc_info.resp_ies_len = 0;
+
+               if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+                       /* receive assoc. resp. IEs */
+                       spos += 9;
+                       /* get IE's length */
+                       bytes = os_strlen(spos);
+                       if (!bytes)
+                               goto done;
+
+                       resp_ies = os_malloc(bytes);
+                       if (resp_ies == NULL)
+                               goto done;
+                       os_memcpy(resp_ies, spos, bytes);
+                       data.assoc_info.resp_ies = resp_ies;
+                       data.assoc_info.resp_ies_len = bytes;
+               }
+
+               wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+       done:
+               /* free allocated memory */
+               os_free(resp_ies);
+               os_free(req_ies);
+       }
+}
+
+static void ralink_interface_up(struct wpa_driver_ralink_data *drv)
+{
+       union wpa_event_data event;
+       int enable_wpa_supplicant = 0;
+       drv->g_driver_down = 0;
+       os_memset(&event, 0, sizeof(event));
+       os_snprintf(event.interface_status.ifname,
+                   sizeof(event.interface_status.ifname), "%s", drv->ifname);
+
+       event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+
+       if (drv->ap_scan == 1)
+               enable_wpa_supplicant = 1;
+       else
+               enable_wpa_supplicant = 2;
+       /* trigger driver support wpa_supplicant */
+       if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+                          (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0)
+       {
+               wpa_printf(MSG_INFO, "RALINK: Failed to set "
+                          "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+                          (int) enable_wpa_supplicant);
+               wpa_printf(MSG_ERROR, "ralink. Driver does not support "
+                          "wpa_supplicant");
+       }
+}
+
+static void
+wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv,
+                                void *ctx, char *data, int len)
+{
+       struct iw_event iwe_buf, *iwe = &iwe_buf;
+       char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos;
+#if 0
+       BOOLEAN ieee8021x_required_key = FALSE;
+#endif
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       assoc_info_buf = info_pos = NULL;
+       pos = data;
+       end = data + len;
+
+       while (pos + IW_EV_LCP_LEN <= end) {
+               /* Event data may be unaligned, so make a local, aligned copy
+                * before processing. */
+               os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+               wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+                          iwe->cmd, iwe->len);
+               if (iwe->len <= IW_EV_LCP_LEN)
+                       return;
+
+               custom = pos + IW_EV_POINT_LEN;
+
+               if (drv->we_version_compiled > 18 && iwe->cmd == IWEVCUSTOM) {
+                       /* WE-19 removed the pointer from struct iw_point */
+                       char *dpos = (char *) &iwe_buf.u.data.length;
+                       int dlen = dpos - (char *) &iwe_buf;
+                       os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+                                 sizeof(struct iw_event) - dlen);
+               } else {
+                       os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+                       custom += IW_EV_POINT_OFF;
+               }
+
+               switch (iwe->cmd) {
+               case IWEVCUSTOM:
+                       if (custom + iwe->u.data.length > end)
+                               return;
+                       buf = os_malloc(iwe->u.data.length + 1);
+                       if (buf == NULL)
+                               return;
+                       os_memcpy(buf, custom, iwe->u.data.length);
+                       buf[iwe->u.data.length] = '\0';
+
+                       if (drv->ap_scan == 1) {
+                               if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG)
+                                   || (iwe->u.data.flags ==
+                                       RT_REQIE_EVENT_FLAG) ||
+                                   (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG)
+                                   || (iwe->u.data.flags ==
+                                       RT_ASSOCINFO_EVENT_FLAG)) {
+                                       if (drv->scanning_done == 0) {
+                                               os_free(buf);
+                                               return;
+                                       }
+                               }
+                       }
+
+                       if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) {
+                               wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+                               wpa_printf(MSG_DEBUG, "Custom wireless event: "
+                                          "receive ASSOCIATED_EVENT !!!");
+                       } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) {
+                               wpa_printf(MSG_DEBUG, "Custom wireless event: "
+                                          "receive ReqIEs !!!");
+                               drv->assoc_req_ies =
+                                       os_malloc(iwe->u.data.length);
+                               if (drv->assoc_req_ies == NULL) {
+                                       os_free(buf);
+                                       return;
+                               }
+
+                               drv->assoc_req_ies_len = iwe->u.data.length;
+                               os_memcpy(drv->assoc_req_ies, custom,
+                                         iwe->u.data.length);
+                       } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) {
+                               wpa_printf(MSG_DEBUG, "Custom wireless event: "
+                                          "receive RespIEs !!!");
+                               drv->assoc_resp_ies =
+                                       os_malloc(iwe->u.data.length);
+                               if (drv->assoc_resp_ies == NULL) {
+                                       os_free(drv->assoc_req_ies);
+                                       drv->assoc_req_ies = NULL;
+                                       os_free(buf);
+                                       return;
+                               }
+
+                               drv->assoc_resp_ies_len = iwe->u.data.length;
+                               os_memcpy(drv->assoc_resp_ies, custom,
+                                         iwe->u.data.length);
+                       } else if (iwe->u.data.flags ==
+                                  RT_ASSOCINFO_EVENT_FLAG) {
+                               wpa_printf(MSG_DEBUG, "Custom wireless event: "
+                                          "receive ASSOCINFO_EVENT !!!");
+
+                               assoc_info_buf =
+                                       os_zalloc(drv->assoc_req_ies_len +
+                                                 drv->assoc_resp_ies_len + 1);
+
+                               if (assoc_info_buf == NULL) {
+                                       os_free(drv->assoc_req_ies);
+                                       drv->assoc_req_ies = NULL;
+                                       os_free(drv->assoc_resp_ies);
+                                       drv->assoc_resp_ies = NULL;
+                                       os_free(buf);
+                                       return;
+                               }
+
+                               if (drv->assoc_req_ies) {
+                                       os_memcpy(assoc_info_buf,
+                                                 drv->assoc_req_ies,
+                                                 drv->assoc_req_ies_len);
+                               }
+                               info_pos = assoc_info_buf +
+                                       drv->assoc_req_ies_len;
+                               if (drv->assoc_resp_ies) {
+                                       os_memcpy(info_pos,
+                                                 drv->assoc_resp_ies,
+                                                 drv->assoc_resp_ies_len);
+                               }
+                               assoc_info_buf[drv->assoc_req_ies_len +
+                                              drv->assoc_resp_ies_len] = '\0';
+                               wpa_driver_ralink_event_wireless_custom(
+                                       drv, ctx, assoc_info_buf);
+                               os_free(drv->assoc_req_ies);
+                               drv->assoc_req_ies = NULL;
+                               os_free(drv->assoc_resp_ies);
+                               drv->assoc_resp_ies = NULL;
+                               os_free(assoc_info_buf);
+                       } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG)
+                       {
+                               wpa_printf(MSG_DEBUG, "Custom wireless event: "
+                                          "receive DISASSOCIATED_EVENT !!!");
+                               wpa_supplicant_event(ctx, EVENT_DISASSOC,
+                                                    NULL);
+                       } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) {
+                               wpa_printf(MSG_DEBUG, "Custom wireless event: "
+                                          "receive PMKIDCAND_EVENT !!!");
+                               wpa_driver_ralink_event_pmkid(
+                                       drv, (const u8 *) custom,
+                                       iwe->u.data.length);
+                       } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) {
+                               drv->g_driver_down = 1;
+                               eloop_terminate();
+                       } else if (iwe->u.data.flags == RT_INTERFACE_UP) {
+                               ralink_interface_up(drv);
+                       } else {
+                               wpa_driver_ralink_event_wireless_custom(
+                                       drv, ctx, buf);
+                       }
+                       os_free(buf);
+                       break;
+               }
+
+               pos += iwe->len;
+       }
+}
+
+static void
+wpa_driver_ralink_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi, 
+                                   u8 *buf, size_t len)
+{
+       struct wpa_driver_ralink_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg));
+
+       attrlen = len;
+       wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen);
+       attr = (struct rtattr *) buf;
+       wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr));
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len);
+       while (RTA_OK(attr, attrlen)) {
+               wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type);
+               if (attr->rta_type == IFLA_WIRELESS) {
+                       wpa_driver_ralink_event_wireless(
+                               drv, ctx,
+                               ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len);
+               }
+               attr = RTA_NEXT(attr, attrlen);
+               wpa_hexdump(MSG_DEBUG, "attr3: ",
+                           (u8 *) attr, sizeof(struct rtattr));
+       }
+}
+
+static int
+ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv)
+{
+       struct iwreq iwr;
+       UINT we_version_compiled = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) &we_version_compiled;
+       iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED;
+
+       if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: failed", __func__);
+               return -1;
+       }
+
+       drv->we_version_compiled = we_version_compiled;
+
+       return 0;
+}
+
+static void * wpa_driver_ralink_init(void *ctx, const char *ifname)
+{
+       int s;
+       struct wpa_driver_ralink_data *drv;
+       struct ifreq ifr;
+       UCHAR enable_wpa_supplicant = 0;
+       struct netlink_config *cfg;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       /* open socket to kernel */
+       if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+               perror("socket");
+               return NULL;
+       }
+       /* do it */
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+       if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) {
+               perror(ifr.ifr_name);
+               return NULL;
+       }
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+
+       drv->scanning_done = 1;
+       drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->ioctl_sock = s;
+       drv->g_driver_down = 0;
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL) {
+               close(drv->ioctl_sock);
+               os_free(drv);
+               return NULL;
+       }
+       cfg->ctx = drv;
+       cfg->newlink_cb = wpa_driver_ralink_event_rtm_newlink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               close(drv->ioctl_sock);
+               os_free(drv);
+               return NULL;
+       }
+
+       drv->no_of_pmkid = 4; /* Number of PMKID saved supported */
+
+       linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+       ralink_get_we_version_compiled(drv);
+       wpa_driver_ralink_flush_pmkid(drv);
+
+       if (drv->ap_scan == 1)
+               enable_wpa_supplicant = 1;
+       else
+               enable_wpa_supplicant = 2;
+       /* trigger driver support wpa_supplicant */
+       if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+                          (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0)
+       {
+               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                          "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+                          (int) enable_wpa_supplicant);
+               wpa_printf(MSG_ERROR, "RALINK: Driver does not support "
+                          "wpa_supplicant");
+               close(s);
+               close(drv->ioctl_sock);
+               os_free(drv);
+               return NULL;
+       }
+
+       if (drv->ap_scan == 1)
+               drv->scanning_done = 0;
+
+       return drv;
+}
+
+static void wpa_driver_ralink_deinit(void *priv)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       UCHAR enable_wpa_supplicant;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       enable_wpa_supplicant = 0;
+
+       if (drv->g_driver_down == 0) {
+               /* trigger driver disable wpa_supplicant support */
+               if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+                                  (char *) &enable_wpa_supplicant,
+                                  sizeof(BOOLEAN)) < 0) {
+                       wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                                  "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)",
+                                  (int) enable_wpa_supplicant);
+               }
+
+               wpa_driver_ralink_flush_pmkid(drv);
+
+               sleep(1);
+               /* linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0); */
+       }
+
+       eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx);
+       netlink_deinit(drv->netlink);
+       close(drv->ioctl_sock);
+       os_free(drv);
+}
+
+static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_ralink_data *drv = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+
+       drv->scanning_done = 1;
+
+}
+
+static int wpa_driver_ralink_scan(void *priv,
+                                 struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+#if 0
+       if (ssid_len > IW_ESSID_MAX_SIZE) {
+               wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
+                          __FUNCTION__, (unsigned long) ssid_len);
+               return -1;
+       }
+
+       /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */
+#endif
+
+       if (ralink_set_oid(drv, RT_OID_WPS_PROBE_REQ_IE,
+                          (char *) params->extra_ies, params->extra_ies_len) <
+           0) {
+               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                          "RT_OID_WPS_PROBE_REQ_IE");
+       }
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+               perror("ioctl[SIOCSIWSCAN]");
+               ret = -1;
+       }
+
+       /* Not all drivers generate "scan completed" wireless event, so try to
+        * read results after a timeout. */
+       eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv,
+                              drv->ctx);
+
+       drv->scanning_done = 0;
+
+       return ret;
+}
+
+static struct wpa_scan_results *
+wpa_driver_ralink_get_scan_results(void *priv)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       UCHAR *buf = NULL;
+       size_t buf_len;
+       NDIS_802_11_BSSID_LIST_EX *wsr;
+       NDIS_WLAN_BSSID_EX *wbi;
+       struct iwreq iwr;
+       size_t ap_num;
+       u8 *pos;
+       struct wpa_scan_results *res;
+
+       if (drv->g_driver_down == 1)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (drv->we_version_compiled >= 17)
+               buf_len = 8192;
+       else
+               buf_len = 4096;
+
+       for (;;) {
+               buf = os_zalloc(buf_len);
+               iwr.u.data.length = buf_len;
+               if (buf == NULL)
+                       return NULL;
+
+               wsr = (NDIS_802_11_BSSID_LIST_EX *) buf;
+
+               wsr->NumberOfItems = 0;
+               os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+               iwr.u.data.pointer = (void *) buf;
+               iwr.u.data.flags = OID_802_11_BSSID_LIST;
+
+               if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) == 0)
+                       break;
+
+               if (errno == E2BIG && buf_len < 65535) {
+                       os_free(buf);
+                       buf = NULL;
+                       buf_len *= 2;
+                       if (buf_len > 65535)
+                               buf_len = 65535; /* 16-bit length field */
+                       wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+                                  "trying larger buffer (%lu bytes)",
+                                  (unsigned long) buf_len);
+               } else {
+                       perror("ioctl[RT_PRIV_IOCTL]");
+                       os_free(buf);
+                       return NULL;
+               }
+       }
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL) {
+               os_free(buf);
+               return NULL;
+       }
+
+       res->res = os_zalloc(wsr->NumberOfItems *
+                            sizeof(struct wpa_scan_res *));
+       if (res->res == NULL) {
+               os_free(res);
+               os_free(buf);
+               return NULL;
+       }
+
+       for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems;
+            ++ap_num) {
+               struct wpa_scan_res *r = NULL;
+               size_t extra_len = 0, var_ie_len = 0;
+               u8 *pos2;
+
+               /* SSID data element */
+               extra_len += 2 + wbi->Ssid.SsidLength;
+               var_ie_len = wbi->IELength - sizeof(NDIS_802_11_FIXED_IEs);
+               r = os_zalloc(sizeof(*r) + extra_len + var_ie_len);
+               if (r == NULL)
+                       break;
+               res->res[res->num++] = r;
+
+               wpa_printf(MSG_DEBUG, "SSID - %s", wbi->Ssid.Ssid);
+               /* get ie's */
+               wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs",
+                           (u8 *) &wbi->IEs[0], wbi->IELength);
+
+               os_memcpy(r->bssid, wbi->MacAddress, ETH_ALEN);
+
+               extra_len += (2 + wbi->Ssid.SsidLength);
+               r->ie_len = extra_len + var_ie_len;
+               pos2 = (u8 *) (r + 1);
+
+               /*
+                * Generate a fake SSID IE since the driver did not report
+                * a full IE list.
+                */
+               *pos2++ = WLAN_EID_SSID;
+               *pos2++ = wbi->Ssid.SsidLength;
+               os_memcpy(pos2, wbi->Ssid.Ssid, wbi->Ssid.SsidLength);
+               pos2 += wbi->Ssid.SsidLength;
+
+               r->freq = (wbi->Configuration.DSConfig / 1000);
+
+               pos = (u8 *) wbi + sizeof(*wbi) - 1;
+
+               pos += sizeof(NDIS_802_11_FIXED_IEs) - 2;
+               os_memcpy(&(r->caps), pos, 2);
+               pos += 2;
+
+               if (wbi->IELength > sizeof(NDIS_802_11_FIXED_IEs))
+                       os_memcpy(pos2, pos, var_ie_len);
+
+               wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length);
+       }
+
+       os_free(buf);
+       return res;
+}
+
+static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv,
+                               NDIS_802_11_AUTHENTICATION_MODE mode)
+{
+       NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE,
+                          (char *) &auth_mode, sizeof(auth_mode)) < 0) {
+               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                          "OID_802_11_AUTHENTICATION_MODE (%d)",
+                          (int) auth_mode);
+               return -1;
+       }
+       return 0;
+}
+
+static int ralink_set_encr_type(struct wpa_driver_ralink_data *drv,
+                               NDIS_802_11_WEP_STATUS encr_type)
+{
+       NDIS_802_11_WEP_STATUS wep_status = encr_type;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (ralink_set_oid(drv, OID_802_11_WEP_STATUS,
+                          (char *) &wep_status, sizeof(wep_status)) < 0) {
+               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                          "OID_802_11_WEP_STATUS (%d)",
+                          (int) wep_status);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv,
+                                       int key_idx, const u8 *addr,
+                                       const u8 *bssid, int pairwise)
+{
+       NDIS_802_11_REMOVE_KEY rkey;
+       NDIS_802_11_KEY_INDEX _index;
+       int res, res2;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       os_memset(&rkey, 0, sizeof(rkey));
+
+       rkey.Length = sizeof(rkey);
+       rkey.KeyIndex = key_idx;
+
+       if (pairwise)
+               rkey.KeyIndex |= 1 << 30;
+
+       os_memcpy(rkey.BSSID, bssid, ETH_ALEN);
+
+       res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey,
+                            sizeof(rkey));
+
+       /* AlbertY@20060210 removed it */
+       if (0 /* !pairwise */) {
+               res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP,
+                                     (char *) &_index, sizeof(_index));
+       } else
+               res2 = 0;
+
+       if (res < 0 && res2 < 0)
+               return res;
+       return 0;
+}
+
+static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv,
+                                    int pairwise, int key_idx, int set_tx,
+                                    const u8 *key, size_t key_len)
+{
+       NDIS_802_11_WEP *wep;
+       size_t len;
+       int res;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       len = 12 + key_len;
+       wep = os_zalloc(len);
+       if (wep == NULL)
+               return -1;
+
+       wep->Length = len;
+       wep->KeyIndex = key_idx;
+
+       if (set_tx)
+               wep->KeyIndex |= 0x80000000;
+
+       wep->KeyLength = key_len;
+       os_memcpy(wep->KeyMaterial, key, key_len);
+
+       wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP",
+                       (const u8 *) wep, len);
+       res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len);
+
+       os_free(wep);
+
+       return res;
+}
+
+static int wpa_driver_ralink_set_key(const char *ifname, void *priv,
+                                    enum wpa_alg alg, const u8 *addr,
+                                    int key_idx, int set_tx,
+                                    const u8 *seq, size_t seq_len,
+                                    const u8 *key, size_t key_len)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       size_t len, i;
+       NDIS_802_11_KEY *nkey;
+       int res, pairwise;
+       u8 bssid[ETH_ALEN];
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       drv->bAddWepKey = FALSE;
+
+       if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff",
+                                     ETH_ALEN) == 0) {
+               /* Group Key */
+               pairwise = 0;
+               wpa_driver_ralink_get_bssid(drv, bssid);
+       } else {
+               /* Pairwise Key */
+               pairwise = 1;
+               os_memcpy(bssid, addr, ETH_ALEN);
+       }
+
+       if (alg == WPA_ALG_NONE || key_len == 0) {
+               return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid,
+                                                   pairwise);
+       }
+
+       if (alg == WPA_ALG_WEP) {
+               drv->bAddWepKey = TRUE;
+               return wpa_driver_ralink_add_wep(drv, pairwise, key_idx,
+                                                set_tx, key, key_len);
+       }
+
+       len = 12 + 6 + 6 + 8 + key_len;
+
+       nkey = os_zalloc(len);
+       if (nkey == NULL)
+               return -1;
+
+       nkey->Length = len;
+       nkey->KeyIndex = key_idx;
+
+       if (set_tx)
+               nkey->KeyIndex |= 1 << 31;
+
+       if (pairwise)
+               nkey->KeyIndex |= 1 << 30;
+
+       if (seq && seq_len)
+               nkey->KeyIndex |= 1 << 29;
+
+       nkey->KeyLength = key_len;
+       os_memcpy(nkey->BSSID, bssid, ETH_ALEN);
+
+       if (seq && seq_len) {
+               for (i = 0; i < seq_len; i++)
+                       nkey->KeyRSC |= seq[i] << (i * 8);
+       }
+       if (alg == WPA_ALG_TKIP && key_len == 32) {
+               os_memcpy(nkey->KeyMaterial, key, 16);
+               os_memcpy(nkey->KeyMaterial + 16, key + 24, 8);
+               os_memcpy(nkey->KeyMaterial + 24, key + 16, 8);
+       } else {
+               os_memcpy(nkey->KeyMaterial, key, key_len);
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+                  "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY",
+                       (const u8 *) nkey, len);
+       res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len);
+       os_free(nkey);
+
+       return res;
+}
+
+static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr,
+                                       int reason_code)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, "    ", 4) < 0) {
+               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                          "OID_802_11_DISASSOCIATE");
+       }
+
+       return 0;
+}
+
+static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down);
+
+       if (drv->g_driver_down == 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       if (ralink_get_new_driver_flag(drv) == 0) {
+               return wpa_driver_ralink_disassociate(priv, addr, reason_code);
+       } else {
+               MLME_DEAUTH_REQ_STRUCT mlme;
+               os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT));
+               mlme.Reason = reason_code;
+               os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN);
+               return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION,
+                                     (char *) &mlme,
+                                     sizeof(MLME_DEAUTH_REQ_STRUCT));
+       }
+}
+
+static int wpa_driver_ralink_set_gen_ie(void *priv, const u8 *ie,
+                                       size_t ie_len)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) ie;
+       iwr.u.data.length = ie_len;
+
+       wpa_hexdump(MSG_DEBUG, "wpa_driver_ralink_set_gen_ie: ",
+                   (u8 *) ie, ie_len);
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+               perror("ioctl[SIOCSIWGENIE]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+static int
+wpa_driver_ralink_associate(void *priv,
+                           struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+
+       NDIS_802_11_NETWORK_INFRASTRUCTURE mode;
+       NDIS_802_11_AUTHENTICATION_MODE auth_mode;
+       NDIS_802_11_WEP_STATUS encr;
+       BOOLEAN         ieee8021xMode;
+       BOOLEAN         ieee8021x_required_key = TRUE;
+
+       if (drv->g_driver_down == 1)
+               return -1;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (params->mode == IEEE80211_MODE_IBSS)
+               mode = Ndis802_11IBSS;
+       else
+               mode = Ndis802_11Infrastructure;
+
+       if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE,
+                        (char *) &mode, sizeof(mode)) < 0) {
+               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                          "OID_802_11_INFRASTRUCTURE_MODE (%d)",
+                          (int) mode);
+               /* Try to continue anyway */
+       }
+
+       if (params->key_mgmt_suite == KEY_MGMT_WPS) {
+               UCHAR enable_wps = 0x80;
+               /* trigger driver support wpa_supplicant */
+               if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+                                  (PCHAR) &enable_wps, sizeof(UCHAR)) < 0) {
+                       wpa_printf(MSG_INFO, "RALINK: Failed to set "
+                                  "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)",
+                                  (int) enable_wps);
+               }
+
+               wpa_driver_ralink_set_gen_ie(priv, params->wpa_ie,
+                                            params->wpa_ie_len);
+
+               ralink_set_auth_mode(drv, Ndis802_11AuthModeOpen);
+
+               ralink_set_encr_type(drv, Ndis802_11EncryptionDisabled);
+       } else {
+#ifdef CONFIG_WPS
+               UCHAR enable_wpa_supplicant;
+
+               if (drv->ap_scan == 1)
+                       enable_wpa_supplicant = 0x01;
+               else
+                       enable_wpa_supplicant = 0x02;
+
+               /* trigger driver support wpa_supplicant */
+               if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT,
+                                  (PCHAR) &enable_wpa_supplicant,
+                                  sizeof(UCHAR)) < 0) {
+                       wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                                  "RT_OID_WPA_SUPPLICANT_SUPPORT (%d)",
+                                  (int) enable_wpa_supplicant);
+               }
+
+               wpa_driver_ralink_set_gen_ie(priv, (u8 *) "", 0);
+#endif /* CONFIG_WPS */
+
+               if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+                       if (params->auth_alg & WPA_AUTH_ALG_SHARED) {
+                               if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+                                       auth_mode = Ndis802_11AuthModeAutoSwitch;
+                               else
+                                       auth_mode = Ndis802_11AuthModeShared;
+                       } else
+                               auth_mode = Ndis802_11AuthModeOpen;
+               } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
+                       if (params->key_mgmt_suite == KEY_MGMT_PSK)
+                               auth_mode = Ndis802_11AuthModeWPA2PSK;
+                       else
+                               auth_mode = Ndis802_11AuthModeWPA2;
+               } else {
+                       if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE)
+                               auth_mode = Ndis802_11AuthModeWPANone;
+                       else if (params->key_mgmt_suite == KEY_MGMT_PSK)
+                               auth_mode = Ndis802_11AuthModeWPAPSK;
+                       else
+                               auth_mode = Ndis802_11AuthModeWPA;
+               }
+
+               switch (params->pairwise_suite) {
+               case CIPHER_CCMP:
+                       encr = Ndis802_11Encryption3Enabled;
+                       break;
+               case CIPHER_TKIP:
+                       encr = Ndis802_11Encryption2Enabled;
+                       break;
+               case CIPHER_WEP40:
+               case CIPHER_WEP104:
+                       encr = Ndis802_11Encryption1Enabled;
+                       break;
+               case CIPHER_NONE:
+                       if (params->group_suite == CIPHER_CCMP)
+                               encr = Ndis802_11Encryption3Enabled;
+                       else if (params->group_suite == CIPHER_TKIP)
+                               encr = Ndis802_11Encryption2Enabled;
+                       else
+                               encr = Ndis802_11EncryptionDisabled;
+                       break;
+               default:
+                       encr = Ndis802_11EncryptionDisabled;
+                       break;
+               }
+
+               ralink_set_auth_mode(drv, auth_mode);
+
+               /* notify driver that IEEE8021x mode is enabled */
+               if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+                       ieee8021xMode = TRUE;
+                       if (drv->bAddWepKey)
+                               ieee8021x_required_key = FALSE;
+               } else
+                       ieee8021xMode = FALSE;
+
+               if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY,
+                                  (char *) &ieee8021x_required_key,
+                                  sizeof(BOOLEAN)) < 0) {
+                       wpa_printf(MSG_DEBUG, "ERROR: Failed to set "
+                                  "OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)",
+                                  (int) ieee8021x_required_key);
+               } else {
+                       wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s",
+                                  ieee8021x_required_key ? "TRUE" : "FALSE");
+               }
+
+               if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X,
+                                  (char *) &ieee8021xMode, sizeof(BOOLEAN)) <
+                   0) {
+                       wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                                  "OID_802_11_SET_IEEE8021X(%d)",
+                                  (int) ieee8021xMode);
+               }
+
+               ralink_set_encr_type(drv, encr);
+
+               if ((ieee8021xMode == FALSE) &&
+                   (encr == Ndis802_11Encryption1Enabled)) {
+                       /* static WEP */
+                       int enabled = 0;
+                       if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED,
+                                          (char *) &enabled, sizeof(enabled))
+                           < 0) {
+                               wpa_printf(MSG_DEBUG, "RALINK: Failed to set "
+                                          "OID_802_11_DROP_UNENCRYPTED(%d)",
+                                          (int) encr);
+                       }
+               }
+       }
+
+       return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len);
+}
+
+static int
+wpa_driver_ralink_set_countermeasures(void *priv, int enabled)
+{
+       struct wpa_driver_ralink_data *drv = priv;
+       if (drv->g_driver_down == 1)
+               return -1;
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+       return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled,
+                             sizeof(int));
+}
+
+const struct wpa_driver_ops wpa_driver_ralink_ops = {
+       .name = "ralink",
+       .desc = "Ralink Wireless Client driver",
+       .get_bssid = wpa_driver_ralink_get_bssid,
+       .get_ssid = wpa_driver_ralink_get_ssid,
+       .set_key = wpa_driver_ralink_set_key,
+       .init = wpa_driver_ralink_init,
+       .deinit = wpa_driver_ralink_deinit,
+       .set_countermeasures    = wpa_driver_ralink_set_countermeasures,
+       .scan2 = wpa_driver_ralink_scan,
+       .get_scan_results2 = wpa_driver_ralink_get_scan_results,
+       .deauthenticate = wpa_driver_ralink_deauthenticate,
+       .disassociate = wpa_driver_ralink_disassociate,
+       .associate = wpa_driver_ralink_associate,
+       .add_pmkid = wpa_driver_ralink_add_pmkid,
+       .remove_pmkid = wpa_driver_ralink_remove_pmkid,
+       .flush_pmkid = wpa_driver_ralink_flush_pmkid,
+};
diff --git a/src/drivers/driver_ralink.h b/src/drivers/driver_ralink.h
new file mode 100644 (file)
index 0000000..d13df28
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * WPA Supplicant - driver_ralink exported functions
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw>
+ *
+ * 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.
+ */
+
+// Ralink defined OIDs
+#if WIRELESS_EXT <= 11
+#ifndef SIOCDEVPRIVATE
+#define SIOCDEVPRIVATE                              0x8BE0
+#endif
+#define SIOCIWFIRSTPRIV                                                                SIOCDEVPRIVATE
+#endif
+
+#define RT_PRIV_IOCTL                                                          (SIOCIWFIRSTPRIV + 0x0E)  
+#define RTPRIV_IOCTL_SET                                                       (SIOCIWFIRSTPRIV + 0x02)
+
+// IEEE 802.11 OIDs  &  Ralink defined OIDs  ******
+
+// (RaConfig Set/QueryInform) ==>
+#define OID_GET_SET_TOGGLE                                                     0x8000
+
+#define OID_802_11_ADD_WEP                          0x0112
+#define OID_802_11_REMOVE_WEP                       0x0113
+#define OID_802_11_DISASSOCIATE                     0x0114
+#define OID_802_11_PRIVACY_FILTER                   0x0118
+#define OID_802_11_ASSOCIATION_INFORMATION          0x011E
+#define OID_802_11_BSSID_LIST_SCAN                  0x0508
+#define OID_802_11_SSID                             0x0509
+#define OID_802_11_BSSID                            0x050A
+#define OID_802_11_WEP_STATUS                       0x0510
+#define OID_802_11_AUTHENTICATION_MODE              0x0511
+#define OID_802_11_INFRASTRUCTURE_MODE              0x0512
+#define OID_802_11_TX_POWER_LEVEL                   0x0517
+#define OID_802_11_REMOVE_KEY                       0x0519
+#define OID_802_11_ADD_KEY                          0x0520
+#define OID_802_11_DEAUTHENTICATION                 0x0526
+#define OID_802_11_DROP_UNENCRYPTED                 0x0527
+#define OID_802_11_BSSID_LIST                       0x0609
+#define OID_802_3_CURRENT_ADDRESS                   0x060A
+#define OID_SET_COUNTERMEASURES                     0x0616
+#define OID_802_11_SET_IEEE8021X                    0x0617     // For IEEE8021x mode 
+#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY        0x0618  // For DynamicWEP in IEEE802.1x mode
+#define OID_802_11_PMKID                            0x0620
+#define RT_OID_WPA_SUPPLICANT_SUPPORT               0x0621  // for trigger driver enable/disable wpa_supplicant support 
+#define RT_OID_WE_VERSION_COMPILED                  0x0622
+#define RT_OID_NEW_DRIVER                           0x0623
+#define RT_OID_WPS_PROBE_REQ_IE                                                0x0625
+
+#define PACKED  __attribute__ ((packed))
+
+//wpa_supplicant event flags
+#define        RT_ASSOC_EVENT_FLAG                         0x0101
+#define        RT_DISASSOC_EVENT_FLAG                      0x0102
+#define        RT_REQIE_EVENT_FLAG                         0x0103
+#define        RT_RESPIE_EVENT_FLAG                        0x0104
+#define        RT_ASSOCINFO_EVENT_FLAG                     0x0105
+#define RT_PMKIDCAND_FLAG                           0x0106
+#define RT_INTERFACE_DOWN                           0x0107
+#define RT_INTERFACE_UP                                0x0108
+
+//
+// IEEE 802.11 Structures and definitions
+//
+// new types for Media Specific Indications
+
+#ifndef ULONG
+#define CHAR            char
+#define INT             int
+#define SHORT           int
+#define UINT            u32
+#undef  ULONG           
+//#define ULONG           u32
+#define ULONG           unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */
+#define USHORT          unsigned short
+#define UCHAR           unsigned char
+
+#define uint32         u32
+#define uint8          u8
+
+
+#define BOOLEAN         u8
+//#define LARGE_INTEGER s64
+#define VOID            void
+#define LONG            long
+#define LONGLONG        s64
+#define ULONGLONG       u64
+typedef VOID            *PVOID;
+typedef CHAR            *PCHAR;
+typedef UCHAR           *PUCHAR;
+typedef USHORT          *PUSHORT;
+typedef LONG            *PLONG;
+typedef ULONG           *PULONG;
+
+typedef union _LARGE_INTEGER {
+    struct {
+        ULONG LowPart;
+        LONG HighPart;
+    }vv;
+    struct {
+        ULONG LowPart;
+        LONG HighPart;
+    } u;
+    s64 QuadPart;
+} LARGE_INTEGER;
+
+#endif
+
+#define NDIS_802_11_LENGTH_SSID         32
+#define NDIS_802_11_LENGTH_RATES        8
+#define NDIS_802_11_LENGTH_RATES_EX     16
+#define MAX_LEN_OF_SSID                 32
+#define MAC_ADDR_LEN                    6
+
+typedef UCHAR   NDIS_802_11_MAC_ADDRESS[6];
+
+// mask for authentication/integrity fields
+#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS        0x0f
+
+#define NDIS_802_11_AUTH_REQUEST_REAUTH             0x01
+#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE          0x02
+#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR     0x06
+#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR        0x0E
+
+// Added new types for OFDM 5G and 2.4G
+typedef enum _NDIS_802_11_NETWORK_TYPE
+{
+    Ndis802_11FH, 
+    Ndis802_11DS, 
+    Ndis802_11OFDM5,
+    Ndis802_11OFDM24,
+    Ndis802_11Automode,
+    Ndis802_11NetworkTypeMax    // not a real type, defined as an upper bound
+} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE;
+
+//
+// Received Signal Strength Indication
+//
+typedef LONG    NDIS_802_11_RSSI;           // in dBm
+
+typedef struct _NDIS_802_11_CONFIGURATION_FH
+{
+   ULONG           Length;            // Length of structure
+   ULONG           HopPattern;        // As defined by 802.11, MSB set 
+   ULONG           HopSet;            // to one if non-802.11
+   ULONG           DwellTime;         // units are Kusec
+} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH;
+
+typedef struct _NDIS_802_11_CONFIGURATION
+{
+   ULONG                           Length;             // Length of structure
+   ULONG                           BeaconPeriod;       // units are Kusec
+   ULONG                           ATIMWindow;         // units are Kusec
+   ULONG                           DSConfig;           // Frequency, units are kHz
+   NDIS_802_11_CONFIGURATION_FH    FHConfig;
+} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION;
+
+typedef  ULONG  NDIS_802_11_KEY_INDEX;
+typedef ULONGLONG   NDIS_802_11_KEY_RSC;
+
+// Key mapping keys require a BSSID
+typedef struct _NDIS_802_11_KEY
+{
+    UINT           Length;             // Length of this structure
+    UINT           KeyIndex;           
+    UINT           KeyLength;          // length of key in bytes
+    NDIS_802_11_MAC_ADDRESS BSSID;
+    NDIS_802_11_KEY_RSC KeyRSC;
+    UCHAR           KeyMaterial[1];     // variable length depending on above field
+} NDIS_802_11_KEY, *PNDIS_802_11_KEY;
+
+typedef struct _NDIS_802_11_REMOVE_KEY
+{
+    UINT                   Length;        // Length of this structure
+    UINT                   KeyIndex;           
+    NDIS_802_11_MAC_ADDRESS BSSID;      
+} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY;
+
+typedef struct PACKED _NDIS_802_11_WEP
+{
+   UINT     Length;        // Length of this structure
+   UINT           KeyIndex;           // 0 is the per-client key, 1-N are the
+                                        // global keys
+   UINT     KeyLength;     // length of key in bytes
+   UCHAR     KeyMaterial[1];// variable length depending on above field
+} NDIS_802_11_WEP, *PNDIS_802_11_WEP;
+
+
+typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE
+{
+   Ndis802_11IBSS,
+   Ndis802_11Infrastructure,
+   Ndis802_11AutoUnknown,
+   Ndis802_11InfrastructureMax     // Not a real value, defined as upper bound
+} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE;
+
+// PMKID Structures
+typedef UCHAR   NDIS_802_11_PMKID_VALUE[16];
+
+typedef struct _BSSID_INFO
+{
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       NDIS_802_11_PMKID_VALUE PMKID;
+} BSSID_INFO, *PBSSID_INFO;
+
+typedef struct _NDIS_802_11_PMKID
+{
+       ULONG Length;
+       ULONG BSSIDInfoCount;
+       BSSID_INFO BSSIDInfo[1];
+} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID;
+
+//Added new types for PMKID Candidate lists.
+typedef struct _PMKID_CANDIDATE {
+       NDIS_802_11_MAC_ADDRESS BSSID;
+       ULONG Flags;
+} PMKID_CANDIDATE, *PPMKID_CANDIDATE;
+
+typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST
+{
+       ULONG Version;       // Version of the structure
+       ULONG NumCandidates; // No. of pmkid candidates
+       PMKID_CANDIDATE CandidateList[1];
+} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST;
+
+//Flags for PMKID Candidate list structure
+#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED    0x01
+
+// Add new authentication modes
+typedef enum _NDIS_802_11_AUTHENTICATION_MODE
+{
+   Ndis802_11AuthModeOpen,
+   Ndis802_11AuthModeShared,
+   Ndis802_11AuthModeAutoSwitch,
+   Ndis802_11AuthModeWPA,
+   Ndis802_11AuthModeWPAPSK,
+   Ndis802_11AuthModeWPANone,
+   Ndis802_11AuthModeWPA2,
+   Ndis802_11AuthModeWPA2PSK,    
+   Ndis802_11AuthModeMax           // Not a real mode, defined as upper bound
+} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE;
+
+typedef UCHAR  NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES];        // Set of 8 data rates
+typedef UCHAR  NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX];  // Set of 16 data rates
+
+typedef struct PACKED _NDIS_802_11_SSID 
+{
+    INT   SsidLength;         // length of SSID field below, in bytes;
+                                // this can be zero.
+    UCHAR   Ssid[NDIS_802_11_LENGTH_SSID];           // SSID information field
+} NDIS_802_11_SSID, *PNDIS_802_11_SSID;
+
+
+typedef struct PACKED _NDIS_WLAN_BSSID
+{
+   ULONG                               Length;     // Length of this structure
+   NDIS_802_11_MAC_ADDRESS             MacAddress; // BSSID
+   UCHAR                               Reserved[2];
+   NDIS_802_11_SSID                    Ssid;       // SSID
+   ULONG                               Privacy;    // WEP encryption requirement
+    NDIS_802_11_RSSI                    Rssi;               // receive signal
+                                                            // strength in dBm
+   NDIS_802_11_NETWORK_TYPE            NetworkTypeInUse;
+   NDIS_802_11_CONFIGURATION           Configuration;
+   NDIS_802_11_NETWORK_INFRASTRUCTURE  InfrastructureMode;
+   NDIS_802_11_RATES                   SupportedRates;
+} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID;
+
+typedef struct PACKED _NDIS_802_11_BSSID_LIST
+{
+   UINT             NumberOfItems;      // in list below, at least 1
+   NDIS_WLAN_BSSID Bssid[1];
+} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST;
+
+// Added Capabilities, IELength and IEs for each BSSID
+typedef struct PACKED _NDIS_WLAN_BSSID_EX
+{
+    ULONG                               Length;             // Length of this structure
+    NDIS_802_11_MAC_ADDRESS             MacAddress;         // BSSID
+    UCHAR                               Reserved[2];
+    NDIS_802_11_SSID                    Ssid;               // SSID
+    UINT                                Privacy;            // WEP encryption requirement
+    NDIS_802_11_RSSI                    Rssi;               // receive signal
+                                                            // strength in dBm
+    NDIS_802_11_NETWORK_TYPE            NetworkTypeInUse;
+    NDIS_802_11_CONFIGURATION           Configuration;
+    NDIS_802_11_NETWORK_INFRASTRUCTURE  InfrastructureMode;
+    NDIS_802_11_RATES_EX                SupportedRates;
+    ULONG                               IELength;
+    UCHAR                               IEs[1];
+} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX;
+
+typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX
+{
+    UINT                   NumberOfItems;      // in list below, at least 1
+    NDIS_WLAN_BSSID_EX      Bssid[1];
+} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX;
+
+typedef struct PACKED _NDIS_802_11_FIXED_IEs 
+{
+    UCHAR Timestamp[8];
+    USHORT BeaconInterval;
+    USHORT Capabilities;
+} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs;
+
+// Added new encryption types
+// Also aliased typedef to new name
+typedef enum _NDIS_802_11_WEP_STATUS
+{
+   Ndis802_11WEPEnabled,
+   Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled,
+   Ndis802_11WEPDisabled,
+   Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled,
+   Ndis802_11WEPKeyAbsent,
+   Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent,
+   Ndis802_11WEPNotSupported,
+   Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported,
+   Ndis802_11Encryption2Enabled,
+   Ndis802_11Encryption2KeyAbsent,
+   Ndis802_11Encryption3Enabled,
+   Ndis802_11Encryption3KeyAbsent
+} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS,
+  NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS;
+
+typedef enum _NDIS_802_11_RELOAD_DEFAULTS
+{
+   Ndis802_11ReloadWEPKeys
+} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS;
+
+#define NDIS_802_11_AI_REQFI_CAPABILITIES      1
+#define NDIS_802_11_AI_REQFI_LISTENINTERVAL    2
+#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS  4
+
+#define NDIS_802_11_AI_RESFI_CAPABILITIES      1
+#define NDIS_802_11_AI_RESFI_STATUSCODE        2
+#define NDIS_802_11_AI_RESFI_ASSOCIATIONID     4
+
+typedef struct _NDIS_802_11_AI_REQFI
+{
+    USHORT Capabilities;
+    USHORT ListenInterval;
+    NDIS_802_11_MAC_ADDRESS  CurrentAPAddress;
+} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI;
+
+typedef struct _NDIS_802_11_AI_RESFI
+{
+    USHORT Capabilities;
+    USHORT StatusCode;
+    USHORT AssociationId;
+} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI;
+
+typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION
+{
+    ULONG                   Length;
+    USHORT                  AvailableRequestFixedIEs;
+    NDIS_802_11_AI_REQFI    RequestFixedIEs;
+    ULONG                   RequestIELength;
+    ULONG                   OffsetRequestIEs;
+    USHORT                  AvailableResponseFixedIEs;
+    NDIS_802_11_AI_RESFI    ResponseFixedIEs;
+    ULONG                   ResponseIELength;
+    ULONG                   OffsetResponseIEs;
+} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION;
+
+struct ndis_pmkid_entry {
+       struct ndis_pmkid_entry *next;
+       u8 bssid[ETH_ALEN];
+       u8 pmkid[16];
+};
+
+typedef struct _MLME_DEAUTH_REQ_STRUCT {
+    UCHAR        Addr[MAC_ADDR_LEN];
+    USHORT       Reason;
+} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT;
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
new file mode 100644 (file)
index 0000000..6877eda
--- /dev/null
@@ -0,0 +1,480 @@
+/*
+ * WPA Supplicant - roboswitch driver interface
+ * Copyright (c) 2008-2009 Jouke Witteveen
+ *
+ * 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 <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/sockios.h>
+#include <linux/if_ether.h>
+#include <linux/mii.h>
+
+#include "common.h"
+#include "driver.h"
+#include "l2_packet/l2_packet.h"
+
+#define ROBO_PHY_ADDR          0x1e    /* RoboSwitch PHY address */
+
+/* MII access registers */
+#define ROBO_MII_PAGE          0x10    /* MII page register */
+#define ROBO_MII_ADDR          0x11    /* MII address register */
+#define ROBO_MII_DATA_OFFSET   0x18    /* Start of MII data registers */
+
+#define ROBO_MII_PAGE_ENABLE   0x01    /* MII page op code */
+#define ROBO_MII_ADDR_WRITE    0x01    /* MII address write op code */
+#define ROBO_MII_ADDR_READ     0x02    /* MII address read op code */
+#define ROBO_MII_DATA_MAX         4    /* Consecutive MII data registers */
+#define ROBO_MII_RETRY_MAX       10    /* Read attempts before giving up */
+
+/* Page numbers */
+#define ROBO_ARLCTRL_PAGE      0x04    /* ARL control page */
+#define ROBO_VLAN_PAGE         0x34    /* VLAN page */
+
+/* ARL control page registers */
+#define ROBO_ARLCTRL_CONF      0x00    /* ARL configuration register */
+#define ROBO_ARLCTRL_ADDR_1    0x10    /* Multiport address 1 */
+#define ROBO_ARLCTRL_VEC_1     0x16    /* Multiport vector 1 */
+#define ROBO_ARLCTRL_ADDR_2    0x20    /* Multiport address 2 */
+#define ROBO_ARLCTRL_VEC_2     0x26    /* Multiport vector 2 */
+
+/* VLAN page registers */
+#define ROBO_VLAN_ACCESS       0x08    /* VLAN table access register */
+#define ROBO_VLAN_ACCESS_5350  0x06    /* VLAN table access register (5350) */
+#define ROBO_VLAN_READ         0x0c    /* VLAN read register */
+#define ROBO_VLAN_MAX          0xff    /* Maximum number of VLANs */
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_roboswitch_data {
+       void *ctx;
+       struct l2_packet_data *l2;
+       char ifname[IFNAMSIZ + 1];
+       u8 own_addr[ETH_ALEN];
+       struct ifreq ifr;
+       int fd, is_5350;
+       u16 ports;
+};
+
+
+/* Copied from the kernel-only part of mii.h. */
+static inline struct mii_ioctl_data *if_mii(struct ifreq *rq)
+{
+       return (struct mii_ioctl_data *) &rq->ifr_ifru;
+}
+
+
+/*
+ * RoboSwitch uses 16-bit Big Endian addresses.
+ * The ordering of the words is reversed in the MII registers.
+ */
+static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be)
+{
+       int i;
+       for (i = 0; i < ETH_ALEN; i += 2)
+               be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i);
+}
+
+
+static u16 wpa_driver_roboswitch_mdio_read(
+       struct wpa_driver_roboswitch_data *drv, u8 reg)
+{
+       struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+       mii->phy_id = ROBO_PHY_ADDR;
+       mii->reg_num = reg;
+
+       if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
+               perror("ioctl[SIOCGMIIREG]");
+               return 0x00;
+       }
+       return mii->val_out;
+}
+
+
+static void wpa_driver_roboswitch_mdio_write(
+       struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val)
+{
+       struct mii_ioctl_data *mii = if_mii(&drv->ifr);
+
+       mii->phy_id = ROBO_PHY_ADDR;
+       mii->reg_num = reg;
+       mii->val_in = val;
+
+       if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
+               perror("ioctl[SIOCSMIIREG");
+       }
+}
+
+
+static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv,
+                                    u8 page, u8 reg, u8 op)
+{
+       int i;
+
+       /* set page number */
+       wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE,
+                                        (page << 8) | ROBO_MII_PAGE_ENABLE);
+       /* set register address */
+       wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op);
+
+       /* check if operation completed */
+       for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) {
+               if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3)
+                   == 0)
+                       return 0;
+       }
+       /* timeout */
+       return -1;
+}
+
+
+static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv,
+                                     u8 page, u8 reg, u16 *val, int len)
+{
+       int i;
+
+       if (len > ROBO_MII_DATA_MAX ||
+           wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0)
+               return -1;
+
+       for (i = 0; i < len; ++i) {
+               val[i] = wpa_driver_roboswitch_mdio_read(
+                       drv, ROBO_MII_DATA_OFFSET + i);
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv,
+                                      u8 page, u8 reg, u16 *val, int len)
+{
+       int i;
+
+       if (len > ROBO_MII_DATA_MAX) return -1;
+       for (i = 0; i < len; ++i) {
+               wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i,
+                                                val[i]);
+       }
+       return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE);
+}
+
+
+static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr,
+                                         const u8 *buf, size_t len)
+{
+       struct wpa_driver_roboswitch_data *drv = priv;
+
+       if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL &&
+           os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0)
+               drv_event_eapol_rx(drv->ctx, src_addr, buf + 14, len - 14);
+}
+
+
+static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid)
+{
+       ssid[0] = 0;
+       return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid)
+{
+       /* Report PAE group address as the "BSSID" for wired connection. */
+       os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int wpa_driver_roboswitch_get_capa(void *priv,
+                                         struct wpa_driver_capa *capa)
+{
+       os_memset(capa, 0, sizeof(*capa));
+       capa->flags = WPA_DRIVER_FLAGS_WIRED;
+       return 0;
+}
+
+
+static int wpa_driver_roboswitch_set_param(void *priv, const char *param)
+{
+       struct wpa_driver_roboswitch_data *drv = priv;
+       char *sep;
+
+       if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) {
+               sep = drv->ifname + os_strlen(drv->ifname);
+               *sep = '.';
+               drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL,
+                                        wpa_driver_roboswitch_receive, drv,
+                                        1);
+               if (drv->l2 == NULL) {
+                       wpa_printf(MSG_INFO, "%s: Unable to listen on %s",
+                                  __func__, drv->ifname);
+                       return -1;
+               }
+               *sep = '\0';
+               l2_packet_get_own_addr(drv->l2, drv->own_addr);
+       } else {
+               wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__);
+               drv->l2 = NULL;
+       }
+       return 0;
+}
+
+
+static const char * wpa_driver_roboswitch_get_ifname(void *priv)
+{
+       struct wpa_driver_roboswitch_data *drv = priv;
+       return drv->ifname;
+}
+
+
+static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv,
+                                     u16 ports, const u8 *addr)
+{
+       u16 read1[3], read2[3], addr_be16[3];
+
+       wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+       if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                      ROBO_ARLCTRL_CONF, read1, 1) < 0)
+               return -1;
+       if (!(read1[0] & (1 << 4))) {
+               /* multiport addresses are not yet enabled */
+               read1[0] |= 1 << 4;
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_VEC_1, &ports, 1);
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_ADDR_2, addr_be16, 3);
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_VEC_2, &ports, 1);
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_CONF, read1, 1);
+       } else {
+               /* if both multiport addresses are the same we can add */
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_ADDR_1, read1, 3);
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_ADDR_2, read2, 3);
+               if (os_memcmp(read1, read2, 6) != 0)
+                       return -1;
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_VEC_1, read1, 1);
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_VEC_2, read2, 1);
+               if (read1[0] != read2[0])
+                       return -1;
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
+               wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                           ROBO_ARLCTRL_VEC_1, &ports, 1);
+       }
+       return 0;
+}
+
+
+static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
+                                      u16 ports, const u8 *addr)
+{
+       u16 _read, addr_be16[3], addr_read[3], ports_read;
+
+       wpa_driver_roboswitch_addr_be16(addr, addr_be16);
+
+       wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF,
+                                  &_read, 1);
+       /* If ARL control is disabled, there is nothing to leave. */
+       if (!(_read & (1 << 4))) return -1;
+
+       wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                  ROBO_ARLCTRL_ADDR_1, addr_read, 3);
+       wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
+                                  &ports_read, 1);
+       /* check if we occupy multiport address 1 */
+       if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+               /* and multiport address 2 */
+               if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+                   ports_read == ports) {
+                       _read &= ~(1 << 4);
+                       wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                                   ROBO_ARLCTRL_CONF, &_read,
+                                                   1);
+               } else {
+                       wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                                  ROBO_ARLCTRL_ADDR_1,
+                                                  addr_read, 3);
+                       wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                                  ROBO_ARLCTRL_VEC_1,
+                                                  &ports_read, 1);
+                       wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                                   ROBO_ARLCTRL_ADDR_2,
+                                                   addr_read, 3);
+                       wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                                   ROBO_ARLCTRL_VEC_2,
+                                                   &ports_read, 1);
+               }
+       } else {
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_ADDR_2, addr_read, 3);
+               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                          ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+               /* or multiport address 2 */
+               if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
+                   ports_read == ports) {
+                       wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                                   ROBO_ARLCTRL_ADDR_1,
+                                                   addr_read, 3);
+                       wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
+                                                   ROBO_ARLCTRL_VEC_1,
+                                                   &ports_read, 1);
+               } else return -1;
+       }
+       return 0;
+}
+
+
+static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_roboswitch_data *drv;
+       char *sep;
+       u16 vlan = 0, _read[2];
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL) return NULL;
+       drv->ctx = ctx;
+       drv->own_addr[0] = '\0';
+
+       /* copy ifname and take a pointer to the second to last character */
+       sep = drv->ifname +
+             os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2;
+       /* find the '.' seperating <interface> and <vlan> */
+       while (sep > drv->ifname && *sep != '.') sep--;
+       if (sep <= drv->ifname) {
+               wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in "
+                          "interface name %s", __func__, drv->ifname);
+               os_free(drv);
+               return NULL;
+       }
+       *sep = '\0';
+       while (*++sep) {
+               if (*sep < '0' || *sep > '9') {
+                       wpa_printf(MSG_INFO, "%s: Invalid vlan specification "
+                                  "in interface name %s", __func__, ifname);
+                       os_free(drv);
+                       return NULL;
+               }
+               vlan *= 10;
+               vlan += *sep - '0';
+               if (vlan > ROBO_VLAN_MAX) {
+                       wpa_printf(MSG_INFO, "%s: VLAN out of range in "
+                                  "interface name %s", __func__, ifname);
+                       os_free(drv);
+                       return NULL;
+               }
+       }
+
+       drv->fd = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->fd < 0) {
+               wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__);
+               os_free(drv);
+               return NULL;
+       }
+
+       os_memset(&drv->ifr, 0, sizeof(drv->ifr));
+       os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
+       if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
+               perror("ioctl[SIOCGMIIPHY]");
+               os_free(drv);
+               return NULL;
+       }
+       if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) {
+               wpa_printf(MSG_INFO, "%s: Invalid phy address (not a "
+                          "RoboSwitch?)", __func__);
+               os_free(drv);
+               return NULL;
+       }
+
+       /* set and read back to see if the register can be used */
+       _read[0] = ROBO_VLAN_MAX;
+       wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+                                   _read, 1);
+       wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350,
+                                  _read + 1, 1);
+       drv->is_5350 = _read[0] == _read[1];
+
+       /* set the read bit */
+       vlan |= 1 << 13;
+       wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE,
+                                   drv->is_5350 ? ROBO_VLAN_ACCESS_5350
+                                                : ROBO_VLAN_ACCESS,
+                                   &vlan, 1);
+       wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read,
+                                  drv->is_5350 ? 2 : 1);
+       if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) {
+               wpa_printf(MSG_INFO, "%s: Could not get port information for "
+                                    "VLAN %d", __func__, vlan & ~(1 << 13));
+               os_free(drv);
+               return NULL;
+       }
+       drv->ports = _read[0] & 0x001F;
+       /* add the MII port */
+       drv->ports |= 1 << 8;
+       if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) {
+               wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__);
+               os_free(drv);
+               return NULL;
+       } else {
+               wpa_printf(MSG_DEBUG, "%s: Added PAE group address to "
+                          "RoboSwitch ARL", __func__);
+       }
+
+       return drv;
+}
+
+
+static void wpa_driver_roboswitch_deinit(void *priv)
+{
+       struct wpa_driver_roboswitch_data *drv = priv;
+
+       if (drv->l2) {
+               l2_packet_deinit(drv->l2);
+               drv->l2 = NULL;
+       }
+       if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group",
+                          __func__);
+       }
+
+       close(drv->fd);
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_roboswitch_ops = {
+       .name = "roboswitch",
+       .desc = "wpa_supplicant roboswitch driver",
+       .get_ssid = wpa_driver_roboswitch_get_ssid,
+       .get_bssid = wpa_driver_roboswitch_get_bssid,
+       .get_capa = wpa_driver_roboswitch_get_capa,
+       .init = wpa_driver_roboswitch_init,
+       .deinit = wpa_driver_roboswitch_deinit,
+       .set_param = wpa_driver_roboswitch_set_param,
+       .get_ifname = wpa_driver_roboswitch_get_ifname,
+};
diff --git a/src/drivers/driver_test.c b/src/drivers/driver_test.c
new file mode 100644 (file)
index 0000000..fb24673
--- /dev/null
@@ -0,0 +1,2753 @@
+/*
+ * Testing driver interface for a simulated network driver
+ * Copyright (c) 2004-2010, 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.
+ */
+
+/* Make sure we get winsock2.h for Windows build to get sockaddr_storage */
+#include "build_config.h"
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock2.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "utils/includes.h"
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/un.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#define DRIVER_TEST_UNIX
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "utils/trace.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/sha1.h"
+#include "l2_packet/l2_packet.h"
+#include "driver.h"
+
+
+struct test_client_socket {
+       struct test_client_socket *next;
+       u8 addr[ETH_ALEN];
+       struct sockaddr_un un;
+       socklen_t unlen;
+       struct test_driver_bss *bss;
+};
+
+struct test_driver_bss {
+       struct wpa_driver_test_data *drv;
+       struct dl_list list;
+       void *bss_ctx;
+       char ifname[IFNAMSIZ];
+       u8 bssid[ETH_ALEN];
+       u8 *ie;
+       size_t ielen;
+       u8 *wps_beacon_ie;
+       size_t wps_beacon_ie_len;
+       u8 *wps_probe_resp_ie;
+       size_t wps_probe_resp_ie_len;
+       u8 ssid[32];
+       size_t ssid_len;
+       int privacy;
+};
+
+struct wpa_driver_test_global {
+       int bss_add_used;
+       u8 req_addr[ETH_ALEN];
+};
+
+struct wpa_driver_test_data {
+       struct wpa_driver_test_global *global;
+       void *ctx;
+       WPA_TRACE_REF(ctx);
+       u8 own_addr[ETH_ALEN];
+       int test_socket;
+#ifdef DRIVER_TEST_UNIX
+       struct sockaddr_un hostapd_addr;
+#endif /* DRIVER_TEST_UNIX */
+       int hostapd_addr_set;
+       struct sockaddr_in hostapd_addr_udp;
+       int hostapd_addr_udp_set;
+       char *own_socket_path;
+       char *test_dir;
+#define MAX_SCAN_RESULTS 30
+       struct wpa_scan_res *scanres[MAX_SCAN_RESULTS];
+       size_t num_scanres;
+       int use_associnfo;
+       u8 assoc_wpa_ie[80];
+       size_t assoc_wpa_ie_len;
+       int use_mlme;
+       int associated;
+       u8 *probe_req_ie;
+       size_t probe_req_ie_len;
+       u8 probe_req_ssid[32];
+       size_t probe_req_ssid_len;
+       int ibss;
+       int ap;
+
+       struct test_client_socket *cli;
+       struct dl_list bss;
+       int udp_port;
+
+       int alloc_iface_idx;
+
+       int probe_req_report;
+       unsigned int remain_on_channel_freq;
+       unsigned int remain_on_channel_duration;
+
+       int current_freq;
+};
+
+
+static void wpa_driver_test_deinit(void *priv);
+static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
+                                 const char *dir, int ap);
+static void wpa_driver_test_close_test_socket(
+       struct wpa_driver_test_data *drv);
+static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+static void test_driver_free_bss(struct test_driver_bss *bss)
+{
+       os_free(bss->ie);
+       os_free(bss->wps_beacon_ie);
+       os_free(bss->wps_probe_resp_ie);
+       os_free(bss);
+}
+
+
+static void test_driver_free_bsses(struct wpa_driver_test_data *drv)
+{
+       struct test_driver_bss *bss, *tmp;
+
+       dl_list_for_each_safe(bss, tmp, &drv->bss, struct test_driver_bss,
+                             list) {
+               dl_list_del(&bss->list);
+               test_driver_free_bss(bss);
+       }
+}
+
+
+static struct test_client_socket *
+test_driver_get_cli(struct wpa_driver_test_data *drv, struct sockaddr_un *from,
+                   socklen_t fromlen)
+{
+       struct test_client_socket *cli = drv->cli;
+
+       while (cli) {
+               if (cli->unlen == fromlen &&
+                   strncmp(cli->un.sun_path, from->sun_path,
+                           fromlen - sizeof(cli->un.sun_family)) == 0)
+                       return cli;
+               cli = cli->next;
+       }
+
+       return NULL;
+}
+
+
+static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data,
+                                 size_t data_len, int encrypt,
+                                 const u8 *own_addr)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct test_client_socket *cli;
+       struct msghdr msg;
+       struct iovec io[3];
+       struct l2_ethhdr eth;
+
+       if (drv->test_socket < 0)
+               return -1;
+
+       cli = drv->cli;
+       while (cli) {
+               if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
+                       break;
+               cli = cli->next;
+       }
+
+       if (!cli) {
+               wpa_printf(MSG_DEBUG, "%s: no destination client entry",
+                          __func__);
+               return -1;
+       }
+
+       memcpy(eth.h_dest, addr, ETH_ALEN);
+       memcpy(eth.h_source, own_addr, ETH_ALEN);
+       eth.h_proto = host_to_be16(ETH_P_EAPOL);
+
+       io[0].iov_base = "EAPOL ";
+       io[0].iov_len = 6;
+       io[1].iov_base = &eth;
+       io[1].iov_len = sizeof(eth);
+       io[2].iov_base = (u8 *) data;
+       io[2].iov_len = data_len;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 3;
+       msg.msg_name = &cli->un;
+       msg.msg_namelen = cli->unlen;
+       return sendmsg(drv->test_socket, &msg, 0);
+}
+
+
+static int test_driver_send_ether(void *priv, const u8 *dst, const u8 *src,
+                                 u16 proto, const u8 *data, size_t data_len)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct msghdr msg;
+       struct iovec io[3];
+       struct l2_ethhdr eth;
+       char desttxt[30];
+       struct sockaddr_un addr;
+       struct dirent *dent;
+       DIR *dir;
+       int ret = 0, broadcast = 0, count = 0;
+
+       if (drv->test_socket < 0 || drv->test_dir == NULL) {
+               wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d "
+                          "test_dir=%p)",
+                          __func__, drv->test_socket, drv->test_dir);
+               return -1;
+       }
+
+       broadcast = memcmp(dst, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
+       snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dst));
+
+       memcpy(eth.h_dest, dst, ETH_ALEN);
+       memcpy(eth.h_source, src, ETH_ALEN);
+       eth.h_proto = host_to_be16(proto);
+
+       io[0].iov_base = "ETHER ";
+       io[0].iov_len = 6;
+       io[1].iov_base = &eth;
+       io[1].iov_len = sizeof(eth);
+       io[2].iov_base = (u8 *) data;
+       io[2].iov_len = data_len;
+
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 3;
+
+       dir = opendir(drv->test_dir);
+       if (dir == NULL) {
+               perror("test_driver: opendir");
+               return -1;
+       }
+       while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+               /* Skip the file if it is not a socket. Also accept
+                * DT_UNKNOWN (0) in case the C library or underlying file
+                * system does not support d_type. */
+               if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
+                       continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+               if (strcmp(dent->d_name, ".") == 0 ||
+                   strcmp(dent->d_name, "..") == 0)
+                       continue;
+
+               memset(&addr, 0, sizeof(addr));
+               addr.sun_family = AF_UNIX;
+               snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+                        drv->test_dir, dent->d_name);
+
+               if (strcmp(addr.sun_path, drv->own_socket_path) == 0)
+                       continue;
+               if (!broadcast && strstr(dent->d_name, desttxt) == NULL)
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "%s: Send ether frame to %s",
+                          __func__, dent->d_name);
+
+               msg.msg_name = &addr;
+               msg.msg_namelen = sizeof(addr);
+               ret = sendmsg(drv->test_socket, &msg, 0);
+               if (ret < 0)
+                       perror("driver_test: sendmsg");
+               count++;
+       }
+       closedir(dir);
+
+       if (!broadcast && count == 0) {
+               wpa_printf(MSG_DEBUG, "%s: Destination " MACSTR " not found",
+                          __func__, MAC2STR(dst));
+               return -1;
+       }
+
+       return ret;
+}
+
+
+static int wpa_driver_test_send_mlme(void *priv, const u8 *data,
+                                    size_t data_len)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct msghdr msg;
+       struct iovec io[2];
+       const u8 *dest;
+       struct sockaddr_un addr;
+       struct dirent *dent;
+       DIR *dir;
+       int broadcast;
+       int ret = 0;
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       char cmd[50];
+       int freq;
+#ifdef HOSTAPD
+       char desttxt[30];
+#endif /* HOSTAPD */
+       union wpa_event_data event;
+
+       wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len);
+       if (drv->test_socket < 0 || data_len < 10) {
+               wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%lu"
+                          " test_dir=%p)",
+                          __func__, drv->test_socket,
+                          (unsigned long) data_len,
+                          drv->test_dir);
+               return -1;
+       }
+
+       dest = data + 4;
+       broadcast = os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0;
+
+#ifdef HOSTAPD
+       snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest));
+#endif /* HOSTAPD */
+
+       if (drv->remain_on_channel_freq)
+               freq = drv->remain_on_channel_freq;
+       else
+               freq = drv->current_freq;
+       wpa_printf(MSG_DEBUG, "test_driver(%s): MLME TX on freq %d MHz",
+                  dbss->ifname, freq);
+       os_snprintf(cmd, sizeof(cmd), "MLME freq=%d ", freq);
+       io[0].iov_base = cmd;
+       io[0].iov_len = os_strlen(cmd);
+       io[1].iov_base = (void *) data;
+       io[1].iov_len = data_len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 2;
+
+#ifdef HOSTAPD
+       if (drv->test_dir == NULL) {
+               wpa_printf(MSG_DEBUG, "%s: test_dir == NULL", __func__);
+               return -1;
+       }
+
+       dir = opendir(drv->test_dir);
+       if (dir == NULL) {
+               perror("test_driver: opendir");
+               return -1;
+       }
+       while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+               /* Skip the file if it is not a socket. Also accept
+                * DT_UNKNOWN (0) in case the C library or underlying file
+                * system does not support d_type. */
+               if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
+                       continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+               if (os_strcmp(dent->d_name, ".") == 0 ||
+                   os_strcmp(dent->d_name, "..") == 0)
+                       continue;
+
+               os_memset(&addr, 0, sizeof(addr));
+               addr.sun_family = AF_UNIX;
+               os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+                           drv->test_dir, dent->d_name);
+
+               if (os_strcmp(addr.sun_path, drv->own_socket_path) == 0)
+                       continue;
+               if (!broadcast && os_strstr(dent->d_name, desttxt) == NULL)
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "%s: Send management frame to %s",
+                          __func__, dent->d_name);
+
+               msg.msg_name = &addr;
+               msg.msg_namelen = sizeof(addr);
+               ret = sendmsg(drv->test_socket, &msg, 0);
+               if (ret < 0)
+                       perror("driver_test: sendmsg(test_socket)");
+       }
+       closedir(dir);
+#else /* HOSTAPD */
+
+       if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
+           drv->test_dir == NULL) {
+               if (drv->hostapd_addr_udp_set) {
+                       msg.msg_name = &drv->hostapd_addr_udp;
+                       msg.msg_namelen = sizeof(drv->hostapd_addr_udp);
+               } else {
+#ifdef DRIVER_TEST_UNIX
+                       msg.msg_name = &drv->hostapd_addr;
+                       msg.msg_namelen = sizeof(drv->hostapd_addr);
+#endif /* DRIVER_TEST_UNIX */
+               }
+       } else if (broadcast) {
+               dir = opendir(drv->test_dir);
+               if (dir == NULL)
+                       return -1;
+               while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+                       /* Skip the file if it is not a socket.
+                        * Also accept DT_UNKNOWN (0) in case
+                        * the C library or underlying file
+                        * system does not support d_type. */
+                       if (dent->d_type != DT_SOCK &&
+                           dent->d_type != DT_UNKNOWN)
+                               continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+                       if (os_strcmp(dent->d_name, ".") == 0 ||
+                           os_strcmp(dent->d_name, "..") == 0)
+                               continue;
+                       wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s",
+                                  __func__, dent->d_name);
+                       os_memset(&addr, 0, sizeof(addr));
+                       addr.sun_family = AF_UNIX;
+                       os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+                                   "%s/%s", drv->test_dir, dent->d_name);
+
+                       msg.msg_name = &addr;
+                       msg.msg_namelen = sizeof(addr);
+
+                       ret = sendmsg(drv->test_socket, &msg, 0);
+                       if (ret < 0)
+                               perror("driver_test: sendmsg(test_socket)");
+               }
+               closedir(dir);
+               return ret;
+       } else {
+               struct stat st;
+               os_memset(&addr, 0, sizeof(addr));
+               addr.sun_family = AF_UNIX;
+               os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+                           "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest));
+               if (stat(addr.sun_path, &st) < 0) {
+                       os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+                                   "%s/STA-" MACSTR,
+                                   drv->test_dir, MAC2STR(dest));
+               }
+               msg.msg_name = &addr;
+               msg.msg_namelen = sizeof(addr);
+       }
+
+       if (sendmsg(drv->test_socket, &msg, 0) < 0) {
+               perror("sendmsg(test_socket)");
+               return -1;
+       }
+#endif /* HOSTAPD */
+
+       hdr = (struct ieee80211_hdr *) data;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = data;
+       event.tx_status.data_len = data_len;
+       event.tx_status.ack = ret >= 0;
+       wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+
+       return ret;
+}
+
+
+static void test_driver_scan(struct wpa_driver_test_data *drv,
+                            struct sockaddr_un *from, socklen_t fromlen,
+                            char *data)
+{
+       char buf[512], *pos, *end;
+       int ret;
+       struct test_driver_bss *bss;
+       u8 sa[ETH_ALEN];
+       u8 ie[512];
+       size_t ielen;
+       union wpa_event_data event;
+
+       /* data: optional [ ' ' | STA-addr | ' ' | IEs(hex) ] */
+
+       wpa_printf(MSG_DEBUG, "test_driver: SCAN");
+
+       if (*data) {
+               if (*data != ' ' ||
+                   hwaddr_aton(data + 1, sa)) {
+                       wpa_printf(MSG_DEBUG, "test_driver: Unexpected SCAN "
+                                  "command format");
+                       return;
+               }
+
+               data += 18;
+               while (*data == ' ')
+                       data++;
+               ielen = os_strlen(data) / 2;
+               if (ielen > sizeof(ie))
+                       ielen = sizeof(ie);
+               if (hexstr2bin(data, ie, ielen) < 0)
+                       ielen = 0;
+
+               wpa_printf(MSG_DEBUG, "test_driver: Scan from " MACSTR,
+                          MAC2STR(sa));
+               wpa_hexdump(MSG_MSGDUMP, "test_driver: scan IEs", ie, ielen);
+
+               os_memset(&event, 0, sizeof(event));
+               event.rx_probe_req.sa = sa;
+               event.rx_probe_req.ie = ie;
+               event.rx_probe_req.ie_len = ielen;
+               wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
+       }
+
+       dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
+               pos = buf;
+               end = buf + sizeof(buf);
+
+               /* reply: SCANRESP BSSID SSID IEs */
+               ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
+                              MAC2STR(bss->bssid));
+               if (ret < 0 || ret >= end - pos)
+                       return;
+               pos += ret;
+               pos += wpa_snprintf_hex(pos, end - pos,
+                                       bss->ssid, bss->ssid_len);
+               ret = snprintf(pos, end - pos, " ");
+               if (ret < 0 || ret >= end - pos)
+                       return;
+               pos += ret;
+               pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen);
+               pos += wpa_snprintf_hex(pos, end - pos, bss->wps_probe_resp_ie,
+                                       bss->wps_probe_resp_ie_len);
+
+               if (bss->privacy) {
+                       ret = snprintf(pos, end - pos, " PRIVACY");
+                       if (ret < 0 || ret >= end - pos)
+                               return;
+                       pos += ret;
+               }
+
+               sendto(drv->test_socket, buf, pos - buf, 0,
+                      (struct sockaddr *) from, fromlen);
+       }
+}
+
+
+static void test_driver_assoc(struct wpa_driver_test_data *drv,
+                             struct sockaddr_un *from, socklen_t fromlen,
+                             char *data)
+{
+       struct test_client_socket *cli;
+       u8 ie[256], ssid[32];
+       size_t ielen, ssid_len = 0;
+       char *pos, *pos2, cmd[50];
+       struct test_driver_bss *bss, *tmp;
+
+       /* data: STA-addr SSID(hex) IEs(hex) */
+
+       cli = os_zalloc(sizeof(*cli));
+       if (cli == NULL)
+               return;
+
+       if (hwaddr_aton(data, cli->addr)) {
+               printf("test_socket: Invalid MAC address '%s' in ASSOC\n",
+                      data);
+               os_free(cli);
+               return;
+       }
+       pos = data + 17;
+       while (*pos == ' ')
+               pos++;
+       pos2 = strchr(pos, ' ');
+       ielen = 0;
+       if (pos2) {
+               ssid_len = (pos2 - pos) / 2;
+               if (hexstr2bin(pos, ssid, ssid_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__);
+                       os_free(cli);
+                       return;
+               }
+               wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID",
+                                 ssid, ssid_len);
+
+               pos = pos2 + 1;
+               ielen = strlen(pos) / 2;
+               if (ielen > sizeof(ie))
+                       ielen = sizeof(ie);
+               if (hexstr2bin(pos, ie, ielen) < 0)
+                       ielen = 0;
+       }
+
+       bss = NULL;
+       dl_list_for_each(tmp, &drv->bss, struct test_driver_bss, list) {
+               if (tmp->ssid_len == ssid_len &&
+                   os_memcmp(tmp->ssid, ssid, ssid_len) == 0) {
+                       bss = tmp;
+                       break;
+               }
+       }
+       if (bss == NULL) {
+               wpa_printf(MSG_DEBUG, "%s: No matching SSID found from "
+                          "configured BSSes", __func__);
+               os_free(cli);
+               return;
+       }
+
+       cli->bss = bss;
+       memcpy(&cli->un, from, sizeof(cli->un));
+       cli->unlen = fromlen;
+       cli->next = drv->cli;
+       drv->cli = cli;
+       wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path",
+                         (const u8 *) cli->un.sun_path,
+                         cli->unlen - sizeof(cli->un.sun_family));
+
+       snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0",
+                MAC2STR(bss->bssid));
+       sendto(drv->test_socket, cmd, strlen(cmd), 0,
+              (struct sockaddr *) from, fromlen);
+
+       drv_event_assoc(bss->bss_ctx, cli->addr, ie, ielen);
+}
+
+
+static void test_driver_disassoc(struct wpa_driver_test_data *drv,
+                                struct sockaddr_un *from, socklen_t fromlen)
+{
+       struct test_client_socket *cli;
+
+       cli = test_driver_get_cli(drv, from, fromlen);
+       if (!cli)
+               return;
+
+       drv_event_disassoc(drv->ctx, cli->addr);
+}
+
+
+static void test_driver_eapol(struct wpa_driver_test_data *drv,
+                             struct sockaddr_un *from, socklen_t fromlen,
+                             u8 *data, size_t datalen)
+{
+#ifdef HOSTAPD
+       struct test_client_socket *cli;
+#endif /* HOSTAPD */
+       const u8 *src = NULL;
+
+       if (datalen > 14) {
+               /* Skip Ethernet header */
+               src = data + ETH_ALEN;
+               wpa_printf(MSG_DEBUG, "test_driver: dst=" MACSTR " src="
+                          MACSTR " proto=%04x",
+                          MAC2STR(data), MAC2STR(src),
+                          WPA_GET_BE16(data + 2 * ETH_ALEN));
+               data += 14;
+               datalen -= 14;
+       }
+
+#ifdef HOSTAPD
+       cli = test_driver_get_cli(drv, from, fromlen);
+       if (cli) {
+               drv_event_eapol_rx(cli->bss->bss_ctx, cli->addr, data,
+                                  datalen);
+       } else {
+               wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown "
+                          "client");
+       }
+#else /* HOSTAPD */
+       if (src)
+               drv_event_eapol_rx(drv->ctx, src, data, datalen);
+#endif /* HOSTAPD */
+}
+
+
+static void test_driver_ether(struct wpa_driver_test_data *drv,
+                             struct sockaddr_un *from, socklen_t fromlen,
+                             u8 *data, size_t datalen)
+{
+       struct l2_ethhdr *eth;
+
+       if (datalen < sizeof(*eth))
+               return;
+
+       eth = (struct l2_ethhdr *) data;
+       wpa_printf(MSG_DEBUG, "test_driver: RX ETHER dst=" MACSTR " src="
+                  MACSTR " proto=%04x",
+                  MAC2STR(eth->h_dest), MAC2STR(eth->h_source),
+                  be_to_host16(eth->h_proto));
+
+#ifdef CONFIG_IEEE80211R
+       if (be_to_host16(eth->h_proto) == ETH_P_RRB) {
+               union wpa_event_data ev;
+               os_memset(&ev, 0, sizeof(ev));
+               ev.ft_rrb_rx.src = eth->h_source;
+               ev.ft_rrb_rx.data = data + sizeof(*eth);
+               ev.ft_rrb_rx.data_len = datalen - sizeof(*eth);
+       }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static void test_driver_mlme(struct wpa_driver_test_data *drv,
+                            struct sockaddr_un *from, socklen_t fromlen,
+                            u8 *data, size_t datalen)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       union wpa_event_data event;
+       int freq = 0, own_freq;
+       struct test_driver_bss *bss;
+
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+       if (datalen > 6 && os_memcmp(data, "freq=", 5) == 0) {
+               size_t pos;
+               for (pos = 5; pos < datalen; pos++) {
+                       if (data[pos] == ' ')
+                               break;
+               }
+               if (pos < datalen) {
+                       freq = atoi((const char *) &data[5]);
+                       wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on "
+                                  "freq %d MHz", bss->ifname, freq);
+                       pos++;
+                       data += pos;
+                       datalen -= pos;
+               }
+       }
+
+       if (drv->remain_on_channel_freq)
+               own_freq = drv->remain_on_channel_freq;
+       else
+               own_freq = drv->current_freq;
+
+       if (freq && own_freq && freq != own_freq) {
+               wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on "
+                          "another frequency %d MHz (own %d MHz)",
+                          bss->ifname, freq, own_freq);
+               return;
+       }
+
+       hdr = (struct ieee80211_hdr *) data;
+
+       if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) {
+               struct test_client_socket *cli;
+               cli = os_zalloc(sizeof(*cli));
+               if (cli == NULL)
+                       return;
+               wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR,
+                          MAC2STR(hdr->addr2));
+               memcpy(cli->addr, hdr->addr2, ETH_ALEN);
+               memcpy(&cli->un, from, sizeof(cli->un));
+               cli->unlen = fromlen;
+               cli->next = drv->cli;
+               drv->cli = cli;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame",
+                   data, datalen);
+       fc = le_to_host16(hdr->frame_control);
+       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) {
+               wpa_printf(MSG_ERROR, "%s: received non-mgmt frame",
+                          __func__);
+               return;
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_mgmt.frame = data;
+       event.rx_mgmt.frame_len = datalen;
+       wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+}
+
+
+static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_test_data *drv = eloop_ctx;
+       char buf[2000];
+       int res;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+
+       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               perror("recvfrom(test_socket)");
+               return;
+       }
+       buf[res] = '\0';
+
+       wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
+
+       if (strncmp(buf, "SCAN", 4) == 0) {
+               test_driver_scan(drv, &from, fromlen, buf + 4);
+       } else if (strncmp(buf, "ASSOC ", 6) == 0) {
+               test_driver_assoc(drv, &from, fromlen, buf + 6);
+       } else if (strcmp(buf, "DISASSOC") == 0) {
+               test_driver_disassoc(drv, &from, fromlen);
+       } else if (strncmp(buf, "EAPOL ", 6) == 0) {
+               test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6,
+                                 res - 6);
+       } else if (strncmp(buf, "ETHER ", 6) == 0) {
+               test_driver_ether(drv, &from, fromlen, (u8 *) buf + 6,
+                                 res - 6);
+       } else if (strncmp(buf, "MLME ", 5) == 0) {
+               test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5);
+       } else {
+               wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command",
+                                 (u8 *) buf, res);
+       }
+}
+
+
+static int test_driver_set_generic_elem(void *priv,
+                                       const u8 *elem, size_t elem_len)
+{
+       struct test_driver_bss *bss = priv;
+
+       os_free(bss->ie);
+
+       if (elem == NULL) {
+               bss->ie = NULL;
+               bss->ielen = 0;
+               return 0;
+       }
+
+       bss->ie = os_malloc(elem_len);
+       if (bss->ie == NULL) {
+               bss->ielen = 0;
+               return -1;
+       }
+
+       memcpy(bss->ie, elem, elem_len);
+       bss->ielen = elem_len;
+       return 0;
+}
+
+
+static int test_driver_set_ap_wps_ie(void *priv, const struct wpabuf *beacon,
+                                    const struct wpabuf *proberesp)
+{
+       struct test_driver_bss *bss = priv;
+
+       if (beacon == NULL)
+               wpa_printf(MSG_DEBUG, "test_driver: Clear Beacon WPS IE");
+       else
+               wpa_hexdump_buf(MSG_DEBUG, "test_driver: Beacon WPS IE",
+                               beacon);
+
+       os_free(bss->wps_beacon_ie);
+
+       if (beacon == NULL) {
+               bss->wps_beacon_ie = NULL;
+               bss->wps_beacon_ie_len = 0;
+       } else {
+               bss->wps_beacon_ie = os_malloc(wpabuf_len(beacon));
+               if (bss->wps_beacon_ie == NULL) {
+                       bss->wps_beacon_ie_len = 0;
+                       return -1;
+               }
+
+               os_memcpy(bss->wps_beacon_ie, wpabuf_head(beacon),
+                         wpabuf_len(beacon));
+               bss->wps_beacon_ie_len = wpabuf_len(beacon);
+       }
+
+       if (proberesp == NULL)
+               wpa_printf(MSG_DEBUG, "test_driver: Clear Probe Response WPS "
+                          "IE");
+       else
+               wpa_hexdump_buf(MSG_DEBUG, "test_driver: Probe Response WPS "
+                               "IE", proberesp);
+
+       os_free(bss->wps_probe_resp_ie);
+
+       if (proberesp == NULL) {
+               bss->wps_probe_resp_ie = NULL;
+               bss->wps_probe_resp_ie_len = 0;
+       } else {
+               bss->wps_probe_resp_ie = os_malloc(wpabuf_len(proberesp));
+               if (bss->wps_probe_resp_ie == NULL) {
+                       bss->wps_probe_resp_ie_len = 0;
+                       return -1;
+               }
+
+               os_memcpy(bss->wps_probe_resp_ie, wpabuf_head(proberesp),
+                         wpabuf_len(proberesp));
+               bss->wps_probe_resp_ie_len = wpabuf_len(proberesp);
+       }
+
+       return 0;
+}
+
+
+static int test_driver_sta_deauth(void *priv, const u8 *own_addr,
+                                 const u8 *addr, int reason)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct test_client_socket *cli;
+
+       if (drv->test_socket < 0)
+               return -1;
+
+       cli = drv->cli;
+       while (cli) {
+               if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
+                       break;
+               cli = cli->next;
+       }
+
+       if (!cli)
+               return -1;
+
+       return sendto(drv->test_socket, "DEAUTH", 6, 0,
+                     (struct sockaddr *) &cli->un, cli->unlen);
+}
+
+
+static int test_driver_sta_disassoc(void *priv, const u8 *own_addr,
+                                   const u8 *addr, int reason)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct test_client_socket *cli;
+
+       if (drv->test_socket < 0)
+               return -1;
+
+       cli = drv->cli;
+       while (cli) {
+               if (memcmp(cli->addr, addr, ETH_ALEN) == 0)
+                       break;
+               cli = cli->next;
+       }
+
+       if (!cli)
+               return -1;
+
+       return sendto(drv->test_socket, "DISASSOC", 8, 0,
+                     (struct sockaddr *) &cli->un, cli->unlen);
+}
+
+
+static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid,
+                              void *bss_ctx, void **drv_priv)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct test_driver_bss *bss;
+
+       wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")",
+                  __func__, ifname, MAC2STR(bssid));
+
+       bss = os_zalloc(sizeof(*bss));
+       if (bss == NULL)
+               return -1;
+
+       bss->bss_ctx = bss_ctx;
+       bss->drv = drv;
+       os_strlcpy(bss->ifname, ifname, IFNAMSIZ);
+       os_memcpy(bss->bssid, bssid, ETH_ALEN);
+
+       dl_list_add(&drv->bss, &bss->list);
+       if (drv->global) {
+               drv->global->bss_add_used = 1;
+               os_memcpy(drv->global->req_addr, bssid, ETH_ALEN);
+       }
+
+       if (drv_priv)
+               *drv_priv = bss;
+
+       return 0;
+}
+
+
+static int test_driver_bss_remove(void *priv, const char *ifname)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct test_driver_bss *bss;
+       struct test_client_socket *cli, *prev_c;
+
+       wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname);
+
+       dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
+               if (strcmp(bss->ifname, ifname) != 0)
+                       continue;
+
+               for (prev_c = NULL, cli = drv->cli; cli;
+                    prev_c = cli, cli = cli->next) {
+                       if (cli->bss != bss)
+                               continue;
+                       if (prev_c)
+                               prev_c->next = cli->next;
+                       else
+                               drv->cli = cli->next;
+                       os_free(cli);
+                       break;
+               }
+
+               dl_list_del(&bss->list);
+               test_driver_free_bss(bss);
+               return 0;
+       }
+
+       return -1;
+}
+
+
+static int test_driver_if_add(void *priv, enum wpa_driver_if_type type,
+                             const char *ifname, const u8 *addr,
+                             void *bss_ctx, void **drv_priv,
+                             char *force_ifname, u8 *if_addr)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+
+       wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s bss_ctx=%p)",
+                  __func__, type, ifname, bss_ctx);
+       if (addr)
+               os_memcpy(if_addr, addr, ETH_ALEN);
+       else {
+               drv->alloc_iface_idx++;
+               if_addr[0] = 0x02; /* locally administered */
+               sha1_prf(drv->own_addr, ETH_ALEN,
+                        "hostapd test addr generation",
+                        (const u8 *) &drv->alloc_iface_idx,
+                        sizeof(drv->alloc_iface_idx),
+                        if_addr + 1, ETH_ALEN - 1);
+       }
+       if (type == WPA_IF_AP_BSS)
+               return test_driver_bss_add(priv, ifname, if_addr, bss_ctx,
+                                          drv_priv);
+       return 0;
+}
+
+
+static int test_driver_if_remove(void *priv, enum wpa_driver_if_type type,
+                                const char *ifname)
+{
+       wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname);
+       if (type == WPA_IF_AP_BSS)
+               return test_driver_bss_remove(priv, ifname);
+       return 0;
+}
+
+
+static int test_driver_valid_bss_mask(void *priv, const u8 *addr,
+                                     const u8 *mask)
+{
+       return 0;
+}
+
+
+static int test_driver_set_ssid(void *priv, const u8 *buf, int len)
+{
+       struct test_driver_bss *bss = priv;
+
+       wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, bss->ifname);
+       wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len);
+
+       if (len < 0 || (size_t) len > sizeof(bss->ssid))
+               return -1;
+
+       os_memcpy(bss->ssid, buf, len);
+       bss->ssid_len = len;
+
+       return 0;
+}
+
+
+static int test_driver_set_privacy(void *priv, int enabled)
+{
+       struct test_driver_bss *dbss = priv;
+
+       wpa_printf(MSG_DEBUG, "%s(enabled=%d)",  __func__, enabled);
+       dbss->privacy = enabled;
+
+       return 0;
+}
+
+
+static int test_driver_set_sta_vlan(void *priv, const u8 *addr,
+                                   const char *ifname, int vlan_id)
+{
+       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)",
+                  __func__, MAC2STR(addr), ifname, vlan_id);
+       return 0;
+}
+
+
+static int test_driver_sta_add(void *priv,
+                              struct hostapd_sta_add_params *params)
+{
+       struct test_driver_bss *bss = priv;
+       struct wpa_driver_test_data *drv = bss->drv;
+       struct test_client_socket *cli;
+
+       wpa_printf(MSG_DEBUG, "%s(ifname=%s addr=" MACSTR " aid=%d "
+                  "capability=0x%x listen_interval=%d)",
+                  __func__, bss->ifname, MAC2STR(params->addr), params->aid,
+                  params->capability, params->listen_interval);
+       wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates",
+                   params->supp_rates, params->supp_rates_len);
+
+       cli = drv->cli;
+       while (cli) {
+               if (os_memcmp(cli->addr, params->addr, ETH_ALEN) == 0)
+                       break;
+               cli = cli->next;
+       }
+       if (!cli) {
+               wpa_printf(MSG_DEBUG, "%s: no matching client entry",
+                          __func__);
+               return -1;
+       }
+
+       cli->bss = bss;
+
+       return 0;
+}
+
+
+static struct wpa_driver_test_data * test_alloc_data(void *ctx,
+                                                    const char *ifname)
+{
+       struct wpa_driver_test_data *drv;
+       struct test_driver_bss *bss;
+
+       drv = os_zalloc(sizeof(struct wpa_driver_test_data));
+       if (drv == NULL) {
+               wpa_printf(MSG_ERROR, "Could not allocate memory for test "
+                          "driver data");
+               return NULL;
+       }
+
+       bss = os_zalloc(sizeof(struct test_driver_bss));
+       if (bss == NULL) {
+               os_free(drv);
+               return NULL;
+       }
+
+       drv->ctx = ctx;
+       wpa_trace_add_ref(drv, ctx, ctx);
+       dl_list_init(&drv->bss);
+       dl_list_add(&drv->bss, &bss->list);
+       os_strlcpy(bss->ifname, ifname, IFNAMSIZ);
+       bss->bss_ctx = ctx;
+       bss->drv = drv;
+
+       /* Generate a MAC address to help testing with multiple STAs */
+       drv->own_addr[0] = 0x02; /* locally administered */
+       sha1_prf((const u8 *) ifname, os_strlen(ifname),
+                "test mac addr generation",
+                NULL, 0, drv->own_addr + 1, ETH_ALEN - 1);
+
+       return drv;
+}
+
+
+static void * test_driver_init(struct hostapd_data *hapd,
+                              struct wpa_init_params *params)
+{
+       struct wpa_driver_test_data *drv;
+       struct sockaddr_un addr_un;
+       struct sockaddr_in addr_in;
+       struct sockaddr *addr;
+       socklen_t alen;
+       struct test_driver_bss *bss;
+
+       drv = test_alloc_data(hapd, params->ifname);
+       if (drv == NULL)
+               return NULL;
+       drv->ap = 1;
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+       bss->bss_ctx = hapd;
+       os_memcpy(bss->bssid, drv->own_addr, ETH_ALEN);
+       os_memcpy(params->own_addr, drv->own_addr, ETH_ALEN);
+
+       if (params->test_socket) {
+               if (os_strlen(params->test_socket) >=
+                   sizeof(addr_un.sun_path)) {
+                       printf("Too long test_socket path\n");
+                       wpa_driver_test_deinit(bss);
+                       return NULL;
+               }
+               if (strncmp(params->test_socket, "DIR:", 4) == 0) {
+                       size_t len = strlen(params->test_socket) + 30;
+                       drv->test_dir = os_strdup(params->test_socket + 4);
+                       drv->own_socket_path = os_malloc(len);
+                       if (drv->own_socket_path) {
+                               snprintf(drv->own_socket_path, len,
+                                        "%s/AP-" MACSTR,
+                                        params->test_socket + 4,
+                                        MAC2STR(params->own_addr));
+                       }
+               } else if (strncmp(params->test_socket, "UDP:", 4) == 0) {
+                       drv->udp_port = atoi(params->test_socket + 4);
+               } else {
+                       drv->own_socket_path = os_strdup(params->test_socket);
+               }
+               if (drv->own_socket_path == NULL && drv->udp_port == 0) {
+                       wpa_driver_test_deinit(bss);
+                       return NULL;
+               }
+
+               drv->test_socket = socket(drv->udp_port ? PF_INET : PF_UNIX,
+                                         SOCK_DGRAM, 0);
+               if (drv->test_socket < 0) {
+                       perror("socket");
+                       wpa_driver_test_deinit(bss);
+                       return NULL;
+               }
+
+               if (drv->udp_port) {
+                       os_memset(&addr_in, 0, sizeof(addr_in));
+                       addr_in.sin_family = AF_INET;
+                       addr_in.sin_port = htons(drv->udp_port);
+                       addr = (struct sockaddr *) &addr_in;
+                       alen = sizeof(addr_in);
+               } else {
+                       os_memset(&addr_un, 0, sizeof(addr_un));
+                       addr_un.sun_family = AF_UNIX;
+                       os_strlcpy(addr_un.sun_path, drv->own_socket_path,
+                                  sizeof(addr_un.sun_path));
+                       addr = (struct sockaddr *) &addr_un;
+                       alen = sizeof(addr_un);
+               }
+               if (bind(drv->test_socket, addr, alen) < 0) {
+                       perror("bind(PF_UNIX)");
+                       close(drv->test_socket);
+                       if (drv->own_socket_path)
+                               unlink(drv->own_socket_path);
+                       wpa_driver_test_deinit(bss);
+                       return NULL;
+               }
+               eloop_register_read_sock(drv->test_socket,
+                                        test_driver_receive_unix, drv, NULL);
+       } else
+               drv->test_socket = -1;
+
+       return bss;
+}
+
+
+static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_test_data *drv = eloop_ctx;
+
+#ifdef DRIVER_TEST_UNIX
+       if (drv->associated && drv->hostapd_addr_set) {
+               struct stat st;
+               if (stat(drv->hostapd_addr.sun_path, &st) < 0) {
+                       wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s",
+                                  __func__, strerror(errno));
+                       drv->associated = 0;
+                       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+               }
+       }
+#endif /* DRIVER_TEST_UNIX */
+
+       eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
+}
+
+
+static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+#ifdef DRIVER_TEST_UNIX
+static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv,
+                               const char *path)
+{
+       struct dirent *dent;
+       DIR *dir;
+       struct sockaddr_un addr;
+       char cmd[512], *pos, *end;
+       int ret;
+
+       dir = opendir(path);
+       if (dir == NULL)
+               return;
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, "SCAN " MACSTR,
+                         MAC2STR(drv->own_addr));
+       if (ret >= 0 && ret < end - pos)
+               pos += ret;
+       if (drv->probe_req_ie) {
+               ret = os_snprintf(pos, end - pos, " ");
+               if (ret >= 0 && ret < end - pos)
+                       pos += ret;
+               pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie,
+                                       drv->probe_req_ie_len);
+       }
+       if (drv->probe_req_ssid_len) {
+               /* Add SSID IE */
+               ret = os_snprintf(pos, end - pos, "%02x%02x",
+                                 WLAN_EID_SSID,
+                                 (unsigned int) drv->probe_req_ssid_len);
+               if (ret >= 0 && ret < end - pos)
+                       pos += ret;
+               pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ssid,
+                                       drv->probe_req_ssid_len);
+       }
+       end[-1] = '\0';
+
+       while ((dent = readdir(dir))) {
+               if (os_strncmp(dent->d_name, "AP-", 3) != 0 &&
+                   os_strncmp(dent->d_name, "STA-", 4) != 0)
+                       continue;
+               if (drv->own_socket_path) {
+                       size_t olen, dlen;
+                       olen = os_strlen(drv->own_socket_path);
+                       dlen = os_strlen(dent->d_name);
+                       if (olen >= dlen &&
+                           os_strcmp(dent->d_name,
+                                     drv->own_socket_path + olen - dlen) == 0)
+                               continue;
+               }
+               wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name);
+
+               os_memset(&addr, 0, sizeof(addr));
+               addr.sun_family = AF_UNIX;
+               os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s",
+                           path, dent->d_name);
+
+               if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
+                          (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+                       perror("sendto(test_socket)");
+               }
+       }
+       closedir(dir);
+}
+#endif /* DRIVER_TEST_UNIX */
+
+
+static int wpa_driver_test_scan(void *priv,
+                               struct wpa_driver_scan_params *params)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       size_t i;
+
+       wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv);
+
+       os_free(drv->probe_req_ie);
+       if (params->extra_ies) {
+               drv->probe_req_ie = os_malloc(params->extra_ies_len);
+               if (drv->probe_req_ie == NULL) {
+                       drv->probe_req_ie_len = 0;
+                       return -1;
+               }
+               os_memcpy(drv->probe_req_ie, params->extra_ies,
+                         params->extra_ies_len);
+               drv->probe_req_ie_len = params->extra_ies_len;
+       } else {
+               drv->probe_req_ie = NULL;
+               drv->probe_req_ie_len = 0;
+       }
+
+       for (i = 0; i < params->num_ssids; i++)
+               wpa_hexdump(MSG_DEBUG, "Scan SSID",
+                           params->ssids[i].ssid, params->ssids[i].ssid_len);
+       drv->probe_req_ssid_len = 0;
+       if (params->num_ssids) {
+               os_memcpy(drv->probe_req_ssid, params->ssids[0].ssid,
+                         params->ssids[0].ssid_len);
+               drv->probe_req_ssid_len = params->ssids[0].ssid_len;
+       }
+       wpa_hexdump(MSG_DEBUG, "Scan extra IE(s)",
+                   params->extra_ies, params->extra_ies_len);
+
+       drv->num_scanres = 0;
+
+#ifdef DRIVER_TEST_UNIX
+       if (drv->test_socket >= 0 && drv->test_dir)
+               wpa_driver_scan_dir(drv, drv->test_dir);
+
+       if (drv->test_socket >= 0 && drv->hostapd_addr_set &&
+           sendto(drv->test_socket, "SCAN", 4, 0,
+                  (struct sockaddr *) &drv->hostapd_addr,
+                  sizeof(drv->hostapd_addr)) < 0) {
+               perror("sendto(test_socket)");
+       }
+#endif /* DRIVER_TEST_UNIX */
+
+       if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
+           sendto(drv->test_socket, "SCAN", 4, 0,
+                  (struct sockaddr *) &drv->hostapd_addr_udp,
+                  sizeof(drv->hostapd_addr_udp)) < 0) {
+               perror("sendto(test_socket)");
+       }
+
+       eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv,
+                              drv->ctx);
+       return 0;
+}
+
+
+static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct wpa_scan_results *res;
+       size_t i;
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL)
+               return NULL;
+
+       res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *));
+       if (res->res == NULL) {
+               os_free(res);
+               return NULL;
+       }
+
+       for (i = 0; i < drv->num_scanres; i++) {
+               struct wpa_scan_res *r;
+               if (drv->scanres[i] == NULL)
+                       continue;
+               r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len);
+               if (r == NULL)
+                       break;
+               os_memcpy(r, drv->scanres[i],
+                         sizeof(*r) + drv->scanres[i]->ie_len);
+               res->res[res->num++] = r;
+       }
+
+       return res;
+}
+
+
+static int wpa_driver_test_set_key(const char *ifname, void *priv,
+                                  enum wpa_alg alg, const u8 *addr,
+                                  int key_idx, int set_tx,
+                                  const u8 *seq, size_t seq_len,
+                                  const u8 *key, size_t key_len)
+{
+       wpa_printf(MSG_DEBUG, "%s: ifname=%s priv=%p alg=%d key_idx=%d "
+                  "set_tx=%d",
+                  __func__, ifname, priv, alg, key_idx, set_tx);
+       if (addr)
+               wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
+       if (seq)
+               wpa_hexdump(MSG_DEBUG, "   seq", seq, seq_len);
+       if (key)
+               wpa_hexdump_key(MSG_DEBUG, "   key", key, key_len);
+       return 0;
+}
+
+
+static int wpa_driver_update_mode(struct wpa_driver_test_data *drv, int ap)
+{
+       if (ap && !drv->ap) {
+               wpa_driver_test_close_test_socket(drv);
+               wpa_driver_test_attach(drv, drv->test_dir, 1);
+               drv->ap = 1;
+       } else if (!ap && drv->ap) {
+               wpa_driver_test_close_test_socket(drv);
+               wpa_driver_test_attach(drv, drv->test_dir, 0);
+               drv->ap = 0;
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_test_associate(
+       void *priv, struct wpa_driver_associate_params *params)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
+                  "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
+                  __func__, priv, params->freq, params->pairwise_suite,
+                  params->group_suite, params->key_mgmt_suite,
+                  params->auth_alg, params->mode);
+       if (params->bssid) {
+               wpa_printf(MSG_DEBUG, "   bssid=" MACSTR,
+                          MAC2STR(params->bssid));
+       }
+       if (params->ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "   ssid",
+                                 params->ssid, params->ssid_len);
+       }
+       if (params->wpa_ie) {
+               wpa_hexdump(MSG_DEBUG, "   wpa_ie",
+                           params->wpa_ie, params->wpa_ie_len);
+               drv->assoc_wpa_ie_len = params->wpa_ie_len;
+               if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie))
+                       drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie);
+               os_memcpy(drv->assoc_wpa_ie, params->wpa_ie,
+                         drv->assoc_wpa_ie_len);
+       } else
+               drv->assoc_wpa_ie_len = 0;
+
+       wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
+
+       drv->ibss = params->mode == IEEE80211_MODE_IBSS;
+       dbss->privacy = params->key_mgmt_suite &
+               (WPA_KEY_MGMT_IEEE8021X |
+                WPA_KEY_MGMT_PSK |
+                WPA_KEY_MGMT_WPA_NONE |
+                WPA_KEY_MGMT_FT_IEEE8021X |
+                WPA_KEY_MGMT_FT_PSK |
+                WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                WPA_KEY_MGMT_PSK_SHA256);
+       if (params->wep_key_len[params->wep_tx_keyidx])
+               dbss->privacy = 1;
+
+#ifdef DRIVER_TEST_UNIX
+       if (drv->test_dir && params->bssid &&
+           params->mode != IEEE80211_MODE_IBSS) {
+               os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
+               drv->hostapd_addr.sun_family = AF_UNIX;
+               os_snprintf(drv->hostapd_addr.sun_path,
+                           sizeof(drv->hostapd_addr.sun_path),
+                           "%s/AP-" MACSTR,
+                           drv->test_dir, MAC2STR(params->bssid));
+               drv->hostapd_addr_set = 1;
+       }
+#endif /* DRIVER_TEST_UNIX */
+
+       if (params->mode == IEEE80211_MODE_AP) {
+               os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+               dbss->ssid_len = params->ssid_len;
+               os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN);
+               if (params->wpa_ie && params->wpa_ie_len) {
+                       dbss->ie = os_malloc(params->wpa_ie_len);
+                       if (dbss->ie) {
+                               os_memcpy(dbss->ie, params->wpa_ie,
+                                         params->wpa_ie_len);
+                               dbss->ielen = params->wpa_ie_len;
+                       }
+               }
+       } else if (drv->test_socket >= 0 &&
+                  (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) {
+               char cmd[200], *pos, *end;
+               int ret;
+               end = cmd + sizeof(cmd);
+               pos = cmd;
+               ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ",
+                                 MAC2STR(drv->own_addr));
+               if (ret >= 0 && ret < end - pos)
+                       pos += ret;
+               pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
+                                       params->ssid_len);
+               ret = os_snprintf(pos, end - pos, " ");
+               if (ret >= 0 && ret < end - pos)
+                       pos += ret;
+               pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie,
+                                       params->wpa_ie_len);
+               end[-1] = '\0';
+#ifdef DRIVER_TEST_UNIX
+               if (drv->hostapd_addr_set &&
+                   sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
+                          (struct sockaddr *) &drv->hostapd_addr,
+                          sizeof(drv->hostapd_addr)) < 0) {
+                       perror("sendto(test_socket)");
+                       return -1;
+               }
+#endif /* DRIVER_TEST_UNIX */
+               if (drv->hostapd_addr_udp_set &&
+                   sendto(drv->test_socket, cmd, os_strlen(cmd), 0,
+                          (struct sockaddr *) &drv->hostapd_addr_udp,
+                          sizeof(drv->hostapd_addr_udp)) < 0) {
+                       perror("sendto(test_socket)");
+                       return -1;
+               }
+
+               os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+               dbss->ssid_len = params->ssid_len;
+       } else {
+               drv->associated = 1;
+               if (params->mode == IEEE80211_MODE_IBSS) {
+                       os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+                       dbss->ssid_len = params->ssid_len;
+                       if (params->bssid)
+                               os_memcpy(dbss->bssid, params->bssid,
+                                         ETH_ALEN);
+                       else {
+                               os_get_random(dbss->bssid, ETH_ALEN);
+                               dbss->bssid[0] &= ~0x01;
+                               dbss->bssid[0] |= 0x02;
+                       }
+               }
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_test_get_bssid(void *priv, u8 *bssid)
+{
+       struct test_driver_bss *dbss = priv;
+       os_memcpy(bssid, dbss->bssid, ETH_ALEN);
+       return 0;
+}
+
+
+static int wpa_driver_test_get_ssid(void *priv, u8 *ssid)
+{
+       struct test_driver_bss *dbss = priv;
+       os_memcpy(ssid, dbss->ssid, 32);
+       return dbss->ssid_len;
+}
+
+
+static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv)
+{
+#ifdef DRIVER_TEST_UNIX
+       if (drv->test_socket >= 0 &&
+           sendto(drv->test_socket, "DISASSOC", 8, 0,
+                  (struct sockaddr *) &drv->hostapd_addr,
+                  sizeof(drv->hostapd_addr)) < 0) {
+               perror("sendto(test_socket)");
+               return -1;
+       }
+#endif /* DRIVER_TEST_UNIX */
+       if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set &&
+           sendto(drv->test_socket, "DISASSOC", 8, 0,
+                  (struct sockaddr *) &drv->hostapd_addr_udp,
+                  sizeof(drv->hostapd_addr_udp)) < 0) {
+               perror("sendto(test_socket)");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+                  __func__, MAC2STR(addr), reason_code);
+       os_memset(dbss->bssid, 0, ETH_ALEN);
+       drv->associated = 0;
+       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+       return wpa_driver_test_send_disassoc(drv);
+}
+
+
+static int wpa_driver_test_disassociate(void *priv, const u8 *addr,
+                                       int reason_code)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d",
+                  __func__, MAC2STR(addr), reason_code);
+       os_memset(dbss->bssid, 0, ETH_ALEN);
+       drv->associated = 0;
+       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+       return wpa_driver_test_send_disassoc(drv);
+}
+
+
+static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+       const u8 *end, *pos;
+
+       pos = (const u8 *) (res + 1);
+       end = pos + res->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv,
+                                    struct sockaddr *from,
+                                    socklen_t fromlen,
+                                    const char *data)
+{
+       struct wpa_scan_res *res;
+       const char *pos, *pos2;
+       size_t len;
+       u8 *ie_pos, *ie_start, *ie_end;
+#define MAX_IE_LEN 1000
+       const u8 *ds_params;
+
+       wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data);
+       if (drv->num_scanres >= MAX_SCAN_RESULTS) {
+               wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan "
+                          "result");
+               return;
+       }
+
+       /* SCANRESP BSSID SSID IEs */
+
+       res = os_zalloc(sizeof(*res) + MAX_IE_LEN);
+       if (res == NULL)
+               return;
+       ie_start = ie_pos = (u8 *) (res + 1);
+       ie_end = ie_pos + MAX_IE_LEN;
+
+       if (hwaddr_aton(data, res->bssid)) {
+               wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres");
+               os_free(res);
+               return;
+       }
+
+       pos = data + 17;
+       while (*pos == ' ')
+               pos++;
+       pos2 = os_strchr(pos, ' ');
+       if (pos2 == NULL) {
+               wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination "
+                          "in scanres");
+               os_free(res);
+               return;
+       }
+       len = (pos2 - pos) / 2;
+       if (len > 32)
+               len = 32;
+       /*
+        * Generate SSID IE from the SSID field since this IE is not included
+        * in the main IE field.
+        */
+       *ie_pos++ = WLAN_EID_SSID;
+       *ie_pos++ = len;
+       if (hexstr2bin(pos, ie_pos, len) < 0) {
+               wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres");
+               os_free(res);
+               return;
+       }
+       ie_pos += len;
+
+       pos = pos2 + 1;
+       pos2 = os_strchr(pos, ' ');
+       if (pos2 == NULL)
+               len = os_strlen(pos) / 2;
+       else
+               len = (pos2 - pos) / 2;
+       if ((int) len > ie_end - ie_pos)
+               len = ie_end - ie_pos;
+       if (hexstr2bin(pos, ie_pos, len) < 0) {
+               wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres");
+               os_free(res);
+               return;
+       }
+       ie_pos += len;
+       res->ie_len = ie_pos - ie_start;
+
+       if (pos2) {
+               pos = pos2 + 1;
+               while (*pos == ' ')
+                       pos++;
+               if (os_strstr(pos, "PRIVACY"))
+                       res->caps |= IEEE80211_CAP_PRIVACY;
+               if (os_strstr(pos, "IBSS"))
+                       res->caps |= IEEE80211_CAP_IBSS;
+       }
+
+       ds_params = wpa_scan_get_ie(res, WLAN_EID_DS_PARAMS);
+       if (ds_params && ds_params[1] > 0) {
+               if (ds_params[2] >= 1 && ds_params[2] <= 13)
+                       res->freq = 2407 + ds_params[2] * 5;
+       }
+
+       os_free(drv->scanres[drv->num_scanres]);
+       drv->scanres[drv->num_scanres++] = res;
+}
+
+
+static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv,
+                                     struct sockaddr *from,
+                                     socklen_t fromlen,
+                                     const char *data)
+{
+       struct test_driver_bss *bss;
+
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+       /* ASSOCRESP BSSID <res> */
+       if (hwaddr_aton(data, bss->bssid)) {
+               wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in "
+                          "assocresp");
+       }
+       if (drv->use_associnfo) {
+               union wpa_event_data event;
+               os_memset(&event, 0, sizeof(event));
+               event.assoc_info.req_ies = drv->assoc_wpa_ie;
+               event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len;
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event);
+       }
+       drv->associated = 1;
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv,
+                                    struct sockaddr *from,
+                                    socklen_t fromlen)
+{
+       drv->associated = 0;
+       wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL);
+}
+
+
+static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv,
+                                 struct sockaddr *from,
+                                 socklen_t fromlen,
+                                 const u8 *data, size_t data_len)
+{
+       const u8 *src;
+       struct test_driver_bss *bss;
+
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+       if (data_len > 14) {
+               /* Skip Ethernet header */
+               src = data + ETH_ALEN;
+               data += 14;
+               data_len -= 14;
+       } else
+               src = bss->bssid;
+
+       drv_event_eapol_rx(drv->ctx, src, data, data_len);
+}
+
+
+static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
+                                struct sockaddr *from,
+                                socklen_t fromlen,
+                                const u8 *data, size_t data_len)
+{
+       int freq = 0, own_freq;
+       union wpa_event_data event;
+       struct test_driver_bss *bss;
+
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+       if (data_len > 6 && os_memcmp(data, "freq=", 5) == 0) {
+               size_t pos;
+               for (pos = 5; pos < data_len; pos++) {
+                       if (data[pos] == ' ')
+                               break;
+               }
+               if (pos < data_len) {
+                       freq = atoi((const char *) &data[5]);
+                       wpa_printf(MSG_DEBUG, "test_driver(%s): MLME RX on "
+                                  "freq %d MHz", bss->ifname, freq);
+                       pos++;
+                       data += pos;
+                       data_len -= pos;
+               }
+       }
+
+       if (drv->remain_on_channel_freq)
+               own_freq = drv->remain_on_channel_freq;
+       else
+               own_freq = drv->current_freq;
+
+       if (freq && own_freq && freq != own_freq) {
+               wpa_printf(MSG_DEBUG, "test_driver(%s): Ignore MLME RX on "
+                          "another frequency %d MHz (own %d MHz)",
+                          bss->ifname, freq, own_freq);
+               return;
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       event.mlme_rx.buf = data;
+       event.mlme_rx.len = data_len;
+       event.mlme_rx.freq = freq;
+       wpa_supplicant_event(drv->ctx, EVENT_MLME_RX, &event);
+
+       if (drv->probe_req_report && data_len >= 24) {
+               const struct ieee80211_mgmt *mgmt;
+               u16 fc;
+
+               mgmt = (const struct ieee80211_mgmt *) data;
+               fc = le_to_host16(mgmt->frame_control);
+               if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_REQ) {
+                       os_memset(&event, 0, sizeof(event));
+                       event.rx_probe_req.sa = mgmt->sa;
+                       event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+                       event.rx_probe_req.ie_len =
+                               data_len - (mgmt->u.probe_req.variable - data);
+                       wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
+                                            &event);
+               }
+       }
+}
+
+
+static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv,
+                                    struct sockaddr *from,
+                                    socklen_t fromlen,
+                                    const u8 *data, size_t data_len)
+{
+       char buf[512], *pos, *end;
+       int ret;
+       struct test_driver_bss *bss;
+
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+
+       /* data: optional [ STA-addr | ' ' | IEs(hex) ] */
+
+       if (!drv->ibss)
+               return;
+
+       pos = buf;
+       end = buf + sizeof(buf);
+
+       /* reply: SCANRESP BSSID SSID IEs */
+       ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
+                      MAC2STR(bss->bssid));
+       if (ret < 0 || ret >= end - pos)
+               return;
+       pos += ret;
+       pos += wpa_snprintf_hex(pos, end - pos,
+                               bss->ssid, bss->ssid_len);
+       ret = snprintf(pos, end - pos, " ");
+       if (ret < 0 || ret >= end - pos)
+               return;
+       pos += ret;
+       pos += wpa_snprintf_hex(pos, end - pos, drv->assoc_wpa_ie,
+                               drv->assoc_wpa_ie_len);
+
+       if (bss->privacy) {
+               ret = snprintf(pos, end - pos, " PRIVACY");
+               if (ret < 0 || ret >= end - pos)
+                       return;
+               pos += ret;
+       }
+
+       ret = snprintf(pos, end - pos, " IBSS");
+       if (ret < 0 || ret >= end - pos)
+               return;
+       pos += ret;
+
+       sendto(drv->test_socket, buf, pos - buf, 0,
+              (struct sockaddr *) from, fromlen);
+}
+
+
+static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx,
+                                        void *sock_ctx)
+{
+       struct wpa_driver_test_data *drv = eloop_ctx;
+       char *buf;
+       int res;
+       struct sockaddr_storage from;
+       socklen_t fromlen = sizeof(from);
+       const size_t buflen = 2000;
+
+       if (drv->ap) {
+               test_driver_receive_unix(sock, eloop_ctx, sock_ctx);
+               return;
+       }
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       res = recvfrom(sock, buf, buflen - 1, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               perror("recvfrom(test_socket)");
+               os_free(buf);
+               return;
+       }
+       buf[res] = '\0';
+
+       wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res);
+
+       if (os_strncmp(buf, "SCANRESP ", 9) == 0) {
+               wpa_driver_test_scanresp(drv, (struct sockaddr *) &from,
+                                        fromlen, buf + 9);
+       } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) {
+               wpa_driver_test_assocresp(drv, (struct sockaddr *) &from,
+                                         fromlen, buf + 10);
+       } else if (os_strcmp(buf, "DISASSOC") == 0) {
+               wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
+                                        fromlen);
+       } else if (os_strcmp(buf, "DEAUTH") == 0) {
+               wpa_driver_test_disassoc(drv, (struct sockaddr *) &from,
+                                        fromlen);
+       } else if (os_strncmp(buf, "EAPOL ", 6) == 0) {
+               wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen,
+                                     (const u8 *) buf + 6, res - 6);
+       } else if (os_strncmp(buf, "MLME ", 5) == 0) {
+               wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen,
+                                    (const u8 *) buf + 5, res - 5);
+       } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
+               wpa_driver_test_scan_cmd(drv, (struct sockaddr *) &from,
+                                        fromlen,
+                                        (const u8 *) buf + 5, res - 5);
+       } else {
+               wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command",
+                                 (u8 *) buf, res);
+       }
+       os_free(buf);
+}
+
+
+static void * wpa_driver_test_init2(void *ctx, const char *ifname,
+                                   void *global_priv)
+{
+       struct wpa_driver_test_data *drv;
+       struct wpa_driver_test_global *global = global_priv;
+       struct test_driver_bss *bss;
+
+       drv = test_alloc_data(ctx, ifname);
+       if (drv == NULL)
+               return NULL;
+       bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
+       drv->global = global_priv;
+       drv->test_socket = -1;
+
+       /* Set dummy BSSID and SSID for testing. */
+       bss->bssid[0] = 0x02;
+       bss->bssid[1] = 0x00;
+       bss->bssid[2] = 0x00;
+       bss->bssid[3] = 0x00;
+       bss->bssid[4] = 0x00;
+       bss->bssid[5] = 0x01;
+       os_memcpy(bss->ssid, "test", 5);
+       bss->ssid_len = 4;
+
+       if (global->bss_add_used) {
+               os_memcpy(drv->own_addr, global->req_addr, ETH_ALEN);
+               global->bss_add_used = 0;
+       }
+
+       eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL);
+
+       return bss;
+}
+
+
+static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv)
+{
+       if (drv->test_socket >= 0) {
+               eloop_unregister_read_sock(drv->test_socket);
+               close(drv->test_socket);
+               drv->test_socket = -1;
+       }
+
+       if (drv->own_socket_path) {
+               unlink(drv->own_socket_path);
+               os_free(drv->own_socket_path);
+               drv->own_socket_path = NULL;
+       }
+}
+
+
+static void wpa_driver_test_deinit(void *priv)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       struct test_client_socket *cli, *prev;
+       int i;
+
+       cli = drv->cli;
+       while (cli) {
+               prev = cli;
+               cli = cli->next;
+               os_free(prev);
+       }
+
+#ifdef HOSTAPD
+       /* There should be only one BSS remaining at this point. */
+       if (dl_list_len(&drv->bss) != 1)
+               wpa_printf(MSG_ERROR, "%s: %u remaining BSS entries",
+                          __func__, dl_list_len(&drv->bss));
+#endif /* HOSTAPD */
+
+       test_driver_free_bsses(drv);
+
+       wpa_driver_test_close_test_socket(drv);
+       eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx);
+       eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL);
+       eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
+       os_free(drv->test_dir);
+       for (i = 0; i < MAX_SCAN_RESULTS; i++)
+               os_free(drv->scanres[i]);
+       os_free(drv->probe_req_ie);
+       wpa_trace_remove_ref(drv, ctx, drv->ctx);
+       os_free(drv);
+}
+
+
+static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
+                                 const char *dir, int ap)
+{
+#ifdef DRIVER_TEST_UNIX
+       static unsigned int counter = 0;
+       struct sockaddr_un addr;
+       size_t len;
+
+       os_free(drv->own_socket_path);
+       if (dir) {
+               len = os_strlen(dir) + 30;
+               drv->own_socket_path = os_malloc(len);
+               if (drv->own_socket_path == NULL)
+                       return -1;
+               os_snprintf(drv->own_socket_path, len, "%s/%s-" MACSTR,
+                           dir, ap ? "AP" : "STA", MAC2STR(drv->own_addr));
+       } else {
+               drv->own_socket_path = os_malloc(100);
+               if (drv->own_socket_path == NULL)
+                       return -1;
+               os_snprintf(drv->own_socket_path, 100,
+                           "/tmp/wpa_supplicant_test-%d-%d",
+                           getpid(), counter++);
+       }
+
+       drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (drv->test_socket < 0) {
+               perror("socket(PF_UNIX)");
+               os_free(drv->own_socket_path);
+               drv->own_socket_path = NULL;
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
+       if (bind(drv->test_socket, (struct sockaddr *) &addr,
+                sizeof(addr)) < 0) {
+               perror("bind(PF_UNIX)");
+               close(drv->test_socket);
+               unlink(drv->own_socket_path);
+               os_free(drv->own_socket_path);
+               drv->own_socket_path = NULL;
+               return -1;
+       }
+
+       eloop_register_read_sock(drv->test_socket,
+                                wpa_driver_test_receive_unix, drv, NULL);
+
+       return 0;
+#else /* DRIVER_TEST_UNIX */
+       return -1;
+#endif /* DRIVER_TEST_UNIX */
+}
+
+
+static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv,
+                                     char *dst)
+{
+       char *pos;
+
+       pos = os_strchr(dst, ':');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+       wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos);
+
+       drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->test_socket < 0) {
+               perror("socket(PF_INET)");
+               return -1;
+       }
+
+       os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp));
+       drv->hostapd_addr_udp.sin_family = AF_INET;
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
+       {
+               int a[4];
+               u8 *pos;
+               sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
+               pos = (u8 *) &drv->hostapd_addr_udp.sin_addr;
+               *pos++ = a[0];
+               *pos++ = a[1];
+               *pos++ = a[2];
+               *pos++ = a[3];
+       }
+#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+       inet_aton(dst, &drv->hostapd_addr_udp.sin_addr);
+#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+       drv->hostapd_addr_udp.sin_port = htons(atoi(pos));
+
+       drv->hostapd_addr_udp_set = 1;
+
+       eloop_register_read_sock(drv->test_socket,
+                                wpa_driver_test_receive_unix, drv, NULL);
+
+       return 0;
+}
+
+
+static int wpa_driver_test_set_param(void *priv, const char *param)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       const char *pos;
+
+       wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param);
+       if (param == NULL)
+               return 0;
+
+       wpa_driver_test_close_test_socket(drv);
+
+#ifdef DRIVER_TEST_UNIX
+       pos = os_strstr(param, "test_socket=");
+       if (pos) {
+               const char *pos2;
+               size_t len;
+
+               pos += 12;
+               pos2 = os_strchr(pos, ' ');
+               if (pos2)
+                       len = pos2 - pos;
+               else
+                       len = os_strlen(pos);
+               if (len > sizeof(drv->hostapd_addr.sun_path))
+                       return -1;
+               os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr));
+               drv->hostapd_addr.sun_family = AF_UNIX;
+               os_memcpy(drv->hostapd_addr.sun_path, pos, len);
+               drv->hostapd_addr_set = 1;
+       }
+#endif /* DRIVER_TEST_UNIX */
+
+       pos = os_strstr(param, "test_dir=");
+       if (pos) {
+               char *end;
+               os_free(drv->test_dir);
+               drv->test_dir = os_strdup(pos + 9);
+               if (drv->test_dir == NULL)
+                       return -1;
+               end = os_strchr(drv->test_dir, ' ');
+               if (end)
+                       *end = '\0';
+               if (wpa_driver_test_attach(drv, drv->test_dir, 0))
+                       return -1;
+       } else {
+               pos = os_strstr(param, "test_udp=");
+               if (pos) {
+                       char *dst, *epos;
+                       dst = os_strdup(pos + 9);
+                       if (dst == NULL)
+                               return -1;
+                       epos = os_strchr(dst, ' ');
+                       if (epos)
+                               *epos = '\0';
+                       if (wpa_driver_test_attach_udp(drv, dst))
+                               return -1;
+                       os_free(dst);
+               } else if (wpa_driver_test_attach(drv, NULL, 0))
+                       return -1;
+       }
+
+       if (os_strstr(param, "use_associnfo=1")) {
+               wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events");
+               drv->use_associnfo = 1;
+       }
+
+#ifdef CONFIG_CLIENT_MLME
+       if (os_strstr(param, "use_mlme=1")) {
+               wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME");
+               drv->use_mlme = 1;
+       }
+#endif /* CONFIG_CLIENT_MLME */
+
+       return 0;
+}
+
+
+static const u8 * wpa_driver_test_get_mac_addr(void *priv)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       return drv->own_addr;
+}
+
+
+static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto,
+                                     const u8 *data, size_t data_len)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       char *msg;
+       size_t msg_len;
+       struct l2_ethhdr eth;
+       struct sockaddr *addr;
+       socklen_t alen;
+#ifdef DRIVER_TEST_UNIX
+       struct sockaddr_un addr_un;
+#endif /* DRIVER_TEST_UNIX */
+
+       wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len);
+
+       os_memset(&eth, 0, sizeof(eth));
+       os_memcpy(eth.h_dest, dest, ETH_ALEN);
+       os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN);
+       eth.h_proto = host_to_be16(proto);
+
+       msg_len = 6 + sizeof(eth) + data_len;
+       msg = os_malloc(msg_len);
+       if (msg == NULL)
+               return -1;
+       os_memcpy(msg, "EAPOL ", 6);
+       os_memcpy(msg + 6, &eth, sizeof(eth));
+       os_memcpy(msg + 6 + sizeof(eth), data, data_len);
+
+       if (os_memcmp(dest, dbss->bssid, ETH_ALEN) == 0 ||
+           drv->test_dir == NULL) {
+               if (drv->hostapd_addr_udp_set) {
+                       addr = (struct sockaddr *) &drv->hostapd_addr_udp;
+                       alen = sizeof(drv->hostapd_addr_udp);
+               } else {
+#ifdef DRIVER_TEST_UNIX
+                       addr = (struct sockaddr *) &drv->hostapd_addr;
+                       alen = sizeof(drv->hostapd_addr);
+#else /* DRIVER_TEST_UNIX */
+                       os_free(msg);
+                       return -1;
+#endif /* DRIVER_TEST_UNIX */
+               }
+       } else {
+#ifdef DRIVER_TEST_UNIX
+               struct stat st;
+               os_memset(&addr_un, 0, sizeof(addr_un));
+               addr_un.sun_family = AF_UNIX;
+               os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
+                           "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest));
+               if (stat(addr_un.sun_path, &st) < 0) {
+                       os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path),
+                                   "%s/AP-" MACSTR,
+                                   drv->test_dir, MAC2STR(dest));
+               }
+               addr = (struct sockaddr *) &addr_un;
+               alen = sizeof(addr_un);
+#else /* DRIVER_TEST_UNIX */
+               os_free(msg);
+               return -1;
+#endif /* DRIVER_TEST_UNIX */
+       }
+
+       if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) {
+               perror("sendmsg(test_socket)");
+               os_free(msg);
+               return -1;
+       }
+
+       os_free(msg);
+       return 0;
+}
+
+
+static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       os_memset(capa, 0, sizeof(*capa));
+       capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE |
+               WPA_DRIVER_CAPA_KEY_MGMT_FT |
+               WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+       capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 |
+               WPA_DRIVER_CAPA_ENC_WEP104 |
+               WPA_DRIVER_CAPA_ENC_TKIP |
+               WPA_DRIVER_CAPA_ENC_CCMP;
+       capa->auth = WPA_DRIVER_AUTH_OPEN |
+               WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
+       if (drv->use_mlme)
+               capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME;
+       capa->flags |= WPA_DRIVER_FLAGS_AP;
+       capa->max_scan_ssids = 2;
+       capa->max_remain_on_chan = 60000;
+
+       return 0;
+}
+
+
+static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr,
+                                             int protect_type,
+                                             int key_type)
+{
+       wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d",
+                  __func__, protect_type, key_type);
+
+       if (addr) {
+               wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR,
+                          __func__, MAC2STR(addr));
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_test_set_channel(void *priv,
+                                      enum hostapd_hw_mode phymode,
+                                      int chan, int freq)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d",
+                  __func__, phymode, chan, freq);
+       drv->current_freq = freq;
+       return 0;
+}
+
+
+static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr,
+                                       const u8 *supp_rates,
+                                       size_t supp_rates_len)
+{
+       wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
+       return 0;
+}
+
+
+static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr)
+{
+       wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr));
+       return 0;
+}
+
+
+static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid,
+                                   size_t ssid_len)
+{
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       return 0;
+}
+
+
+static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid)
+{
+       wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid));
+       return 0;
+}
+
+
+static void * wpa_driver_test_global_init(void)
+{
+       struct wpa_driver_test_global *global;
+
+       global = os_zalloc(sizeof(*global));
+       return global;
+}
+
+
+static void wpa_driver_test_global_deinit(void *priv)
+{
+       struct wpa_driver_test_global *global = priv;
+       os_free(global);
+}
+
+
+static struct wpa_interface_info *
+wpa_driver_test_get_interfaces(void *global_priv)
+{
+       /* struct wpa_driver_test_global *global = priv; */
+       struct wpa_interface_info *iface;
+
+       iface = os_zalloc(sizeof(*iface));
+       if (iface == NULL)
+               return iface;
+       iface->ifname = os_strdup("sta0");
+       iface->desc = os_strdup("test interface 0");
+       iface->drv_name = "test";
+       iface->next = os_zalloc(sizeof(*iface));
+       if (iface->next) {
+               iface->next->ifname = os_strdup("sta1");
+               iface->next->desc = os_strdup("test interface 1");
+               iface->next->drv_name = "test";
+       }
+
+       return iface;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+       struct hostapd_hw_modes *modes;
+       size_t i;
+
+       *num_modes = 3;
+       *flags = 0;
+       modes = os_zalloc(*num_modes * sizeof(struct hostapd_hw_modes));
+       if (modes == NULL)
+               return NULL;
+       modes[0].mode = HOSTAPD_MODE_IEEE80211G;
+       modes[0].num_channels = 11;
+       modes[0].num_rates = 12;
+       modes[0].channels =
+               os_zalloc(11 * sizeof(struct hostapd_channel_data));
+       modes[0].rates = os_zalloc(modes[0].num_rates * sizeof(int));
+       if (modes[0].channels == NULL || modes[0].rates == NULL)
+               goto fail;
+       for (i = 0; i < 11; i++) {
+               modes[0].channels[i].chan = i + 1;
+               modes[0].channels[i].freq = 2412 + 5 * i;
+               modes[0].channels[i].flag = 0;
+       }
+       modes[0].rates[0] = 10;
+       modes[0].rates[1] = 20;
+       modes[0].rates[2] = 55;
+       modes[0].rates[3] = 110;
+       modes[0].rates[4] = 60;
+       modes[0].rates[5] = 90;
+       modes[0].rates[6] = 120;
+       modes[0].rates[7] = 180;
+       modes[0].rates[8] = 240;
+       modes[0].rates[9] = 360;
+       modes[0].rates[10] = 480;
+       modes[0].rates[11] = 540;
+
+       modes[1].mode = HOSTAPD_MODE_IEEE80211B;
+       modes[1].num_channels = 11;
+       modes[1].num_rates = 4;
+       modes[1].channels =
+               os_zalloc(11 * sizeof(struct hostapd_channel_data));
+       modes[1].rates = os_zalloc(modes[1].num_rates * sizeof(int));
+       if (modes[1].channels == NULL || modes[1].rates == NULL)
+               goto fail;
+       for (i = 0; i < 11; i++) {
+               modes[1].channels[i].chan = i + 1;
+               modes[1].channels[i].freq = 2412 + 5 * i;
+               modes[1].channels[i].flag = 0;
+       }
+       modes[1].rates[0] = 10;
+       modes[1].rates[1] = 20;
+       modes[1].rates[2] = 55;
+       modes[1].rates[3] = 110;
+
+       modes[2].mode = HOSTAPD_MODE_IEEE80211A;
+       modes[2].num_channels = 1;
+       modes[2].num_rates = 8;
+       modes[2].channels = os_zalloc(sizeof(struct hostapd_channel_data));
+       modes[2].rates = os_zalloc(modes[2].num_rates * sizeof(int));
+       if (modes[2].channels == NULL || modes[2].rates == NULL)
+               goto fail;
+       modes[2].channels[0].chan = 60;
+       modes[2].channels[0].freq = 5300;
+       modes[2].channels[0].flag = 0;
+       modes[2].rates[0] = 60;
+       modes[2].rates[1] = 90;
+       modes[2].rates[2] = 120;
+       modes[2].rates[3] = 180;
+       modes[2].rates[4] = 240;
+       modes[2].rates[5] = 360;
+       modes[2].rates[6] = 480;
+       modes[2].rates[7] = 540;
+
+       return modes;
+
+fail:
+       if (modes) {
+               for (i = 0; i < *num_modes; i++) {
+                       os_free(modes[i].channels);
+                       os_free(modes[i].rates);
+               }
+               os_free(modes);
+       }
+       return NULL;
+}
+
+
+static int wpa_driver_test_set_freq(void *priv,
+                                   struct hostapd_freq_params *freq)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "test: set_freq %u MHz", freq->freq);
+       drv->current_freq = freq->freq;
+       return 0;
+}
+
+
+static int wpa_driver_test_send_action(void *priv, unsigned int freq,
+                                      const u8 *dst, const u8 *src,
+                                      const u8 *bssid,
+                                      const u8 *data, size_t data_len)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       int ret = -1;
+       u8 *buf;
+       struct ieee80211_hdr *hdr;
+
+       wpa_printf(MSG_DEBUG, "test: Send Action frame");
+
+       if ((drv->remain_on_channel_freq &&
+            freq != drv->remain_on_channel_freq) ||
+           (drv->remain_on_channel_freq == 0 &&
+            freq != (unsigned int) drv->current_freq)) {
+               wpa_printf(MSG_DEBUG, "test: Reject Action frame TX on "
+                          "unexpected channel: freq=%u MHz (current_freq=%u "
+                          "MHz, remain-on-channel freq=%u MHz)",
+                          freq, drv->current_freq,
+                          drv->remain_on_channel_freq);
+               return -1;
+       }
+
+       buf = os_zalloc(24 + data_len);
+       if (buf == NULL)
+               return ret;
+       os_memcpy(buf + 24, data, data_len);
+       hdr = (struct ieee80211_hdr *) buf;
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+       os_memcpy(hdr->addr1, dst, ETH_ALEN);
+       os_memcpy(hdr->addr2, src, ETH_ALEN);
+       os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+
+       ret = wpa_driver_test_send_mlme(priv, buf, 24 + data_len);
+       os_free(buf);
+       return ret;
+}
+
+
+static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_test_data *drv = eloop_ctx;
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "test: Remain-on-channel timeout");
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = drv->remain_on_channel_freq;
+       data.remain_on_channel.duration = drv->remain_on_channel_duration;
+       wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
+
+       drv->remain_on_channel_freq = 0;
+}
+
+
+static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq,
+                                            unsigned int duration)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "%s(freq=%u, duration=%u)",
+                  __func__, freq, duration);
+       if (drv->remain_on_channel_freq &&
+           drv->remain_on_channel_freq != freq) {
+               wpa_printf(MSG_DEBUG, "test: Refuse concurrent "
+                          "remain_on_channel request");
+               return -1;
+       }
+
+       drv->remain_on_channel_freq = freq;
+       drv->remain_on_channel_duration = duration;
+       eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
+       eloop_register_timeout(duration / 1000, (duration % 1000) * 1000,
+                              test_remain_on_channel_timeout, drv, NULL);
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = freq;
+       data.remain_on_channel.duration = duration;
+       wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
+
+       return 0;
+}
+
+
+static int wpa_driver_test_cancel_remain_on_channel(void *priv)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+       if (!drv->remain_on_channel_freq)
+               return -1;
+       drv->remain_on_channel_freq = 0;
+       eloop_cancel_timeout(test_remain_on_channel_timeout, drv, NULL);
+       return 0;
+}
+
+
+static int wpa_driver_test_probe_req_report(void *priv, int report)
+{
+       struct test_driver_bss *dbss = priv;
+       struct wpa_driver_test_data *drv = dbss->drv;
+       wpa_printf(MSG_DEBUG, "%s(report=%d)", __func__, report);
+       drv->probe_req_report = report;
+       return 0;
+}
+
+
+const struct wpa_driver_ops wpa_driver_test_ops = {
+       "test",
+       "wpa_supplicant test driver",
+       .hapd_init = test_driver_init,
+       .hapd_deinit = wpa_driver_test_deinit,
+       .hapd_send_eapol = test_driver_send_eapol,
+       .send_mlme = wpa_driver_test_send_mlme,
+       .set_generic_elem = test_driver_set_generic_elem,
+       .sta_deauth = test_driver_sta_deauth,
+       .sta_disassoc = test_driver_sta_disassoc,
+       .get_hw_feature_data = wpa_driver_test_get_hw_feature_data,
+       .if_add = test_driver_if_add,
+       .if_remove = test_driver_if_remove,
+       .valid_bss_mask = test_driver_valid_bss_mask,
+       .hapd_set_ssid = test_driver_set_ssid,
+       .set_privacy = test_driver_set_privacy,
+       .set_sta_vlan = test_driver_set_sta_vlan,
+       .sta_add = test_driver_sta_add,
+       .send_ether = test_driver_send_ether,
+       .set_ap_wps_ie = test_driver_set_ap_wps_ie,
+       .get_bssid = wpa_driver_test_get_bssid,
+       .get_ssid = wpa_driver_test_get_ssid,
+       .set_key = wpa_driver_test_set_key,
+       .deinit = wpa_driver_test_deinit,
+       .set_param = wpa_driver_test_set_param,
+       .deauthenticate = wpa_driver_test_deauthenticate,
+       .disassociate = wpa_driver_test_disassociate,
+       .associate = wpa_driver_test_associate,
+       .get_capa = wpa_driver_test_get_capa,
+       .get_mac_addr = wpa_driver_test_get_mac_addr,
+       .send_eapol = wpa_driver_test_send_eapol,
+       .mlme_setprotection = wpa_driver_test_mlme_setprotection,
+       .set_channel = wpa_driver_test_set_channel,
+       .set_ssid = wpa_driver_test_set_ssid,
+       .set_bssid = wpa_driver_test_set_bssid,
+       .mlme_add_sta = wpa_driver_test_mlme_add_sta,
+       .mlme_remove_sta = wpa_driver_test_mlme_remove_sta,
+       .get_scan_results2 = wpa_driver_test_get_scan_results2,
+       .global_init = wpa_driver_test_global_init,
+       .global_deinit = wpa_driver_test_global_deinit,
+       .init2 = wpa_driver_test_init2,
+       .get_interfaces = wpa_driver_test_get_interfaces,
+       .scan2 = wpa_driver_test_scan,
+       .set_freq = wpa_driver_test_set_freq,
+       .send_action = wpa_driver_test_send_action,
+       .remain_on_channel = wpa_driver_test_remain_on_channel,
+       .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
+       .probe_req_report = wpa_driver_test_probe_req_report,
+};
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
new file mode 100644 (file)
index 0000000..2614f23
--- /dev/null
@@ -0,0 +1,2189 @@
+/*
+ * Driver interaction with generic Linux Wireless Extensions
+ * Copyright (c) 2003-2010, 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.
+ *
+ * This file implements a driver interface for the Linux Wireless Extensions.
+ * When used with WE-18 or newer, this interface can be used as-is with number
+ * of drivers. In addition to this, some of the common functions in this file
+ * can be used by other driver interface implementations that use generic WE
+ * ioctls, but require private ioctls for some of the functionality.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <net/if_arp.h>
+
+#include "wireless_copy.h"
+#include "common.h"
+#include "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_common.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+#include "linux_ioctl.h"
+#include "driver.h"
+#include "driver_wext.h"
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv);
+static int wpa_driver_wext_get_range(void *priv);
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv);
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg);
+
+
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+                                  int idx, u32 value)
+{
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.param.flags = idx & IW_AUTH_INDEX;
+       iwr.u.param.value = value;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) {
+               if (errno != EOPNOTSUPP) {
+                       wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d "
+                                  "value 0x%x) failed: %s)",
+                                  idx, value, strerror(errno));
+               }
+               ret = errno == EOPNOTSUPP ? -2 : -1;
+       }
+
+       return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: Buffer for BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
+               perror("ioctl[SIOCGIWAP]");
+               ret = -1;
+       }
+       os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
+
+       return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @bssid: BSSID
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.ap_addr.sa_family = ARPHRD_ETHER;
+       if (bssid)
+               os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN);
+       else
+               os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
+               perror("ioctl[SIOCSIWAP]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+/**
+ * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: Buffer for the SSID; must be at least 32 bytes long
+ * Returns: SSID length on success, -1 on failure
+ */
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.essid.pointer = (caddr_t) ssid;
+       iwr.u.essid.length = 32;
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCGIWESSID]");
+               ret = -1;
+       } else {
+               ret = iwr.u.essid.length;
+               if (ret > 32)
+                       ret = 32;
+               /* Some drivers include nul termination in the SSID, so let's
+                * remove it here before further processing. WE-21 changes this
+                * to explicitly require the length _not_ to include nul
+                * termination. */
+               if (ret > 0 && ssid[ret - 1] == '\0' &&
+                   drv->we_version_compiled < 21)
+                       ret--;
+       }
+
+       return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @ssid: SSID
+ * @ssid_len: Length of SSID (0..32)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+       char buf[33];
+
+       if (ssid_len > 32)
+               return -1;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       /* flags: 1 = ESSID is active, 0 = not (promiscuous) */
+       iwr.u.essid.flags = (ssid_len != 0);
+       os_memset(buf, 0, sizeof(buf));
+       os_memcpy(buf, ssid, ssid_len);
+       iwr.u.essid.pointer = (caddr_t) buf;
+       if (drv->we_version_compiled < 21) {
+               /* For historic reasons, set SSID length to include one extra
+                * character, C string nul termination, even though SSID is
+                * really an octet string that should not be presented as a C
+                * string. Some Linux drivers decrement the length by one and
+                * can thus end up missing the last octet of the SSID if the
+                * length is not incremented here. WE-21 changes this to
+                * explicitly require the length _not_ to include nul
+                * termination. */
+               if (ssid_len)
+                       ssid_len++;
+       }
+       iwr.u.essid.length = ssid_len;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
+               perror("ioctl[SIOCSIWESSID]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @freq: Frequency in MHz
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_freq(void *priv, int freq)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.freq.m = freq * 100000;
+       iwr.u.freq.e = 1;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
+               perror("ioctl[SIOCSIWFREQ]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+static void
+wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
+{
+       union wpa_event_data data;
+
+       wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'",
+                  custom);
+
+       os_memset(&data, 0, sizeof(data));
+       /* Host AP driver */
+       if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) {
+               data.michael_mic_failure.unicast =
+                       os_strstr(custom, " unicast ") != NULL;
+               /* TODO: parse parameters(?) */
+               wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+       } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) {
+               char *spos;
+               int bytes;
+               u8 *req_ies = NULL, *resp_ies = NULL;
+
+               spos = custom + 17;
+
+               bytes = strspn(spos, "0123456789abcdefABCDEF");
+               if (!bytes || (bytes & 1))
+                       return;
+               bytes /= 2;
+
+               req_ies = os_malloc(bytes);
+               if (req_ies == NULL ||
+                   hexstr2bin(spos, req_ies, bytes) < 0)
+                       goto done;
+               data.assoc_info.req_ies = req_ies;
+               data.assoc_info.req_ies_len = bytes;
+
+               spos += bytes * 2;
+
+               data.assoc_info.resp_ies = NULL;
+               data.assoc_info.resp_ies_len = 0;
+
+               if (os_strncmp(spos, " RespIEs=", 9) == 0) {
+                       spos += 9;
+
+                       bytes = strspn(spos, "0123456789abcdefABCDEF");
+                       if (!bytes || (bytes & 1))
+                               goto done;
+                       bytes /= 2;
+
+                       resp_ies = os_malloc(bytes);
+                       if (resp_ies == NULL ||
+                           hexstr2bin(spos, resp_ies, bytes) < 0)
+                               goto done;
+                       data.assoc_info.resp_ies = resp_ies;
+                       data.assoc_info.resp_ies_len = bytes;
+               }
+
+               wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data);
+
+       done:
+               os_free(resp_ies);
+               os_free(req_ies);
+#ifdef CONFIG_PEERKEY
+       } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) {
+               if (hwaddr_aton(custom + 17, data.stkstart.peer)) {
+                       wpa_printf(MSG_DEBUG, "WEXT: unrecognized "
+                                  "STKSTART.request '%s'", custom + 17);
+                       return;
+               }
+               wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
+#endif /* CONFIG_PEERKEY */
+       }
+}
+
+
+static int wpa_driver_wext_event_wireless_michaelmicfailure(
+       void *ctx, const char *ev, size_t len)
+{
+       const struct iw_michaelmicfailure *mic;
+       union wpa_event_data data;
+
+       if (len < sizeof(*mic))
+               return -1;
+
+       mic = (const struct iw_michaelmicfailure *) ev;
+
+       wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: "
+                  "flags=0x%x src_addr=" MACSTR, mic->flags,
+                  MAC2STR(mic->src_addr.sa_data));
+
+       os_memset(&data, 0, sizeof(data));
+       data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP);
+       wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+
+       return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_pmkidcand(
+       struct wpa_driver_wext_data *drv, const char *ev, size_t len)
+{
+       const struct iw_pmkid_cand *cand;
+       union wpa_event_data data;
+       const u8 *addr;
+
+       if (len < sizeof(*cand))
+               return -1;
+
+       cand = (const struct iw_pmkid_cand *) ev;
+       addr = (const u8 *) cand->bssid.sa_data;
+
+       wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: "
+                  "flags=0x%x index=%d bssid=" MACSTR, cand->flags,
+                  cand->index, MAC2STR(addr));
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN);
+       data.pmkid_candidate.index = cand->index;
+       data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH;
+       wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+
+       return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocreqie(
+       struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+       if (len < 0)
+               return -1;
+
+       wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev,
+                   len);
+       os_free(drv->assoc_req_ies);
+       drv->assoc_req_ies = os_malloc(len);
+       if (drv->assoc_req_ies == NULL) {
+               drv->assoc_req_ies_len = 0;
+               return -1;
+       }
+       os_memcpy(drv->assoc_req_ies, ev, len);
+       drv->assoc_req_ies_len = len;
+
+       return 0;
+}
+
+
+static int wpa_driver_wext_event_wireless_assocrespie(
+       struct wpa_driver_wext_data *drv, const char *ev, int len)
+{
+       if (len < 0)
+               return -1;
+
+       wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev,
+                   len);
+       os_free(drv->assoc_resp_ies);
+       drv->assoc_resp_ies = os_malloc(len);
+       if (drv->assoc_resp_ies == NULL) {
+               drv->assoc_resp_ies_len = 0;
+               return -1;
+       }
+       os_memcpy(drv->assoc_resp_ies, ev, len);
+       drv->assoc_resp_ies_len = len;
+
+       return 0;
+}
+
+
+static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv)
+{
+       union wpa_event_data data;
+
+       if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       if (drv->assoc_req_ies) {
+               data.assoc_info.req_ies = drv->assoc_req_ies;
+               data.assoc_info.req_ies_len = drv->assoc_req_ies_len;
+       }
+       if (drv->assoc_resp_ies) {
+               data.assoc_info.resp_ies = drv->assoc_resp_ies;
+               data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len;
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data);
+
+       os_free(drv->assoc_req_ies);
+       drv->assoc_req_ies = NULL;
+       os_free(drv->assoc_resp_ies);
+       drv->assoc_resp_ies = NULL;
+}
+
+
+static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv,
+                                          char *data, int len)
+{
+       struct iw_event iwe_buf, *iwe = &iwe_buf;
+       char *pos, *end, *custom, *buf;
+
+       pos = data;
+       end = data + len;
+
+       while (pos + IW_EV_LCP_LEN <= end) {
+               /* Event data may be unaligned, so make a local, aligned copy
+                * before processing. */
+               os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+               wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d",
+                          iwe->cmd, iwe->len);
+               if (iwe->len <= IW_EV_LCP_LEN)
+                       return;
+
+               custom = pos + IW_EV_POINT_LEN;
+               if (drv->we_version_compiled > 18 &&
+                   (iwe->cmd == IWEVMICHAELMICFAILURE ||
+                    iwe->cmd == IWEVCUSTOM ||
+                    iwe->cmd == IWEVASSOCREQIE ||
+                    iwe->cmd == IWEVASSOCRESPIE ||
+                    iwe->cmd == IWEVPMKIDCAND)) {
+                       /* WE-19 removed the pointer from struct iw_point */
+                       char *dpos = (char *) &iwe_buf.u.data.length;
+                       int dlen = dpos - (char *) &iwe_buf;
+                       os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+                                 sizeof(struct iw_event) - dlen);
+               } else {
+                       os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+                       custom += IW_EV_POINT_OFF;
+               }
+
+               switch (iwe->cmd) {
+               case SIOCGIWAP:
+                       wpa_printf(MSG_DEBUG, "Wireless event: new AP: "
+                                  MACSTR,
+                                  MAC2STR((u8 *) iwe->u.ap_addr.sa_data));
+                       if (is_zero_ether_addr(
+                                   (const u8 *) iwe->u.ap_addr.sa_data) ||
+                           os_memcmp(iwe->u.ap_addr.sa_data,
+                                     "\x44\x44\x44\x44\x44\x44", ETH_ALEN) ==
+                           0) {
+                               os_free(drv->assoc_req_ies);
+                               drv->assoc_req_ies = NULL;
+                               os_free(drv->assoc_resp_ies);
+                               drv->assoc_resp_ies = NULL;
+                               wpa_supplicant_event(drv->ctx, EVENT_DISASSOC,
+                                                    NULL);
+                       
+                       } else {
+                               wpa_driver_wext_event_assoc_ies(drv);
+                               wpa_supplicant_event(drv->ctx, EVENT_ASSOC,
+                                                    NULL);
+                       }
+                       break;
+               case IWEVMICHAELMICFAILURE:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVMICHAELMICFAILURE length");
+                               return;
+                       }
+                       wpa_driver_wext_event_wireless_michaelmicfailure(
+                               drv->ctx, custom, iwe->u.data.length);
+                       break;
+               case IWEVCUSTOM:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVCUSTOM length");
+                               return;
+                       }
+                       buf = os_malloc(iwe->u.data.length + 1);
+                       if (buf == NULL)
+                               return;
+                       os_memcpy(buf, custom, iwe->u.data.length);
+                       buf[iwe->u.data.length] = '\0';
+                       wpa_driver_wext_event_wireless_custom(drv->ctx, buf);
+                       os_free(buf);
+                       break;
+               case SIOCGIWSCAN:
+                       drv->scan_complete_events = 1;
+                       eloop_cancel_timeout(wpa_driver_wext_scan_timeout,
+                                            drv, drv->ctx);
+                       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS,
+                                            NULL);
+                       break;
+               case IWEVASSOCREQIE:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVASSOCREQIE length");
+                               return;
+                       }
+                       wpa_driver_wext_event_wireless_assocreqie(
+                               drv, custom, iwe->u.data.length);
+                       break;
+               case IWEVASSOCRESPIE:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVASSOCRESPIE length");
+                               return;
+                       }
+                       wpa_driver_wext_event_wireless_assocrespie(
+                               drv, custom, iwe->u.data.length);
+                       break;
+               case IWEVPMKIDCAND:
+                       if (custom + iwe->u.data.length > end) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Invalid "
+                                          "IWEVPMKIDCAND length");
+                               return;
+                       }
+                       wpa_driver_wext_event_wireless_pmkidcand(
+                               drv, custom, iwe->u.data.length);
+                       break;
+               }
+
+               pos += iwe->len;
+       }
+}
+
+
+static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv,
+                                      char *buf, size_t len, int del)
+{
+       union wpa_event_data event;
+
+       os_memset(&event, 0, sizeof(event));
+       if (len > sizeof(event.interface_status.ifname))
+               len = sizeof(event.interface_status.ifname) - 1;
+       os_memcpy(event.interface_status.ifname, buf, len);
+       event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
+               EVENT_INTERFACE_ADDED;
+
+       wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
+                  del ? "DEL" : "NEW",
+                  event.interface_status.ifname,
+                  del ? "removed" : "added");
+
+       if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) {
+               if (del)
+                       drv->if_removed = 1;
+               else
+                       drv->if_removed = 0;
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv,
+                                     u8 *buf, size_t len)
+{
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_IFNAME) {
+                       if (os_strcmp(((char *) attr) + rta_len, drv->ifname)
+                           == 0)
+                               return 1;
+                       else
+                               break;
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+
+       return 0;
+}
+
+
+static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv,
+                                      int ifindex, u8 *buf, size_t len)
+{
+       if (drv->ifindex == ifindex || drv->ifindex2 == ifindex)
+               return 1;
+
+       if (drv->if_removed && wpa_driver_wext_own_ifname(drv, buf, len)) {
+               drv->ifindex = if_nametoindex(drv->ifname);
+               wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed "
+                          "interface");
+               wpa_driver_wext_finish_drv_init(drv);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void wpa_driver_wext_event_rtm_newlink(void *ctx, struct ifinfomsg *ifi,
+                                             u8 *buf, size_t len)
+{
+       struct wpa_driver_wext_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, buf, len)) {
+               wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d",
+                          ifi->ifi_index);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
+                  "(%s%s%s%s)",
+                  drv->operstate, ifi->ifi_flags,
+                  (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+                  (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+                  (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+                  (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+       /*
+        * Some drivers send the association event before the operup event--in
+        * this case, lifting operstate in wpa_driver_wext_set_operstate()
+        * fails. This will hit us when wpa_supplicant does not need to do
+        * IEEE 802.1X authentication
+        */
+       if (drv->operstate == 1 &&
+           (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
+           !(ifi->ifi_flags & IFF_RUNNING))
+               netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                                      -1, IF_OPER_UP);
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_WIRELESS) {
+                       wpa_driver_wext_event_wireless(
+                               drv, ((char *) attr) + rta_len,
+                               attr->rta_len - rta_len);
+               } else if (attr->rta_type == IFLA_IFNAME) {
+                       wpa_driver_wext_event_link(drv,
+                                                  ((char *) attr) + rta_len,
+                                                  attr->rta_len - rta_len, 0);
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+}
+
+
+static void wpa_driver_wext_event_rtm_dellink(void *ctx, struct ifinfomsg *ifi,
+                                             u8 *buf, size_t len)
+{
+       struct wpa_driver_wext_data *drv = ctx;
+       int attrlen, rta_len;
+       struct rtattr *attr;
+
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+
+       rta_len = RTA_ALIGN(sizeof(struct rtattr));
+       while (RTA_OK(attr, attrlen)) {
+               if (attr->rta_type == IFLA_IFNAME) {
+                       wpa_driver_wext_event_link(drv,
+                                                  ((char *) attr) + rta_len,
+                                                  attr->rta_len - rta_len, 1);
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+}
+
+
+/**
+ * wpa_driver_wext_init - Initialize WE driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * Returns: Pointer to private data, %NULL on failure
+ */
+void * wpa_driver_wext_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_wext_data *drv;
+       struct netlink_config *cfg;
+       char path[128];
+       struct stat buf;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->ctx = ctx;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+       os_snprintf(path, sizeof(path), "/sys/class/net/%s/phy80211", ifname);
+       if (stat(path, &buf) == 0) {
+               wpa_printf(MSG_DEBUG, "WEXT: cfg80211-based driver detected");
+               drv->cfg80211 = 1;
+       }
+
+       drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (drv->ioctl_sock < 0) {
+               perror("socket(PF_INET,SOCK_DGRAM)");
+               goto err1;
+       }
+
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               goto err1;
+       cfg->ctx = drv;
+       cfg->newlink_cb = wpa_driver_wext_event_rtm_newlink;
+       cfg->dellink_cb = wpa_driver_wext_event_rtm_dellink;
+       drv->netlink = netlink_init(cfg);
+       if (drv->netlink == NULL) {
+               os_free(cfg);
+               goto err2;
+       }
+
+       drv->mlme_sock = -1;
+
+       if (wpa_driver_wext_finish_drv_init(drv) < 0)
+               goto err3;
+
+       wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 1);
+
+       return drv;
+
+err3:
+       netlink_deinit(drv->netlink);
+err2:
+       close(drv->ioctl_sock);
+err1:
+       os_free(drv);
+       return NULL;
+}
+
+
+static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv)
+{
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1) < 0)
+               return -1;
+
+       /*
+        * Make sure that the driver does not have any obsolete PMKID entries.
+        */
+       wpa_driver_wext_flush_pmkid(drv);
+
+       if (wpa_driver_wext_set_mode(drv, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not configure driver to use "
+                          "managed mode");
+               /* Try to use it anyway */
+       }
+
+       wpa_driver_wext_get_range(drv);
+
+       /*
+        * Unlock the driver's BSSID and force to a random SSID to clear any
+        * previous association the driver might have when the supplicant
+        * starts up.
+        */
+       wpa_driver_wext_disconnect(drv);
+
+       drv->ifindex = if_nametoindex(drv->ifname);
+
+       if (os_strncmp(drv->ifname, "wlan", 4) == 0) {
+               /*
+                * Host AP driver may use both wlan# and wifi# interface in
+                * wireless events. Since some of the versions included WE-18
+                * support, let's add the alternative ifindex also from
+                * driver_wext.c for the time being. This may be removed at
+                * some point once it is believed that old versions of the
+                * driver are not in use anymore.
+                */
+               char ifname2[IFNAMSIZ + 1];
+               os_strlcpy(ifname2, drv->ifname, sizeof(ifname2));
+               os_memcpy(ifname2, "wifi", 4);
+               wpa_driver_wext_alternative_ifindex(drv, ifname2);
+       }
+
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex,
+                              1, IF_OPER_DORMANT);
+
+       return 0;
+}
+
+
+/**
+ * wpa_driver_wext_deinit - Deinitialize WE driver interface
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_wext_init().
+ */
+void wpa_driver_wext_deinit(void *priv)
+{
+       struct wpa_driver_wext_data *drv = priv;
+
+       wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, 0);
+
+       eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+
+       /*
+        * Clear possibly configured driver parameters in order to make it
+        * easier to use the driver after wpa_supplicant has been terminated.
+        */
+       wpa_driver_wext_disconnect(drv);
+
+       netlink_send_oper_ifla(drv->netlink, drv->ifindex, 0, IF_OPER_UP);
+       netlink_deinit(drv->netlink);
+
+       if (drv->mlme_sock >= 0)
+               eloop_unregister_read_sock(drv->mlme_sock);
+
+       (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0);
+
+       close(drv->ioctl_sock);
+       if (drv->mlme_sock >= 0)
+               close(drv->mlme_sock);
+       os_free(drv->assoc_req_ies);
+       os_free(drv->assoc_resp_ies);
+       os_free(drv);
+}
+
+
+/**
+ * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Unused
+ * @timeout_ctx: ctx argument given to wpa_driver_wext_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+/**
+ * wpa_driver_wext_scan - Request the driver to initiate scan
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @param: Scan parameters (specific SSID to scan for (ProbeReq), etc.)
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0, timeout;
+       struct iw_scan_req req;
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+
+       if (ssid_len > IW_ESSID_MAX_SIZE) {
+               wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)",
+                          __FUNCTION__, (unsigned long) ssid_len);
+               return -1;
+       }
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       if (ssid && ssid_len) {
+               os_memset(&req, 0, sizeof(req));
+               req.essid_len = ssid_len;
+               req.bssid.sa_family = ARPHRD_ETHER;
+               os_memset(req.bssid.sa_data, 0xff, ETH_ALEN);
+               os_memcpy(req.essid, ssid, ssid_len);
+               iwr.u.data.pointer = (caddr_t) &req;
+               iwr.u.data.length = sizeof(req);
+               iwr.u.data.flags = IW_SCAN_THIS_ESSID;
+       }
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
+               perror("ioctl[SIOCSIWSCAN]");
+               ret = -1;
+       }
+
+       /* Not all drivers generate "scan completed" wireless event, so try to
+        * read results after a timeout. */
+       timeout = 5;
+       if (drv->scan_complete_events) {
+               /*
+                * The driver seems to deliver SIOCGIWSCAN events to notify
+                * when scan is complete, so use longer timeout to avoid race
+                * conditions with scanning and following association request.
+                */
+               timeout = 30;
+       }
+       wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+                  "seconds", ret, timeout);
+       eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv,
+                              drv->ctx);
+
+       return ret;
+}
+
+
+static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
+                                   size_t *len)
+{
+       struct iwreq iwr;
+       u8 *res_buf;
+       size_t res_buf_len;
+
+       res_buf_len = IW_SCAN_MAX_DATA;
+       for (;;) {
+               res_buf = os_malloc(res_buf_len);
+               if (res_buf == NULL)
+                       return NULL;
+               os_memset(&iwr, 0, sizeof(iwr));
+               os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+               iwr.u.data.pointer = res_buf;
+               iwr.u.data.length = res_buf_len;
+
+               if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0)
+                       break;
+
+               if (errno == E2BIG && res_buf_len < 65535) {
+                       os_free(res_buf);
+                       res_buf = NULL;
+                       res_buf_len *= 2;
+                       if (res_buf_len > 65535)
+                               res_buf_len = 65535; /* 16-bit length field */
+                       wpa_printf(MSG_DEBUG, "Scan results did not fit - "
+                                  "trying larger buffer (%lu bytes)",
+                                  (unsigned long) res_buf_len);
+               } else {
+                       perror("ioctl[SIOCGIWSCAN]");
+                       os_free(res_buf);
+                       return NULL;
+               }
+       }
+
+       if (iwr.u.data.length > res_buf_len) {
+               os_free(res_buf);
+               return NULL;
+       }
+       *len = iwr.u.data.length;
+
+       return res_buf;
+}
+
+
+/*
+ * Data structure for collecting WEXT scan results. This is needed to allow
+ * the various methods of reporting IEs to be combined into a single IE buffer.
+ */
+struct wext_scan_data {
+       struct wpa_scan_res res;
+       u8 *ie;
+       size_t ie_len;
+       u8 ssid[32];
+       size_t ssid_len;
+       int maxrate;
+};
+
+
+static void wext_get_scan_mode(struct iw_event *iwe,
+                              struct wext_scan_data *res)
+{
+       if (iwe->u.mode == IW_MODE_ADHOC)
+               res->res.caps |= IEEE80211_CAP_IBSS;
+       else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA)
+               res->res.caps |= IEEE80211_CAP_ESS;
+}
+
+
+static void wext_get_scan_ssid(struct iw_event *iwe,
+                              struct wext_scan_data *res, char *custom,
+                              char *end)
+{
+       int ssid_len = iwe->u.essid.length;
+       if (custom + ssid_len > end)
+               return;
+       if (iwe->u.essid.flags &&
+           ssid_len > 0 &&
+           ssid_len <= IW_ESSID_MAX_SIZE) {
+               os_memcpy(res->ssid, custom, ssid_len);
+               res->ssid_len = ssid_len;
+       }
+}
+
+
+static void wext_get_scan_freq(struct iw_event *iwe,
+                              struct wext_scan_data *res)
+{
+       int divi = 1000000, i;
+
+       if (iwe->u.freq.e == 0) {
+               /*
+                * Some drivers do not report frequency, but a channel.
+                * Try to map this to frequency by assuming they are using
+                * IEEE 802.11b/g.  But don't overwrite a previously parsed
+                * frequency if the driver sends both frequency and channel,
+                * since the driver may be sending an A-band channel that we
+                * don't handle here.
+                */
+
+               if (res->res.freq)
+                       return;
+
+               if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) {
+                       res->res.freq = 2407 + 5 * iwe->u.freq.m;
+                       return;
+               } else if (iwe->u.freq.m == 14) {
+                       res->res.freq = 2484;
+                       return;
+               }
+       }
+
+       if (iwe->u.freq.e > 6) {
+               wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID="
+                          MACSTR " m=%d e=%d)",
+                          MAC2STR(res->res.bssid), iwe->u.freq.m,
+                          iwe->u.freq.e);
+               return;
+       }
+
+       for (i = 0; i < iwe->u.freq.e; i++)
+               divi /= 10;
+       res->res.freq = iwe->u.freq.m / divi;
+}
+
+
+static void wext_get_scan_qual(struct iw_event *iwe,
+                              struct wext_scan_data *res)
+{
+       res->res.qual = iwe->u.qual.qual;
+       res->res.noise = iwe->u.qual.noise;
+       res->res.level = iwe->u.qual.level;
+       if (iwe->u.qual.updated & IW_QUAL_QUAL_INVALID)
+               res->res.flags |= WPA_SCAN_QUAL_INVALID;
+       if (iwe->u.qual.updated & IW_QUAL_LEVEL_INVALID)
+               res->res.flags |= WPA_SCAN_LEVEL_INVALID;
+       if (iwe->u.qual.updated & IW_QUAL_NOISE_INVALID)
+               res->res.flags |= WPA_SCAN_NOISE_INVALID;
+       if (iwe->u.qual.updated & IW_QUAL_DBM)
+               res->res.flags |= WPA_SCAN_LEVEL_DBM;
+}
+
+
+static void wext_get_scan_encode(struct iw_event *iwe,
+                                struct wext_scan_data *res)
+{
+       if (!(iwe->u.data.flags & IW_ENCODE_DISABLED))
+               res->res.caps |= IEEE80211_CAP_PRIVACY;
+}
+
+
+static void wext_get_scan_rate(struct iw_event *iwe,
+                              struct wext_scan_data *res, char *pos,
+                              char *end)
+{
+       int maxrate;
+       char *custom = pos + IW_EV_LCP_LEN;
+       struct iw_param p;
+       size_t clen;
+
+       clen = iwe->len;
+       if (custom + clen > end)
+               return;
+       maxrate = 0;
+       while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) {
+               /* Note: may be misaligned, make a local, aligned copy */
+               os_memcpy(&p, custom, sizeof(struct iw_param));
+               if (p.value > maxrate)
+                       maxrate = p.value;
+               clen -= sizeof(struct iw_param);
+               custom += sizeof(struct iw_param);
+       }
+
+       /* Convert the maxrate from WE-style (b/s units) to
+        * 802.11 rates (500000 b/s units).
+        */
+       res->maxrate = maxrate / 500000;
+}
+
+
+static void wext_get_scan_iwevgenie(struct iw_event *iwe,
+                                   struct wext_scan_data *res, char *custom,
+                                   char *end)
+{
+       char *genie, *gpos, *gend;
+       u8 *tmp;
+
+       if (iwe->u.data.length == 0)
+               return;
+
+       gpos = genie = custom;
+       gend = genie + iwe->u.data.length;
+       if (gend > end) {
+               wpa_printf(MSG_INFO, "IWEVGENIE overflow");
+               return;
+       }
+
+       tmp = os_realloc(res->ie, res->ie_len + gend - gpos);
+       if (tmp == NULL)
+               return;
+       os_memcpy(tmp + res->ie_len, gpos, gend - gpos);
+       res->ie = tmp;
+       res->ie_len += gend - gpos;
+}
+
+
+static void wext_get_scan_custom(struct iw_event *iwe,
+                                struct wext_scan_data *res, char *custom,
+                                char *end)
+{
+       size_t clen;
+       u8 *tmp;
+
+       clen = iwe->u.data.length;
+       if (custom + clen > end)
+               return;
+
+       if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) {
+               char *spos;
+               int bytes;
+               spos = custom + 7;
+               bytes = custom + clen - spos;
+               if (bytes & 1 || bytes == 0)
+                       return;
+               bytes /= 2;
+               tmp = os_realloc(res->ie, res->ie_len + bytes);
+               if (tmp == NULL)
+                       return;
+               res->ie = tmp;
+               if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+                       return;
+               res->ie_len += bytes;
+       } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) {
+               char *spos;
+               int bytes;
+               spos = custom + 7;
+               bytes = custom + clen - spos;
+               if (bytes & 1 || bytes == 0)
+                       return;
+               bytes /= 2;
+               tmp = os_realloc(res->ie, res->ie_len + bytes);
+               if (tmp == NULL)
+                       return;
+               res->ie = tmp;
+               if (hexstr2bin(spos, tmp + res->ie_len, bytes) < 0)
+                       return;
+               res->ie_len += bytes;
+       } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) {
+               char *spos;
+               int bytes;
+               u8 bin[8];
+               spos = custom + 4;
+               bytes = custom + clen - spos;
+               if (bytes != 16) {
+                       wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes);
+                       return;
+               }
+               bytes /= 2;
+               if (hexstr2bin(spos, bin, bytes) < 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Invalid TSF value");
+                       return;
+               }
+               res->res.tsf += WPA_GET_BE64(bin);
+       }
+}
+
+
+static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd)
+{
+       return drv->we_version_compiled > 18 &&
+               (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE ||
+                cmd == IWEVGENIE || cmd == IWEVCUSTOM);
+}
+
+
+static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res,
+                                          struct wext_scan_data *data)
+{
+       struct wpa_scan_res **tmp;
+       struct wpa_scan_res *r;
+       size_t extra_len;
+       u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL;
+
+       /* Figure out whether we need to fake any IEs */
+       pos = data->ie;
+       end = pos + data->ie_len;
+       while (pos && pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_SSID)
+                       ssid_ie = pos;
+               else if (pos[0] == WLAN_EID_SUPP_RATES)
+                       rate_ie = pos;
+               else if (pos[0] == WLAN_EID_EXT_SUPP_RATES)
+                       rate_ie = pos;
+               pos += 2 + pos[1];
+       }
+
+       extra_len = 0;
+       if (ssid_ie == NULL)
+               extra_len += 2 + data->ssid_len;
+       if (rate_ie == NULL && data->maxrate)
+               extra_len += 3;
+
+       r = os_zalloc(sizeof(*r) + extra_len + data->ie_len);
+       if (r == NULL)
+               return;
+       os_memcpy(r, &data->res, sizeof(*r));
+       r->ie_len = extra_len + data->ie_len;
+       pos = (u8 *) (r + 1);
+       if (ssid_ie == NULL) {
+               /*
+                * Generate a fake SSID IE since the driver did not report
+                * a full IE list.
+                */
+               *pos++ = WLAN_EID_SSID;
+               *pos++ = data->ssid_len;
+               os_memcpy(pos, data->ssid, data->ssid_len);
+               pos += data->ssid_len;
+       }
+       if (rate_ie == NULL && data->maxrate) {
+               /*
+                * Generate a fake Supported Rates IE since the driver did not
+                * report a full IE list.
+                */
+               *pos++ = WLAN_EID_SUPP_RATES;
+               *pos++ = 1;
+               *pos++ = data->maxrate;
+       }
+       if (data->ie)
+               os_memcpy(pos, data->ie, data->ie_len);
+
+       tmp = os_realloc(res->res,
+                        (res->num + 1) * sizeof(struct wpa_scan_res *));
+       if (tmp == NULL) {
+               os_free(r);
+               return;
+       }
+       tmp[res->num++] = r;
+       res->res = tmp;
+}
+                                     
+
+/**
+ * wpa_driver_wext_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       size_t ap_num = 0, len;
+       int first;
+       u8 *res_buf;
+       struct iw_event iwe_buf, *iwe = &iwe_buf;
+       char *pos, *end, *custom;
+       struct wpa_scan_results *res;
+       struct wext_scan_data data;
+
+       res_buf = wpa_driver_wext_giwscan(drv, &len);
+       if (res_buf == NULL)
+               return NULL;
+
+       ap_num = 0;
+       first = 1;
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL) {
+               os_free(res_buf);
+               return NULL;
+       }
+
+       pos = (char *) res_buf;
+       end = (char *) res_buf + len;
+       os_memset(&data, 0, sizeof(data));
+
+       while (pos + IW_EV_LCP_LEN <= end) {
+               /* Event data may be unaligned, so make a local, aligned copy
+                * before processing. */
+               os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN);
+               if (iwe->len <= IW_EV_LCP_LEN)
+                       break;
+
+               custom = pos + IW_EV_POINT_LEN;
+               if (wext_19_iw_point(drv, iwe->cmd)) {
+                       /* WE-19 removed the pointer from struct iw_point */
+                       char *dpos = (char *) &iwe_buf.u.data.length;
+                       int dlen = dpos - (char *) &iwe_buf;
+                       os_memcpy(dpos, pos + IW_EV_LCP_LEN,
+                                 sizeof(struct iw_event) - dlen);
+               } else {
+                       os_memcpy(&iwe_buf, pos, sizeof(struct iw_event));
+                       custom += IW_EV_POINT_OFF;
+               }
+
+               switch (iwe->cmd) {
+               case SIOCGIWAP:
+                       if (!first)
+                               wpa_driver_wext_add_scan_entry(res, &data);
+                       first = 0;
+                       os_free(data.ie);
+                       os_memset(&data, 0, sizeof(data));
+                       os_memcpy(data.res.bssid,
+                                 iwe->u.ap_addr.sa_data, ETH_ALEN);
+                       break;
+               case SIOCGIWMODE:
+                       wext_get_scan_mode(iwe, &data);
+                       break;
+               case SIOCGIWESSID:
+                       wext_get_scan_ssid(iwe, &data, custom, end);
+                       break;
+               case SIOCGIWFREQ:
+                       wext_get_scan_freq(iwe, &data);
+                       break;
+               case IWEVQUAL:
+                       wext_get_scan_qual(iwe, &data);
+                       break;
+               case SIOCGIWENCODE:
+                       wext_get_scan_encode(iwe, &data);
+                       break;
+               case SIOCGIWRATE:
+                       wext_get_scan_rate(iwe, &data, pos, end);
+                       break;
+               case IWEVGENIE:
+                       wext_get_scan_iwevgenie(iwe, &data, custom, end);
+                       break;
+               case IWEVCUSTOM:
+                       wext_get_scan_custom(iwe, &data, custom, end);
+                       break;
+               }
+
+               pos += iwe->len;
+       }
+       os_free(res_buf);
+       res_buf = NULL;
+       if (!first)
+               wpa_driver_wext_add_scan_entry(res, &data);
+       os_free(data.ie);
+
+       wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)",
+                  (unsigned long) len, (unsigned long) res->num);
+
+       return res;
+}
+
+
+static int wpa_driver_wext_get_range(void *priv)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iw_range *range;
+       struct iwreq iwr;
+       int minlen;
+       size_t buflen;
+
+       /*
+        * Use larger buffer than struct iw_range in order to allow the
+        * structure to grow in the future.
+        */
+       buflen = sizeof(struct iw_range) + 500;
+       range = os_zalloc(buflen);
+       if (range == NULL)
+               return -1;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) range;
+       iwr.u.data.length = buflen;
+
+       minlen = ((char *) &range->enc_capa) - (char *) range +
+               sizeof(range->enc_capa);
+
+       if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWRANGE]");
+               os_free(range);
+               return -1;
+       } else if (iwr.u.data.length >= minlen &&
+                  range->we_version_compiled >= 18) {
+               wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d "
+                          "WE(source)=%d enc_capa=0x%x",
+                          range->we_version_compiled,
+                          range->we_version_source,
+                          range->enc_capa);
+               drv->has_capability = 1;
+               drv->we_version_compiled = range->we_version_compiled;
+               if (range->enc_capa & IW_ENC_CAPA_WPA) {
+                       drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK;
+               }
+               if (range->enc_capa & IW_ENC_CAPA_WPA2) {
+                       drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+               }
+               drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 |
+                       WPA_DRIVER_CAPA_ENC_WEP104;
+               if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP)
+                       drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+               if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
+                       drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+               if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
+                       drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+               drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+                       WPA_DRIVER_AUTH_SHARED |
+                       WPA_DRIVER_AUTH_LEAP;
+               drv->capa.max_scan_ssids = 1;
+
+               wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
+                          "flags 0x%x",
+                          drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
+       } else {
+               wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
+                          "assuming WPA is not supported");
+       }
+
+       os_free(range);
+       return 0;
+}
+
+
+static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
+                                  const u8 *psk)
+{
+       struct iw_encode_ext *ext;
+       struct iwreq iwr;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+               return 0;
+
+       if (!psk)
+               return 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+
+       ext = os_zalloc(sizeof(*ext) + PMK_LEN);
+       if (ext == NULL)
+               return -1;
+
+       iwr.u.encoding.pointer = (caddr_t) ext;
+       iwr.u.encoding.length = sizeof(*ext) + PMK_LEN;
+       ext->key_len = PMK_LEN;
+       os_memcpy(&ext->key, psk, ext->key_len);
+       ext->alg = IW_ENCODE_ALG_PMK;
+
+       ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
+       if (ret < 0)
+               perror("ioctl[SIOCSIWENCODEEXT] PMK");
+       os_free(ext);
+
+       return ret;
+}
+
+
+static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
+                                      const u8 *addr, int key_idx,
+                                      int set_tx, const u8 *seq,
+                                      size_t seq_len,
+                                      const u8 *key, size_t key_len)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+       struct iw_encode_ext *ext;
+
+       if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) {
+               wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu",
+                          __FUNCTION__, (unsigned long) seq_len);
+               return -1;
+       }
+
+       ext = os_zalloc(sizeof(*ext) + key_len);
+       if (ext == NULL)
+               return -1;
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.encoding.flags = key_idx + 1;
+       iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+       if (alg == WPA_ALG_NONE)
+               iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+       iwr.u.encoding.pointer = (caddr_t) ext;
+       iwr.u.encoding.length = sizeof(*ext) + key_len;
+
+       if (addr == NULL ||
+           os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0)
+               ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY;
+       if (set_tx)
+               ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY;
+
+       ext->addr.sa_family = ARPHRD_ETHER;
+       if (addr)
+               os_memcpy(ext->addr.sa_data, addr, ETH_ALEN);
+       else
+               os_memset(ext->addr.sa_data, 0xff, ETH_ALEN);
+       if (key && key_len) {
+               os_memcpy(ext + 1, key, key_len);
+               ext->key_len = key_len;
+       }
+       switch (alg) {
+       case WPA_ALG_NONE:
+               ext->alg = IW_ENCODE_ALG_NONE;
+               break;
+       case WPA_ALG_WEP:
+               ext->alg = IW_ENCODE_ALG_WEP;
+               break;
+       case WPA_ALG_TKIP:
+               ext->alg = IW_ENCODE_ALG_TKIP;
+               break;
+       case WPA_ALG_CCMP:
+               ext->alg = IW_ENCODE_ALG_CCMP;
+               break;
+       case WPA_ALG_PMK:
+               ext->alg = IW_ENCODE_ALG_PMK;
+               break;
+#ifdef CONFIG_IEEE80211W
+       case WPA_ALG_IGTK:
+               ext->alg = IW_ENCODE_ALG_AES_CMAC;
+               break;
+#endif /* CONFIG_IEEE80211W */
+       default:
+               wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d",
+                          __FUNCTION__, alg);
+               os_free(ext);
+               return -1;
+       }
+
+       if (seq && seq_len) {
+               ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID;
+               os_memcpy(ext->rx_seq, seq, seq_len);
+       }
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) {
+               ret = errno == EOPNOTSUPP ? -2 : -1;
+               if (errno == ENODEV) {
+                       /*
+                        * ndiswrapper seems to be returning incorrect error
+                        * code.. */
+                       ret = -2;
+               }
+
+               perror("ioctl[SIOCSIWENCODEEXT]");
+       }
+
+       os_free(ext);
+       return ret;
+}
+
+
+/**
+ * wpa_driver_wext_set_key - Configure encryption key
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @priv: Private driver interface data
+ * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
+ *     %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key.
+ * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for
+ *     broadcast/default keys
+ * @key_idx: key index (0..3), usually 0 for unicast keys
+ * @set_tx: Configure this key as the default Tx key (only used when
+ *     driver does not support separate unicast/individual key
+ * @seq: Sequence number/packet number, seq_len octets, the next
+ *     packet number to be used for in replay protection; configured
+ *     for Rx keys (in most cases, this is only used with broadcast
+ *     keys and set to zero for unicast keys)
+ * @seq_len: Length of the seq, depends on the algorithm:
+ *     TKIP: 6 octets, CCMP: 6 octets
+ * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key,
+ *     8-byte Rx Mic Key
+ * @key_len: Length of the key buffer in octets (WEP: 5 or 13,
+ *     TKIP: 32, CCMP: 16)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function uses SIOCSIWENCODEEXT by default, but tries to use
+ * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key.
+ */
+int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+                           const u8 *addr, int key_idx,
+                           int set_tx, const u8 *seq, size_t seq_len,
+                           const u8 *key, size_t key_len)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu "
+                  "key_len=%lu",
+                  __FUNCTION__, alg, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+
+       ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx,
+                                         seq, seq_len, key, key_len);
+       if (ret == 0)
+               return 0;
+
+       if (ret == -2 &&
+           (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) {
+               wpa_printf(MSG_DEBUG, "Driver did not support "
+                          "SIOCSIWENCODEEXT, trying SIOCSIWENCODE");
+               ret = 0;
+       } else {
+               wpa_printf(MSG_DEBUG, "Driver did not support "
+                          "SIOCSIWENCODEEXT");
+               return ret;
+       }
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.encoding.flags = key_idx + 1;
+       iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+       if (alg == WPA_ALG_NONE)
+               iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+       iwr.u.encoding.pointer = (caddr_t) key;
+       iwr.u.encoding.length = key_len;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+               perror("ioctl[SIOCSIWENCODE]");
+               ret = -1;
+       }
+
+       if (set_tx && alg != WPA_ALG_NONE) {
+               os_memset(&iwr, 0, sizeof(iwr));
+               os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+               iwr.u.encoding.flags = key_idx + 1;
+               iwr.u.encoding.flags |= IW_ENCODE_TEMP;
+               iwr.u.encoding.pointer = (caddr_t) NULL;
+               iwr.u.encoding.length = 0;
+               if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+                       perror("ioctl[SIOCSIWENCODE] (set_tx)");
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+
+static int wpa_driver_wext_set_countermeasures(void *priv,
+                                              int enabled)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       return wpa_driver_wext_set_auth_param(drv,
+                                             IW_AUTH_TKIP_COUNTERMEASURES,
+                                             enabled);
+}
+
+
+static int wpa_driver_wext_set_drop_unencrypted(void *priv,
+                                               int enabled)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       drv->use_crypt = enabled;
+       return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED,
+                                             enabled);
+}
+
+
+static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
+                               const u8 *addr, int cmd, int reason_code)
+{
+       struct iwreq iwr;
+       struct iw_mlme mlme;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       os_memset(&mlme, 0, sizeof(mlme));
+       mlme.cmd = cmd;
+       mlme.reason_code = reason_code;
+       mlme.addr.sa_family = ARPHRD_ETHER;
+       os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN);
+       iwr.u.data.pointer = (caddr_t) &mlme;
+       iwr.u.data.length = sizeof(mlme);
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
+               perror("ioctl[SIOCSIWMLME]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
+{
+       struct iwreq iwr;
+       const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+       u8 ssid[32];
+       int i;
+
+       /*
+        * Only force-disconnect when the card is in infrastructure mode,
+        * otherwise the driver might interpret the cleared BSSID and random
+        * SSID as an attempt to create a new ad-hoc network.
+        */
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWMODE]");
+               iwr.u.mode = IW_MODE_INFRA;
+       }
+
+       if (iwr.u.mode == IW_MODE_INFRA) {
+               if (drv->cfg80211) {
+                       /*
+                        * cfg80211 supports SIOCSIWMLME commands, so there is
+                        * no need for the random SSID hack, but clear the
+                        * BSSID and SSID.
+                        */
+                       if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
+                           wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+                               wpa_printf(MSG_DEBUG, "WEXT: Failed to clear "
+                                          "to disconnect");
+                       }
+                       return;
+               }
+               /*
+                * Clear the BSSID selection and set a random SSID to make sure
+                * the driver will not be trying to associate with something
+                * even if it does not understand SIOCSIWMLME commands (or
+                * tries to associate automatically after deauth/disassoc).
+                */
+               for (i = 0; i < 32; i++)
+                       ssid[i] = rand() & 0xFF;
+               if (wpa_driver_wext_set_bssid(drv, null_bssid) < 0 ||
+                   wpa_driver_wext_set_ssid(drv, ssid, 32) < 0) {
+                       wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
+                                  "BSSID/SSID to disconnect");
+               }
+       }
+}
+
+
+static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr,
+                                         int reason_code)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       int ret;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code);
+       wpa_driver_wext_disconnect(drv);
+       return ret;
+}
+
+
+static int wpa_driver_wext_disassociate(void *priv, const u8 *addr,
+                                       int reason_code)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       int ret;
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+       ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code);
+       wpa_driver_wext_disconnect(drv);
+       return ret;
+}
+
+
+static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
+                                     size_t ie_len)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) ie;
+       iwr.u.data.length = ie_len;
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
+               perror("ioctl[SIOCSIWGENIE]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+int wpa_driver_wext_cipher2wext(int cipher)
+{
+       switch (cipher) {
+       case CIPHER_NONE:
+               return IW_AUTH_CIPHER_NONE;
+       case CIPHER_WEP40:
+               return IW_AUTH_CIPHER_WEP40;
+       case CIPHER_TKIP:
+               return IW_AUTH_CIPHER_TKIP;
+       case CIPHER_CCMP:
+               return IW_AUTH_CIPHER_CCMP;
+       case CIPHER_WEP104:
+               return IW_AUTH_CIPHER_WEP104;
+       default:
+               return 0;
+       }
+}
+
+
+int wpa_driver_wext_keymgmt2wext(int keymgmt)
+{
+       switch (keymgmt) {
+       case KEY_MGMT_802_1X:
+       case KEY_MGMT_802_1X_NO_WPA:
+               return IW_AUTH_KEY_MGMT_802_1X;
+       case KEY_MGMT_PSK:
+               return IW_AUTH_KEY_MGMT_PSK;
+       default:
+               return 0;
+       }
+}
+
+
+static int
+wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
+                                 struct wpa_driver_associate_params *params)
+{
+       struct iwreq iwr;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "WEXT: Driver did not support "
+                  "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE");
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       /* Just changing mode, not actual keys */
+       iwr.u.encoding.flags = 0;
+       iwr.u.encoding.pointer = (caddr_t) NULL;
+       iwr.u.encoding.length = 0;
+
+       /*
+        * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two
+        * different things. Here they are used to indicate Open System vs.
+        * Shared Key authentication algorithm. However, some drivers may use
+        * them to select between open/restricted WEP encrypted (open = allow
+        * both unencrypted and encrypted frames; restricted = only allow
+        * encrypted frames).
+        */
+
+       if (!drv->use_crypt) {
+               iwr.u.encoding.flags |= IW_ENCODE_DISABLED;
+       } else {
+               if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+                       iwr.u.encoding.flags |= IW_ENCODE_OPEN;
+               if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+                       iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED;
+       }
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
+               perror("ioctl[SIOCSIWENCODE]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+int wpa_driver_wext_associate(void *priv,
+                             struct wpa_driver_associate_params *params)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       int ret = 0;
+       int allow_unencrypted_eapol;
+       int value;
+
+       wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
+
+       if (drv->cfg80211) {
+               /*
+                * Stop cfg80211 from trying to associate before we are done
+                * with all parameters.
+                */
+               wpa_driver_wext_set_ssid(drv, (u8 *) "", 0);
+       }
+
+       if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
+           < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_auth_alg(drv, params->auth_alg) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_mode(drv, params->mode) < 0)
+               ret = -1;
+
+       /*
+        * If the driver did not support SIOCSIWAUTH, fallback to
+        * SIOCSIWENCODE here.
+        */
+       if (drv->auth_alg_fallback &&
+           wpa_driver_wext_auth_alg_fallback(drv, params) < 0)
+               ret = -1;
+
+       if (!params->bssid &&
+           wpa_driver_wext_set_bssid(drv, NULL) < 0)
+               ret = -1;
+
+       /* TODO: should consider getting wpa version and cipher/key_mgmt suites
+        * from configuration, not from here, where only the selected suite is
+        * available */
+       if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
+           < 0)
+               ret = -1;
+       if (params->wpa_ie == NULL || params->wpa_ie_len == 0)
+               value = IW_AUTH_WPA_VERSION_DISABLED;
+       else if (params->wpa_ie[0] == WLAN_EID_RSN)
+               value = IW_AUTH_WPA_VERSION_WPA2;
+       else
+               value = IW_AUTH_WPA_VERSION_WPA;
+       if (wpa_driver_wext_set_auth_param(drv,
+                                          IW_AUTH_WPA_VERSION, value) < 0)
+               ret = -1;
+       value = wpa_driver_wext_cipher2wext(params->pairwise_suite);
+       if (wpa_driver_wext_set_auth_param(drv,
+                                          IW_AUTH_CIPHER_PAIRWISE, value) < 0)
+               ret = -1;
+       value = wpa_driver_wext_cipher2wext(params->group_suite);
+       if (wpa_driver_wext_set_auth_param(drv,
+                                          IW_AUTH_CIPHER_GROUP, value) < 0)
+               ret = -1;
+       value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite);
+       if (wpa_driver_wext_set_auth_param(drv,
+                                          IW_AUTH_KEY_MGMT, value) < 0)
+               ret = -1;
+       value = params->key_mgmt_suite != KEY_MGMT_NONE ||
+               params->pairwise_suite != CIPHER_NONE ||
+               params->group_suite != CIPHER_NONE ||
+               params->wpa_ie_len;
+       if (wpa_driver_wext_set_auth_param(drv,
+                                          IW_AUTH_PRIVACY_INVOKED, value) < 0)
+               ret = -1;
+
+       /* Allow unencrypted EAPOL messages even if pairwise keys are set when
+        * not using WPA. IEEE 802.1X specifies that these frames are not
+        * encrypted, but WPA encrypts them when pairwise keys are in use. */
+       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
+           params->key_mgmt_suite == KEY_MGMT_PSK)
+               allow_unencrypted_eapol = 0;
+       else
+               allow_unencrypted_eapol = 1;
+
+       if (wpa_driver_wext_set_psk(drv, params->psk) < 0)
+               ret = -1;
+       if (wpa_driver_wext_set_auth_param(drv,
+                                          IW_AUTH_RX_UNENCRYPTED_EAPOL,
+                                          allow_unencrypted_eapol) < 0)
+               ret = -1;
+#ifdef CONFIG_IEEE80211W
+       switch (params->mgmt_frame_protection) {
+       case NO_MGMT_FRAME_PROTECTION:
+               value = IW_AUTH_MFP_DISABLED;
+               break;
+       case MGMT_FRAME_PROTECTION_OPTIONAL:
+               value = IW_AUTH_MFP_OPTIONAL;
+               break;
+       case MGMT_FRAME_PROTECTION_REQUIRED:
+               value = IW_AUTH_MFP_REQUIRED;
+               break;
+       };
+       if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
+               ret = -1;
+#endif /* CONFIG_IEEE80211W */
+       if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0)
+               ret = -1;
+       if (!drv->cfg80211 &&
+           wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+               ret = -1;
+       if (params->bssid &&
+           wpa_driver_wext_set_bssid(drv, params->bssid) < 0)
+               ret = -1;
+       if (drv->cfg80211 &&
+           wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
+               ret = -1;
+
+       return ret;
+}
+
+
+static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       int algs = 0, res;
+
+       if (auth_alg & WPA_AUTH_ALG_OPEN)
+               algs |= IW_AUTH_ALG_OPEN_SYSTEM;
+       if (auth_alg & WPA_AUTH_ALG_SHARED)
+               algs |= IW_AUTH_ALG_SHARED_KEY;
+       if (auth_alg & WPA_AUTH_ALG_LEAP)
+               algs |= IW_AUTH_ALG_LEAP;
+       if (algs == 0) {
+               /* at least one algorithm should be set */
+               algs = IW_AUTH_ALG_OPEN_SYSTEM;
+       }
+
+       res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG,
+                                            algs);
+       drv->auth_alg_fallback = res == -2;
+       return res;
+}
+
+
+/**
+ * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE
+ * @priv: Pointer to private wext data from wpa_driver_wext_init()
+ * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_wext_set_mode(void *priv, int mode)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       struct iwreq iwr;
+       int ret = -1;
+       unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.mode = new_mode;
+       if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) {
+               ret = 0;
+               goto done;
+       }
+
+       if (errno != EBUSY) {
+               perror("ioctl[SIOCSIWMODE]");
+               goto done;
+       }
+
+       /* mac80211 doesn't allow mode changes while the device is up, so if
+        * the device isn't in the mode we're about to change to, take device
+        * down, try to set the mode again, and bring it back up.
+        */
+       if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
+               perror("ioctl[SIOCGIWMODE]");
+               goto done;
+       }
+
+       if (iwr.u.mode == new_mode) {
+               ret = 0;
+               goto done;
+       }
+
+       if (linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 0) == 0) {
+               /* Try to set the mode again while the interface is down */
+               iwr.u.mode = new_mode;
+               if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
+                       perror("ioctl[SIOCSIWMODE]");
+               else
+                       ret = 0;
+
+               (void) linux_set_iface_flags(drv->ioctl_sock, drv->ifname, 1);
+       }
+
+done:
+       return ret;
+}
+
+
+static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
+                                u32 cmd, const u8 *bssid, const u8 *pmkid)
+{
+       struct iwreq iwr;
+       struct iw_pmksa pmksa;
+       int ret = 0;
+
+       os_memset(&iwr, 0, sizeof(iwr));
+       os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       os_memset(&pmksa, 0, sizeof(pmksa));
+       pmksa.cmd = cmd;
+       pmksa.bssid.sa_family = ARPHRD_ETHER;
+       if (bssid)
+               os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN);
+       if (pmkid)
+               os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN);
+       iwr.u.data.pointer = (caddr_t) &pmksa;
+       iwr.u.data.length = sizeof(pmksa);
+
+       if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
+               if (errno != EOPNOTSUPP)
+                       perror("ioctl[SIOCSIWPMKSA]");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid,
+                                    const u8 *pmkid)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid,
+                                       const u8 *pmkid)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid);
+}
+
+
+static int wpa_driver_wext_flush_pmkid(void *priv)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL);
+}
+
+
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       struct wpa_driver_wext_data *drv = priv;
+       if (!drv->has_capability)
+               return -1;
+       os_memcpy(capa, &drv->capa, sizeof(*capa));
+       return 0;
+}
+
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+                                       const char *ifname)
+{
+       if (ifname == NULL) {
+               drv->ifindex2 = -1;
+               return 0;
+       }
+
+       drv->ifindex2 = if_nametoindex(ifname);
+       if (drv->ifindex2 <= 0)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for "
+                  "wireless events", drv->ifindex2, ifname);
+
+       return 0;
+}
+
+
+int wpa_driver_wext_set_operstate(void *priv, int state)
+{
+       struct wpa_driver_wext_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
+                  __func__, drv->operstate, state, state ? "UP" : "DORMANT");
+       drv->operstate = state;
+       return netlink_send_oper_ifla(drv->netlink, drv->ifindex, -1,
+                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
+}
+
+
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv)
+{
+       return drv->we_version_compiled;
+}
+
+
+const struct wpa_driver_ops wpa_driver_wext_ops = {
+       .name = "wext",
+       .desc = "Linux wireless extensions (generic)",
+       .get_bssid = wpa_driver_wext_get_bssid,
+       .get_ssid = wpa_driver_wext_get_ssid,
+       .set_key = wpa_driver_wext_set_key,
+       .set_countermeasures = wpa_driver_wext_set_countermeasures,
+       .scan2 = wpa_driver_wext_scan,
+       .get_scan_results2 = wpa_driver_wext_get_scan_results,
+       .deauthenticate = wpa_driver_wext_deauthenticate,
+       .disassociate = wpa_driver_wext_disassociate,
+       .associate = wpa_driver_wext_associate,
+       .init = wpa_driver_wext_init,
+       .deinit = wpa_driver_wext_deinit,
+       .add_pmkid = wpa_driver_wext_add_pmkid,
+       .remove_pmkid = wpa_driver_wext_remove_pmkid,
+       .flush_pmkid = wpa_driver_wext_flush_pmkid,
+       .get_capa = wpa_driver_wext_get_capa,
+       .set_operstate = wpa_driver_wext_set_operstate,
+};
diff --git a/src/drivers/driver_wext.h b/src/drivers/driver_wext.h
new file mode 100644 (file)
index 0000000..602c7e1
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * WPA Supplicant - driver_wext exported functions
+ * 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 DRIVER_WEXT_H
+#define DRIVER_WEXT_H
+
+#include <net/if.h>
+
+struct wpa_driver_wext_data {
+       void *ctx;
+       struct netlink_data *netlink;
+       int ioctl_sock;
+       int mlme_sock;
+       char ifname[IFNAMSIZ + 1];
+       int ifindex;
+       int ifindex2;
+       int if_removed;
+       u8 *assoc_req_ies;
+       size_t assoc_req_ies_len;
+       u8 *assoc_resp_ies;
+       size_t assoc_resp_ies_len;
+       struct wpa_driver_capa capa;
+       int has_capability;
+       int we_version_compiled;
+
+       /* for set_auth_alg fallback */
+       int use_crypt;
+       int auth_alg_fallback;
+
+       int operstate;
+
+       char mlmedev[IFNAMSIZ + 1];
+
+       int scan_complete_events;
+
+       int cfg80211; /* whether driver is using cfg80211 */
+};
+
+int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
+int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid);
+int wpa_driver_wext_get_ssid(void *priv, u8 *ssid);
+int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len);
+int wpa_driver_wext_set_freq(void *priv, int freq);
+int wpa_driver_wext_set_mode(void *priv, int mode);
+int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
+                           const u8 *addr, int key_idx,
+                           int set_tx, const u8 *seq, size_t seq_len,
+                           const u8 *key, size_t key_len);
+int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params);
+struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv);
+
+void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+
+int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv,
+                                       const char *ifname);
+
+void * wpa_driver_wext_init(void *ctx, const char *ifname);
+void wpa_driver_wext_deinit(void *priv);
+
+int wpa_driver_wext_set_operstate(void *priv, int state);
+int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv);
+
+int wpa_driver_wext_associate(void *priv,
+                             struct wpa_driver_associate_params *params);
+int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa);
+int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv,
+                                  int idx, u32 value);
+int wpa_driver_wext_cipher2wext(int cipher);
+int wpa_driver_wext_keymgmt2wext(int keymgmt);
+
+#endif /* DRIVER_WEXT_H */
diff --git a/src/drivers/driver_wired.c b/src/drivers/driver_wired.c
new file mode 100644 (file)
index 0000000..2b197f0
--- /dev/null
@@ -0,0 +1,622 @@
+/*
+ * Wired Ethernet driver interface
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+
+#include "common.h"
+#include "eloop.h"
+#include "driver.h"
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee8023_hdr {
+       u8 dest[6];
+       u8 src[6];
+       u16 ethertype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+
+struct wpa_driver_wired_data {
+       char ifname[IFNAMSIZ + 1];
+       void *ctx;
+
+       int sock; /* raw packet socket for driver access */
+       int dhcp_sock; /* socket for dhcp packets */
+       int use_pae_group_addr;
+
+       int pf_sock;
+       int membership, multi, iff_allmulti, iff_up;
+};
+
+
+/* TODO: detecting new devices should eventually be changed from using DHCP
+ * snooping to trigger on any packet from a new layer 2 MAC address, e.g.,
+ * based on ebtables, etc. */
+
+struct dhcp_message {
+       u_int8_t op;
+       u_int8_t htype;
+       u_int8_t hlen;
+       u_int8_t hops;
+       u_int32_t xid;
+       u_int16_t secs;
+       u_int16_t flags;
+       u_int32_t ciaddr;
+       u_int32_t yiaddr;
+       u_int32_t siaddr;
+       u_int32_t giaddr;
+       u_int8_t chaddr[16];
+       u_int8_t sname[64];
+       u_int8_t file[128];
+       u_int32_t cookie;
+       u_int8_t options[308]; /* 312 - cookie */
+};
+
+
+static int wired_multicast_membership(int sock, int ifindex,
+                                     const u8 *addr, int add)
+{
+#ifdef __linux__
+       struct packet_mreq mreq;
+
+       if (sock < 0)
+               return -1;
+
+       os_memset(&mreq, 0, sizeof(mreq));
+       mreq.mr_ifindex = ifindex;
+       mreq.mr_type = PACKET_MR_MULTICAST;
+       mreq.mr_alen = ETH_ALEN;
+       os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+       if (setsockopt(sock, SOL_PACKET,
+                      add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+                      &mreq, sizeof(mreq)) < 0) {
+               perror("setsockopt");
+               return -1;
+       }
+       return 0;
+#else /* __linux__ */
+       return -1;
+#endif /* __linux__ */
+}
+
+
+#ifdef __linux__
+static void handle_data(void *ctx, unsigned char *buf, size_t len)
+{
+#ifdef HOSTAPD
+       struct ieee8023_hdr *hdr;
+       u8 *pos, *sa;
+       size_t left;
+       union wpa_event_data event;
+
+       /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest,
+        * 2 byte ethertype */
+       if (len < 14) {
+               wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)",
+                          (unsigned long) len);
+               return;
+       }
+
+       hdr = (struct ieee8023_hdr *) buf;
+
+       switch (ntohs(hdr->ethertype)) {
+               case ETH_P_PAE:
+                       wpa_printf(MSG_MSGDUMP, "Received EAPOL packet");
+                       sa = hdr->src;
+                       os_memset(&event, 0, sizeof(event));
+                       event.new_sta.addr = sa;
+                       wpa_supplicant_event(ctx, EVENT_NEW_STA, &event);
+
+                       pos = (u8 *) (hdr + 1);
+                       left = len - sizeof(*hdr);
+                       drv_event_eapol_rx(ctx, sa, pos, left);
+               break;
+
+       default:
+               wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame",
+                          ntohs(hdr->ethertype));
+               break;
+       }
+#endif /* HOSTAPD */
+}
+
+
+static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       int len;
+       unsigned char buf[3000];
+
+       len = recv(sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       handle_data(eloop_ctx, buf, len);
+}
+
+
+static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       int len;
+       unsigned char buf[3000];
+       struct dhcp_message *msg;
+       u8 *mac_address;
+       union wpa_event_data event;
+
+       len = recv(sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               perror("recv");
+               return;
+       }
+
+       /* must contain at least dhcp_message->chaddr */
+       if (len < 44) {
+               wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len);
+               return;
+       }
+
+       msg = (struct dhcp_message *) buf;
+       mac_address = (u8 *) &(msg->chaddr);
+
+       wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR,
+                  MAC2STR(mac_address));
+
+       os_memset(&event, 0, sizeof(event));
+       event.new_sta.addr = mac_address;
+       wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event);
+}
+#endif /* __linux__ */
+
+
+static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
+{
+#ifdef __linux__
+       struct ifreq ifr;
+       struct sockaddr_ll addr;
+       struct sockaddr_in addr2;
+       int n = 1;
+
+       drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
+       if (drv->sock < 0) {
+               perror("socket[PF_PACKET,SOCK_RAW]");
+               return -1;
+       }
+
+       if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
+               printf("Could not register read socket\n");
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+       if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
+               perror("ioctl(SIOCGIFINDEX)");
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sll_family = AF_PACKET;
+       addr.sll_ifindex = ifr.ifr_ifindex;
+       wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d",
+                  addr.sll_ifindex);
+
+       if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind");
+               return -1;
+       }
+
+       /* filter multicast address */
+       if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex,
+                                      pae_group_addr, 1) < 0) {
+               wpa_printf(MSG_ERROR, "wired: Failed to add multicast group "
+                          "membership");
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
+       if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
+               perror("ioctl(SIOCGIFHWADDR)");
+               return -1;
+       }
+
+       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+               printf("Invalid HW-addr family 0x%04x\n",
+                      ifr.ifr_hwaddr.sa_family);
+               return -1;
+       }
+       os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+       /* setup dhcp listen socket for sta detection */
+       if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+               perror("socket call failed for dhcp");
+               return -1;
+       }
+
+       if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
+                                    NULL)) {
+               printf("Could not register read socket\n");
+               return -1;
+       }
+
+       os_memset(&addr2, 0, sizeof(addr2));
+       addr2.sin_family = AF_INET;
+       addr2.sin_port = htons(67);
+       addr2.sin_addr.s_addr = INADDR_ANY;
+
+       if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
+                      sizeof(n)) == -1) {
+               perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]");
+               return -1;
+       }
+       if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
+                      sizeof(n)) == -1) {
+               perror("setsockopt[SOL_SOCKET,SO_BROADCAST]");
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
+       if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
+                      (char *) &ifr, sizeof(ifr)) < 0) {
+               perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]");
+               return -1;
+       }
+
+       if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
+                sizeof(struct sockaddr)) == -1) {
+               perror("bind");
+               return -1;
+       }
+
+       return 0;
+#else /* __linux__ */
+       return -1;
+#endif /* __linux__ */
+}
+
+
+static int wired_send_eapol(void *priv, const u8 *addr,
+                           const u8 *data, size_t data_len, int encrypt,
+                           const u8 *own_addr)
+{
+       struct wpa_driver_wired_data *drv = priv;
+       struct ieee8023_hdr *hdr;
+       size_t len;
+       u8 *pos;
+       int res;
+
+       len = sizeof(*hdr) + data_len;
+       hdr = os_zalloc(len);
+       if (hdr == NULL) {
+               printf("malloc() failed for wired_send_eapol(len=%lu)\n",
+                      (unsigned long) len);
+               return -1;
+       }
+
+       os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr,
+                 ETH_ALEN);
+       os_memcpy(hdr->src, own_addr, ETH_ALEN);
+       hdr->ethertype = htons(ETH_P_PAE);
+
+       pos = (u8 *) (hdr + 1);
+       os_memcpy(pos, data, data_len);
+
+       res = send(drv->sock, (u8 *) hdr, len, 0);
+       os_free(hdr);
+
+       if (res < 0) {
+               perror("wired_send_eapol: send");
+               printf("wired_send_eapol - packet len: %lu - failed\n",
+                      (unsigned long) len);
+       }
+
+       return res;
+}
+
+
+static void * wired_driver_hapd_init(struct hostapd_data *hapd,
+                                    struct wpa_init_params *params)
+{
+       struct wpa_driver_wired_data *drv;
+
+       drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
+       if (drv == NULL) {
+               printf("Could not allocate memory for wired driver data\n");
+               return NULL;
+       }
+
+       drv->ctx = hapd;
+       os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
+       drv->use_pae_group_addr = params->use_pae_group_addr;
+
+       if (wired_init_sockets(drv, params->own_addr)) {
+               os_free(drv);
+               return NULL;
+       }
+
+       return drv;
+}
+
+
+static void wired_driver_hapd_deinit(void *priv)
+{
+       struct wpa_driver_wired_data *drv = priv;
+
+       if (drv->sock >= 0)
+               close(drv->sock);
+
+       if (drv->dhcp_sock >= 0)
+               close(drv->dhcp_sock);
+
+       os_free(drv);
+}
+
+
+static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+       ssid[0] = 0;
+       return 0;
+}
+
+
+static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+       /* Report PAE group address as the "BSSID" for wired connection. */
+       os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       os_memset(capa, 0, sizeof(*capa));
+       capa->flags = WPA_DRIVER_FLAGS_WIRED;
+       return 0;
+}
+
+
+static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+               perror("ioctl[SIOCGIFFLAGS]");
+               close(s);
+               return -1;
+       }
+       close(s);
+       *flags = ifr.ifr_flags & 0xffff;
+       return 0;
+}
+
+
+static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       ifr.ifr_flags = flags & 0xffff;
+       if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+               perror("ioctl[SIOCSIFFLAGS]");
+               close(s);
+               return -1;
+       }
+       close(s);
+       return 0;
+}
+
+
+static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+       ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+       os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+       {
+               struct sockaddr_dl *dlp;
+               dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+               dlp->sdl_len = sizeof(struct sockaddr_dl);
+               dlp->sdl_family = AF_LINK;
+               dlp->sdl_index = 0;
+               dlp->sdl_nlen = 0;
+               dlp->sdl_alen = ETH_ALEN;
+               dlp->sdl_slen = 0;
+               os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+       }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+       {
+               struct sockaddr *sap;
+               sap = (struct sockaddr *) &ifr.ifr_addr;
+               sap->sa_len = sizeof(struct sockaddr);
+               sap->sa_family = AF_UNSPEC;
+               os_memcpy(sap->sa_data, addr, ETH_ALEN);
+       }
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+       if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+               perror("ioctl[SIOC{ADD/DEL}MULTI]");
+               close(s);
+               return -1;
+       }
+       close(s);
+       return 0;
+}
+
+
+static void * wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+       struct wpa_driver_wired_data *drv;
+       int flags;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->ctx = ctx;
+
+#ifdef __linux__
+       drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (drv->pf_sock < 0)
+               perror("socket(PF_PACKET)");
+#else /* __linux__ */
+       drv->pf_sock = -1;
+#endif /* __linux__ */
+
+       if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 &&
+           !(flags & IFF_UP) &&
+           wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) {
+               drv->iff_up = 1;
+       }
+
+       if (wired_multicast_membership(drv->pf_sock,
+                                      if_nametoindex(drv->ifname),
+                                      pae_group_addr, 1) == 0) {
+               wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
+                          "packet socket", __func__);
+               drv->membership = 1;
+       } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) {
+               wpa_printf(MSG_DEBUG, "%s: Added multicast membership with "
+                          "SIOCADDMULTI", __func__);
+               drv->multi = 1;
+       } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) {
+               wpa_printf(MSG_INFO, "%s: Could not get interface "
+                          "flags", __func__);
+               os_free(drv);
+               return NULL;
+       } else if (flags & IFF_ALLMULTI) {
+               wpa_printf(MSG_DEBUG, "%s: Interface is already configured "
+                          "for multicast", __func__);
+       } else if (wpa_driver_wired_set_ifflags(ifname,
+                                               flags | IFF_ALLMULTI) < 0) {
+               wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
+                          __func__);
+               os_free(drv);
+               return NULL;
+       } else {
+               wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode",
+                          __func__);
+               drv->iff_allmulti = 1;
+       }
+
+       return drv;
+}
+
+
+static void wpa_driver_wired_deinit(void *priv)
+{
+       struct wpa_driver_wired_data *drv = priv;
+       int flags;
+
+       if (drv->membership &&
+           wired_multicast_membership(drv->pf_sock,
+                                      if_nametoindex(drv->ifname),
+                                      pae_group_addr, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+                          "group (PACKET)", __func__);
+       }
+
+       if (drv->multi &&
+           wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast "
+                          "group (SIOCDELMULTI)", __func__);
+       }
+
+       if (drv->iff_allmulti &&
+           (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 ||
+            wpa_driver_wired_set_ifflags(drv->ifname,
+                                         flags & ~IFF_ALLMULTI) < 0)) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+                          __func__);
+       }
+
+       if (drv->iff_up &&
+           wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 &&
+           (flags & IFF_UP) &&
+           wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+                          __func__);
+       }
+
+       if (drv->pf_sock != -1)
+               close(drv->pf_sock);
+
+       os_free(drv);
+}
+
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+       .name = "wired",
+       .desc = "Wired Ethernet driver",
+       .hapd_init = wired_driver_hapd_init,
+       .hapd_deinit = wired_driver_hapd_deinit,
+       .hapd_send_eapol = wired_send_eapol,
+       .get_ssid = wpa_driver_wired_get_ssid,
+       .get_bssid = wpa_driver_wired_get_bssid,
+       .get_capa = wpa_driver_wired_get_capa,
+       .init = wpa_driver_wired_init,
+       .deinit = wpa_driver_wired_deinit,
+};
diff --git a/src/drivers/drivers.c b/src/drivers/drivers.c
new file mode 100644 (file)
index 0000000..bffbbde
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Driver interface list
+ * 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.
+ */
+
+#include "includes.h"
+
+
+#ifdef CONFIG_DRIVER_WEXT
+extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_HERMES
+extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */
+#endif /* CONFIG_DRIVER_HERMES */
+#ifdef CONFIG_DRIVER_MADWIFI
+extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_ATMEL
+extern struct wpa_driver_ops wpa_driver_atmel_ops; /* driver_atmel.c */
+#endif /* CONFIG_DRIVER_ATMEL */
+#ifdef CONFIG_DRIVER_NDISWRAPPER
+/* driver_ndiswrapper.c */
+extern struct wpa_driver_ops wpa_driver_ndiswrapper_ops;
+#endif /* CONFIG_DRIVER_NDISWRAPPER */
+#ifdef CONFIG_DRIVER_BROADCOM
+extern struct wpa_driver_ops wpa_driver_broadcom_ops; /* driver_broadcom.c */
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_IPW
+extern struct wpa_driver_ops wpa_driver_ipw_ops; /* driver_ipw.c */
+#endif /* CONFIG_DRIVER_IPW */
+#ifdef CONFIG_DRIVER_BSD
+extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_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_RALINK
+extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */
+#endif /* CONFIG_DRIVER_RALINK */
+#ifdef CONFIG_DRIVER_OSX
+extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */
+#endif /* CONFIG_DRIVER_OSX */
+#ifdef CONFIG_DRIVER_IPHONE
+extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */
+#endif /* CONFIG_DRIVER_IPHONE */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+/* driver_roboswitch.c */
+extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */
+#endif /* CONFIG_DRIVER_NONE */
+
+
+struct wpa_driver_ops *wpa_drivers[] =
+{
+#ifdef CONFIG_DRIVER_WEXT
+       &wpa_driver_wext_ops,
+#endif /* CONFIG_DRIVER_WEXT */
+#ifdef CONFIG_DRIVER_NL80211
+       &wpa_driver_nl80211_ops,
+#endif /* CONFIG_DRIVER_NL80211 */
+#ifdef CONFIG_DRIVER_HOSTAP
+       &wpa_driver_hostap_ops,
+#endif /* CONFIG_DRIVER_HOSTAP */
+#ifdef CONFIG_DRIVER_HERMES
+       &wpa_driver_hermes_ops,
+#endif /* CONFIG_DRIVER_HERMES */
+#ifdef CONFIG_DRIVER_MADWIFI
+       &wpa_driver_madwifi_ops,
+#endif /* CONFIG_DRIVER_MADWIFI */
+#ifdef CONFIG_DRIVER_ATMEL
+       &wpa_driver_atmel_ops,
+#endif /* CONFIG_DRIVER_ATMEL */
+#ifdef CONFIG_DRIVER_NDISWRAPPER
+       &wpa_driver_ndiswrapper_ops,
+#endif /* CONFIG_DRIVER_NDISWRAPPER */
+#ifdef CONFIG_DRIVER_BROADCOM
+       &wpa_driver_broadcom_ops,
+#endif /* CONFIG_DRIVER_BROADCOM */
+#ifdef CONFIG_DRIVER_IPW
+       &wpa_driver_ipw_ops,
+#endif /* CONFIG_DRIVER_IPW */
+#ifdef CONFIG_DRIVER_BSD
+       &wpa_driver_bsd_ops,
+#endif /* CONFIG_DRIVER_BSD */
+#ifdef CONFIG_DRIVER_NDIS
+       &wpa_driver_ndis_ops,
+#endif /* CONFIG_DRIVER_NDIS */
+#ifdef CONFIG_DRIVER_WIRED
+       &wpa_driver_wired_ops,
+#endif /* CONFIG_DRIVER_WIRED */
+#ifdef CONFIG_DRIVER_TEST
+       &wpa_driver_test_ops,
+#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_RALINK
+       &wpa_driver_ralink_ops,
+#endif /* CONFIG_DRIVER_RALINK */
+#ifdef CONFIG_DRIVER_OSX
+       &wpa_driver_osx_ops,
+#endif /* CONFIG_DRIVER_OSX */
+#ifdef CONFIG_DRIVER_IPHONE
+       &wpa_driver_iphone_ops,
+#endif /* CONFIG_DRIVER_IPHONE */
+#ifdef CONFIG_DRIVER_ROBOSWITCH
+       &wpa_driver_roboswitch_ops,
+#endif /* CONFIG_DRIVER_ROBOSWITCH */
+#ifdef CONFIG_DRIVER_ATHEROS
+       &wpa_driver_atheros_ops,
+#endif /* CONFIG_DRIVER_ATHEROS */
+#ifdef CONFIG_DRIVER_NONE
+       &wpa_driver_none_ops,
+#endif /* CONFIG_DRIVER_NONE */
+       NULL
+};
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
new file mode 100644 (file)
index 0000000..b76b229
--- /dev/null
@@ -0,0 +1,181 @@
+##### COMMON DRIVERS
+
+ifdef CONFIG_DRIVER_HOSTAP
+DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP
+DRV_OBJS += ../src/drivers/driver_hostap.o
+CONFIG_WIRELESS_EXTENSION=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_WIRED
+DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
+DRV_OBJS += ../src/drivers/driver_wired.o
+endif
+
+ifdef CONFIG_DRIVER_MADWIFI
+DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI
+DRV_OBJS += ../src/drivers/driver_madwifi.o
+CONFIG_WIRELESS_EXTENSION=y
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_NL80211
+DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
+DRV_OBJS += ../src/drivers/driver_nl80211.o
+DRV_OBJS += ../src/utils/radiotap.o
+NEED_SME=y
+NEED_AP_MLME=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+DRV_LIBS += -lnl
+
+ifdef CONFIG_LIBNL20
+DRV_LIBS += -lnl-genl
+DRV_CFLAGS += -DCONFIG_LIBNL20
+endif
+endif
+
+ifdef CONFIG_DRIVER_BSD
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=freebsd
+endif
+DRV_CFLAGS += -DCONFIG_DRIVER_BSD
+DRV_OBJS += ../src/drivers/driver_bsd.o
+CONFIG_L2_FREEBSD=y
+CONFIG_DNET_PCAP=y
+endif
+
+ifdef CONFIG_DRIVER_TEST
+DRV_CFLAGS += -DCONFIG_DRIVER_TEST
+DRV_OBJS += ../src/drivers/driver_test.o
+NEED_AP_MLME=y
+endif
+
+ifdef CONFIG_DRIVER_NONE
+DRV_CFLAGS += -DCONFIG_DRIVER_NONE
+DRV_OBJS += ../src/drivers/driver_none.o
+endif
+
+##### PURE AP DRIVERS
+
+ifdef CONFIG_DRIVER_ATHEROS
+DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
+DRV_AP_OBJS += ../src/drivers/driver_atheros.o
+CONFIG_L2_PACKET=linux
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+##### PURE CLIENT DRIVERS
+
+ifdef CONFIG_DRIVER_WEXT
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT
+CONFIG_WIRELESS_EXTENSION=y
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_HERMES
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES
+DRV_WPA_OBJS += ../src/drivers/driver_hermes.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_ATMEL
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL
+DRV_WPA_OBJS += ../src/drivers/driver_atmel.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_NDISWRAPPER
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER
+DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_RALINK
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK
+DRV_WPA_OBJS += ../src/drivers/driver_ralink.o
+NEED_NETLINK=y
+NEED_LINUX_IOCTL=y
+endif
+
+ifdef CONFIG_DRIVER_BROADCOM
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM
+DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o
+endif
+
+ifdef CONFIG_DRIVER_IPW
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW
+DRV_WPA_OBJS += ../src/drivers/driver_ipw.o
+CONFIG_WIRELESS_EXTENSION=y
+endif
+
+ifdef CONFIG_DRIVER_NDIS
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS
+DRV_WPA_OBJS += ../src/drivers/driver_ndis.o
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o
+endif
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=pcap
+endif
+CONFIG_WINPCAP=y
+ifdef CONFIG_USE_NDISUIO
+DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO
+endif
+endif
+
+ifdef CONFIG_DRIVER_OSX
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX
+DRV_WPA_OBJS += ../src/drivers/driver_osx.o
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211
+endif
+
+ifdef CONFIG_DRIVER_IPHONE
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE
+DRV_WPA_OBJS += ../src/drivers/driver_iphone.o
+DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o
+DRV_WPA_LDFLAGS += -framework CoreFoundation
+endif
+
+ifdef CONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH
+DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o
+endif
+
+ifdef CONFIG_WIRELESS_EXTENSION
+DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION
+DRV_WPA_OBJS += ../src/drivers/driver_wext.o
+endif
+
+ifdef NEED_NETLINK
+DRV_OBJS += ../src/drivers/netlink.o
+endif
+
+ifdef NEED_LINUX_IOCTL
+DRV_OBJS += ../src/drivers/linux_ioctl.o
+endif
+
+
+##### COMMON VARS
+DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
+DRV_WPA_CFLAGS += $(DRV_CFLAGS)
+DRV_AP_CFLAGS += $(DRV_CFLAGS)
+
+DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS)
+DRV_WPA_LIBS += $(DRV_LIBS)
+DRV_AP_LIBS += $(DRV_LIBS)
+
+DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS)
+DRV_WPA_OBJS += $(DRV_OBJS)
+DRV_AP_OBJS += $(DRV_OBJS)
+
+DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS)
+DRV_WPA_LDFLAGS += $(DRV_LDFLAGS)
+DRV_AP_LDFLAGS += $(DRV_LDFLAGS)
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
new file mode 100644 (file)
index 0000000..0d6cf54
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, 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 "utils/includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+
+#include "utils/common.h"
+#include "linux_ioctl.h"
+
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up)
+{
+       struct ifreq ifr;
+
+       if (sock < 0)
+               return -1;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+
+       if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) {
+               wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s",
+                          ifname, strerror(errno));
+               return -1;
+       }
+
+       if (dev_up) {
+               if (ifr.ifr_flags & IFF_UP)
+                       return 0;
+               ifr.ifr_flags |= IFF_UP;
+       } else {
+               if (!(ifr.ifr_flags & IFF_UP))
+                       return 0;
+               ifr.ifr_flags &= ~IFF_UP;
+       }
+
+       if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) {
+               wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s",
+                          ifname, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr)
+{
+       struct ifreq ifr;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       if (ioctl(sock, SIOCGIFHWADDR, &ifr)) {
+               wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s",
+                          ifname, strerror(errno));
+               return -1;
+       }
+
+       if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
+               wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x",
+                          ifname, ifr.ifr_hwaddr.sa_family);
+               return -1;
+       }
+       os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+       return 0;
+}
+
+
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
+{
+       struct ifreq ifr;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+       ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER;
+
+       if (ioctl(sock, SIOCSIFHWADDR, &ifr)) {
+               wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s",
+                          ifname, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+#ifndef SIOCBRADDBR
+#define SIOCBRADDBR 0x89a0
+#endif
+#ifndef SIOCBRDELBR
+#define SIOCBRDELBR 0x89a1
+#endif
+#ifndef SIOCBRADDIF
+#define SIOCBRADDIF 0x89a2
+#endif
+#ifndef SIOCBRDELIF
+#define SIOCBRDELIF 0x89a3
+#endif
+
+
+int linux_br_add(int sock, const char *brname)
+{
+       if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
+                          brname, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int linux_br_del(int sock, const char *brname)
+{
+       if (ioctl(sock, SIOCBRDELBR, brname) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s",
+                          brname, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int linux_br_add_if(int sock, const char *brname, const char *ifname)
+{
+       struct ifreq ifr;
+       int ifindex;
+
+       ifindex = if_nametoindex(ifname);
+       if (ifindex == 0)
+               return -1;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+       ifr.ifr_ifindex = ifindex;
+       if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
+                          "%s: %s", ifname, brname, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int linux_br_del_if(int sock, const char *brname, const char *ifname)
+{
+       struct ifreq ifr;
+       int ifindex;
+
+       ifindex = if_nametoindex(ifname);
+       if (ifindex == 0)
+               return -1;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
+       ifr.ifr_ifindex = ifindex;
+       if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not remove interface %s from "
+                          "bridge %s: %s", ifname, brname, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int linux_br_get(char *brname, const char *ifname)
+{
+       char path[128], brlink[128], *pos;
+       os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
+                   ifname);
+       os_memset(brlink, 0, sizeof(brlink));
+       if (readlink(path, brlink, sizeof(brlink) - 1) < 0)
+               return -1;
+       pos = os_strrchr(brlink, '/');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       os_strlcpy(brname, pos, IFNAMSIZ);
+       return 0;
+}
diff --git a/src/drivers/linux_ioctl.h b/src/drivers/linux_ioctl.h
new file mode 100644 (file)
index 0000000..a555738
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Linux ioctl helper functions for driver wrappers
+ * Copyright (c) 2002-2010, 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 LINUX_IOCTL_H
+#define LINUX_IOCTL_H
+
+int linux_set_iface_flags(int sock, const char *ifname, int dev_up);
+int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr);
+int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr);
+int linux_br_add(int sock, const char *brname);
+int linux_br_del(int sock, const char *brname);
+int linux_br_add_if(int sock, const char *brname, const char *ifname);
+int linux_br_del_if(int sock, const char *brname, const char *ifname);
+int linux_br_get(char *brname, const char *ifname);
+
+#endif /* LINUX_IOCTL_H */
diff --git a/src/drivers/ndis_events.c b/src/drivers/ndis_events.c
new file mode 100644 (file)
index 0000000..f6eaa7c
--- /dev/null
@@ -0,0 +1,808 @@
+/*
+ * ndis_events - Receive NdisMIndicateStatus() events using WMI
+ * Copyright (c) 2004-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.
+ */
+
+#define _WIN32_WINNT    0x0400
+
+#include "includes.h"
+
+#ifndef COBJMACROS
+#define COBJMACROS
+#endif /* COBJMACROS */
+#include <wbemidl.h>
+
+#include "common.h"
+
+
+static int wmi_refcnt = 0;
+static int wmi_first = 1;
+
+struct ndis_events_data {
+       IWbemObjectSink sink;
+       IWbemObjectSinkVtbl sink_vtbl;
+
+       IWbemServices *pSvc;
+       IWbemLocator *pLoc;
+
+       HANDLE read_pipe, write_pipe, event_avail;
+       UINT ref;
+       int terminating;
+       char *ifname; /* {GUID..} */
+       WCHAR *adapter_desc;
+};
+
+#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL
+#define BstrFree(x) if (x) SysFreeString(x)
+
+/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to
+ * BSTRs */
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery(
+       IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+       long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum)
+{
+       BSTR bsQueryLanguage, bsQuery;
+       HRESULT hr;
+
+       bsQueryLanguage = BstrAlloc(strQueryLanguage);
+       bsQuery = BstrAlloc(strQuery);
+
+       hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags,
+                                    pCtx, ppEnum);
+
+       BstrFree(bsQueryLanguage);
+       BstrFree(bsQuery);
+
+       return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync(
+       IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery,
+       long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler)
+{
+       BSTR bsQueryLanguage, bsQuery;
+       HRESULT hr;
+
+       bsQueryLanguage = BstrAlloc(strQueryLanguage);
+       bsQuery = BstrAlloc(strQuery);
+
+       hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage,
+                                                     bsQuery, lFlags, pCtx,
+                                                     pResponseHandler);
+
+       BstrFree(bsQueryLanguage);
+       BstrFree(bsQuery);
+
+       return hr;
+}
+
+
+HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer(
+       IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser,
+       LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags,
+       LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace)
+{
+       BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority;
+       HRESULT hr;
+
+       bsNetworkResource = BstrAlloc(strNetworkResource);
+       bsUser = BstrAlloc(strUser);
+       bsPassword = BstrAlloc(strPassword);
+       bsLocale = BstrAlloc(strLocale);
+       bsAuthority = BstrAlloc(strAuthority);
+
+       hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser,
+                                       bsPassword, bsLocale, lSecurityFlags,
+                                       bsAuthority, pCtx, ppNamespace);
+
+       BstrFree(bsNetworkResource);
+       BstrFree(bsUser);
+       BstrFree(bsPassword);
+       BstrFree(bsLocale);
+       BstrFree(bsAuthority);
+
+       return hr;
+}
+
+
+enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC,
+                  EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL };
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+                                  const char *ifname, const char *desc);
+
+
+static int ndis_events_constructor(struct ndis_events_data *events)
+{
+       events->ref = 1;
+
+       if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) {
+               wpa_printf(MSG_ERROR, "CreatePipe() failed: %d",
+                          (int) GetLastError());
+               return -1;
+       }
+       events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (events->event_avail == NULL) {
+               wpa_printf(MSG_ERROR, "CreateEvent() failed: %d",
+                          (int) GetLastError());
+               CloseHandle(events->read_pipe);
+               CloseHandle(events->write_pipe);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void ndis_events_destructor(struct ndis_events_data *events)
+{
+       CloseHandle(events->read_pipe);
+       CloseHandle(events->write_pipe);
+       CloseHandle(events->event_avail);
+       IWbemServices_Release(events->pSvc);
+       IWbemLocator_Release(events->pLoc);
+       if (--wmi_refcnt == 0)
+               CoUninitialize();
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj)
+{
+       *obj = NULL;
+
+       if (IsEqualIID(riid, &IID_IUnknown) ||
+           IsEqualIID(riid, &IID_IWbemObjectSink)) {
+               *obj = this;
+               IWbemObjectSink_AddRef(this);
+               return NOERROR;
+       }
+
+       return E_NOINTERFACE;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this)
+{
+       struct ndis_events_data *events = (struct ndis_events_data *) this;
+       return ++events->ref;
+}
+
+
+static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this)
+{
+       struct ndis_events_data *events = (struct ndis_events_data *) this;
+
+       if (--events->ref != 0)
+               return events->ref;
+
+       ndis_events_destructor(events);
+       wpa_printf(MSG_DEBUG, "ndis_events: terminated");
+       os_free(events->adapter_desc);
+       os_free(events->ifname);
+       os_free(events);
+       return 0;
+}
+
+
+static int ndis_events_send_event(struct ndis_events_data *events,
+                                 enum event_types type,
+                                 char *data, size_t data_len)
+{
+       char buf[512], *pos, *end;
+       int _type;
+       DWORD written;
+
+       end = buf + sizeof(buf);
+       _type = (int) type;
+       os_memcpy(buf, &_type, sizeof(_type));
+       pos = buf + sizeof(_type);
+
+       if (data) {
+               if (2 + data_len > (size_t) (end - pos)) {
+                       wpa_printf(MSG_DEBUG, "Not enough room for send_event "
+                                  "data (%d)", data_len);
+                       return -1;
+               }
+               *pos++ = data_len >> 8;
+               *pos++ = data_len & 0xff;
+               os_memcpy(pos, data, data_len);
+               pos += data_len;
+       }
+
+       if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) {
+               SetEvent(events->event_avail);
+               return 0;
+       }
+       wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError());
+       return -1;
+}
+
+
+static void ndis_events_media_connect(struct ndis_events_data *events)
+{
+       wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect");
+       ndis_events_send_event(events, EVENT_CONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_disconnect(struct ndis_events_data *events)
+{
+       wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect");
+       ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0);
+}
+
+
+static void ndis_events_media_specific(struct ndis_events_data *events,
+                                      IWbemClassObject *pObj)
+{
+       VARIANT vt;
+       HRESULT hr;
+       LONG lower, upper, k;
+       UCHAR ch;
+       char *data, *pos;
+       size_t data_len;
+
+       wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication");
+
+       /* This is the StatusBuffer from NdisMIndicateStatus() call */
+       hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication",
+                                 0, &vt, NULL, NULL);
+       if (FAILED(hr)) {
+               wpa_printf(MSG_DEBUG, "Could not get "
+                          "NdisStatusMediaSpecificIndication from "
+                          "the object?!");
+               return;
+       }
+
+       SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower);
+       SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper);
+       data_len = upper - lower + 1;
+       data = os_malloc(data_len);
+       if (data == NULL) {
+               wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event "
+                          "data");
+               VariantClear(&vt);
+               return;
+       }
+
+       pos = data;
+       for (k = lower; k <= upper; k++) {
+               SafeArrayGetElement(V_ARRAY(&vt), &k, &ch);
+               *pos++ = ch;
+       }
+       wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len);
+
+       VariantClear(&vt);
+
+       ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len);
+
+       os_free(data);
+}
+
+
+static void ndis_events_adapter_arrival(struct ndis_events_data *events)
+{
+       wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival");
+       ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0);
+}
+
+
+static void ndis_events_adapter_removal(struct ndis_events_data *events)
+{
+       wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval");
+       ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0);
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_indicate(IWbemObjectSink *this, long lObjectCount,
+                    IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray)
+{
+       struct ndis_events_data *events = (struct ndis_events_data *) this;
+       long i;
+
+       if (events->terminating) {
+               wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+                          "indication - terminating");
+               return WBEM_NO_ERROR;
+       }
+       /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)",
+          lObjectCount); */
+
+       for (i = 0; i < lObjectCount; i++) {
+               IWbemClassObject *pObj = ppObjArray[i];
+               HRESULT hr;
+               VARIANT vtClass, vt;
+
+               hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL,
+                                         NULL);
+               if (FAILED(hr)) {
+                       wpa_printf(MSG_DEBUG, "Failed to get __CLASS from "
+                                  "event.");
+                       break;
+               }
+               /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */
+
+               hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL,
+                                         NULL);
+               if (FAILED(hr)) {
+                       wpa_printf(MSG_DEBUG, "Failed to get InstanceName "
+                                  "from event.");
+                       VariantClear(&vtClass);
+                       break;
+               }
+
+               if (wcscmp(vtClass.bstrVal,
+                          L"MSNdis_NotifyAdapterArrival") == 0) {
+                       wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to "
+                                  "update adapter description since it may "
+                                  "have changed with new adapter instance");
+                       ndis_events_get_adapter(events, events->ifname, NULL);
+               }
+
+               if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) {
+                       wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore "
+                                  "indication for foreign adapter: "
+                                  "InstanceName: '%S' __CLASS: '%S'",
+                                  vt.bstrVal, vtClass.bstrVal);
+                       VariantClear(&vtClass);
+                       VariantClear(&vt);
+                       continue;
+               }
+               VariantClear(&vt);
+
+               if (wcscmp(vtClass.bstrVal,
+                          L"MSNdis_StatusMediaSpecificIndication") == 0) {
+                       ndis_events_media_specific(events, pObj);
+               } else if (wcscmp(vtClass.bstrVal,
+                                 L"MSNdis_StatusMediaConnect") == 0) {
+                       ndis_events_media_connect(events);
+               } else if (wcscmp(vtClass.bstrVal,
+                                 L"MSNdis_StatusMediaDisconnect") == 0) {
+                       ndis_events_media_disconnect(events);
+               } else if (wcscmp(vtClass.bstrVal,
+                                 L"MSNdis_NotifyAdapterArrival") == 0) {
+                       ndis_events_adapter_arrival(events);
+               } else if (wcscmp(vtClass.bstrVal,
+                                 L"MSNdis_NotifyAdapterRemoval") == 0) {
+                       ndis_events_adapter_removal(events);
+               } else {
+                       wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: "
+                                  "'%S'", vtClass.bstrVal);
+               }
+
+               VariantClear(&vtClass);
+       }
+
+       return WBEM_NO_ERROR;
+}
+
+
+static HRESULT STDMETHODCALLTYPE
+ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult,
+                      BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam)
+{
+       return WBEM_NO_ERROR;
+}
+
+
+static int notification_query(IWbemObjectSink *pDestSink,
+                             IWbemServices *pSvc, const char *class_name)
+{
+       HRESULT hr;
+       WCHAR query[256];
+
+       _snwprintf(query, 256,
+                 L"SELECT * FROM %S", class_name);
+       wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+       hr = call_IWbemServices_ExecNotificationQueryAsync(
+               pSvc, L"WQL", query, 0, 0, pDestSink);
+       if (FAILED(hr)) {
+               wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s "
+                          "failed with hresult of 0x%x",
+                          class_name, (int) hr);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int register_async_notification(IWbemObjectSink *pDestSink,
+                                      IWbemServices *pSvc)
+{
+       int i;
+       const char *class_list[] = {
+               "MSNdis_StatusMediaConnect",
+               "MSNdis_StatusMediaDisconnect",
+               "MSNdis_StatusMediaSpecificIndication",
+               "MSNdis_NotifyAdapterArrival",
+               "MSNdis_NotifyAdapterRemoval",
+               NULL
+       };
+
+       for (i = 0; class_list[i]; i++) {
+               if (notification_query(pDestSink, pSvc, class_list[i]) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+void ndis_events_deinit(struct ndis_events_data *events)
+{
+       events->terminating = 1;
+       IWbemServices_CancelAsyncCall(events->pSvc, &events->sink);
+       IWbemObjectSink_Release(&events->sink);
+       /*
+        * Rest of deinitialization is done in ndis_events_destructor() once
+        * all reference count drops to zero.
+        */
+}
+
+
+static int ndis_events_use_desc(struct ndis_events_data *events,
+                               const char *desc)
+{
+       char *tmp, *pos;
+       size_t len;
+
+       if (desc == NULL) {
+               if (events->adapter_desc == NULL)
+                       return -1;
+               /* Continue using old description */
+               return 0;
+       }
+
+       tmp = os_strdup(desc);
+       if (tmp == NULL)
+               return -1;
+
+       pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)");
+       if (pos)
+               *pos = '\0';
+
+       len = os_strlen(tmp);
+       events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR));
+       if (events->adapter_desc == NULL) {
+               os_free(tmp);
+               return -1;
+       }
+       _snwprintf(events->adapter_desc, len + 1, L"%S", tmp);
+       os_free(tmp);
+       return 0;
+}
+
+
+static int ndis_events_get_adapter(struct ndis_events_data *events,
+                                  const char *ifname, const char *desc)
+{
+       HRESULT hr;
+       IWbemServices *pSvc;
+#define MAX_QUERY_LEN 256
+       WCHAR query[MAX_QUERY_LEN];
+       IEnumWbemClassObject *pEnumerator;
+       IWbemClassObject *pObj;
+       ULONG uReturned;
+       VARIANT vt;
+       int len, pos;
+
+       /*
+        * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter
+        * to have better probability of matching with InstanceName from
+        * MSNdis events. If this fails, use the provided description.
+        */
+
+       os_free(events->adapter_desc);
+       events->adapter_desc = NULL;
+
+       hr = call_IWbemLocator_ConnectServer(
+               events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc);
+       if (FAILED(hr)) {
+               wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI "
+                          "server (ROOT\\CIMV2) - error 0x%x", (int) hr);
+               return ndis_events_use_desc(events, desc);
+       }
+       wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2.");
+
+       _snwprintf(query, MAX_QUERY_LEN,
+                 L"SELECT Index FROM Win32_NetworkAdapterConfiguration "
+                 L"WHERE SettingID='%S'", ifname);
+       wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+       hr = call_IWbemServices_ExecQuery(
+               pSvc, L"WQL", query,
+               WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+               NULL, &pEnumerator);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+                          "GUID from Win32_NetworkAdapterConfiguration: "
+                          "0x%x", (int) hr);
+               IWbemServices_Release(pSvc);
+               return ndis_events_use_desc(events, desc);
+       }
+
+       uReturned = 0;
+       hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+                                      &pObj, &uReturned);
+       if (!SUCCEEDED(hr) || uReturned == 0) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+                          "GUID from Win32_NetworkAdapterConfiguration: "
+                          "0x%x", (int) hr);
+               IEnumWbemClassObject_Release(pEnumerator);
+               IWbemServices_Release(pSvc);
+               return ndis_events_use_desc(events, desc);
+       }
+       IEnumWbemClassObject_Release(pEnumerator);
+
+       VariantInit(&vt);
+       hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from "
+                          "Win32_NetworkAdapterConfiguration: 0x%x",
+                          (int) hr);
+               IWbemServices_Release(pSvc);
+               return ndis_events_use_desc(events, desc);
+       }
+
+       _snwprintf(query, MAX_QUERY_LEN,
+                 L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE "
+                 L"Index=%d",
+                 vt.uintVal);
+       wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+       VariantClear(&vt);
+       IWbemClassObject_Release(pObj);
+
+       hr = call_IWbemServices_ExecQuery(
+               pSvc, L"WQL", query,
+               WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+               NULL, &pEnumerator);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+                          "from Win32_NetworkAdapter: 0x%x", (int) hr);
+               IWbemServices_Release(pSvc);
+               return ndis_events_use_desc(events, desc);
+       }
+
+       uReturned = 0;
+       hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+                                      &pObj, &uReturned);
+       if (!SUCCEEDED(hr) || uReturned == 0) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+                          "from Win32_NetworkAdapter: 0x%x", (int) hr);
+               IEnumWbemClassObject_Release(pEnumerator);
+               IWbemServices_Release(pSvc);
+               return ndis_events_use_desc(events, desc);
+       }
+       IEnumWbemClassObject_Release(pEnumerator);
+
+       hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+                          "Win32_NetworkAdapter: 0x%x", (int) hr);
+               IWbemClassObject_Release(pObj);
+               IWbemServices_Release(pSvc);
+               return ndis_events_use_desc(events, desc);
+       }
+
+       wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'",
+                  vt.bstrVal);
+       events->adapter_desc = _wcsdup(vt.bstrVal);
+       VariantClear(&vt);
+
+       /*
+        * Try to get even better candidate for matching with InstanceName
+        * from Win32_PnPEntity. This is needed at least for some USB cards
+        * that can change the InstanceName whenever being unplugged and
+        * plugged again.
+        */
+
+       hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID "
+                          "from Win32_NetworkAdapter: 0x%x", (int) hr);
+               IWbemClassObject_Release(pObj);
+               IWbemServices_Release(pSvc);
+               if (events->adapter_desc == NULL)
+                       return ndis_events_use_desc(events, desc);
+               return 0; /* use Win32_NetworkAdapter::Name */
+       }
+
+       wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID="
+                  "'%S'", vt.bstrVal);
+
+       len = _snwprintf(query, MAX_QUERY_LEN,
+                       L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='");
+       if (len < 0 || len >= MAX_QUERY_LEN - 1) {
+               VariantClear(&vt);
+               IWbemClassObject_Release(pObj);
+               IWbemServices_Release(pSvc);
+               if (events->adapter_desc == NULL)
+                       return ndis_events_use_desc(events, desc);
+               return 0; /* use Win32_NetworkAdapter::Name */
+       }
+
+       /* Escape \ as \\ */
+       for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) {
+               if (vt.bstrVal[pos] == '\\') {
+                       if (len >= MAX_QUERY_LEN - 3)
+                               break;
+                       query[len++] = '\\';
+               }
+               query[len++] = vt.bstrVal[pos];
+       }
+       query[len++] = L'\'';
+       query[len] = L'\0';
+       VariantClear(&vt);
+       IWbemClassObject_Release(pObj);
+       wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query);
+
+       hr = call_IWbemServices_ExecQuery(
+               pSvc, L"WQL", query,
+               WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY,
+               NULL, &pEnumerator);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface "
+                          "Name from Win32_PnPEntity: 0x%x", (int) hr);
+               IWbemServices_Release(pSvc);
+               if (events->adapter_desc == NULL)
+                       return ndis_events_use_desc(events, desc);
+               return 0; /* use Win32_NetworkAdapter::Name */
+       }
+
+       uReturned = 0;
+       hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1,
+                                      &pObj, &uReturned);
+       if (!SUCCEEDED(hr) || uReturned == 0) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface "
+                          "from Win32_PnPEntity: 0x%x", (int) hr);
+               IEnumWbemClassObject_Release(pEnumerator);
+               IWbemServices_Release(pSvc);
+               if (events->adapter_desc == NULL)
+                       return ndis_events_use_desc(events, desc);
+               return 0; /* use Win32_NetworkAdapter::Name */
+       }
+       IEnumWbemClassObject_Release(pEnumerator);
+
+       hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL);
+       if (!SUCCEEDED(hr)) {
+               wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from "
+                          "Win32_PnPEntity: 0x%x", (int) hr);
+               IWbemClassObject_Release(pObj);
+               IWbemServices_Release(pSvc);
+               if (events->adapter_desc == NULL)
+                       return ndis_events_use_desc(events, desc);
+               return 0; /* use Win32_NetworkAdapter::Name */
+       }
+
+       wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'",
+                  vt.bstrVal);
+       os_free(events->adapter_desc);
+       events->adapter_desc = _wcsdup(vt.bstrVal);
+       VariantClear(&vt);
+
+       IWbemClassObject_Release(pObj);
+
+       IWbemServices_Release(pSvc);
+
+       if (events->adapter_desc == NULL)
+               return ndis_events_use_desc(events, desc);
+
+       return 0;
+}
+
+
+struct ndis_events_data *
+ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail,
+                const char *ifname, const char *desc)
+{
+       HRESULT hr;
+       IWbemObjectSink *pSink;
+       struct ndis_events_data *events;
+
+       events = os_zalloc(sizeof(*events));
+       if (events == NULL) {
+               wpa_printf(MSG_ERROR, "Could not allocate sink for events.");
+               return NULL;
+       }
+       events->ifname = os_strdup(ifname);
+       if (events->ifname == NULL) {
+               os_free(events);
+               return NULL;
+       }
+
+       if (wmi_refcnt++ == 0) {
+               hr = CoInitializeEx(0, COINIT_MULTITHREADED);
+               if (FAILED(hr)) {
+                       wpa_printf(MSG_ERROR, "CoInitializeEx() failed - "
+                                  "returned 0x%x", (int) hr);
+                       os_free(events);
+                       return NULL;
+               }
+       }
+
+       if (wmi_first) {
+               /* CoInitializeSecurity() must be called once and only once
+                * per process, so let's use wmi_first flag to protect against
+                * multiple calls. */
+               wmi_first = 0;
+
+               hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
+                                         RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
+                                         RPC_C_IMP_LEVEL_IMPERSONATE,
+                                         NULL, EOAC_SECURE_REFS, NULL);
+               if (FAILED(hr)) {
+                       wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed "
+                                  "- returned 0x%x", (int) hr);
+                       os_free(events);
+                       return NULL;
+               }
+       }
+
+       hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER,
+                             &IID_IWbemLocator,
+                             (LPVOID *) (void *) &events->pLoc);
+       if (FAILED(hr)) {
+               wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned "
+                          "0x%x", (int) hr);
+               CoUninitialize();
+               os_free(events);
+               return NULL;
+       }
+
+       if (ndis_events_get_adapter(events, ifname, desc) < 0) {
+               CoUninitialize();
+               os_free(events);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'",
+                  events->adapter_desc);
+
+       hr = call_IWbemLocator_ConnectServer(
+               events->pLoc, L"ROOT\\WMI", NULL, NULL,
+               0, 0, 0, 0, &events->pSvc);
+       if (FAILED(hr)) {
+               wpa_printf(MSG_ERROR, "Could not connect to server - error "
+                          "0x%x", (int) hr);
+               CoUninitialize();
+               os_free(events->adapter_desc);
+               os_free(events);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI.");
+
+       ndis_events_constructor(events);
+       pSink = &events->sink;
+       pSink->lpVtbl = &events->sink_vtbl;
+       events->sink_vtbl.QueryInterface = ndis_events_query_interface;
+       events->sink_vtbl.AddRef = ndis_events_add_ref;
+       events->sink_vtbl.Release = ndis_events_release;
+       events->sink_vtbl.Indicate = ndis_events_indicate;
+       events->sink_vtbl.SetStatus = ndis_events_set_status;
+
+       if (register_async_notification(pSink, events->pSvc) < 0) {
+               wpa_printf(MSG_DEBUG, "Failed to register async "
+                          "notifications");
+               ndis_events_destructor(events);
+               os_free(events->adapter_desc);
+               os_free(events);
+               return NULL;
+       }
+
+       *read_pipe = events->read_pipe;
+       *event_avail = events->event_avail;
+
+       return events;
+}
diff --git a/src/drivers/netlink.c b/src/drivers/netlink.c
new file mode 100644 (file)
index 0000000..ad15b1d
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * 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"
+
+#include "common.h"
+#include "eloop.h"
+#include "priv_netlink.h"
+#include "netlink.h"
+
+
+struct netlink_data {
+       struct netlink_config *cfg;
+       int sock;
+};
+
+
+static void netlink_receive_link(struct netlink_data *netlink,
+                                void (*cb)(void *ctx, struct ifinfomsg *ifi,
+                                           u8 *buf, size_t len),
+                                struct nlmsghdr *h)
+{
+       if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg))
+               return;
+       cb(netlink->cfg->ctx, NLMSG_DATA(h),
+          NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)),
+          NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg)));
+}
+
+
+static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct netlink_data *netlink = eloop_ctx;
+       char buf[8192];
+       int left;
+       struct sockaddr_nl from;
+       socklen_t fromlen;
+       struct nlmsghdr *h;
+       int max_events = 10;
+
+try_again:
+       fromlen = sizeof(from);
+       left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT,
+                       (struct sockaddr *) &from, &fromlen);
+       if (left < 0) {
+               if (errno != EINTR && errno != EAGAIN)
+                       wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s",
+                                  strerror(errno));
+               return;
+       }
+
+       h = (struct nlmsghdr *) buf;
+       while (NLMSG_OK(h, left)) {
+               switch (h->nlmsg_type) {
+               case RTM_NEWLINK:
+                       netlink_receive_link(netlink, netlink->cfg->newlink_cb,
+                                            h);
+                       break;
+               case RTM_DELLINK:
+                       netlink_receive_link(netlink, netlink->cfg->dellink_cb,
+                                            h);
+                       break;
+               }
+
+               h = NLMSG_NEXT(h, left);
+       }
+
+       if (left > 0) {
+               wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of "
+                          "netlink message", left);
+       }
+
+       if (--max_events > 0) {
+               /*
+                * Try to receive all events in one eloop call in order to
+                * limit race condition on cases where AssocInfo event, Assoc
+                * event, and EAPOL frames are received more or less at the
+                * same time. We want to process the event messages first
+                * before starting EAPOL processing.
+                */
+               goto try_again;
+       }
+}
+
+
+struct netlink_data * netlink_init(struct netlink_config *cfg)
+{
+       struct netlink_data *netlink;
+       struct sockaddr_nl local;
+
+       netlink = os_zalloc(sizeof(*netlink));
+       if (netlink == NULL)
+               return NULL;
+
+       netlink->cfg = cfg;
+
+       netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+       if (netlink->sock < 0) {
+               wpa_printf(MSG_ERROR, "netlink: Failed to open netlink "
+                          "socket: %s", strerror(errno));
+               netlink_deinit(netlink);
+               return NULL;
+       }
+
+       os_memset(&local, 0, sizeof(local));
+       local.nl_family = AF_NETLINK;
+       local.nl_groups = RTMGRP_LINK;
+       if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0)
+       {
+               wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink "
+                          "socket: %s", strerror(errno));
+               netlink_deinit(netlink);
+               return NULL;
+       }
+
+       eloop_register_read_sock(netlink->sock, netlink_receive, netlink,
+                                NULL);
+
+       return netlink;
+}
+
+
+void netlink_deinit(struct netlink_data *netlink)
+{
+       if (netlink == NULL)
+               return;
+       if (netlink->sock >= 0) {
+               eloop_unregister_read_sock(netlink->sock);
+               close(netlink->sock);
+       }
+       os_free(netlink->cfg);
+       os_free(netlink);
+}
+
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+                          int linkmode, int operstate)
+{
+       struct {
+               struct nlmsghdr hdr;
+               struct ifinfomsg ifinfo;
+               char opts[16];
+       } req;
+       struct rtattr *rta;
+       static int nl_seq;
+       ssize_t ret;
+
+       os_memset(&req, 0, sizeof(req));
+
+       req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
+       req.hdr.nlmsg_type = RTM_SETLINK;
+       req.hdr.nlmsg_flags = NLM_F_REQUEST;
+       req.hdr.nlmsg_seq = ++nl_seq;
+       req.hdr.nlmsg_pid = 0;
+
+       req.ifinfo.ifi_family = AF_UNSPEC;
+       req.ifinfo.ifi_type = 0;
+       req.ifinfo.ifi_index = ifindex;
+       req.ifinfo.ifi_flags = 0;
+       req.ifinfo.ifi_change = 0;
+
+       if (linkmode != -1) {
+               rta = aliasing_hide_typecast(
+                       ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+                       struct rtattr);
+               rta->rta_type = IFLA_LINKMODE;
+               rta->rta_len = RTA_LENGTH(sizeof(char));
+               *((char *) RTA_DATA(rta)) = linkmode;
+               req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
+                       RTA_LENGTH(sizeof(char));
+       }
+       if (operstate != -1) {
+               rta = aliasing_hide_typecast(
+                       ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)),
+                       struct rtattr);
+               rta->rta_type = IFLA_OPERSTATE;
+               rta->rta_len = RTA_LENGTH(sizeof(char));
+               *((char *) RTA_DATA(rta)) = operstate;
+               req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
+                       RTA_LENGTH(sizeof(char));
+       }
+
+       wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
+                  linkmode, operstate);
+
+       ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA "
+                          "failed: %s (assume operstate is not supported)",
+                          strerror(errno));
+       }
+
+       return ret < 0 ? -1 : 0;
+}
diff --git a/src/drivers/netlink.h b/src/drivers/netlink.h
new file mode 100644 (file)
index 0000000..bcbfbb5
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Netlink helper functions for driver wrappers
+ * 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.
+ */
+
+#ifndef NETLINK_H
+#define NETLINK_H
+
+struct netlink_data;
+
+struct netlink_config {
+       void *ctx;
+       void (*newlink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf,
+                          size_t len);
+       void (*dellink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf,
+                          size_t len);
+};
+
+struct netlink_data * netlink_init(struct netlink_config *cfg);
+void netlink_deinit(struct netlink_data *netlink);
+int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
+                          int linkmode, int operstate);
+
+#endif /* NETLINK_H */
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
new file mode 100644 (file)
index 0000000..2ea3ede
--- /dev/null
@@ -0,0 +1,1644 @@
+#ifndef __LINUX_NL80211_H
+#define __LINUX_NL80211_H
+/*
+ * 802.11 netlink interface public header
+ *
+ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2008 Michael Wu <flamingice@sourmilk.net>
+ * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com>
+ * Copyright 2008 Michael Buesch <mb@bu3sch.de>
+ * Copyright 2008, 2009 Luis R. Rodriguez <lrodriguez@atheros.com>
+ * Copyright 2008 Jouni Malinen <jouni.malinen@atheros.com>
+ * Copyright 2008 Colin McCabe <colin@cozybit.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include <linux/types.h>
+
+/**
+ * DOC: Station handling
+ *
+ * Stations are added per interface, but a special case exists with VLAN
+ * interfaces. When a station is bound to an AP interface, it may be moved
+ * into a VLAN identified by a VLAN interface index (%NL80211_ATTR_STA_VLAN).
+ * The station is still assumed to belong to the AP interface it was added
+ * to.
+ *
+ * TODO: need more info?
+ */
+
+/**
+ * enum nl80211_commands - supported nl80211 commands
+ *
+ * @NL80211_CMD_UNSPEC: unspecified command to catch errors
+ *
+ * @NL80211_CMD_GET_WIPHY: request information about a wiphy or dump request
+ *     to get a list of all present wiphys.
+ * @NL80211_CMD_SET_WIPHY: set wiphy parameters, needs %NL80211_ATTR_WIPHY or
+ *     %NL80211_ATTR_IFINDEX; can be used to set %NL80211_ATTR_WIPHY_NAME,
+ *     %NL80211_ATTR_WIPHY_TXQ_PARAMS, %NL80211_ATTR_WIPHY_FREQ,
+ *     %NL80211_ATTR_WIPHY_CHANNEL_TYPE, %NL80211_ATTR_WIPHY_RETRY_SHORT,
+ *     %NL80211_ATTR_WIPHY_RETRY_LONG, %NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+ *     and/or %NL80211_ATTR_WIPHY_RTS_THRESHOLD.
+ * @NL80211_CMD_NEW_WIPHY: Newly created wiphy, response to get request
+ *     or rename notification. Has attributes %NL80211_ATTR_WIPHY and
+ *     %NL80211_ATTR_WIPHY_NAME.
+ * @NL80211_CMD_DEL_WIPHY: Wiphy deleted. Has attributes
+ *     %NL80211_ATTR_WIPHY and %NL80211_ATTR_WIPHY_NAME.
+ *
+ * @NL80211_CMD_GET_INTERFACE: Request an interface's configuration;
+ *     either a dump request on a %NL80211_ATTR_WIPHY or a specific get
+ *     on an %NL80211_ATTR_IFINDEX is supported.
+ * @NL80211_CMD_SET_INTERFACE: Set type of a virtual interface, requires
+ *     %NL80211_ATTR_IFINDEX and %NL80211_ATTR_IFTYPE.
+ * @NL80211_CMD_NEW_INTERFACE: Newly created virtual interface or response
+ *     to %NL80211_CMD_GET_INTERFACE. Has %NL80211_ATTR_IFINDEX,
+ *     %NL80211_ATTR_WIPHY and %NL80211_ATTR_IFTYPE attributes. Can also
+ *     be sent from userspace to request creation of a new virtual interface,
+ *     then requires attributes %NL80211_ATTR_WIPHY, %NL80211_ATTR_IFTYPE and
+ *     %NL80211_ATTR_IFNAME.
+ * @NL80211_CMD_DEL_INTERFACE: Virtual interface was deleted, has attributes
+ *     %NL80211_ATTR_IFINDEX and %NL80211_ATTR_WIPHY. Can also be sent from
+ *     userspace to request deletion of a virtual interface, then requires
+ *     attribute %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_KEY: Get sequence counter information for a key specified
+ *     by %NL80211_ATTR_KEY_IDX and/or %NL80211_ATTR_MAC.
+ * @NL80211_CMD_SET_KEY: Set key attributes %NL80211_ATTR_KEY_DEFAULT,
+ *     %NL80211_ATTR_KEY_DEFAULT_MGMT, or %NL80211_ATTR_KEY_THRESHOLD.
+ * @NL80211_CMD_NEW_KEY: add a key with given %NL80211_ATTR_KEY_DATA,
+ *     %NL80211_ATTR_KEY_IDX, %NL80211_ATTR_MAC, %NL80211_ATTR_KEY_CIPHER,
+ *     and %NL80211_ATTR_KEY_SEQ attributes.
+ * @NL80211_CMD_DEL_KEY: delete a key identified by %NL80211_ATTR_KEY_IDX
+ *     or %NL80211_ATTR_MAC.
+ *
+ * @NL80211_CMD_GET_BEACON: retrieve beacon information (returned in a
+ *     %NL80222_CMD_NEW_BEACON message)
+ * @NL80211_CMD_SET_BEACON: set the beacon on an access point interface
+ *     using the %NL80211_ATTR_BEACON_INTERVAL, %NL80211_ATTR_DTIM_PERIOD,
+ *     %NL80211_ATTR_BEACON_HEAD and %NL80211_ATTR_BEACON_TAIL attributes.
+ * @NL80211_CMD_NEW_BEACON: add a new beacon to an access point interface,
+ *     parameters are like for %NL80211_CMD_SET_BEACON.
+ * @NL80211_CMD_DEL_BEACON: remove the beacon, stop sending it
+ *
+ * @NL80211_CMD_GET_STATION: Get station attributes for station identified by
+ *     %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_STATION: Set station attributes for station identified by
+ *     %NL80211_ATTR_MAC on the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_STATION: Add a station with given attributes to the
+ *     the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
+ *     or, if no MAC address given, all stations, on the interface identified
+ *     by %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
+ *     destination %NL80211_ATTR_MAC on the interface identified by
+ *     %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_MPATH:  Set mesh path attributes for mesh path to
+ *     destination %NL80211_ATTR_MAC on the interface identified by
+ *     %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_NEW_PATH: Add a mesh path with given attributes to the
+ *     the interface identified by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_DEL_PATH: Remove a mesh path identified by %NL80211_ATTR_MAC
+ *     or, if no MAC address given, all mesh paths, on the interface identified
+ *     by %NL80211_ATTR_IFINDEX.
+ * @NL80211_CMD_SET_BSS: Set BSS attributes for BSS identified by
+ *     %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_GET_REG: ask the wireless core to send us its currently set
+ *     regulatory domain.
+ * @NL80211_CMD_SET_REG: Set current regulatory domain. CRDA sends this command
+ *     after being queried by the kernel. CRDA replies by sending a regulatory
+ *     domain structure which consists of %NL80211_ATTR_REG_ALPHA set to our
+ *     current alpha2 if it found a match. It also provides
+ *     NL80211_ATTR_REG_RULE_FLAGS, and a set of regulatory rules. Each
+ *     regulatory rule is a nested set of attributes  given by
+ *     %NL80211_ATTR_REG_RULE_FREQ_[START|END] and
+ *     %NL80211_ATTR_FREQ_RANGE_MAX_BW with an attached power rule given by
+ *     %NL80211_ATTR_REG_RULE_POWER_MAX_ANT_GAIN and
+ *     %NL80211_ATTR_REG_RULE_POWER_MAX_EIRP.
+ * @NL80211_CMD_REQ_SET_REG: ask the wireless core to set the regulatory domain
+ *     to the the specified ISO/IEC 3166-1 alpha2 country code. The core will
+ *     store this as a valid request and then query userspace for it.
+ *
+ * @NL80211_CMD_GET_MESH_PARAMS: Get mesh networking properties for the
+ *     interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MESH_PARAMS: Set mesh networking properties for the
+ *      interface identified by %NL80211_ATTR_IFINDEX
+ *
+ * @NL80211_CMD_SET_MGMT_EXTRA_IE: Set extra IEs for management frames. The
+ *     interface is identified with %NL80211_ATTR_IFINDEX and the management
+ *     frame subtype with %NL80211_ATTR_MGMT_SUBTYPE. The extra IE data to be
+ *     added to the end of the specified management frame is specified with
+ *     %NL80211_ATTR_IE. If the command succeeds, the requested data will be
+ *     added to all specified management frames generated by
+ *     kernel/firmware/driver.
+ *     Note: This command has been removed and it is only reserved at this
+ *     point to avoid re-using existing command number. The functionality this
+ *     command was planned for has been provided with cleaner design with the
+ *     option to specify additional IEs in NL80211_CMD_TRIGGER_SCAN,
+ *     NL80211_CMD_AUTHENTICATE, NL80211_CMD_ASSOCIATE,
+ *     NL80211_CMD_DEAUTHENTICATE, and NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_CMD_GET_SCAN: get scan results
+ * @NL80211_CMD_TRIGGER_SCAN: trigger a new scan with the given parameters
+ * @NL80211_CMD_NEW_SCAN_RESULTS: scan notification (as a reply to
+ *     NL80211_CMD_GET_SCAN and on the "scan" multicast group)
+ * @NL80211_CMD_SCAN_ABORTED: scan was aborted, for unspecified reasons,
+ *     partial scan results may be available
+ *
+ * @NL80211_CMD_GET_SURVEY: get survey resuls, e.g. channel occupation
+ *      or noise level
+ * @NL80211_CMD_NEW_SURVEY_RESULTS: survey data notification (as a reply to
+ *     NL80211_CMD_GET_SURVEY and on the "scan" multicast group)
+ *
+ * @NL80211_CMD_REG_CHANGE: indicates to userspace the regulatory domain
+ *     has been changed and provides details of the request information
+ *     that caused the change such as who initiated the regulatory request
+ *     (%NL80211_ATTR_REG_INITIATOR), the wiphy_idx
+ *     (%NL80211_ATTR_REG_ALPHA2) on which the request was made from if
+ *     the initiator was %NL80211_REGDOM_SET_BY_COUNTRY_IE or
+ *     %NL80211_REGDOM_SET_BY_DRIVER, the type of regulatory domain
+ *     set (%NL80211_ATTR_REG_TYPE), if the type of regulatory domain is
+ *     %NL80211_REG_TYPE_COUNTRY the alpha2 to which we have moved on
+ *     to (%NL80211_ATTR_REG_ALPHA2).
+ * @NL80211_CMD_REG_BEACON_HINT: indicates to userspace that an AP beacon
+ *     has been found while world roaming thus enabling active scan or
+ *     any mode of operation that initiates TX (beacons) on a channel
+ *     where we would not have been able to do either before. As an example
+ *     if you are world roaming (regulatory domain set to world or if your
+ *     driver is using a custom world roaming regulatory domain) and while
+ *     doing a passive scan on the 5 GHz band you find an AP there (if not
+ *     on a DFS channel) you will now be able to actively scan for that AP
+ *     or use AP mode on your card on that same channel. Note that this will
+ *     never be used for channels 1-11 on the 2 GHz band as they are always
+ *     enabled world wide. This beacon hint is only sent if your device had
+ *     either disabled active scanning or beaconing on a channel. We send to
+ *     userspace the wiphy on which we removed a restriction from
+ *     (%NL80211_ATTR_WIPHY) and the channel on which this occurred
+ *     before (%NL80211_ATTR_FREQ_BEFORE) and after (%NL80211_ATTR_FREQ_AFTER)
+ *     the beacon hint was processed.
+ *
+ * @NL80211_CMD_AUTHENTICATE: authentication request and notification.
+ *     This command is used both as a command (request to authenticate) and
+ *     as an event on the "mlme" multicast group indicating completion of the
+ *     authentication process.
+ *     When used as a command, %NL80211_ATTR_IFINDEX is used to identify the
+ *     interface. %NL80211_ATTR_MAC is used to specify PeerSTAAddress (and
+ *     BSSID in case of station mode). %NL80211_ATTR_SSID is used to specify
+ *     the SSID (mainly for association, but is included in authentication
+ *     request, too, to help BSS selection. %NL80211_ATTR_WIPHY_FREQ is used
+ *     to specify the frequence of the channel in MHz. %NL80211_ATTR_AUTH_TYPE
+ *     is used to specify the authentication type. %NL80211_ATTR_IE is used to
+ *     define IEs (VendorSpecificInfo, but also including RSN IE and FT IEs)
+ *     to be added to the frame.
+ *     When used as an event, this reports reception of an Authentication
+ *     frame in station and IBSS modes when the local MLME processed the
+ *     frame, i.e., it was for the local STA and was received in correct
+ *     state. This is similar to MLME-AUTHENTICATE.confirm primitive in the
+ *     MLME SAP interface (kernel providing MLME, userspace SME). The
+ *     included %NL80211_ATTR_FRAME attribute contains the management frame
+ *     (including both the header and frame body, but not FCS). This event is
+ *     also used to indicate if the authentication attempt timed out. In that
+ *     case the %NL80211_ATTR_FRAME attribute is replaced with a
+ *     %NL80211_ATTR_TIMED_OUT flag (and %NL80211_ATTR_MAC to indicate which
+ *     pending authentication timed out).
+ * @NL80211_CMD_ASSOCIATE: association request and notification; like
+ *     NL80211_CMD_AUTHENTICATE but for Association and Reassociation
+ *     (similar to MLME-ASSOCIATE.request, MLME-REASSOCIATE.request,
+ *     MLME-ASSOCIATE.confirm or MLME-REASSOCIATE.confirm primitives).
+ * @NL80211_CMD_DEAUTHENTICATE: deauthentication request and notification; like
+ *     NL80211_CMD_AUTHENTICATE but for Deauthentication frames (similar to
+ *     MLME-DEAUTHENTICATION.request and MLME-DEAUTHENTICATE.indication
+ *     primitives).
+ * @NL80211_CMD_DISASSOCIATE: disassociation request and notification; like
+ *     NL80211_CMD_AUTHENTICATE but for Disassociation frames (similar to
+ *     MLME-DISASSOCIATE.request and MLME-DISASSOCIATE.indication primitives).
+ *
+ * @NL80211_CMD_MICHAEL_MIC_FAILURE: notification of a locally detected Michael
+ *     MIC (part of TKIP) failure; sent on the "mlme" multicast group; the
+ *     event includes %NL80211_ATTR_MAC to describe the source MAC address of
+ *     the frame with invalid MIC, %NL80211_ATTR_KEY_TYPE to show the key
+ *     type, %NL80211_ATTR_KEY_IDX to indicate the key identifier, and
+ *     %NL80211_ATTR_KEY_SEQ to indicate the TSC value of the frame; this
+ *     event matches with MLME-MICHAELMICFAILURE.indication() primitive
+ *
+ * @NL80211_CMD_JOIN_IBSS: Join a new IBSS -- given at least an SSID and a
+ *     FREQ attribute (for the initial frequency if no peer can be found)
+ *     and optionally a MAC (as BSSID) and FREQ_FIXED attribute if those
+ *     should be fixed rather than automatically determined. Can only be
+ *     executed on a network interface that is UP, and fixed BSSID/FREQ
+ *     may be rejected. Another optional parameter is the beacon interval,
+ *     given in the %NL80211_ATTR_BEACON_INTERVAL attribute, which if not
+ *     given defaults to 100 TU (102.4ms).
+ * @NL80211_CMD_LEAVE_IBSS: Leave the IBSS -- no special arguments, the IBSS is
+ *     determined by the network interface.
+ *
+ * @NL80211_CMD_TESTMODE: testmode command, takes a wiphy (or ifindex) attribute
+ *     to identify the device, and the TESTDATA blob attribute to pass through
+ *     to the driver.
+ *
+ * @NL80211_CMD_CONNECT: connection request and notification; this command
+ *     requests to connect to a specified network but without separating
+ *     auth and assoc steps. For this, you need to specify the SSID in a
+ *     %NL80211_ATTR_SSID attribute, and can optionally specify the association
+ *     IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_MAC,
+ *     %NL80211_ATTR_WIPHY_FREQ and %NL80211_ATTR_CONTROL_PORT.
+ *     It is also sent as an event, with the BSSID and response IEs when the
+ *     connection is established or failed to be established. This can be
+ *     determined by the STATUS_CODE attribute.
+ * @NL80211_CMD_ROAM: request that the card roam (currently not implemented),
+ *     sent as an event when the card/driver roamed by itself.
+ * @NL80211_CMD_DISCONNECT: drop a given connection; also used to notify
+ *     userspace that a connection was dropped by the AP or due to other
+ *     reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
+ *     %NL80211_ATTR_REASON_CODE attributes are used.
+ *
+ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices
+ *     associated with this wiphy must be down and will follow.
+ *
+ * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified
+ *     channel for the specified amount of time. This can be used to do
+ *     off-channel operations like transmit a Public Action frame and wait for
+ *     a response while being associated to an AP on another channel.
+ *     %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which
+ *     radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the
+ *     frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be
+ *     optionally used to specify additional channel parameters.
+ *     %NL80211_ATTR_DURATION is used to specify the duration in milliseconds
+ *     to remain on the channel. This command is also used as an event to
+ *     notify when the requested duration starts (it may take a while for the
+ *     driver to schedule this time due to other concurrent needs for the
+ *     radio).
+ *     When called, this operation returns a cookie (%NL80211_ATTR_COOKIE)
+ *     that will be included with any events pertaining to this request;
+ *     the cookie is also used to cancel the request.
+ * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a
+ *     pending remain-on-channel duration if the desired operation has been
+ *     completed prior to expiration of the originally requested duration.
+ *     %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the
+ *     radio. The %NL80211_ATTR_COOKIE attribute must be given as well to
+ *     uniquely identify the request.
+ *     This command is also used as an event to notify when a requested
+ *     remain-on-channel duration has expired.
+ *
+ * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX
+ *     rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface
+ *     and @NL80211_ATTR_TX_RATES the set of allowed rates.
+ *
+ * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames
+ *     (via @NL80211_CMD_ACTION) for processing in userspace. This command
+ *     requires an interface index and a match attribute containing the first
+ *     few bytes of the frame that should match, e.g. a single byte for only
+ *     a category match or four bytes for vendor frames including the OUI.
+ *     The registration cannot be dropped, but is removed automatically
+ *     when the netlink socket is closed. Multiple registrations can be made.
+ * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This
+ *     command is used both as a request to transmit an Action frame and as an
+ *     event indicating reception of an Action frame that was not processed in
+ *     kernel code, but is for us (i.e., which may need to be processed in a
+ *     user space application). %NL80211_ATTR_FRAME is used to specify the
+ *     frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and
+ *     optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on
+ *     which channel the frame is to be transmitted or was received. This
+ *     channel has to be the current channel (remain-on-channel or the
+ *     operational channel). When called, this operation returns a cookie
+ *     (%NL80211_ATTR_COOKIE) that will be included with the TX status event
+ *     pertaining to the TX request.
+ * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame
+ *     transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies
+ *     the TX command and %NL80211_ATTR_FRAME includes the contents of the
+ *     frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
+ *     the frame.
+ * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
+ *     is used to configure connection quality monitoring notification trigger
+ *     levels.
+ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
+ *     command is used as an event to indicate the that a trigger level was
+ *     reached.
+ *
+ * @NL80211_CMD_MAX: highest used command number
+ * @__NL80211_CMD_AFTER_LAST: internal use
+ */
+enum nl80211_commands {
+/* don't change the order or add anything inbetween, this is ABI! */
+       NL80211_CMD_UNSPEC,
+
+       NL80211_CMD_GET_WIPHY,          /* can dump */
+       NL80211_CMD_SET_WIPHY,
+       NL80211_CMD_NEW_WIPHY,
+       NL80211_CMD_DEL_WIPHY,
+
+       NL80211_CMD_GET_INTERFACE,      /* can dump */
+       NL80211_CMD_SET_INTERFACE,
+       NL80211_CMD_NEW_INTERFACE,
+       NL80211_CMD_DEL_INTERFACE,
+
+       NL80211_CMD_GET_KEY,
+       NL80211_CMD_SET_KEY,
+       NL80211_CMD_NEW_KEY,
+       NL80211_CMD_DEL_KEY,
+
+       NL80211_CMD_GET_BEACON,
+       NL80211_CMD_SET_BEACON,
+       NL80211_CMD_NEW_BEACON,
+       NL80211_CMD_DEL_BEACON,
+
+       NL80211_CMD_GET_STATION,
+       NL80211_CMD_SET_STATION,
+       NL80211_CMD_NEW_STATION,
+       NL80211_CMD_DEL_STATION,
+
+       NL80211_CMD_GET_MPATH,
+       NL80211_CMD_SET_MPATH,
+       NL80211_CMD_NEW_MPATH,
+       NL80211_CMD_DEL_MPATH,
+
+       NL80211_CMD_SET_BSS,
+
+       NL80211_CMD_SET_REG,
+       NL80211_CMD_REQ_SET_REG,
+
+       NL80211_CMD_GET_MESH_PARAMS,
+       NL80211_CMD_SET_MESH_PARAMS,
+
+       NL80211_CMD_SET_MGMT_EXTRA_IE /* reserved; not used */,
+
+       NL80211_CMD_GET_REG,
+
+       NL80211_CMD_GET_SCAN,
+       NL80211_CMD_TRIGGER_SCAN,
+       NL80211_CMD_NEW_SCAN_RESULTS,
+       NL80211_CMD_SCAN_ABORTED,
+
+       NL80211_CMD_REG_CHANGE,
+
+       NL80211_CMD_AUTHENTICATE,
+       NL80211_CMD_ASSOCIATE,
+       NL80211_CMD_DEAUTHENTICATE,
+       NL80211_CMD_DISASSOCIATE,
+
+       NL80211_CMD_MICHAEL_MIC_FAILURE,
+
+       NL80211_CMD_REG_BEACON_HINT,
+
+       NL80211_CMD_JOIN_IBSS,
+       NL80211_CMD_LEAVE_IBSS,
+
+       NL80211_CMD_TESTMODE,
+
+       NL80211_CMD_CONNECT,
+       NL80211_CMD_ROAM,
+       NL80211_CMD_DISCONNECT,
+
+       NL80211_CMD_SET_WIPHY_NETNS,
+
+       NL80211_CMD_GET_SURVEY,
+       NL80211_CMD_NEW_SURVEY_RESULTS,
+
+       NL80211_CMD_SET_PMKSA,
+       NL80211_CMD_DEL_PMKSA,
+       NL80211_CMD_FLUSH_PMKSA,
+
+       NL80211_CMD_REMAIN_ON_CHANNEL,
+       NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
+
+       NL80211_CMD_SET_TX_BITRATE_MASK,
+
+       NL80211_CMD_REGISTER_ACTION,
+       NL80211_CMD_ACTION,
+       NL80211_CMD_ACTION_TX_STATUS,
+
+       NL80211_CMD_SET_POWER_SAVE,
+       NL80211_CMD_GET_POWER_SAVE,
+
+       NL80211_CMD_SET_CQM,
+       NL80211_CMD_NOTIFY_CQM,
+
+       /* add new commands above here */
+
+       /* used to define NL80211_CMD_MAX below */
+       __NL80211_CMD_AFTER_LAST,
+       NL80211_CMD_MAX = __NL80211_CMD_AFTER_LAST - 1
+};
+
+/*
+ * Allow user space programs to use #ifdef on new commands by defining them
+ * here
+ */
+#define NL80211_CMD_SET_BSS NL80211_CMD_SET_BSS
+#define NL80211_CMD_SET_MGMT_EXTRA_IE NL80211_CMD_SET_MGMT_EXTRA_IE
+#define NL80211_CMD_REG_CHANGE NL80211_CMD_REG_CHANGE
+#define NL80211_CMD_AUTHENTICATE NL80211_CMD_AUTHENTICATE
+#define NL80211_CMD_ASSOCIATE NL80211_CMD_ASSOCIATE
+#define NL80211_CMD_DEAUTHENTICATE NL80211_CMD_DEAUTHENTICATE
+#define NL80211_CMD_DISASSOCIATE NL80211_CMD_DISASSOCIATE
+#define NL80211_CMD_REG_BEACON_HINT NL80211_CMD_REG_BEACON_HINT
+
+/**
+ * enum nl80211_attrs - nl80211 netlink attributes
+ *
+ * @NL80211_ATTR_UNSPEC: unspecified attribute to catch errors
+ *
+ * @NL80211_ATTR_WIPHY: index of wiphy to operate on, cf.
+ *     /sys/class/ieee80211/<phyname>/index
+ * @NL80211_ATTR_WIPHY_NAME: wiphy name (used for renaming)
+ * @NL80211_ATTR_WIPHY_TXQ_PARAMS: a nested array of TX queue parameters
+ * @NL80211_ATTR_WIPHY_FREQ: frequency of the selected channel in MHz
+ * @NL80211_ATTR_WIPHY_CHANNEL_TYPE: included with NL80211_ATTR_WIPHY_FREQ
+ *     if HT20 or HT40 are allowed (i.e., 802.11n disabled if not included):
+ *     NL80211_CHAN_NO_HT = HT not allowed (i.e., same as not including
+ *             this attribute)
+ *     NL80211_CHAN_HT20 = HT20 only
+ *     NL80211_CHAN_HT40MINUS = secondary channel is below the primary channel
+ *     NL80211_CHAN_HT40PLUS = secondary channel is above the primary channel
+ * @NL80211_ATTR_WIPHY_RETRY_SHORT: TX retry limit for frames whose length is
+ *     less than or equal to the RTS threshold; allowed range: 1..255;
+ *     dot11ShortRetryLimit; u8
+ * @NL80211_ATTR_WIPHY_RETRY_LONG: TX retry limit for frames whose length is
+ *     greater than the RTS threshold; allowed range: 1..255;
+ *     dot11ShortLongLimit; u8
+ * @NL80211_ATTR_WIPHY_FRAG_THRESHOLD: fragmentation threshold, i.e., maximum
+ *     length in octets for frames; allowed range: 256..8000, disable
+ *     fragmentation with (u32)-1; dot11FragmentationThreshold; u32
+ * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length
+ *     larger than or equal to this use RTS/CTS handshake); allowed range:
+ *     0..65536, disable with (u32)-1; dot11RTSThreshold; u32
+ * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11
+ *     section 7.3.2.9; dot11CoverageClass; u8
+ *
+ * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on
+ * @NL80211_ATTR_IFNAME: network interface name
+ * @NL80211_ATTR_IFTYPE: type of virtual interface, see &enum nl80211_iftype
+ *
+ * @NL80211_ATTR_MAC: MAC address (various uses)
+ *
+ * @NL80211_ATTR_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *     16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *     keys
+ * @NL80211_ATTR_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_ATTR_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *     section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_ATTR_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *     CCMP keys, each six bytes in little endian
+ *
+ * @NL80211_ATTR_BEACON_INTERVAL: beacon interval in TU
+ * @NL80211_ATTR_DTIM_PERIOD: DTIM period for beaconing
+ * @NL80211_ATTR_BEACON_HEAD: portion of the beacon before the TIM IE
+ * @NL80211_ATTR_BEACON_TAIL: portion of the beacon after the TIM IE
+ *
+ * @NL80211_ATTR_STA_AID: Association ID for the station (u16)
+ * @NL80211_ATTR_STA_FLAGS: flags, nested element with NLA_FLAG attributes of
+ *     &enum nl80211_sta_flags (deprecated, use %NL80211_ATTR_STA_FLAGS2)
+ * @NL80211_ATTR_STA_LISTEN_INTERVAL: listen interval as defined by
+ *     IEEE 802.11 7.3.1.6 (u16).
+ * @NL80211_ATTR_STA_SUPPORTED_RATES: supported rates, array of supported
+ *     rates as defined by IEEE 802.11 7.3.2.2 but without the length
+ *     restriction (at most %NL80211_MAX_SUPP_RATES).
+ * @NL80211_ATTR_STA_VLAN: interface index of VLAN interface to move station
+ *     to, or the AP interface the station was originally added to to.
+ * @NL80211_ATTR_STA_INFO: information about a station, part of station info
+ *     given for %NL80211_CMD_GET_STATION, nested attribute containing
+ *     info as possible, see &enum nl80211_sta_info.
+ *
+ * @NL80211_ATTR_WIPHY_BANDS: Information about an operating bands,
+ *     consisting of a nested array.
+ *
+ * @NL80211_ATTR_MESH_ID: mesh id (1-32 bytes).
+ * @NL80211_ATTR_PLINK_ACTION: action to perform on the mesh peer link.
+ * @NL80211_ATTR_MPATH_NEXT_HOP: MAC address of the next hop for a mesh path.
+ * @NL80211_ATTR_MPATH_INFO: information about a mesh_path, part of mesh path
+ *     info given for %NL80211_CMD_GET_MPATH, nested attribute described at
+ *     &enum nl80211_mpath_info.
+ *
+ * @NL80211_ATTR_MNTR_FLAGS: flags, nested element with NLA_FLAG attributes of
+ *      &enum nl80211_mntr_flags.
+ *
+ * @NL80211_ATTR_REG_ALPHA2: an ISO-3166-alpha2 country code for which the
+ *     current regulatory domain should be set to or is already set to.
+ *     For example, 'CR', for Costa Rica. This attribute is used by the kernel
+ *     to query the CRDA to retrieve one regulatory domain. This attribute can
+ *     also be used by userspace to query the kernel for the currently set
+ *     regulatory domain. We chose an alpha2 as that is also used by the
+ *     IEEE-802.11d country information element to identify a country.
+ *     Users can also simply ask the wireless core to set regulatory domain
+ *     to a specific alpha2.
+ * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
+ *     rules.
+ *
+ * @NL80211_ATTR_BSS_CTS_PROT: whether CTS protection is enabled (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_PREAMBLE: whether short preamble is enabled
+ *     (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_SHORT_SLOT_TIME: whether short slot time enabled
+ *     (u8, 0 or 1)
+ * @NL80211_ATTR_BSS_BASIC_RATES: basic rates, array of basic
+ *     rates in format defined by IEEE 802.11 7.3.2.2 but without the length
+ *     restriction (at most %NL80211_MAX_SUPP_RATES).
+ *
+ * @NL80211_ATTR_HT_CAPABILITY: HT Capability information element (from
+ *     association request when used with NL80211_CMD_NEW_STATION)
+ *
+ * @NL80211_ATTR_SUPPORTED_IFTYPES: nested attribute containing all
+ *     supported interface types, each a flag attribute with the number
+ *     of the interface mode.
+ *
+ * @NL80211_ATTR_MGMT_SUBTYPE: Management frame subtype for
+ *     %NL80211_CMD_SET_MGMT_EXTRA_IE.
+ *
+ * @NL80211_ATTR_IE: Information element(s) data (used, e.g., with
+ *     %NL80211_CMD_SET_MGMT_EXTRA_IE).
+ *
+ * @NL80211_ATTR_MAX_NUM_SCAN_SSIDS: number of SSIDs you can scan with
+ *     a single scan request, a wiphy attribute.
+ * @NL80211_ATTR_MAX_SCAN_IE_LEN: maximum length of information elements
+ *     that can be added to a scan request
+ *
+ * @NL80211_ATTR_SCAN_FREQUENCIES: nested attribute with frequencies (in MHz)
+ * @NL80211_ATTR_SCAN_SSIDS: nested attribute with SSIDs, leave out for passive
+ *     scanning and include a zero-length SSID (wildcard) for wildcard scan
+ * @NL80211_ATTR_BSS: scan result BSS
+ *
+ * @NL80211_ATTR_REG_INITIATOR: indicates who requested the regulatory domain
+ *     currently in effect. This could be any of the %NL80211_REGDOM_SET_BY_*
+ * @NL80211_ATTR_REG_TYPE: indicates the type of the regulatory domain currently
+ *     set. This can be one of the nl80211_reg_type (%NL80211_REGDOM_TYPE_*)
+ *
+ * @NL80211_ATTR_SUPPORTED_COMMANDS: wiphy attribute that specifies
+ *     an array of command numbers (i.e. a mapping index to command number)
+ *     that the driver for the given wiphy supports.
+ *
+ * @NL80211_ATTR_FRAME: frame data (binary attribute), including frame header
+ *     and body, but not FCS; used, e.g., with NL80211_CMD_AUTHENTICATE and
+ *     NL80211_CMD_ASSOCIATE events
+ * @NL80211_ATTR_SSID: SSID (binary attribute, 0..32 octets)
+ * @NL80211_ATTR_AUTH_TYPE: AuthenticationType, see &enum nl80211_auth_type,
+ *     represented as a u32
+ * @NL80211_ATTR_REASON_CODE: ReasonCode for %NL80211_CMD_DEAUTHENTICATE and
+ *     %NL80211_CMD_DISASSOCIATE, u16
+ *
+ * @NL80211_ATTR_KEY_TYPE: Key Type, see &enum nl80211_key_type, represented as
+ *     a u32
+ *
+ * @NL80211_ATTR_FREQ_BEFORE: A channel which has suffered a regulatory change
+ *     due to considerations from a beacon hint. This attribute reflects
+ *     the state of the channel _before_ the beacon hint processing. This
+ *     attributes consists of a nested attribute containing
+ *     NL80211_FREQUENCY_ATTR_*
+ * @NL80211_ATTR_FREQ_AFTER: A channel which has suffered a regulatory change
+ *     due to considerations from a beacon hint. This attribute reflects
+ *     the state of the channel _after_ the beacon hint processing. This
+ *     attributes consists of a nested attribute containing
+ *     NL80211_FREQUENCY_ATTR_*
+ *
+ * @NL80211_ATTR_CIPHER_SUITES: a set of u32 values indicating the supported
+ *     cipher suites
+ *
+ * @NL80211_ATTR_FREQ_FIXED: a flag indicating the IBSS should not try to look
+ *     for other networks on different channels
+ *
+ * @NL80211_ATTR_TIMED_OUT: a flag indicating than an operation timed out; this
+ *     is used, e.g., with %NL80211_CMD_AUTHENTICATE event
+ *
+ * @NL80211_ATTR_USE_MFP: Whether management frame protection (IEEE 802.11w) is
+ *     used for the association (&enum nl80211_mfp, represented as a u32);
+ *     this attribute can be used
+ *     with %NL80211_CMD_ASSOCIATE request
+ *
+ * @NL80211_ATTR_STA_FLAGS2: Attribute containing a
+ *     &struct nl80211_sta_flag_update.
+ *
+ * @NL80211_ATTR_CONTROL_PORT: A flag indicating whether user space controls
+ *     IEEE 802.1X port, i.e., sets/clears %NL80211_STA_FLAG_AUTHORIZED, in
+ *     station mode. If the flag is included in %NL80211_CMD_ASSOCIATE
+ *     request, the driver will assume that the port is unauthorized until
+ *     authorized by user space. Otherwise, port is marked authorized by
+ *     default in station mode.
+ *
+ * @NL80211_ATTR_TESTDATA: Testmode data blob, passed through to the driver.
+ *     We recommend using nested, driver-specific attributes within this.
+ *
+ * @NL80211_ATTR_DISCONNECTED_BY_AP: A flag indicating that the DISCONNECT
+ *     event was due to the AP disconnecting the station, and not due to
+ *     a local disconnect request.
+ * @NL80211_ATTR_STATUS_CODE: StatusCode for the %NL80211_CMD_CONNECT
+ *     event (u16)
+ * @NL80211_ATTR_PRIVACY: Flag attribute, used with connect(), indicating
+ *     that protected APs should be used.
+ *
+ * @NL80211_ATTR_CIPHERS_PAIRWISE: Used with CONNECT and ASSOCIATE to
+ *     indicate which unicast key ciphers will be used with the connection
+ *     (an array of u32).
+ * @NL80211_ATTR_CIPHER_GROUP: Used with CONNECT and ASSOCIATE to indicate
+ *     which group key cipher will be used with the connection (a u32).
+ * @NL80211_ATTR_WPA_VERSIONS: Used with CONNECT and ASSOCIATE to indicate
+ *     which WPA version(s) the AP we want to associate with is using
+ *     (a u32 with flags from &enum nl80211_wpa_versions).
+ * @NL80211_ATTR_AKM_SUITES: Used with CONNECT and ASSOCIATE to indicate
+ *     which key management algorithm(s) to use (an array of u32).
+ *
+ * @NL80211_ATTR_REQ_IE: (Re)association request information elements as
+ *     sent out by the card, for ROAM and successful CONNECT events.
+ * @NL80211_ATTR_RESP_IE: (Re)association response information elements as
+ *     sent by peer, for ROAM and successful CONNECT events.
+ *
+ * @NL80211_ATTR_PREV_BSSID: previous BSSID, to be used by in ASSOCIATE
+ *     commands to specify using a reassociate frame
+ *
+ * @NL80211_ATTR_KEY: key information in a nested attribute with
+ *     %NL80211_KEY_* sub-attributes
+ * @NL80211_ATTR_KEYS: array of keys for static WEP keys for connect()
+ *     and join_ibss(), key information is in a nested attribute each
+ *     with %NL80211_KEY_* sub-attributes
+ *
+ * @NL80211_ATTR_PID: Process ID of a network namespace.
+ *
+ * @NL80211_ATTR_GENERATION: Used to indicate consistent snapshots for
+ *     dumps. This number increases whenever the object list being
+ *     dumped changes, and as such userspace can verify that it has
+ *     obtained a complete and consistent snapshot by verifying that
+ *     all dump messages contain the same generation number. If it
+ *     changed then the list changed and the dump should be repeated
+ *     completely from scratch.
+ *
+ * @NL80211_ATTR_4ADDR: Use 4-address frames on a virtual interface
+ *
+ * @NL80211_ATTR_SURVEY_INFO: survey information about a channel, part of
+ *      the survey response for %NL80211_CMD_GET_SURVEY, nested attribute
+ *      containing info as possible, see &enum survey_info.
+ *
+ * @NL80211_ATTR_PMKID: PMK material for PMKSA caching.
+ * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can
+ *     cache, a wiphy attribute.
+ *
+ * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32.
+ *
+ * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects.
+ *
+ * @NL80211_ATTR_TX_RATES: Nested set of attributes
+ *     (enum nl80211_tx_rate_attributes) describing TX rates per band. The
+ *     enum nl80211_band value is used as the index (nla_type() of the nested
+ *     data. If a band is not included, it will be configured to allow all
+ *     rates based on negotiated supported rates information. This attribute
+ *     is used with %NL80211_CMD_SET_TX_BITRATE_MASK.
+ *
+ * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain
+ *     at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION.
+ *
+ * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
+ *     acknowledged by the recipient.
+ *
+ * @NL80211_ATTR_CQM: connection quality monitor configuration in a
+ *     nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
+ *
+ * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command
+ *     is requesting a local authentication/association state change without
+ *     invoking actual management frame exchange. This can be used with
+ *     NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE,
+ *     NL80211_CMD_DISASSOCIATE.
+ *
+ * @NL80211_ATTR_MAX: highest attribute number currently defined
+ * @__NL80211_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_attrs {
+/* don't change the order or add anything inbetween, this is ABI! */
+       NL80211_ATTR_UNSPEC,
+
+       NL80211_ATTR_WIPHY,
+       NL80211_ATTR_WIPHY_NAME,
+
+       NL80211_ATTR_IFINDEX,
+       NL80211_ATTR_IFNAME,
+       NL80211_ATTR_IFTYPE,
+
+       NL80211_ATTR_MAC,
+
+       NL80211_ATTR_KEY_DATA,
+       NL80211_ATTR_KEY_IDX,
+       NL80211_ATTR_KEY_CIPHER,
+       NL80211_ATTR_KEY_SEQ,
+       NL80211_ATTR_KEY_DEFAULT,
+
+       NL80211_ATTR_BEACON_INTERVAL,
+       NL80211_ATTR_DTIM_PERIOD,
+       NL80211_ATTR_BEACON_HEAD,
+       NL80211_ATTR_BEACON_TAIL,
+
+       NL80211_ATTR_STA_AID,
+       NL80211_ATTR_STA_FLAGS,
+       NL80211_ATTR_STA_LISTEN_INTERVAL,
+       NL80211_ATTR_STA_SUPPORTED_RATES,
+       NL80211_ATTR_STA_VLAN,
+       NL80211_ATTR_STA_INFO,
+
+       NL80211_ATTR_WIPHY_BANDS,
+
+       NL80211_ATTR_MNTR_FLAGS,
+
+       NL80211_ATTR_MESH_ID,
+       NL80211_ATTR_STA_PLINK_ACTION,
+       NL80211_ATTR_MPATH_NEXT_HOP,
+       NL80211_ATTR_MPATH_INFO,
+
+       NL80211_ATTR_BSS_CTS_PROT,
+       NL80211_ATTR_BSS_SHORT_PREAMBLE,
+       NL80211_ATTR_BSS_SHORT_SLOT_TIME,
+
+       NL80211_ATTR_HT_CAPABILITY,
+
+       NL80211_ATTR_SUPPORTED_IFTYPES,
+
+       NL80211_ATTR_REG_ALPHA2,
+       NL80211_ATTR_REG_RULES,
+
+       NL80211_ATTR_MESH_PARAMS,
+
+       NL80211_ATTR_BSS_BASIC_RATES,
+
+       NL80211_ATTR_WIPHY_TXQ_PARAMS,
+       NL80211_ATTR_WIPHY_FREQ,
+       NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+
+       NL80211_ATTR_KEY_DEFAULT_MGMT,
+
+       NL80211_ATTR_MGMT_SUBTYPE,
+       NL80211_ATTR_IE,
+
+       NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
+
+       NL80211_ATTR_SCAN_FREQUENCIES,
+       NL80211_ATTR_SCAN_SSIDS,
+       NL80211_ATTR_GENERATION, /* replaces old SCAN_GENERATION */
+       NL80211_ATTR_BSS,
+
+       NL80211_ATTR_REG_INITIATOR,
+       NL80211_ATTR_REG_TYPE,
+
+       NL80211_ATTR_SUPPORTED_COMMANDS,
+
+       NL80211_ATTR_FRAME,
+       NL80211_ATTR_SSID,
+       NL80211_ATTR_AUTH_TYPE,
+       NL80211_ATTR_REASON_CODE,
+
+       NL80211_ATTR_KEY_TYPE,
+
+       NL80211_ATTR_MAX_SCAN_IE_LEN,
+       NL80211_ATTR_CIPHER_SUITES,
+
+       NL80211_ATTR_FREQ_BEFORE,
+       NL80211_ATTR_FREQ_AFTER,
+
+       NL80211_ATTR_FREQ_FIXED,
+
+
+       NL80211_ATTR_WIPHY_RETRY_SHORT,
+       NL80211_ATTR_WIPHY_RETRY_LONG,
+       NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
+       NL80211_ATTR_WIPHY_RTS_THRESHOLD,
+
+       NL80211_ATTR_TIMED_OUT,
+
+       NL80211_ATTR_USE_MFP,
+
+       NL80211_ATTR_STA_FLAGS2,
+
+       NL80211_ATTR_CONTROL_PORT,
+
+       NL80211_ATTR_TESTDATA,
+
+       NL80211_ATTR_PRIVACY,
+
+       NL80211_ATTR_DISCONNECTED_BY_AP,
+       NL80211_ATTR_STATUS_CODE,
+
+       NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+       NL80211_ATTR_CIPHER_SUITE_GROUP,
+       NL80211_ATTR_WPA_VERSIONS,
+       NL80211_ATTR_AKM_SUITES,
+
+       NL80211_ATTR_REQ_IE,
+       NL80211_ATTR_RESP_IE,
+
+       NL80211_ATTR_PREV_BSSID,
+
+       NL80211_ATTR_KEY,
+       NL80211_ATTR_KEYS,
+
+       NL80211_ATTR_PID,
+
+       NL80211_ATTR_4ADDR,
+
+       NL80211_ATTR_SURVEY_INFO,
+
+       NL80211_ATTR_PMKID,
+       NL80211_ATTR_MAX_NUM_PMKIDS,
+
+       NL80211_ATTR_DURATION,
+
+       NL80211_ATTR_COOKIE,
+
+       NL80211_ATTR_WIPHY_COVERAGE_CLASS,
+
+       NL80211_ATTR_TX_RATES,
+
+       NL80211_ATTR_FRAME_MATCH,
+
+       NL80211_ATTR_ACK,
+
+       NL80211_ATTR_PS_STATE,
+
+       NL80211_ATTR_CQM,
+
+       NL80211_ATTR_LOCAL_STATE_CHANGE,
+
+       /* add attributes here, update the policy in nl80211.c */
+
+       __NL80211_ATTR_AFTER_LAST,
+       NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
+};
+
+/* source-level API compatibility */
+#define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
+
+/*
+ * Allow user space programs to use #ifdef on new attributes by defining them
+ * here
+ */
+#define NL80211_CMD_CONNECT NL80211_CMD_CONNECT
+#define NL80211_ATTR_HT_CAPABILITY NL80211_ATTR_HT_CAPABILITY
+#define NL80211_ATTR_BSS_BASIC_RATES NL80211_ATTR_BSS_BASIC_RATES
+#define NL80211_ATTR_WIPHY_TXQ_PARAMS NL80211_ATTR_WIPHY_TXQ_PARAMS
+#define NL80211_ATTR_WIPHY_FREQ NL80211_ATTR_WIPHY_FREQ
+#define NL80211_ATTR_WIPHY_CHANNEL_TYPE NL80211_ATTR_WIPHY_CHANNEL_TYPE
+#define NL80211_ATTR_MGMT_SUBTYPE NL80211_ATTR_MGMT_SUBTYPE
+#define NL80211_ATTR_IE NL80211_ATTR_IE
+#define NL80211_ATTR_REG_INITIATOR NL80211_ATTR_REG_INITIATOR
+#define NL80211_ATTR_REG_TYPE NL80211_ATTR_REG_TYPE
+#define NL80211_ATTR_FRAME NL80211_ATTR_FRAME
+#define NL80211_ATTR_SSID NL80211_ATTR_SSID
+#define NL80211_ATTR_AUTH_TYPE NL80211_ATTR_AUTH_TYPE
+#define NL80211_ATTR_REASON_CODE NL80211_ATTR_REASON_CODE
+#define NL80211_ATTR_CIPHER_SUITES_PAIRWISE NL80211_ATTR_CIPHER_SUITES_PAIRWISE
+#define NL80211_ATTR_CIPHER_SUITE_GROUP NL80211_ATTR_CIPHER_SUITE_GROUP
+#define NL80211_ATTR_WPA_VERSIONS NL80211_ATTR_WPA_VERSIONS
+#define NL80211_ATTR_AKM_SUITES NL80211_ATTR_AKM_SUITES
+#define NL80211_ATTR_KEY NL80211_ATTR_KEY
+#define NL80211_ATTR_KEYS NL80211_ATTR_KEYS
+
+#define NL80211_MAX_SUPP_RATES                 32
+#define NL80211_MAX_SUPP_REG_RULES             32
+#define NL80211_TKIP_DATA_OFFSET_ENCR_KEY      0
+#define NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY    16
+#define NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY    24
+#define NL80211_HT_CAPABILITY_LEN              26
+
+#define NL80211_MAX_NR_CIPHER_SUITES           5
+#define NL80211_MAX_NR_AKM_SUITES              2
+
+/**
+ * enum nl80211_iftype - (virtual) interface types
+ *
+ * @NL80211_IFTYPE_UNSPECIFIED: unspecified type, driver decides
+ * @NL80211_IFTYPE_ADHOC: independent BSS member
+ * @NL80211_IFTYPE_STATION: managed BSS member
+ * @NL80211_IFTYPE_AP: access point
+ * @NL80211_IFTYPE_AP_VLAN: VLAN interface for access points
+ * @NL80211_IFTYPE_WDS: wireless distribution interface
+ * @NL80211_IFTYPE_MONITOR: monitor interface receiving all frames
+ * @NL80211_IFTYPE_MESH_POINT: mesh point
+ * @NL80211_IFTYPE_MAX: highest interface type number currently defined
+ * @__NL80211_IFTYPE_AFTER_LAST: internal use
+ *
+ * These values are used with the %NL80211_ATTR_IFTYPE
+ * to set the type of an interface.
+ *
+ */
+enum nl80211_iftype {
+       NL80211_IFTYPE_UNSPECIFIED,
+       NL80211_IFTYPE_ADHOC,
+       NL80211_IFTYPE_STATION,
+       NL80211_IFTYPE_AP,
+       NL80211_IFTYPE_AP_VLAN,
+       NL80211_IFTYPE_WDS,
+       NL80211_IFTYPE_MONITOR,
+       NL80211_IFTYPE_MESH_POINT,
+
+       /* keep last */
+       __NL80211_IFTYPE_AFTER_LAST,
+       NL80211_IFTYPE_MAX = __NL80211_IFTYPE_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_flags - station flags
+ *
+ * Station flags. When a station is added to an AP interface, it is
+ * assumed to be already associated (and hence authenticated.)
+ *
+ * @NL80211_STA_FLAG_AUTHORIZED: station is authorized (802.1X)
+ * @NL80211_STA_FLAG_SHORT_PREAMBLE: station is capable of receiving frames
+ *     with short barker preamble
+ * @NL80211_STA_FLAG_WME: station is WME/QoS capable
+ * @NL80211_STA_FLAG_MFP: station uses management frame protection
+ */
+enum nl80211_sta_flags {
+       __NL80211_STA_FLAG_INVALID,
+       NL80211_STA_FLAG_AUTHORIZED,
+       NL80211_STA_FLAG_SHORT_PREAMBLE,
+       NL80211_STA_FLAG_WME,
+       NL80211_STA_FLAG_MFP,
+
+       /* keep last */
+       __NL80211_STA_FLAG_AFTER_LAST,
+       NL80211_STA_FLAG_MAX = __NL80211_STA_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * struct nl80211_sta_flag_update - station flags mask/set
+ * @mask: mask of station flags to set
+ * @set: which values to set them to
+ *
+ * Both mask and set contain bits as per &enum nl80211_sta_flags.
+ */
+struct nl80211_sta_flag_update {
+       __u32 mask;
+       __u32 set;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_rate_info - bitrate information
+ *
+ * These attribute types are used with %NL80211_STA_INFO_TXRATE
+ * when getting information about the bitrate of a station.
+ *
+ * @__NL80211_RATE_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_RATE_INFO_BITRATE: total bitrate (u16, 100kbit/s)
+ * @NL80211_RATE_INFO_MCS: mcs index for 802.11n (u8)
+ * @NL80211_RATE_INFO_40_MHZ_WIDTH: 40 Mhz dualchannel bitrate
+ * @NL80211_RATE_INFO_SHORT_GI: 400ns guard interval
+ * @NL80211_RATE_INFO_MAX: highest rate_info number currently defined
+ * @__NL80211_RATE_INFO_AFTER_LAST: internal use
+ */
+enum nl80211_rate_info {
+       __NL80211_RATE_INFO_INVALID,
+       NL80211_RATE_INFO_BITRATE,
+       NL80211_RATE_INFO_MCS,
+       NL80211_RATE_INFO_40_MHZ_WIDTH,
+       NL80211_RATE_INFO_SHORT_GI,
+
+       /* keep last */
+       __NL80211_RATE_INFO_AFTER_LAST,
+       NL80211_RATE_INFO_MAX = __NL80211_RATE_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_sta_info - station information
+ *
+ * These attribute types are used with %NL80211_ATTR_STA_INFO
+ * when getting information about a station.
+ *
+ * @__NL80211_STA_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_STA_INFO_INACTIVE_TIME: time since last activity (u32, msecs)
+ * @NL80211_STA_INFO_RX_BYTES: total received bytes (u32, from this station)
+ * @NL80211_STA_INFO_TX_BYTES: total transmitted bytes (u32, to this station)
+ * @__NL80211_STA_INFO_AFTER_LAST: internal
+ * @NL80211_STA_INFO_MAX: highest possible station info attribute
+ * @NL80211_STA_INFO_SIGNAL: signal strength of last received PPDU (u8, dBm)
+ * @NL80211_STA_INFO_TX_BITRATE: current unicast tx rate, nested attribute
+ *     containing info as possible, see &enum nl80211_sta_info_txrate.
+ * @NL80211_STA_INFO_RX_PACKETS: total received packet (u32, from this station)
+ * @NL80211_STA_INFO_TX_PACKETS: total transmitted packets (u32, to this
+ *     station)
+ */
+enum nl80211_sta_info {
+       __NL80211_STA_INFO_INVALID,
+       NL80211_STA_INFO_INACTIVE_TIME,
+       NL80211_STA_INFO_RX_BYTES,
+       NL80211_STA_INFO_TX_BYTES,
+       NL80211_STA_INFO_LLID,
+       NL80211_STA_INFO_PLID,
+       NL80211_STA_INFO_PLINK_STATE,
+       NL80211_STA_INFO_SIGNAL,
+       NL80211_STA_INFO_TX_BITRATE,
+       NL80211_STA_INFO_RX_PACKETS,
+       NL80211_STA_INFO_TX_PACKETS,
+
+       /* keep last */
+       __NL80211_STA_INFO_AFTER_LAST,
+       NL80211_STA_INFO_MAX = __NL80211_STA_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mpath_flags - nl80211 mesh path flags
+ *
+ * @NL80211_MPATH_FLAG_ACTIVE: the mesh path is active
+ * @NL80211_MPATH_FLAG_RESOLVING: the mesh path discovery process is running
+ * @NL80211_MPATH_FLAG_SN_VALID: the mesh path contains a valid SN
+ * @NL80211_MPATH_FLAG_FIXED: the mesh path has been manually set
+ * @NL80211_MPATH_FLAG_RESOLVED: the mesh path discovery process succeeded
+ */
+enum nl80211_mpath_flags {
+       NL80211_MPATH_FLAG_ACTIVE =     1<<0,
+       NL80211_MPATH_FLAG_RESOLVING =  1<<1,
+       NL80211_MPATH_FLAG_SN_VALID =   1<<2,
+       NL80211_MPATH_FLAG_FIXED =      1<<3,
+       NL80211_MPATH_FLAG_RESOLVED =   1<<4,
+};
+
+/**
+ * enum nl80211_mpath_info - mesh path information
+ *
+ * These attribute types are used with %NL80211_ATTR_MPATH_INFO when getting
+ * information about a mesh path.
+ *
+ * @__NL80211_MPATH_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_ATTR_MPATH_FRAME_QLEN: number of queued frames for this destination
+ * @NL80211_ATTR_MPATH_SN: destination sequence number
+ * @NL80211_ATTR_MPATH_METRIC: metric (cost) of this mesh path
+ * @NL80211_ATTR_MPATH_EXPTIME: expiration time for the path, in msec from now
+ * @NL80211_ATTR_MPATH_FLAGS: mesh path flags, enumerated in
+ *     &enum nl80211_mpath_flags;
+ * @NL80211_ATTR_MPATH_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
+ * @NL80211_ATTR_MPATH_DISCOVERY_RETRIES: mesh path discovery retries
+ */
+enum nl80211_mpath_info {
+       __NL80211_MPATH_INFO_INVALID,
+       NL80211_MPATH_INFO_FRAME_QLEN,
+       NL80211_MPATH_INFO_SN,
+       NL80211_MPATH_INFO_METRIC,
+       NL80211_MPATH_INFO_EXPTIME,
+       NL80211_MPATH_INFO_FLAGS,
+       NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
+       NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+
+       /* keep last */
+       __NL80211_MPATH_INFO_AFTER_LAST,
+       NL80211_MPATH_INFO_MAX = __NL80211_MPATH_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band_attr - band attributes
+ * @__NL80211_BAND_ATTR_INVALID: attribute number 0 is reserved
+ * @NL80211_BAND_ATTR_FREQS: supported frequencies in this band,
+ *     an array of nested frequency attributes
+ * @NL80211_BAND_ATTR_RATES: supported bitrates in this band,
+ *     an array of nested bitrate attributes
+ * @NL80211_BAND_ATTR_HT_MCS_SET: 16-byte attribute containing the MCS set as
+ *     defined in 802.11n
+ * @NL80211_BAND_ATTR_HT_CAPA: HT capabilities, as in the HT information IE
+ * @NL80211_BAND_ATTR_HT_AMPDU_FACTOR: A-MPDU factor, as in 11n
+ * @NL80211_BAND_ATTR_HT_AMPDU_DENSITY: A-MPDU density, as in 11n
+ */
+enum nl80211_band_attr {
+       __NL80211_BAND_ATTR_INVALID,
+       NL80211_BAND_ATTR_FREQS,
+       NL80211_BAND_ATTR_RATES,
+
+       NL80211_BAND_ATTR_HT_MCS_SET,
+       NL80211_BAND_ATTR_HT_CAPA,
+       NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
+       NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
+
+       /* keep last */
+       __NL80211_BAND_ATTR_AFTER_LAST,
+       NL80211_BAND_ATTR_MAX = __NL80211_BAND_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_BAND_ATTR_HT_CAPA NL80211_BAND_ATTR_HT_CAPA
+
+/**
+ * enum nl80211_frequency_attr - frequency attributes
+ * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
+ * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
+ *     regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is
+ *     permitted on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted
+ *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
+ *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
+ *     (100 * dBm).
+ */
+enum nl80211_frequency_attr {
+       __NL80211_FREQUENCY_ATTR_INVALID,
+       NL80211_FREQUENCY_ATTR_FREQ,
+       NL80211_FREQUENCY_ATTR_DISABLED,
+       NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
+       NL80211_FREQUENCY_ATTR_NO_IBSS,
+       NL80211_FREQUENCY_ATTR_RADAR,
+       NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
+
+       /* keep last */
+       __NL80211_FREQUENCY_ATTR_AFTER_LAST,
+       NL80211_FREQUENCY_ATTR_MAX = __NL80211_FREQUENCY_ATTR_AFTER_LAST - 1
+};
+
+#define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+
+/**
+ * enum nl80211_bitrate_attr - bitrate attributes
+ * @NL80211_BITRATE_ATTR_RATE: Bitrate in units of 100 kbps
+ * @NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE: Short preamble supported
+ *     in 2.4 GHz band.
+ */
+enum nl80211_bitrate_attr {
+       __NL80211_BITRATE_ATTR_INVALID,
+       NL80211_BITRATE_ATTR_RATE,
+       NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE,
+
+       /* keep last */
+       __NL80211_BITRATE_ATTR_AFTER_LAST,
+       NL80211_BITRATE_ATTR_MAX = __NL80211_BITRATE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_initiator - Indicates the initiator of a reg domain request
+ * @NL80211_REGDOM_SET_BY_CORE: Core queried CRDA for a dynamic world
+ *     regulatory domain.
+ * @NL80211_REGDOM_SET_BY_USER: User asked the wireless core to set the
+ *     regulatory domain.
+ * @NL80211_REGDOM_SET_BY_DRIVER: a wireless drivers has hinted to the
+ *     wireless core it thinks its knows the regulatory domain we should be in.
+ * @NL80211_REGDOM_SET_BY_COUNTRY_IE: the wireless core has received an
+ *     802.11 country information element with regulatory information it
+ *     thinks we should consider.
+ */
+enum nl80211_reg_initiator {
+       NL80211_REGDOM_SET_BY_CORE,
+       NL80211_REGDOM_SET_BY_USER,
+       NL80211_REGDOM_SET_BY_DRIVER,
+       NL80211_REGDOM_SET_BY_COUNTRY_IE,
+};
+
+/**
+ * enum nl80211_reg_type - specifies the type of regulatory domain
+ * @NL80211_REGDOM_TYPE_COUNTRY: the regulatory domain set is one that pertains
+ *     to a specific country. When this is set you can count on the
+ *     ISO / IEC 3166 alpha2 country code being valid.
+ * @NL80211_REGDOM_TYPE_WORLD: the regulatory set domain is the world regulatory
+ *     domain.
+ * @NL80211_REGDOM_TYPE_CUSTOM_WORLD: the regulatory domain set is a custom
+ *     driver specific world regulatory domain. These do not apply system-wide
+ *     and are only applicable to the individual devices which have requested
+ *     them to be applied.
+ * @NL80211_REGDOM_TYPE_INTERSECTION: the regulatory domain set is the product
+ *     of an intersection between two regulatory domains -- the previously
+ *     set regulatory domain on the system and the last accepted regulatory
+ *     domain request to be processed.
+ */
+enum nl80211_reg_type {
+       NL80211_REGDOM_TYPE_COUNTRY,
+       NL80211_REGDOM_TYPE_WORLD,
+       NL80211_REGDOM_TYPE_CUSTOM_WORLD,
+       NL80211_REGDOM_TYPE_INTERSECTION,
+};
+
+/**
+ * enum nl80211_reg_rule_attr - regulatory rule attributes
+ * @NL80211_ATTR_REG_RULE_FLAGS: a set of flags which specify additional
+ *     considerations for a given frequency range. These are the
+ *     &enum nl80211_reg_rule_flags.
+ * @NL80211_ATTR_FREQ_RANGE_START: starting frequencry for the regulatory
+ *     rule in KHz. This is not a center of frequency but an actual regulatory
+ *     band edge.
+ * @NL80211_ATTR_FREQ_RANGE_END: ending frequency for the regulatory rule
+ *     in KHz. This is not a center a frequency but an actual regulatory
+ *     band edge.
+ * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
+ *     frequency range, in KHz.
+ * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
+ *     for a given frequency range. The value is in mBi (100 * dBi).
+ *     If you don't have one then don't send this.
+ * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
+ *     a given frequency range. The value is in mBm (100 * dBm).
+ */
+enum nl80211_reg_rule_attr {
+       __NL80211_REG_RULE_ATTR_INVALID,
+       NL80211_ATTR_REG_RULE_FLAGS,
+
+       NL80211_ATTR_FREQ_RANGE_START,
+       NL80211_ATTR_FREQ_RANGE_END,
+       NL80211_ATTR_FREQ_RANGE_MAX_BW,
+
+       NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
+       NL80211_ATTR_POWER_RULE_MAX_EIRP,
+
+       /* keep last */
+       __NL80211_REG_RULE_ATTR_AFTER_LAST,
+       NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_reg_rule_flags - regulatory rule flags
+ *
+ * @NL80211_RRF_NO_OFDM: OFDM modulation not allowed
+ * @NL80211_RRF_NO_CCK: CCK modulation not allowed
+ * @NL80211_RRF_NO_INDOOR: indoor operation not allowed
+ * @NL80211_RRF_NO_OUTDOOR: outdoor operation not allowed
+ * @NL80211_RRF_DFS: DFS support is required to be used
+ * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
+ * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
+ * @NL80211_RRF_PASSIVE_SCAN: passive scan is required
+ * @NL80211_RRF_NO_IBSS: no IBSS is allowed
+ */
+enum nl80211_reg_rule_flags {
+       NL80211_RRF_NO_OFDM             = 1<<0,
+       NL80211_RRF_NO_CCK              = 1<<1,
+       NL80211_RRF_NO_INDOOR           = 1<<2,
+       NL80211_RRF_NO_OUTDOOR          = 1<<3,
+       NL80211_RRF_DFS                 = 1<<4,
+       NL80211_RRF_PTP_ONLY            = 1<<5,
+       NL80211_RRF_PTMP_ONLY           = 1<<6,
+       NL80211_RRF_PASSIVE_SCAN        = 1<<7,
+       NL80211_RRF_NO_IBSS             = 1<<8,
+};
+
+/**
+ * enum nl80211_survey_info - survey information
+ *
+ * These attribute types are used with %NL80211_ATTR_SURVEY_INFO
+ * when getting information about a survey.
+ *
+ * @__NL80211_SURVEY_INFO_INVALID: attribute number 0 is reserved
+ * @NL80211_SURVEY_INFO_FREQUENCY: center frequency of channel
+ * @NL80211_SURVEY_INFO_NOISE: noise level of channel (u8, dBm)
+ */
+enum nl80211_survey_info {
+       __NL80211_SURVEY_INFO_INVALID,
+       NL80211_SURVEY_INFO_FREQUENCY,
+       NL80211_SURVEY_INFO_NOISE,
+
+       /* keep last */
+       __NL80211_SURVEY_INFO_AFTER_LAST,
+       NL80211_SURVEY_INFO_MAX = __NL80211_SURVEY_INFO_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_mntr_flags - monitor configuration flags
+ *
+ * Monitor configuration flags.
+ *
+ * @__NL80211_MNTR_FLAG_INVALID: reserved
+ *
+ * @NL80211_MNTR_FLAG_FCSFAIL: pass frames with bad FCS
+ * @NL80211_MNTR_FLAG_PLCPFAIL: pass frames with bad PLCP
+ * @NL80211_MNTR_FLAG_CONTROL: pass control frames
+ * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering
+ * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing.
+ *     overrides all other flags.
+ *
+ * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
+ * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
+ */
+enum nl80211_mntr_flags {
+       __NL80211_MNTR_FLAG_INVALID,
+       NL80211_MNTR_FLAG_FCSFAIL,
+       NL80211_MNTR_FLAG_PLCPFAIL,
+       NL80211_MNTR_FLAG_CONTROL,
+       NL80211_MNTR_FLAG_OTHER_BSS,
+       NL80211_MNTR_FLAG_COOK_FRAMES,
+
+       /* keep last */
+       __NL80211_MNTR_FLAG_AFTER_LAST,
+       NL80211_MNTR_FLAG_MAX = __NL80211_MNTR_FLAG_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_meshconf_params - mesh configuration parameters
+ *
+ * Mesh configuration parameters
+ *
+ * @__NL80211_MESHCONF_INVALID: internal use
+ *
+ * @NL80211_MESHCONF_RETRY_TIMEOUT: specifies the initial retry timeout in
+ * millisecond units, used by the Peer Link Open message
+ *
+ * @NL80211_MESHCONF_CONFIRM_TIMEOUT: specifies the inital confirm timeout, in
+ * millisecond units, used by the peer link management to close a peer link
+ *
+ * @NL80211_MESHCONF_HOLDING_TIMEOUT: specifies the holding timeout, in
+ * millisecond units
+ *
+ * @NL80211_MESHCONF_MAX_PEER_LINKS: maximum number of peer links allowed
+ * on this mesh interface
+ *
+ * @NL80211_MESHCONF_MAX_RETRIES: specifies the maximum number of peer link
+ * open retries that can be sent to establish a new peer link instance in a
+ * mesh
+ *
+ * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
+ * point.
+ *
+ * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
+ * open peer links when we detect compatible mesh peers.
+ *
+ * @NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES: the number of action frames
+ * containing a PREQ that an MP can send to a particular destination (path
+ * target)
+ *
+ * @NL80211_MESHCONF_PATH_REFRESH_TIME: how frequently to refresh mesh paths
+ * (in milliseconds)
+ *
+ * @NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT: minimum length of time to wait
+ * until giving up on a path discovery (in milliseconds)
+ *
+ * @NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT: The time (in TUs) for which mesh
+ * points receiving a PREQ shall consider the forwarding information from the
+ * root to be valid. (TU = time unit)
+ *
+ * @NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL: The minimum interval of time (in
+ * TUs) during which an MP can send only one action frame containing a PREQ
+ * reference element
+ *
+ * @NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME: The interval of time (in TUs)
+ * that it takes for an HWMP information element to propagate across the mesh
+ *
+ * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not
+ *
+ * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
+ *
+ * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
+ */
+enum nl80211_meshconf_params {
+       __NL80211_MESHCONF_INVALID,
+       NL80211_MESHCONF_RETRY_TIMEOUT,
+       NL80211_MESHCONF_CONFIRM_TIMEOUT,
+       NL80211_MESHCONF_HOLDING_TIMEOUT,
+       NL80211_MESHCONF_MAX_PEER_LINKS,
+       NL80211_MESHCONF_MAX_RETRIES,
+       NL80211_MESHCONF_TTL,
+       NL80211_MESHCONF_AUTO_OPEN_PLINKS,
+       NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
+       NL80211_MESHCONF_PATH_REFRESH_TIME,
+       NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
+       NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
+       NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
+       NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+       NL80211_MESHCONF_HWMP_ROOTMODE,
+
+       /* keep last */
+       __NL80211_MESHCONF_ATTR_AFTER_LAST,
+       NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_txq_attr - TX queue parameter attributes
+ * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
+ * @NL80211_TXQ_ATTR_QUEUE: TX queue identifier (NL80211_TXQ_Q_*)
+ * @NL80211_TXQ_ATTR_TXOP: Maximum burst time in units of 32 usecs, 0 meaning
+ *     disabled
+ * @NL80211_TXQ_ATTR_CWMIN: Minimum contention window [a value of the form
+ *     2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_CWMAX: Maximum contention window [a value of the form
+ *     2^n-1 in the range 1..32767]
+ * @NL80211_TXQ_ATTR_AIFS: Arbitration interframe space [0..255]
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_TXQ_ATTR_MAX: Maximum TXQ attribute number
+ */
+enum nl80211_txq_attr {
+       __NL80211_TXQ_ATTR_INVALID,
+       NL80211_TXQ_ATTR_QUEUE,
+       NL80211_TXQ_ATTR_TXOP,
+       NL80211_TXQ_ATTR_CWMIN,
+       NL80211_TXQ_ATTR_CWMAX,
+       NL80211_TXQ_ATTR_AIFS,
+
+       /* keep last */
+       __NL80211_TXQ_ATTR_AFTER_LAST,
+       NL80211_TXQ_ATTR_MAX = __NL80211_TXQ_ATTR_AFTER_LAST - 1
+};
+
+enum nl80211_txq_q {
+       NL80211_TXQ_Q_VO,
+       NL80211_TXQ_Q_VI,
+       NL80211_TXQ_Q_BE,
+       NL80211_TXQ_Q_BK
+};
+
+enum nl80211_channel_type {
+       NL80211_CHAN_NO_HT,
+       NL80211_CHAN_HT20,
+       NL80211_CHAN_HT40MINUS,
+       NL80211_CHAN_HT40PLUS
+};
+
+/**
+ * enum nl80211_bss - netlink attributes for a BSS
+ *
+ * @__NL80211_BSS_INVALID: invalid
+ * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
+ * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
+ * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
+ * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
+ * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
+ *     raw information elements from the probe response/beacon (bin);
+ *     if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are
+ *     from a Probe Response frame; otherwise they are from a Beacon frame.
+ *     However, if the driver does not indicate the source of the IEs, these
+ *     IEs may be from either frame subtype.
+ * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
+ *     in mBm (100 * dBm) (s32)
+ * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
+ *     in unspecified units, scaled to 0..100 (u8)
+ * @NL80211_BSS_STATUS: status, if this BSS is "used"
+ * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms
+ * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
+ *     elements from a Beacon frame (bin); not present if no Beacon frame has
+ *     yet been received
+ * @__NL80211_BSS_AFTER_LAST: internal
+ * @NL80211_BSS_MAX: highest BSS attribute
+ */
+enum nl80211_bss {
+       __NL80211_BSS_INVALID,
+       NL80211_BSS_BSSID,
+       NL80211_BSS_FREQUENCY,
+       NL80211_BSS_TSF,
+       NL80211_BSS_BEACON_INTERVAL,
+       NL80211_BSS_CAPABILITY,
+       NL80211_BSS_INFORMATION_ELEMENTS,
+       NL80211_BSS_SIGNAL_MBM,
+       NL80211_BSS_SIGNAL_UNSPEC,
+       NL80211_BSS_STATUS,
+       NL80211_BSS_SEEN_MS_AGO,
+       NL80211_BSS_BEACON_IES,
+
+       /* keep last */
+       __NL80211_BSS_AFTER_LAST,
+       NL80211_BSS_MAX = __NL80211_BSS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_bss_status - BSS "status"
+ */
+enum nl80211_bss_status {
+       NL80211_BSS_STATUS_AUTHENTICATED,
+       NL80211_BSS_STATUS_ASSOCIATED,
+       NL80211_BSS_STATUS_IBSS_JOINED,
+};
+
+/**
+ * enum nl80211_auth_type - AuthenticationType
+ *
+ * @NL80211_AUTHTYPE_OPEN_SYSTEM: Open System authentication
+ * @NL80211_AUTHTYPE_SHARED_KEY: Shared Key authentication (WEP only)
+ * @NL80211_AUTHTYPE_FT: Fast BSS Transition (IEEE 802.11r)
+ * @NL80211_AUTHTYPE_NETWORK_EAP: Network EAP (some Cisco APs and mainly LEAP)
+ * @__NL80211_AUTHTYPE_NUM: internal
+ * @NL80211_AUTHTYPE_MAX: maximum valid auth algorithm
+ * @NL80211_AUTHTYPE_AUTOMATIC: determine automatically (if necessary by
+ *     trying multiple times); this is invalid in netlink -- leave out
+ *     the attribute for this on CONNECT commands.
+ */
+enum nl80211_auth_type {
+       NL80211_AUTHTYPE_OPEN_SYSTEM,
+       NL80211_AUTHTYPE_SHARED_KEY,
+       NL80211_AUTHTYPE_FT,
+       NL80211_AUTHTYPE_NETWORK_EAP,
+
+       /* keep last */
+       __NL80211_AUTHTYPE_NUM,
+       NL80211_AUTHTYPE_MAX = __NL80211_AUTHTYPE_NUM - 1,
+       NL80211_AUTHTYPE_AUTOMATIC
+};
+
+/**
+ * enum nl80211_key_type - Key Type
+ * @NL80211_KEYTYPE_GROUP: Group (broadcast/multicast) key
+ * @NL80211_KEYTYPE_PAIRWISE: Pairwise (unicast/individual) key
+ * @NL80211_KEYTYPE_PEERKEY: PeerKey (DLS)
+ */
+enum nl80211_key_type {
+       NL80211_KEYTYPE_GROUP,
+       NL80211_KEYTYPE_PAIRWISE,
+       NL80211_KEYTYPE_PEERKEY,
+};
+
+/**
+ * enum nl80211_mfp - Management frame protection state
+ * @NL80211_MFP_NO: Management frame protection not used
+ * @NL80211_MFP_REQUIRED: Management frame protection required
+ */
+enum nl80211_mfp {
+       NL80211_MFP_NO,
+       NL80211_MFP_REQUIRED,
+};
+
+enum nl80211_wpa_versions {
+       NL80211_WPA_VERSION_1 = 1 << 0,
+       NL80211_WPA_VERSION_2 = 1 << 1,
+};
+
+/**
+ * enum nl80211_key_attributes - key attributes
+ * @__NL80211_KEY_INVALID: invalid
+ * @NL80211_KEY_DATA: (temporal) key data; for TKIP this consists of
+ *     16 bytes encryption key followed by 8 bytes each for TX and RX MIC
+ *     keys
+ * @NL80211_KEY_IDX: key ID (u8, 0-3)
+ * @NL80211_KEY_CIPHER: key cipher suite (u32, as defined by IEEE 802.11
+ *     section 7.3.2.25.1, e.g. 0x000FAC04)
+ * @NL80211_KEY_SEQ: transmit key sequence number (IV/PN) for TKIP and
+ *     CCMP keys, each six bytes in little endian
+ * @NL80211_KEY_DEFAULT: flag indicating default key
+ * @NL80211_KEY_DEFAULT_MGMT: flag indicating default management key
+ * @__NL80211_KEY_AFTER_LAST: internal
+ * @NL80211_KEY_MAX: highest key attribute
+ */
+enum nl80211_key_attributes {
+       __NL80211_KEY_INVALID,
+       NL80211_KEY_DATA,
+       NL80211_KEY_IDX,
+       NL80211_KEY_CIPHER,
+       NL80211_KEY_SEQ,
+       NL80211_KEY_DEFAULT,
+       NL80211_KEY_DEFAULT_MGMT,
+
+       /* keep last */
+       __NL80211_KEY_AFTER_LAST,
+       NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_tx_rate_attributes - TX rate set attributes
+ * @__NL80211_TXRATE_INVALID: invalid
+ * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection
+ *     in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
+ *     1 = 500 kbps) but without the IE length restriction (at most
+ *     %NL80211_MAX_SUPP_RATES in a single array).
+ * @__NL80211_TXRATE_AFTER_LAST: internal
+ * @NL80211_TXRATE_MAX: highest TX rate attribute
+ */
+enum nl80211_tx_rate_attributes {
+       __NL80211_TXRATE_INVALID,
+       NL80211_TXRATE_LEGACY,
+
+       /* keep last */
+       __NL80211_TXRATE_AFTER_LAST,
+       NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_band - Frequency band
+ * @NL80211_BAND_2GHZ - 2.4 GHz ISM band
+ * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz)
+ */
+enum nl80211_band {
+       NL80211_BAND_2GHZ,
+       NL80211_BAND_5GHZ,
+};
+
+enum nl80211_ps_state {
+       NL80211_PS_DISABLED,
+       NL80211_PS_ENABLED,
+};
+
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
+ *     the threshold for the RSSI level at which an event will be sent. Zero
+ *     to disable.
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
+ *     the minimum amount the RSSI level must change after an event before a
+ *     new event may be issued (to reduce effects of RSSI oscillation).
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+       __NL80211_ATTR_CQM_INVALID,
+       NL80211_ATTR_CQM_RSSI_THOLD,
+       NL80211_ATTR_CQM_RSSI_HYST,
+       NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+
+       /* keep last */
+       __NL80211_ATTR_CQM_AFTER_LAST,
+       NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
+ *      configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
+ *      configured threshold
+ */
+enum nl80211_cqm_rssi_threshold_event {
+       NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+       NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+};
+
+#endif /* __LINUX_NL80211_H */
diff --git a/src/drivers/priv_netlink.h b/src/drivers/priv_netlink.h
new file mode 100644 (file)
index 0000000..23eff83
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions.
+ * 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 PRIV_NETLINK_H
+#define PRIV_NETLINK_H
+
+/*
+ * This should be replaced with user space header once one is available with C
+ * library, etc..
+ */
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IFLA_IFNAME
+#define IFLA_IFNAME 3
+#endif
+#ifndef IFLA_WIRELESS
+#define IFLA_WIRELESS 11
+#endif
+#ifndef IFLA_OPERSTATE
+#define IFLA_OPERSTATE 16
+#endif
+#ifndef IFLA_LINKMODE
+#define IFLA_LINKMODE 17
+#define IF_OPER_DORMANT 5
+#define IF_OPER_UP 6
+#endif
+
+#define NLM_F_REQUEST 1
+
+#define NETLINK_ROUTE 0
+#define RTMGRP_LINK 1
+#define RTM_BASE 0x10
+#define RTM_NEWLINK (RTM_BASE + 0)
+#define RTM_DELLINK (RTM_BASE + 1)
+#define RTM_SETLINK (RTM_BASE + 3)
+
+#define NLMSG_ALIGNTO 4
+#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1))
+#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr)))
+#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
+#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0)))
+#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \
+                            (struct nlmsghdr *) \
+                            (((char *)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
+#define NLMSG_OK(nlh,len) ((len) >= (int) sizeof(struct nlmsghdr) && \
+                          (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) && \
+                          (int) (nlh)->nlmsg_len <= (len))
+#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
+
+#define RTA_ALIGNTO 4
+#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1))
+#define RTA_OK(rta,len) \
+((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \
+(rta)->rta_len <= (len))
+#define RTA_NEXT(rta,attrlen) \
+((attrlen) -= RTA_ALIGN((rta)->rta_len), \
+(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
+#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+
+
+struct sockaddr_nl
+{
+       sa_family_t nl_family;
+       unsigned short nl_pad;
+       u32 nl_pid;
+       u32 nl_groups;
+};
+
+struct nlmsghdr
+{
+       u32 nlmsg_len;
+       u16 nlmsg_type;
+       u16 nlmsg_flags;
+       u32 nlmsg_seq;
+       u32 nlmsg_pid;
+};
+
+struct ifinfomsg
+{
+       unsigned char ifi_family;
+       unsigned char __ifi_pad;
+       unsigned short ifi_type;
+       int ifi_index;
+       unsigned ifi_flags;
+       unsigned ifi_change;
+};
+
+struct rtattr
+{
+       unsigned short rta_len;
+       unsigned short rta_type;
+};
+
+#endif /* PRIV_NETLINK_H */
diff --git a/src/drivers/wireless_copy.h b/src/drivers/wireless_copy.h
new file mode 100644 (file)
index 0000000..ad76466
--- /dev/null
@@ -0,0 +1,1099 @@
+/* This is based on Linux Wireless Extensions header file from WIRELESS_EXT 18.
+ * I have just removed kernel related headers and added some typedefs etc. to
+ * make this easier to include into user space programs.
+ * Jouni Malinen, 2005-03-12.
+ */
+
+
+/*
+ * This file define a set of standard wireless extensions
+ *
+ * Version :   19      18.3.05
+ *
+ * Authors :   Jean Tourrilhes - HPL - <jt@hpl.hp.com>
+ * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved.
+ */
+
+#ifndef _LINUX_WIRELESS_H
+#define _LINUX_WIRELESS_H
+
+/************************** DOCUMENTATION **************************/
+/*
+ * Initial APIs (1996 -> onward) :
+ * -----------------------------
+ * Basically, the wireless extensions are for now a set of standard ioctl
+ * call + /proc/net/wireless
+ *
+ * The entry /proc/net/wireless give statistics and information on the
+ * driver.
+ * This is better than having each driver having its entry because
+ * its centralised and we may remove the driver module safely.
+ *
+ * Ioctl are used to configure the driver and issue commands.  This is
+ * better than command line options of insmod because we may want to
+ * change dynamically (while the driver is running) some parameters.
+ *
+ * The ioctl mechanimsm are copied from standard devices ioctl.
+ * We have the list of command plus a structure descibing the
+ * data exchanged...
+ * Note that to add these ioctl, I was obliged to modify :
+ *     # net/core/dev.c (two place + add include)
+ *     # net/ipv4/af_inet.c (one place + add include)
+ *
+ * /proc/net/wireless is a copy of /proc/net/dev.
+ * We have a structure for data passed from the driver to /proc/net/wireless
+ * Too add this, I've modified :
+ *     # net/core/dev.c (two other places)
+ *     # include/linux/netdevice.h (one place)
+ *     # include/linux/proc_fs.h (one place)
+ *
+ * New driver API (2002 -> onward) :
+ * -------------------------------
+ * This file is only concerned with the user space API and common definitions.
+ * The new driver API is defined and documented in :
+ *     # include/net/iw_handler.h
+ *
+ * Note as well that /proc/net/wireless implementation has now moved in :
+ *     # net/core/wireless.c
+ *
+ * Wireless Events (2002 -> onward) :
+ * --------------------------------
+ * Events are defined at the end of this file, and implemented in :
+ *     # net/core/wireless.c
+ *
+ * Other comments :
+ * --------------
+ * Do not add here things that are redundant with other mechanisms
+ * (drivers init, ifconfig, /proc/net/dev, ...) and with are not
+ * wireless specific.
+ *
+ * These wireless extensions are not magic : each driver has to provide
+ * support for them...
+ *
+ * IMPORTANT NOTE : As everything in the kernel, this is very much a
+ * work in progress. Contact me if you have ideas of improvements...
+ */
+
+/***************************** INCLUDES *****************************/
+
+ /* jkm - replaced linux headers with C library headers, added typedefs */
+#if 0
+/* To minimise problems in user space, I might remove those headers
+ * at some point. Jean II */
+#include <linux/types.h>               /* for "caddr_t" et al          */
+#include <linux/socket.h>              /* for "struct sockaddr" et al  */
+#include <linux/if.h>                  /* for IFNAMSIZ and co... */
+#else
+#include <sys/types.h>
+#include <net/if.h>
+typedef __uint32_t __u32;
+typedef __int32_t __s32;
+typedef __uint16_t __u16;
+typedef __int16_t __s16;
+typedef __uint8_t __u8;
+#ifndef __user
+#define __user
+#endif /* __user */
+#endif
+
+/***************************** VERSION *****************************/
+/*
+ * This constant is used to know the availability of the wireless
+ * extensions and to know which version of wireless extensions it is
+ * (there is some stuff that will be added in the future...)
+ * I just plan to increment with each new version.
+ */
+#define WIRELESS_EXT   19
+
+/*
+ * Changes :
+ *
+ * V2 to V3
+ * --------
+ *     Alan Cox start some incompatibles changes. I've integrated a bit more.
+ *     - Encryption renamed to Encode to avoid US regulation problems
+ *     - Frequency changed from float to struct to avoid problems on old 386
+ *
+ * V3 to V4
+ * --------
+ *     - Add sensitivity
+ *
+ * V4 to V5
+ * --------
+ *     - Missing encoding definitions in range
+ *     - Access points stuff
+ *
+ * V5 to V6
+ * --------
+ *     - 802.11 support (ESSID ioctls)
+ *
+ * V6 to V7
+ * --------
+ *     - define IW_ESSID_MAX_SIZE and IW_MAX_AP
+ *
+ * V7 to V8
+ * --------
+ *     - Changed my e-mail address
+ *     - More 802.11 support (nickname, rate, rts, frag)
+ *     - List index in frequencies
+ *
+ * V8 to V9
+ * --------
+ *     - Support for 'mode of operation' (ad-hoc, managed...)
+ *     - Support for unicast and multicast power saving
+ *     - Change encoding to support larger tokens (>64 bits)
+ *     - Updated iw_params (disable, flags) and use it for NWID
+ *     - Extracted iw_point from iwreq for clarity
+ *
+ * V9 to V10
+ * ---------
+ *     - Add PM capability to range structure
+ *     - Add PM modifier : MAX/MIN/RELATIVE
+ *     - Add encoding option : IW_ENCODE_NOKEY
+ *     - Add TxPower ioctls (work like TxRate)
+ *
+ * V10 to V11
+ * ----------
+ *     - Add WE version in range (help backward/forward compatibility)
+ *     - Add retry ioctls (work like PM)
+ *
+ * V11 to V12
+ * ----------
+ *     - Add SIOCSIWSTATS to get /proc/net/wireless programatically
+ *     - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space
+ *     - Add new statistics (frag, retry, beacon)
+ *     - Add average quality (for user space calibration)
+ *
+ * V12 to V13
+ * ----------
+ *     - Document creation of new driver API.
+ *     - Extract union iwreq_data from struct iwreq (for new driver API).
+ *     - Rename SIOCSIWNAME as SIOCSIWCOMMIT
+ *
+ * V13 to V14
+ * ----------
+ *     - Wireless Events support : define struct iw_event
+ *     - Define additional specific event numbers
+ *     - Add "addr" and "param" fields in union iwreq_data
+ *     - AP scanning stuff (SIOCSIWSCAN and friends)
+ *
+ * V14 to V15
+ * ----------
+ *     - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg
+ *     - Make struct iw_freq signed (both m & e), add explicit padding
+ *     - Add IWEVCUSTOM for driver specific event/scanning token
+ *     - Add IW_MAX_GET_SPY for driver returning a lot of addresses
+ *     - Add IW_TXPOW_RANGE for range of Tx Powers
+ *     - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points
+ *     - Add IW_MODE_MONITOR for passive monitor
+ *
+ * V15 to V16
+ * ----------
+ *     - Increase the number of bitrates in iw_range to 32 (for 802.11g)
+ *     - Increase the number of frequencies in iw_range to 32 (for 802.11b+a)
+ *     - Reshuffle struct iw_range for increases, add filler
+ *     - Increase IW_MAX_AP to 64 for driver returning a lot of addresses
+ *     - Remove IW_MAX_GET_SPY because conflict with enhanced spy support
+ *     - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy"
+ *     - Add IW_ENCODE_TEMP and iw_range->encoding_login_index
+ *
+ * V16 to V17
+ * ----------
+ *     - Add flags to frequency -> auto/fixed
+ *     - Document (struct iw_quality *)->updated, add new flags (INVALID)
+ *     - Wireless Event capability in struct iw_range
+ *     - Add support for relative TxPower (yick !)
+ *
+ * V17 to V18 (From Jouni Malinen <j@w1.fi>)
+ * ----------
+ *     - Add support for WPA/WPA2
+ *     - Add extended encoding configuration (SIOCSIWENCODEEXT and
+ *       SIOCGIWENCODEEXT)
+ *     - Add SIOCSIWGENIE/SIOCGIWGENIE
+ *     - Add SIOCSIWMLME
+ *     - Add SIOCSIWPMKSA
+ *     - Add struct iw_range bit field for supported encoding capabilities
+ *     - Add optional scan request parameters for SIOCSIWSCAN
+ *     - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA
+ *       related parameters (extensible up to 4096 parameter values)
+ *     - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE,
+ *       IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND
+ *
+ * V18 to V19
+ * ----------
+ *     - Remove (struct iw_point *)->pointer from events and streams
+ *     - Remove header includes to help user space
+ *     - Increase IW_ENCODING_TOKEN_MAX from 32 to 64
+ *     - Add IW_QUAL_ALL_UPDATED and IW_QUAL_ALL_INVALID macros
+ *     - Add explicit flag to tell stats are in dBm : IW_QUAL_DBM
+ *     - Add IW_IOCTL_IDX() and IW_EVENT_IDX() macros
+ */
+
+/**************************** CONSTANTS ****************************/
+
+/* -------------------------- IOCTL LIST -------------------------- */
+
+/* Wireless Identification */
+#define SIOCSIWCOMMIT  0x8B00          /* Commit pending changes to driver */
+#define SIOCGIWNAME    0x8B01          /* get name == wireless protocol */
+/* SIOCGIWNAME is used to verify the presence of Wireless Extensions.
+ * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"...
+ * Don't put the name of your driver there, it's useless. */
+
+/* Basic operations */
+#define SIOCSIWNWID    0x8B02          /* set network id (pre-802.11) */
+#define SIOCGIWNWID    0x8B03          /* get network id (the cell) */
+#define SIOCSIWFREQ    0x8B04          /* set channel/frequency (Hz) */
+#define SIOCGIWFREQ    0x8B05          /* get channel/frequency (Hz) */
+#define SIOCSIWMODE    0x8B06          /* set operation mode */
+#define SIOCGIWMODE    0x8B07          /* get operation mode */
+#define SIOCSIWSENS    0x8B08          /* set sensitivity (dBm) */
+#define SIOCGIWSENS    0x8B09          /* get sensitivity (dBm) */
+
+/* Informative stuff */
+#define SIOCSIWRANGE   0x8B0A          /* Unused */
+#define SIOCGIWRANGE   0x8B0B          /* Get range of parameters */
+#define SIOCSIWPRIV    0x8B0C          /* Unused */
+#define SIOCGIWPRIV    0x8B0D          /* get private ioctl interface info */
+#define SIOCSIWSTATS   0x8B0E          /* Unused */
+#define SIOCGIWSTATS   0x8B0F          /* Get /proc/net/wireless stats */
+/* SIOCGIWSTATS is strictly used between user space and the kernel, and
+ * is never passed to the driver (i.e. the driver will never see it). */
+
+/* Spy support (statistics per MAC address - used for Mobile IP support) */
+#define SIOCSIWSPY     0x8B10          /* set spy addresses */
+#define SIOCGIWSPY     0x8B11          /* get spy info (quality of link) */
+#define SIOCSIWTHRSPY  0x8B12          /* set spy threshold (spy event) */
+#define SIOCGIWTHRSPY  0x8B13          /* get spy threshold */
+
+/* Access Point manipulation */
+#define SIOCSIWAP      0x8B14          /* set access point MAC addresses */
+#define SIOCGIWAP      0x8B15          /* get access point MAC addresses */
+#define SIOCGIWAPLIST  0x8B17          /* Deprecated in favor of scanning */
+#define SIOCSIWSCAN    0x8B18          /* trigger scanning (list cells) */
+#define SIOCGIWSCAN    0x8B19          /* get scanning results */
+
+/* 802.11 specific support */
+#define SIOCSIWESSID   0x8B1A          /* set ESSID (network name) */
+#define SIOCGIWESSID   0x8B1B          /* get ESSID */
+#define SIOCSIWNICKN   0x8B1C          /* set node name/nickname */
+#define SIOCGIWNICKN   0x8B1D          /* get node name/nickname */
+/* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit
+ * within the 'iwreq' structure, so we need to use the 'data' member to
+ * point to a string in user space, like it is done for RANGE... */
+
+/* Other parameters useful in 802.11 and some other devices */
+#define SIOCSIWRATE    0x8B20          /* set default bit rate (bps) */
+#define SIOCGIWRATE    0x8B21          /* get default bit rate (bps) */
+#define SIOCSIWRTS     0x8B22          /* set RTS/CTS threshold (bytes) */
+#define SIOCGIWRTS     0x8B23          /* get RTS/CTS threshold (bytes) */
+#define SIOCSIWFRAG    0x8B24          /* set fragmentation thr (bytes) */
+#define SIOCGIWFRAG    0x8B25          /* get fragmentation thr (bytes) */
+#define SIOCSIWTXPOW   0x8B26          /* set transmit power (dBm) */
+#define SIOCGIWTXPOW   0x8B27          /* get transmit power (dBm) */
+#define SIOCSIWRETRY   0x8B28          /* set retry limits and lifetime */
+#define SIOCGIWRETRY   0x8B29          /* get retry limits and lifetime */
+
+/* Encoding stuff (scrambling, hardware security, WEP...) */
+#define SIOCSIWENCODE  0x8B2A          /* set encoding token & mode */
+#define SIOCGIWENCODE  0x8B2B          /* get encoding token & mode */
+/* Power saving stuff (power management, unicast and multicast) */
+#define SIOCSIWPOWER   0x8B2C          /* set Power Management settings */
+#define SIOCGIWPOWER   0x8B2D          /* get Power Management settings */
+
+/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM).
+ * This ioctl uses struct iw_point and data buffer that includes IE id and len
+ * fields. More than one IE may be included in the request. Setting the generic
+ * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers
+ * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers
+ * are required to report the used IE as a wireless event, e.g., when
+ * associating with an AP. */
+#define SIOCSIWGENIE   0x8B30          /* set generic IE */
+#define SIOCGIWGENIE   0x8B31          /* get generic IE */
+
+/* WPA : IEEE 802.11 MLME requests */
+#define SIOCSIWMLME    0x8B16          /* request MLME operation; uses
+                                        * struct iw_mlme */
+/* WPA : Authentication mode parameters */
+#define SIOCSIWAUTH    0x8B32          /* set authentication mode params */
+#define SIOCGIWAUTH    0x8B33          /* get authentication mode params */
+
+/* WPA : Extended version of encoding configuration */
+#define SIOCSIWENCODEEXT 0x8B34                /* set encoding token & mode */
+#define SIOCGIWENCODEEXT 0x8B35                /* get encoding token & mode */
+
+/* WPA2 : PMKSA cache management */
+#define SIOCSIWPMKSA   0x8B36          /* PMKSA cache operation */
+
+/* -------------------- DEV PRIVATE IOCTL LIST -------------------- */
+
+/* These 32 ioctl are wireless device private, for 16 commands.
+ * Each driver is free to use them for whatever purpose it chooses,
+ * however the driver *must* export the description of those ioctls
+ * with SIOCGIWPRIV and *must* use arguments as defined below.
+ * If you don't follow those rules, DaveM is going to hate you (reason :
+ * it make mixed 32/64bit operation impossible).
+ */
+#define SIOCIWFIRSTPRIV        0x8BE0
+#define SIOCIWLASTPRIV 0x8BFF
+/* Previously, we were using SIOCDEVPRIVATE, but we now have our
+ * separate range because of collisions with other tools such as
+ * 'mii-tool'.
+ * We now have 32 commands, so a bit more space ;-).
+ * Also, all 'odd' commands are only usable by root and don't return the
+ * content of ifr/iwr to user (but you are not obliged to use the set/get
+ * convention, just use every other two command). More details in iwpriv.c.
+ * And I repeat : you are not forced to use them with iwpriv, but you
+ * must be compliant with it.
+ */
+
+/* ------------------------- IOCTL STUFF ------------------------- */
+
+/* The first and the last (range) */
+#define SIOCIWFIRST    0x8B00
+#define SIOCIWLAST     SIOCIWLASTPRIV          /* 0x8BFF */
+#define IW_IOCTL_IDX(cmd)      ((cmd) - SIOCIWFIRST)
+
+/* Even : get (world access), odd : set (root access) */
+#define IW_IS_SET(cmd) (!((cmd) & 0x1))
+#define IW_IS_GET(cmd) ((cmd) & 0x1)
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/* Those are *NOT* ioctls, do not issue request on them !!! */
+/* Most events use the same identifier as ioctl requests */
+
+#define IWEVTXDROP     0x8C00          /* Packet dropped to excessive retry */
+#define IWEVQUAL       0x8C01          /* Quality part of statistics (scan) */
+#define IWEVCUSTOM     0x8C02          /* Driver specific ascii string */
+#define IWEVREGISTERED 0x8C03          /* Discovered a new node (AP mode) */
+#define IWEVEXPIRED    0x8C04          /* Expired a node (AP mode) */
+#define IWEVGENIE      0x8C05          /* Generic IE (WPA, RSN, WMM, ..)
+                                        * (scan results); This includes id and
+                                        * length fields. One IWEVGENIE may
+                                        * contain more than one IE. Scan
+                                        * results may contain one or more
+                                        * IWEVGENIE events. */
+#define IWEVMICHAELMICFAILURE 0x8C06   /* Michael MIC failure
+                                        * (struct iw_michaelmicfailure)
+                                        */
+#define IWEVASSOCREQIE 0x8C07          /* IEs used in (Re)Association Request.
+                                        * The data includes id and length
+                                        * fields and may contain more than one
+                                        * IE. This event is required in
+                                        * Managed mode if the driver
+                                        * generates its own WPA/RSN IE. This
+                                        * should be sent just before
+                                        * IWEVREGISTERED event for the
+                                        * association. */
+#define IWEVASSOCRESPIE        0x8C08          /* IEs used in (Re)Association
+                                        * Response. The data includes id and
+                                        * length fields and may contain more
+                                        * than one IE. This may be sent
+                                        * between IWEVASSOCREQIE and
+                                        * IWEVREGISTERED events for the
+                                        * association. */
+#define IWEVPMKIDCAND  0x8C09          /* PMKID candidate for RSN
+                                        * pre-authentication
+                                        * (struct iw_pmkid_cand) */
+
+#define IWEVFIRST      0x8C00
+#define IW_EVENT_IDX(cmd)      ((cmd) - IWEVFIRST)
+
+/* ------------------------- PRIVATE INFO ------------------------- */
+/*
+ * The following is used with SIOCGIWPRIV. It allow a driver to define
+ * the interface (name, type of data) for its private ioctl.
+ * Privates ioctl are SIOCIWFIRSTPRIV -> SIOCIWLASTPRIV
+ */
+
+#define IW_PRIV_TYPE_MASK      0x7000  /* Type of arguments */
+#define IW_PRIV_TYPE_NONE      0x0000
+#define IW_PRIV_TYPE_BYTE      0x1000  /* Char as number */
+#define IW_PRIV_TYPE_CHAR      0x2000  /* Char as character */
+#define IW_PRIV_TYPE_INT       0x4000  /* 32 bits int */
+#define IW_PRIV_TYPE_FLOAT     0x5000  /* struct iw_freq */
+#define IW_PRIV_TYPE_ADDR      0x6000  /* struct sockaddr */
+
+#define IW_PRIV_SIZE_FIXED     0x0800  /* Variable or fixed number of args */
+
+#define IW_PRIV_SIZE_MASK      0x07FF  /* Max number of those args */
+
+/*
+ * Note : if the number of args is fixed and the size < 16 octets,
+ * instead of passing a pointer we will put args in the iwreq struct...
+ */
+
+/* ----------------------- OTHER CONSTANTS ----------------------- */
+
+/* Maximum frequencies in the range struct */
+#define IW_MAX_FREQUENCIES     32
+/* Note : if you have something like 80 frequencies,
+ * don't increase this constant and don't fill the frequency list.
+ * The user will be able to set by channel anyway... */
+
+/* Maximum bit rates in the range struct */
+#define IW_MAX_BITRATES                32
+
+/* Maximum tx powers in the range struct */
+#define IW_MAX_TXPOWER         8
+/* Note : if you more than 8 TXPowers, just set the max and min or
+ * a few of them in the struct iw_range. */
+
+/* Maximum of address that you may set with SPY */
+#define IW_MAX_SPY             8
+
+/* Maximum of address that you may get in the
+   list of access points in range */
+#define IW_MAX_AP              64
+
+/* Maximum size of the ESSID and NICKN strings */
+#define IW_ESSID_MAX_SIZE      32
+
+/* Modes of operation */
+#define IW_MODE_AUTO   0       /* Let the driver decides */
+#define IW_MODE_ADHOC  1       /* Single cell network */
+#define IW_MODE_INFRA  2       /* Multi cell network, roaming, ... */
+#define IW_MODE_MASTER 3       /* Synchronisation master or Access Point */
+#define IW_MODE_REPEAT 4       /* Wireless Repeater (forwarder) */
+#define IW_MODE_SECOND 5       /* Secondary master/repeater (backup) */
+#define IW_MODE_MONITOR        6       /* Passive monitor (listen only) */
+
+/* Statistics flags (bitmask in updated) */
+#define IW_QUAL_QUAL_UPDATED   0x01    /* Value was updated since last read */
+#define IW_QUAL_LEVEL_UPDATED  0x02
+#define IW_QUAL_NOISE_UPDATED  0x04
+#define IW_QUAL_ALL_UPDATED    0x07
+#define IW_QUAL_DBM            0x08    /* Level + Noise are dBm */
+#define IW_QUAL_QUAL_INVALID   0x10    /* Driver doesn't provide value */
+#define IW_QUAL_LEVEL_INVALID  0x20
+#define IW_QUAL_NOISE_INVALID  0x40
+#define IW_QUAL_ALL_INVALID    0x70
+
+/* Frequency flags */
+#define IW_FREQ_AUTO           0x00    /* Let the driver decides */
+#define IW_FREQ_FIXED          0x01    /* Force a specific value */
+
+/* Maximum number of size of encoding token available
+ * they are listed in the range structure */
+#define IW_MAX_ENCODING_SIZES  8
+
+/* Maximum size of the encoding token in bytes */
+#define IW_ENCODING_TOKEN_MAX  64      /* 512 bits (for now) */
+
+/* Flags for encoding (along with the token) */
+#define IW_ENCODE_INDEX                0x00FF  /* Token index (if needed) */
+#define IW_ENCODE_FLAGS                0xFF00  /* Flags defined below */
+#define IW_ENCODE_MODE         0xF000  /* Modes defined below */
+#define IW_ENCODE_DISABLED     0x8000  /* Encoding disabled */
+#define IW_ENCODE_ENABLED      0x0000  /* Encoding enabled */
+#define IW_ENCODE_RESTRICTED   0x4000  /* Refuse non-encoded packets */
+#define IW_ENCODE_OPEN         0x2000  /* Accept non-encoded packets */
+#define IW_ENCODE_NOKEY                0x0800  /* Key is write only, so not present */
+#define IW_ENCODE_TEMP         0x0400  /* Temporary key */
+
+/* Power management flags available (along with the value, if any) */
+#define IW_POWER_ON            0x0000  /* No details... */
+#define IW_POWER_TYPE          0xF000  /* Type of parameter */
+#define IW_POWER_PERIOD                0x1000  /* Value is a period/duration of  */
+#define IW_POWER_TIMEOUT       0x2000  /* Value is a timeout (to go asleep) */
+#define IW_POWER_MODE          0x0F00  /* Power Management mode */
+#define IW_POWER_UNICAST_R     0x0100  /* Receive only unicast messages */
+#define IW_POWER_MULTICAST_R   0x0200  /* Receive only multicast messages */
+#define IW_POWER_ALL_R         0x0300  /* Receive all messages though PM */
+#define IW_POWER_FORCE_S       0x0400  /* Force PM procedure for sending unicast */
+#define IW_POWER_REPEATER      0x0800  /* Repeat broadcast messages in PM period */
+#define IW_POWER_MODIFIER      0x000F  /* Modify a parameter */
+#define IW_POWER_MIN           0x0001  /* Value is a minimum  */
+#define IW_POWER_MAX           0x0002  /* Value is a maximum */
+#define IW_POWER_RELATIVE      0x0004  /* Value is not in seconds/ms/us */
+
+/* Transmit Power flags available */
+#define IW_TXPOW_TYPE          0x00FF  /* Type of value */
+#define IW_TXPOW_DBM           0x0000  /* Value is in dBm */
+#define IW_TXPOW_MWATT         0x0001  /* Value is in mW */
+#define IW_TXPOW_RELATIVE      0x0002  /* Value is in arbitrary units */
+#define IW_TXPOW_RANGE         0x1000  /* Range of value between min/max */
+
+/* Retry limits and lifetime flags available */
+#define IW_RETRY_ON            0x0000  /* No details... */
+#define IW_RETRY_TYPE          0xF000  /* Type of parameter */
+#define IW_RETRY_LIMIT         0x1000  /* Maximum number of retries*/
+#define IW_RETRY_LIFETIME      0x2000  /* Maximum duration of retries in us */
+#define IW_RETRY_MODIFIER      0x000F  /* Modify a parameter */
+#define IW_RETRY_MIN           0x0001  /* Value is a minimum  */
+#define IW_RETRY_MAX           0x0002  /* Value is a maximum */
+#define IW_RETRY_RELATIVE      0x0004  /* Value is not in seconds/ms/us */
+
+/* Scanning request flags */
+#define IW_SCAN_DEFAULT                0x0000  /* Default scan of the driver */
+#define IW_SCAN_ALL_ESSID      0x0001  /* Scan all ESSIDs */
+#define IW_SCAN_THIS_ESSID     0x0002  /* Scan only this ESSID */
+#define IW_SCAN_ALL_FREQ       0x0004  /* Scan all Frequencies */
+#define IW_SCAN_THIS_FREQ      0x0008  /* Scan only this Frequency */
+#define IW_SCAN_ALL_MODE       0x0010  /* Scan all Modes */
+#define IW_SCAN_THIS_MODE      0x0020  /* Scan only this Mode */
+#define IW_SCAN_ALL_RATE       0x0040  /* Scan all Bit-Rates */
+#define IW_SCAN_THIS_RATE      0x0080  /* Scan only this Bit-Rate */
+/* struct iw_scan_req scan_type */
+#define IW_SCAN_TYPE_ACTIVE 0
+#define IW_SCAN_TYPE_PASSIVE 1
+/* Maximum size of returned data */
+#define IW_SCAN_MAX_DATA       4096    /* In bytes */
+
+/* Max number of char in custom event - use multiple of them if needed */
+#define IW_CUSTOM_MAX          256     /* In bytes */
+
+/* Generic information element */
+#define IW_GENERIC_IE_MAX      1024
+
+/* MLME requests (SIOCSIWMLME / struct iw_mlme) */
+#define IW_MLME_DEAUTH         0
+#define IW_MLME_DISASSOC       1
+
+/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */
+#define IW_AUTH_INDEX          0x0FFF
+#define IW_AUTH_FLAGS          0xF000
+/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095)
+ * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the
+ * parameter that is being set/get to; value will be read/written to
+ * struct iw_param value field) */
+#define IW_AUTH_WPA_VERSION            0
+#define IW_AUTH_CIPHER_PAIRWISE                1
+#define IW_AUTH_CIPHER_GROUP           2
+#define IW_AUTH_KEY_MGMT               3
+#define IW_AUTH_TKIP_COUNTERMEASURES   4
+#define IW_AUTH_DROP_UNENCRYPTED       5
+#define IW_AUTH_80211_AUTH_ALG         6
+#define IW_AUTH_WPA_ENABLED            7
+#define IW_AUTH_RX_UNENCRYPTED_EAPOL   8
+#define IW_AUTH_ROAMING_CONTROL                9
+#define IW_AUTH_PRIVACY_INVOKED                10
+#define IW_AUTH_CIPHER_GROUP_MGMT      11
+#define IW_AUTH_MFP                    12
+
+/* IW_AUTH_WPA_VERSION values (bit field) */
+#define IW_AUTH_WPA_VERSION_DISABLED   0x00000001
+#define IW_AUTH_WPA_VERSION_WPA                0x00000002
+#define IW_AUTH_WPA_VERSION_WPA2       0x00000004
+
+/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */
+#define IW_AUTH_CIPHER_NONE    0x00000001
+#define IW_AUTH_CIPHER_WEP40   0x00000002
+#define IW_AUTH_CIPHER_TKIP    0x00000004
+#define IW_AUTH_CIPHER_CCMP    0x00000008
+#define IW_AUTH_CIPHER_WEP104  0x00000010
+
+/* IW_AUTH_KEY_MGMT values (bit field) */
+#define IW_AUTH_KEY_MGMT_802_1X        1
+#define IW_AUTH_KEY_MGMT_PSK   2
+
+/* IW_AUTH_80211_AUTH_ALG values (bit field) */
+#define IW_AUTH_ALG_OPEN_SYSTEM        0x00000001
+#define IW_AUTH_ALG_SHARED_KEY 0x00000002
+#define IW_AUTH_ALG_LEAP       0x00000004
+
+/* IW_AUTH_ROAMING_CONTROL values */
+#define IW_AUTH_ROAMING_ENABLE 0       /* driver/firmware based roaming */
+#define IW_AUTH_ROAMING_DISABLE        1       /* user space program used for roaming
+                                        * control */
+
+/* IW_AUTH_MFP (management frame protection) values */
+#define IW_AUTH_MFP_DISABLED   0       /* MFP disabled */
+#define IW_AUTH_MFP_OPTIONAL   1       /* MFP optional */
+#define IW_AUTH_MFP_REQUIRED   2       /* MFP required */
+
+/* SIOCSIWENCODEEXT definitions */
+#define IW_ENCODE_SEQ_MAX_SIZE 8
+/* struct iw_encode_ext ->alg */
+#define IW_ENCODE_ALG_NONE     0
+#define IW_ENCODE_ALG_WEP      1
+#define IW_ENCODE_ALG_TKIP     2
+#define IW_ENCODE_ALG_CCMP     3
+#define IW_ENCODE_ALG_PMK      4
+#define IW_ENCODE_ALG_AES_CMAC 5
+/* struct iw_encode_ext ->ext_flags */
+#define IW_ENCODE_EXT_TX_SEQ_VALID     0x00000001
+#define IW_ENCODE_EXT_RX_SEQ_VALID     0x00000002
+#define IW_ENCODE_EXT_GROUP_KEY                0x00000004
+#define IW_ENCODE_EXT_SET_TX_KEY       0x00000008
+
+/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */
+#define IW_MICFAILURE_KEY_ID   0x00000003 /* Key ID 0..3 */
+#define IW_MICFAILURE_GROUP    0x00000004
+#define IW_MICFAILURE_PAIRWISE 0x00000008
+#define IW_MICFAILURE_STAKEY   0x00000010
+#define IW_MICFAILURE_COUNT    0x00000060 /* 1 or 2 (0 = count not supported)
+                                           */
+
+/* Bit field values for enc_capa in struct iw_range */
+#define IW_ENC_CAPA_WPA                0x00000001
+#define IW_ENC_CAPA_WPA2       0x00000002
+#define IW_ENC_CAPA_CIPHER_TKIP        0x00000004
+#define IW_ENC_CAPA_CIPHER_CCMP        0x00000008
+#define IW_ENC_CAPA_4WAY_HANDSHAKE     0x00000010
+
+/* Event capability macros - in (struct iw_range *)->event_capa
+ * Because we have more than 32 possible events, we use an array of
+ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */
+#define IW_EVENT_CAPA_BASE(cmd)                ((cmd >= SIOCIWFIRSTPRIV) ? \
+                                        (cmd - SIOCIWFIRSTPRIV + 0x60) : \
+                                        (cmd - SIOCSIWCOMMIT))
+#define IW_EVENT_CAPA_INDEX(cmd)       (IW_EVENT_CAPA_BASE(cmd) >> 5)
+#define IW_EVENT_CAPA_MASK(cmd)                (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F))
+/* Event capability constants - event autogenerated by the kernel
+ * This list is valid for most 802.11 devices, customise as needed... */
+#define IW_EVENT_CAPA_K_0      (IW_EVENT_CAPA_MASK(0x8B04) | \
+                                IW_EVENT_CAPA_MASK(0x8B06) | \
+                                IW_EVENT_CAPA_MASK(0x8B1A))
+#define IW_EVENT_CAPA_K_1      (IW_EVENT_CAPA_MASK(0x8B2A))
+/* "Easy" macro to set events in iw_range (less efficient) */
+#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd))
+#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; }
+
+
+/****************************** TYPES ******************************/
+
+/* --------------------------- SUBTYPES --------------------------- */
+/*
+ *     Generic format for most parameters that fit in an int
+ */
+struct iw_param
+{
+  __s32                value;          /* The value of the parameter itself */
+  __u8         fixed;          /* Hardware should not use auto select */
+  __u8         disabled;       /* Disable the feature */
+  __u16                flags;          /* Various specifc flags (if any) */
+};
+
+/*
+ *     For all data larger than 16 octets, we need to use a
+ *     pointer to memory allocated in user space.
+ */
+struct iw_point
+{
+  void __user  *pointer;       /* Pointer to the data  (in user space) */
+  __u16                length;         /* number of fields or size in bytes */
+  __u16                flags;          /* Optional params */
+};
+
+/*
+ *     A frequency
+ *     For numbers lower than 10^9, we encode the number in 'm' and
+ *     set 'e' to 0
+ *     For number greater than 10^9, we divide it by the lowest power
+ *     of 10 to get 'm' lower than 10^9, with 'm'= f / (10^'e')...
+ *     The power of 10 is in 'e', the result of the division is in 'm'.
+ */
+struct iw_freq
+{
+       __s32           m;              /* Mantissa */
+       __s16           e;              /* Exponent */
+       __u8            i;              /* List index (when in range struct) */
+       __u8            flags;          /* Flags (fixed/auto) */
+};
+
+/*
+ *     Quality of the link
+ */
+struct iw_quality
+{
+       __u8            qual;           /* link quality (%retries, SNR,
+                                          %missed beacons or better...) */
+       __u8            level;          /* signal level (dBm) */
+       __u8            noise;          /* noise level (dBm) */
+       __u8            updated;        /* Flags to know if updated */
+};
+
+/*
+ *     Packet discarded in the wireless adapter due to
+ *     "wireless" specific problems...
+ *     Note : the list of counter and statistics in net_device_stats
+ *     is already pretty exhaustive, and you should use that first.
+ *     This is only additional stats...
+ */
+struct iw_discarded
+{
+       __u32           nwid;           /* Rx : Wrong nwid/essid */
+       __u32           code;           /* Rx : Unable to code/decode (WEP) */
+       __u32           fragment;       /* Rx : Can't perform MAC reassembly */
+       __u32           retries;        /* Tx : Max MAC retries num reached */
+       __u32           misc;           /* Others cases */
+};
+
+/*
+ *     Packet/Time period missed in the wireless adapter due to
+ *     "wireless" specific problems...
+ */
+struct iw_missed
+{
+       __u32           beacon;         /* Missed beacons/superframe */
+};
+
+/*
+ *     Quality range (for spy threshold)
+ */
+struct iw_thrspy
+{
+       struct sockaddr         addr;           /* Source address (hw/mac) */
+       struct iw_quality       qual;           /* Quality of the link */
+       struct iw_quality       low;            /* Low threshold */
+       struct iw_quality       high;           /* High threshold */
+};
+
+/*
+ *     Optional data for scan request
+ *
+ *     Note: these optional parameters are controlling parameters for the
+ *     scanning behavior, these do not apply to getting scan results
+ *     (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and
+ *     provide a merged results with all BSSes even if the previous scan
+ *     request limited scanning to a subset, e.g., by specifying an SSID.
+ *     Especially, scan results are required to include an entry for the
+ *     current BSS if the driver is in Managed mode and associated with an AP.
+ */
+struct iw_scan_req
+{
+       __u8            scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */
+       __u8            essid_len;
+       __u8            num_channels; /* num entries in channel_list;
+                                      * 0 = scan all allowed channels */
+       __u8            flags; /* reserved as padding; use zero, this may
+                               * be used in the future for adding flags
+                               * to request different scan behavior */
+       struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or
+                               * individual address of a specific BSS */
+
+       /*
+        * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using
+        * the current ESSID. This allows scan requests for specific ESSID
+        * without having to change the current ESSID and potentially breaking
+        * the current association.
+        */
+       __u8            essid[IW_ESSID_MAX_SIZE];
+
+       /*
+        * Optional parameters for changing the default scanning behavior.
+        * These are based on the MLME-SCAN.request from IEEE Std 802.11.
+        * TU is 1.024 ms. If these are set to 0, driver is expected to use
+        * reasonable default values. min_channel_time defines the time that
+        * will be used to wait for the first reply on each channel. If no
+        * replies are received, next channel will be scanned after this. If
+        * replies are received, total time waited on the channel is defined by
+        * max_channel_time.
+        */
+       __u32           min_channel_time; /* in TU */
+       __u32           max_channel_time; /* in TU */
+
+       struct iw_freq  channel_list[IW_MAX_FREQUENCIES];
+};
+
+/* ------------------------- WPA SUPPORT ------------------------- */
+
+/*
+ *     Extended data structure for get/set encoding (this is used with
+ *     SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_*
+ *     flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and
+ *     only the data contents changes (key data -> this structure, including
+ *     key data).
+ *
+ *     If the new key is the first group key, it will be set as the default
+ *     TX key. Otherwise, default TX key index is only changed if
+ *     IW_ENCODE_EXT_SET_TX_KEY flag is set.
+ *
+ *     Key will be changed with SIOCSIWENCODEEXT in all cases except for
+ *     special "change TX key index" operation which is indicated by setting
+ *     key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY.
+ *
+ *     tx_seq/rx_seq are only used when respective
+ *     IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal
+ *     TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start
+ *     TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally
+ *     used only by an Authenticator (AP or an IBSS station) to get the
+ *     current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and
+ *     RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for
+ *     debugging/testing.
+ */
+struct iw_encode_ext
+{
+       __u32           ext_flags; /* IW_ENCODE_EXT_* */
+       __u8            tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+       __u8            rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+       struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast
+                              * (group) keys or unicast address for
+                              * individual keys */
+       __u16           alg; /* IW_ENCODE_ALG_* */
+       __u16           key_len;
+       __u8            key[0];
+};
+
+/* SIOCSIWMLME data */
+struct iw_mlme
+{
+       __u16           cmd; /* IW_MLME_* */
+       __u16           reason_code;
+       struct sockaddr addr;
+};
+
+/* SIOCSIWPMKSA data */
+#define IW_PMKSA_ADD           1
+#define IW_PMKSA_REMOVE                2
+#define IW_PMKSA_FLUSH         3
+
+#define IW_PMKID_LEN   16
+
+struct iw_pmksa
+{
+       __u32           cmd; /* IW_PMKSA_* */
+       struct sockaddr bssid;
+       __u8            pmkid[IW_PMKID_LEN];
+};
+
+/* IWEVMICHAELMICFAILURE data */
+struct iw_michaelmicfailure
+{
+       __u32           flags;
+       struct sockaddr src_addr;
+       __u8            tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */
+};
+
+/* IWEVPMKIDCAND data */
+#define IW_PMKID_CAND_PREAUTH  0x00000001 /* RNS pre-authentication enabled */
+struct iw_pmkid_cand
+{
+       __u32           flags; /* IW_PMKID_CAND_* */
+       __u32           index; /* the smaller the index, the higher the
+                               * priority */
+       struct sockaddr bssid;
+};
+
+/* ------------------------ WIRELESS STATS ------------------------ */
+/*
+ * Wireless statistics (used for /proc/net/wireless)
+ */
+struct iw_statistics
+{
+       __u16           status;         /* Status
+                                        * - device dependent for now */
+
+       struct iw_quality       qual;           /* Quality of the link
+                                                * (instant/mean/max) */
+       struct iw_discarded     discard;        /* Packet discarded counts */
+       struct iw_missed        miss;           /* Packet missed counts */
+};
+
+/* ------------------------ IOCTL REQUEST ------------------------ */
+/*
+ * This structure defines the payload of an ioctl, and is used 
+ * below.
+ *
+ * Note that this structure should fit on the memory footprint
+ * of iwreq (which is the same as ifreq), which mean a max size of
+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide...
+ * You should check this when increasing the structures defined
+ * above in this file...
+ */
+union  iwreq_data
+{
+       /* Config - generic */
+       char            name[IFNAMSIZ];
+       /* Name : used to verify the presence of  wireless extensions.
+        * Name of the protocol/provider... */
+
+       struct iw_point essid;          /* Extended network name */
+       struct iw_param nwid;           /* network id (or domain - the cell) */
+       struct iw_freq  freq;           /* frequency or channel :
+                                        * 0-1000 = channel
+                                        * > 1000 = frequency in Hz */
+
+       struct iw_param sens;           /* signal level threshold */
+       struct iw_param bitrate;        /* default bit rate */
+       struct iw_param txpower;        /* default transmit power */
+       struct iw_param rts;            /* RTS threshold threshold */
+       struct iw_param frag;           /* Fragmentation threshold */
+       __u32           mode;           /* Operation mode */
+       struct iw_param retry;          /* Retry limits & lifetime */
+
+       struct iw_point encoding;       /* Encoding stuff : tokens */
+       struct iw_param power;          /* PM duration/timeout */
+       struct iw_quality qual;         /* Quality part of statistics */
+
+       struct sockaddr ap_addr;        /* Access point address */
+       struct sockaddr addr;           /* Destination address (hw/mac) */
+
+       struct iw_param param;          /* Other small parameters */
+       struct iw_point data;           /* Other large parameters */
+};
+
+/*
+ * The structure to exchange data for ioctl.
+ * This structure is the same as 'struct ifreq', but (re)defined for
+ * convenience...
+ * Do I need to remind you about structure size (32 octets) ?
+ */
+struct iwreq 
+{
+       union
+       {
+               char    ifrn_name[IFNAMSIZ];    /* if name, e.g. "eth0" */
+       } ifr_ifrn;
+
+       /* Data part (defined just above) */
+       union   iwreq_data      u;
+};
+
+/* -------------------------- IOCTL DATA -------------------------- */
+/*
+ *     For those ioctl which want to exchange mode data that what could
+ *     fit in the above structure...
+ */
+
+/*
+ *     Range of parameters
+ */
+
+struct iw_range
+{
+       /* Informative stuff (to choose between different interface) */
+       __u32           throughput;     /* To give an idea... */
+       /* In theory this value should be the maximum benchmarked
+        * TCP/IP throughput, because with most of these devices the
+        * bit rate is meaningless (overhead an co) to estimate how
+        * fast the connection will go and pick the fastest one.
+        * I suggest people to play with Netperf or any benchmark...
+        */
+
+       /* NWID (or domain id) */
+       __u32           min_nwid;       /* Minimal NWID we are able to set */
+       __u32           max_nwid;       /* Maximal NWID we are able to set */
+
+       /* Old Frequency (backward compat - moved lower ) */
+       __u16           old_num_channels;
+       __u8            old_num_frequency;
+
+       /* Wireless event capability bitmasks */
+       __u32           event_capa[6];
+
+       /* signal level threshold range */
+       __s32           sensitivity;
+
+       /* Quality of link & SNR stuff */
+       /* Quality range (link, level, noise)
+        * If the quality is absolute, it will be in the range [0 ; max_qual],
+        * if the quality is dBm, it will be in the range [max_qual ; 0].
+        * Don't forget that we use 8 bit arithmetics... */
+       struct iw_quality       max_qual;       /* Quality of the link */
+       /* This should contain the average/typical values of the quality
+        * indicator. This should be the threshold between a "good" and
+        * a "bad" link (example : monitor going from green to orange).
+        * Currently, user space apps like quality monitors don't have any
+        * way to calibrate the measurement. With this, they can split
+        * the range between 0 and max_qual in different quality level
+        * (using a geometric subdivision centered on the average).
+        * I expect that people doing the user space apps will feedback
+        * us on which value we need to put in each driver... */
+       struct iw_quality       avg_qual;       /* Quality of the link */
+
+       /* Rates */
+       __u8            num_bitrates;   /* Number of entries in the list */
+       __s32           bitrate[IW_MAX_BITRATES];       /* list, in bps */
+
+       /* RTS threshold */
+       __s32           min_rts;        /* Minimal RTS threshold */
+       __s32           max_rts;        /* Maximal RTS threshold */
+
+       /* Frag threshold */
+       __s32           min_frag;       /* Minimal frag threshold */
+       __s32           max_frag;       /* Maximal frag threshold */
+
+       /* Power Management duration & timeout */
+       __s32           min_pmp;        /* Minimal PM period */
+       __s32           max_pmp;        /* Maximal PM period */
+       __s32           min_pmt;        /* Minimal PM timeout */
+       __s32           max_pmt;        /* Maximal PM timeout */
+       __u16           pmp_flags;      /* How to decode max/min PM period */
+       __u16           pmt_flags;      /* How to decode max/min PM timeout */
+       __u16           pm_capa;        /* What PM options are supported */
+
+       /* Encoder stuff */
+       __u16   encoding_size[IW_MAX_ENCODING_SIZES];   /* Different token sizes */
+       __u8    num_encoding_sizes;     /* Number of entry in the list */
+       __u8    max_encoding_tokens;    /* Max number of tokens */
+       /* For drivers that need a "login/passwd" form */
+       __u8    encoding_login_index;   /* token index for login token */
+
+       /* Transmit power */
+       __u16           txpower_capa;   /* What options are supported */
+       __u8            num_txpower;    /* Number of entries in the list */
+       __s32           txpower[IW_MAX_TXPOWER];        /* list, in bps */
+
+       /* Wireless Extension version info */
+       __u8            we_version_compiled;    /* Must be WIRELESS_EXT */
+       __u8            we_version_source;      /* Last update of source */
+
+       /* Retry limits and lifetime */
+       __u16           retry_capa;     /* What retry options are supported */
+       __u16           retry_flags;    /* How to decode max/min retry limit */
+       __u16           r_time_flags;   /* How to decode max/min retry life */
+       __s32           min_retry;      /* Minimal number of retries */
+       __s32           max_retry;      /* Maximal number of retries */
+       __s32           min_r_time;     /* Minimal retry lifetime */
+       __s32           max_r_time;     /* Maximal retry lifetime */
+
+       /* Frequency */
+       __u16           num_channels;   /* Number of channels [0; num - 1] */
+       __u8            num_frequency;  /* Number of entry in the list */
+       struct iw_freq  freq[IW_MAX_FREQUENCIES];       /* list */
+       /* Note : this frequency list doesn't need to fit channel numbers,
+        * because each entry contain its channel index */
+
+       __u32           enc_capa; /* IW_ENC_CAPA_* bit field */
+};
+
+/*
+ * Private ioctl interface information
+ */
+struct iw_priv_args
+{
+       __u32           cmd;            /* Number of the ioctl to issue */
+       __u16           set_args;       /* Type and number of args */
+       __u16           get_args;       /* Type and number of args */
+       char            name[IFNAMSIZ]; /* Name of the extension */
+};
+
+/* ----------------------- WIRELESS EVENTS ----------------------- */
+/*
+ * Wireless events are carried through the rtnetlink socket to user
+ * space. They are encapsulated in the IFLA_WIRELESS field of
+ * a RTM_NEWLINK message.
+ */
+
+/*
+ * A Wireless Event. Contains basically the same data as the ioctl...
+ */
+struct iw_event
+{
+       __u16           len;                    /* Real lenght of this stuff */
+       __u16           cmd;                    /* Wireless IOCTL */
+       union iwreq_data        u;              /* IOCTL fixed payload */
+};
+
+/* Size of the Event prefix (including padding and alignement junk) */
+#define IW_EV_LCP_LEN  (sizeof(struct iw_event) - sizeof(union iwreq_data))
+/* Size of the various events */
+#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ)
+#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32))
+#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq))
+#define IW_EV_PARAM_LEN        (IW_EV_LCP_LEN + sizeof(struct iw_param))
+#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr))
+#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality))
+
+/* iw_point events are special. First, the payload (extra data) come at
+ * the end of the event, so they are bigger than IW_EV_POINT_LEN. Second,
+ * we omit the pointer, so start at an offset. */
+#define IW_EV_POINT_OFF (((char *) &(((struct iw_point *) NULL)->length)) - \
+                         (char *) NULL)
+#define IW_EV_POINT_LEN        (IW_EV_LCP_LEN + sizeof(struct iw_point) - \
+                        IW_EV_POINT_OFF)
+
+#endif /* _LINUX_WIRELESS_H */
diff --git a/src/eap_common/Makefile b/src/eap_common/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/eap_common/chap.c b/src/eap_common/chap.c
new file mode 100644 (file)
index 0000000..60bfc1c
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-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 "common.h"
+#include "crypto/crypto.h"
+#include "chap.h"
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+             size_t challenge_len, u8 *response)
+{
+       const u8 *addr[3];
+       size_t len[3];
+
+       addr[0] = &id;
+       len[0] = 1;
+       addr[1] = secret;
+       len[1] = secret_len;
+       addr[2] = challenge;
+       len[2] = challenge_len;
+       return md5_vector(3, addr, len, response);
+}
diff --git a/src/eap_common/chap.h b/src/eap_common/chap.h
new file mode 100644 (file)
index 0000000..b9c400c
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * CHAP-MD5 (RFC 1994)
+ * Copyright (c) 2007-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.
+ */
+
+#ifndef CHAP_H
+#define CHAP_H
+
+#define CHAP_MD5_LEN 16
+
+int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge,
+            size_t challenge_len, u8 *response);
+
+#endif /* CHAP_H */
diff --git a/src/eap_common/eap_common.c b/src/eap_common/eap_common.c
new file mode 100644 (file)
index 0000000..4afa1dd
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * EAP common peer/server 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+
+/**
+ * eap_hdr_validate - Validate EAP header
+ * @vendor: Expected EAP Vendor-Id (0 = IETF)
+ * @eap_type: Expected EAP type number
+ * @msg: EAP frame (starting with EAP header)
+ * @plen: Pointer to variable to contain the returned payload length
+ * Returns: Pointer to EAP payload (after type field), or %NULL on failure
+ *
+ * This is a helper function for EAP method implementations. This is usually
+ * called in the beginning of struct eap_method::process() function to verify
+ * that the received EAP request packet has a valid header. This function is
+ * able to process both legacy and expanded EAP headers and in most cases, the
+ * caller can just use the returned payload pointer (into *plen) for processing
+ * the payload regardless of whether the packet used the expanded EAP header or
+ * not.
+ */
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+                           const struct wpabuf *msg, size_t *plen)
+{
+       const struct eap_hdr *hdr;
+       const u8 *pos;
+       size_t len;
+
+       hdr = wpabuf_head(msg);
+
+       if (wpabuf_len(msg) < sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "EAP: Too short EAP frame");
+               return NULL;
+       }
+
+       len = be_to_host16(hdr->length);
+       if (len < sizeof(*hdr) + 1 || len > wpabuf_len(msg)) {
+               wpa_printf(MSG_INFO, "EAP: Invalid EAP length");
+               return NULL;
+       }
+
+       pos = (const u8 *) (hdr + 1);
+
+       if (*pos == EAP_TYPE_EXPANDED) {
+               int exp_vendor;
+               u32 exp_type;
+               if (len < sizeof(*hdr) + 8) {
+                       wpa_printf(MSG_INFO, "EAP: Invalid expanded EAP "
+                                  "length");
+                       return NULL;
+               }
+               pos++;
+               exp_vendor = WPA_GET_BE24(pos);
+               pos += 3;
+               exp_type = WPA_GET_BE32(pos);
+               pos += 4;
+               if (exp_vendor != vendor || exp_type != (u32) eap_type) {
+                       wpa_printf(MSG_INFO, "EAP: Invalid expanded frame "
+                                  "type");
+                       return NULL;
+               }
+
+               *plen = len - sizeof(*hdr) - 8;
+               return pos;
+       } else {
+               if (vendor != EAP_VENDOR_IETF || *pos != eap_type) {
+                       wpa_printf(MSG_INFO, "EAP: Invalid frame type");
+                       return NULL;
+               }
+               *plen = len - sizeof(*hdr) - 1;
+               return pos + 1;
+       }
+}
+
+
+/**
+ * eap_msg_alloc - Allocate a buffer for an EAP message
+ * @vendor: Vendor-Id (0 = IETF)
+ * @type: EAP type
+ * @payload_len: Payload length in bytes (data after Type)
+ * @code: Message Code (EAP_CODE_*)
+ * @identifier: Identifier
+ * Returns: Pointer to the allocated message buffer or %NULL on error
+ *
+ * This function can be used to allocate a buffer for an EAP message and fill
+ * in the EAP header. This function is automatically using expanded EAP header
+ * if the selected Vendor-Id is not IETF. In other words, most EAP methods do
+ * not need to separately select which header type to use when using this
+ * function to allocate the message buffers. The returned buffer has room for
+ * payload_len bytes and has the EAP header and Type field already filled in.
+ */
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+                             u8 code, u8 identifier)
+{
+       struct wpabuf *buf;
+       struct eap_hdr *hdr;
+       size_t len;
+
+       len = sizeof(struct eap_hdr) + (vendor == EAP_VENDOR_IETF ? 1 : 8) +
+               payload_len;
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return NULL;
+
+       hdr = wpabuf_put(buf, sizeof(*hdr));
+       hdr->code = code;
+       hdr->identifier = identifier;
+       hdr->length = host_to_be16(len);
+
+       if (vendor == EAP_VENDOR_IETF) {
+               wpabuf_put_u8(buf, type);
+       } else {
+               wpabuf_put_u8(buf, EAP_TYPE_EXPANDED);
+               wpabuf_put_be24(buf, vendor);
+               wpabuf_put_be32(buf, type);
+       }
+
+       return buf;
+}
+
+
+/**
+ * eap_update_len - Update EAP header length
+ * @msg: EAP message from eap_msg_alloc
+ *
+ * This function updates the length field in the EAP header to match with the
+ * current length for the buffer. This allows eap_msg_alloc() to be used to
+ * allocate a larger buffer than the exact message length (e.g., if exact
+ * message length is not yet known).
+ */
+void eap_update_len(struct wpabuf *msg)
+{
+       struct eap_hdr *hdr;
+       hdr = wpabuf_mhead(msg);
+       if (wpabuf_len(msg) < sizeof(*hdr))
+               return;
+       hdr->length = host_to_be16(wpabuf_len(msg));
+}
+
+
+/**
+ * eap_get_id - Get EAP Identifier from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The Identifier field from the EAP header
+ */
+u8 eap_get_id(const struct wpabuf *msg)
+{
+       const struct eap_hdr *eap;
+
+       if (wpabuf_len(msg) < sizeof(*eap))
+               return 0;
+
+       eap = wpabuf_head(msg);
+       return eap->identifier;
+}
+
+
+/**
+ * eap_get_id - Get EAP Type from wpabuf
+ * @msg: Buffer starting with an EAP header
+ * Returns: The EAP Type after the EAP header
+ */
+EapType eap_get_type(const struct wpabuf *msg)
+{
+       if (wpabuf_len(msg) < sizeof(struct eap_hdr) + 1)
+               return EAP_TYPE_NONE;
+
+       return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
+}
diff --git a/src/eap_common/eap_common.h b/src/eap_common/eap_common.h
new file mode 100644 (file)
index 0000000..b95e76b
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * EAP common peer/server 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 EAP_COMMON_H
+#define EAP_COMMON_H
+
+#include "wpabuf.h"
+
+const u8 * eap_hdr_validate(int vendor, EapType eap_type,
+                           const struct wpabuf *msg, size_t *plen);
+struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
+                             u8 code, u8 identifier);
+void eap_update_len(struct wpabuf *msg);
+u8 eap_get_id(const struct wpabuf *msg);
+EapType eap_get_type(const struct wpabuf *msg);
+
+#endif /* EAP_COMMON_H */
diff --git a/src/eap_common/eap_defs.h b/src/eap_common/eap_defs.h
new file mode 100644 (file)
index 0000000..0efe7ab
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * EAP server/peer: Shared EAP 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 EAP_DEFS_H
+#define EAP_DEFS_H
+
+/* RFC 3748 - Extensible Authentication Protocol (EAP) */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_hdr {
+       u8 code;
+       u8 identifier;
+       be16 length; /* including code and identifier; network byte order */
+       /* followed by length-4 octets of data */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
+       EAP_CODE_FAILURE = 4 };
+
+/* EAP Request and Response data begins with one octet Type. Success and
+ * Failure do not have additional data. */
+
+/*
+ * EAP Method Types as allocated by IANA:
+ * http://www.iana.org/assignments/eap-numbers
+ */
+typedef enum {
+       EAP_TYPE_NONE = 0,
+       EAP_TYPE_IDENTITY = 1 /* RFC 3748 */,
+       EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */,
+       EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */,
+       EAP_TYPE_MD5 = 4, /* RFC 3748 */
+       EAP_TYPE_OTP = 5 /* RFC 3748 */,
+       EAP_TYPE_GTC = 6, /* RFC 3748 */
+       EAP_TYPE_TLS = 13 /* RFC 2716 */,
+       EAP_TYPE_LEAP = 17 /* Cisco proprietary */,
+       EAP_TYPE_SIM = 18 /* RFC 4186 */,
+       EAP_TYPE_TTLS = 21 /* RFC 5281 */,
+       EAP_TYPE_AKA = 23 /* RFC 4187 */,
+       EAP_TYPE_PEAP = 25 /* draft-josefsson-pppext-eap-tls-eap-06.txt */,
+       EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */,
+       EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */,
+       EAP_TYPE_TNC = 38 /* TNC IF-T v1.0-r3; note: tentative assignment;
+                          * type 38 has previously been allocated for
+                          * EAP-HTTP Digest, (funk.com) */,
+       EAP_TYPE_FAST = 43 /* RFC 4851 */,
+       EAP_TYPE_PAX = 46 /* RFC 4746 */,
+       EAP_TYPE_PSK = 47 /* RFC 4764 */,
+       EAP_TYPE_SAKE = 48 /* RFC 4763 */,
+       EAP_TYPE_IKEV2 = 49 /* RFC 5106 */,
+       EAP_TYPE_AKA_PRIME = 50 /* draft-arkko-eap-aka-kdf-10.txt */,
+       EAP_TYPE_GPSK = 51 /* RFC 5433 */,
+       EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
+} EapType;
+
+
+/* SMI Network Management Private Enterprise Code for vendor specific types */
+enum {
+       EAP_VENDOR_IETF = 0,
+       EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
+       EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */
+};
+
+#define EAP_MSK_LEN 64
+#define EAP_EMSK_LEN 64
+
+#endif /* EAP_DEFS_H */
diff --git a/src/eap_common/eap_fast_common.c b/src/eap_common/eap_fast_common.c
new file mode 100644 (file)
index 0000000..4de34a8
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * EAP-FAST common helper functions (RFC 4851)
+ * 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 "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_defs.h"
+#include "eap_tlv_common.h"
+#include "eap_fast_common.h"
+
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len)
+{
+       struct pac_tlv_hdr hdr;
+       hdr.type = host_to_be16(type);
+       hdr.len = host_to_be16(len);
+       wpabuf_put_data(buf, &hdr, sizeof(hdr));
+}
+
+
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+                            u16 len)
+{
+       eap_fast_put_tlv_hdr(buf, type, len);
+       wpabuf_put_data(buf, data, len);
+}
+
+
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+                                const struct wpabuf *data)
+{
+       eap_fast_put_tlv_hdr(buf, type, wpabuf_len(data));
+       wpabuf_put_buf(buf, data);
+}
+
+
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf)
+{
+       struct wpabuf *e;
+
+       if (buf == NULL)
+               return NULL;
+
+       /* Encapsulate EAP packet in EAP-Payload TLV */
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Add EAP-Payload TLV");
+       e = wpabuf_alloc(sizeof(struct pac_tlv_hdr) + wpabuf_len(buf));
+       if (e == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+                          "for TLV encapsulation");
+               wpabuf_free(buf);
+               return NULL;
+       }
+       eap_fast_put_tlv_buf(e,
+                            EAP_TLV_TYPE_MANDATORY | EAP_TLV_EAP_PAYLOAD_TLV,
+                            buf);
+       wpabuf_free(buf);
+       return e;
+}
+
+
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+                                  const u8 *client_random, u8 *master_secret)
+{
+#define TLS_RANDOM_LEN 32
+#define TLS_MASTER_SECRET_LEN 48
+       u8 seed[2 * TLS_RANDOM_LEN];
+
+       wpa_hexdump(MSG_DEBUG, "EAP-FAST: client_random",
+                   client_random, TLS_RANDOM_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-FAST: server_random",
+                   server_random, TLS_RANDOM_LEN);
+
+       /*
+        * RFC 4851, Section 5.1:
+        * master_secret = T-PRF(PAC-Key, "PAC to master secret label hash", 
+        *                       server_random + client_random, 48)
+        */
+       os_memcpy(seed, server_random, TLS_RANDOM_LEN);
+       os_memcpy(seed + TLS_RANDOM_LEN, client_random, TLS_RANDOM_LEN);
+       sha1_t_prf(pac_key, EAP_FAST_PAC_KEY_LEN,
+                  "PAC to master secret label hash",
+                  seed, sizeof(seed), master_secret, TLS_MASTER_SECRET_LEN);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: master_secret",
+                       master_secret, TLS_MASTER_SECRET_LEN);
+}
+
+
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+                        const char *label, size_t len)
+{
+       struct tls_keys keys;
+       u8 *rnd = NULL, *out;
+       int block_size;
+
+       block_size = tls_connection_get_keyblock_size(ssl_ctx, conn);
+       if (block_size < 0)
+               return NULL;
+
+       out = os_malloc(block_size + len);
+       if (out == NULL)
+               return NULL;
+
+       if (tls_connection_prf(ssl_ctx, conn, label, 1, out, block_size + len)
+           == 0) {
+               os_memmove(out, out + block_size, len);
+               return out;
+       }
+
+       if (tls_connection_get_keys(ssl_ctx, conn, &keys))
+               goto fail;
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       if (rnd == NULL)
+               goto fail;
+
+       os_memcpy(rnd, keys.server_random, keys.server_random_len);
+       os_memcpy(rnd + keys.server_random_len, keys.client_random,
+                 keys.client_random_len);
+
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: master_secret for key "
+                       "expansion", keys.master_key, keys.master_key_len);
+       if (tls_prf(keys.master_key, keys.master_key_len,
+                   label, rnd, keys.client_random_len +
+                   keys.server_random_len, out, block_size + len))
+               goto fail;
+       os_free(rnd);
+       os_memmove(out, out + block_size, len);
+       return out;
+
+fail:
+       os_free(rnd);
+       os_free(out);
+       return NULL;
+}
+
+
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk)
+{
+       /*
+        * RFC 4851, Section 5.4: EAP Master Session Key Generation
+        * MSK = T-PRF(S-IMCK[j], "Session Key Generating Function", 64)
+        */
+
+       sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+                  "Session Key Generating Function", (u8 *) "", 0,
+                  msk, EAP_FAST_KEY_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (MSK)",
+                       msk, EAP_FAST_KEY_LEN);
+}
+
+
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
+{
+       /*
+        * RFC 4851, Section 5.4: EAP Master Session Key Genreration
+        * EMSK = T-PRF(S-IMCK[j],
+        *        "Extended Session Key Generating Function", 64)
+        */
+
+       sha1_t_prf(simck, EAP_FAST_SIMCK_LEN,
+                  "Extended Session Key Generating Function", (u8 *) "", 0,
+                  emsk, EAP_EMSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Derived key (EMSK)",
+                       emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+                      int tlv_type, u8 *pos, int len)
+{
+       switch (tlv_type) {
+       case EAP_TLV_EAP_PAYLOAD_TLV:
+               wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: EAP-Payload TLV",
+                           pos, len);
+               if (tlv->eap_payload_tlv) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+                                  "EAP-Payload TLV in the message");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               tlv->eap_payload_tlv = pos;
+               tlv->eap_payload_tlv_len = len;
+               break;
+       case EAP_TLV_RESULT_TLV:
+               wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Result TLV", pos, len);
+               if (tlv->result) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+                                  "Result TLV in the message");
+                       tlv->result = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               if (len < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+                                  "Result TLV");
+                       tlv->result = EAP_TLV_RESULT_FAILURE;
+                       break;
+               }
+               tlv->result = WPA_GET_BE16(pos);
+               if (tlv->result != EAP_TLV_RESULT_SUCCESS &&
+                   tlv->result != EAP_TLV_RESULT_FAILURE) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Result %d",
+                                  tlv->result);
+                       tlv->result = EAP_TLV_RESULT_FAILURE;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Result: %s",
+                          tlv->result == EAP_TLV_RESULT_SUCCESS ?
+                          "Success" : "Failure");
+               break;
+       case EAP_TLV_INTERMEDIATE_RESULT_TLV:
+               wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Intermediate Result TLV",
+                           pos, len);
+               if (len < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+                                  "Intermediate-Result TLV");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       break;
+               }
+               if (tlv->iresult) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+                                  "Intermediate-Result TLV in the message");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               tlv->iresult = WPA_GET_BE16(pos);
+               if (tlv->iresult != EAP_TLV_RESULT_SUCCESS &&
+                   tlv->iresult != EAP_TLV_RESULT_FAILURE) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Unknown Intermediate "
+                                  "Result %d", tlv->iresult);
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Intermediate Result: %s",
+                          tlv->iresult == EAP_TLV_RESULT_SUCCESS ?
+                          "Success" : "Failure");
+               break;
+       case EAP_TLV_CRYPTO_BINDING_TLV:
+               wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV",
+                           pos, len);
+               if (tlv->crypto_binding) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+                                  "Crypto-Binding TLV in the message");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               tlv->crypto_binding_len = sizeof(struct eap_tlv_hdr) + len;
+               if (tlv->crypto_binding_len < sizeof(*tlv->crypto_binding)) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+                                  "Crypto-Binding TLV");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               tlv->crypto_binding = (struct eap_tlv_crypto_binding_tlv *)
+                       (pos - sizeof(struct eap_tlv_hdr));
+               break;
+       case EAP_TLV_REQUEST_ACTION_TLV:
+               wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Request-Action TLV",
+                           pos, len);
+               if (tlv->request_action) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+                                  "Request-Action TLV in the message");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               if (len < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Too short "
+                                  "Request-Action TLV");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       break;
+               }
+               tlv->request_action = WPA_GET_BE16(pos);
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Request-Action: %d",
+                          tlv->request_action);
+               break;
+       case EAP_TLV_PAC_TLV:
+               wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: PAC TLV", pos, len);
+               if (tlv->pac) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: More than one "
+                                  "PAC TLV in the message");
+                       tlv->iresult = EAP_TLV_RESULT_FAILURE;
+                       return -2;
+               }
+               tlv->pac = pos;
+               tlv->pac_len = len;
+               break;
+       default:
+               /* Unknown TLV */
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/src/eap_common/eap_fast_common.h b/src/eap_common/eap_fast_common.h
new file mode 100644 (file)
index 0000000..c85fd37
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * EAP-FAST definitions (RFC 4851)
+ * 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 EAP_FAST_H
+#define EAP_FAST_H
+
+#define EAP_FAST_VERSION 1
+#define EAP_FAST_KEY_LEN 64
+#define EAP_FAST_SIMCK_LEN 40
+#define EAP_FAST_SKS_LEN 40
+#define EAP_FAST_CMK_LEN 20
+
+#define TLS_EXT_PAC_OPAQUE 35
+
+/*
+ * RFC 5422: Section 4.2.1 - Formats for PAC TLV Attributes / Type Field
+ * Note: bit 0x8000 (Mandatory) and bit 0x4000 (Reserved) are also defined
+ * in the general PAC TLV format (Section 4.2).
+ */
+#define PAC_TYPE_PAC_KEY 1
+#define PAC_TYPE_PAC_OPAQUE 2
+#define PAC_TYPE_CRED_LIFETIME 3
+#define PAC_TYPE_A_ID 4
+#define PAC_TYPE_I_ID 5
+/*
+ * 6 was previous assigned for SERVER_PROTECTED_DATA, but
+ * draft-cam-winget-eap-fast-provisioning-02.txt changed this to Reserved.
+ */
+#define PAC_TYPE_A_ID_INFO 7
+#define PAC_TYPE_PAC_ACKNOWLEDGEMENT 8
+#define PAC_TYPE_PAC_INFO 9
+#define PAC_TYPE_PAC_TYPE 10
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct pac_tlv_hdr {
+       be16 type;
+       be16 len;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+#define EAP_FAST_PAC_KEY_LEN 32
+
+/* RFC 5422: 4.2.6 PAC-Type TLV */
+#define PAC_TYPE_TUNNEL_PAC 1
+/* Application Specific Short Lived PACs (only in volatile storage) */
+/* User Authorization PAC */
+#define PAC_TYPE_USER_AUTHORIZATION 3
+/* Application Specific Long Lived PACs */
+/* Machine Authentication PAC */
+#define PAC_TYPE_MACHINE_AUTHENTICATION 2
+
+
+/*
+ * RFC 5422:
+ * Section 3.3 - Key Derivations Used in the EAP-FAST Provisioning Exchange
+ */
+struct eap_fast_key_block_provisioning {
+       /* Extra key material after TLS key_block */
+       u8 session_key_seed[EAP_FAST_SKS_LEN];
+       u8 server_challenge[16]; /* MSCHAPv2 ServerChallenge */
+       u8 client_challenge[16]; /* MSCHAPv2 ClientChallenge */
+};
+
+
+struct wpabuf;
+struct tls_connection;
+
+struct eap_fast_tlv_parse {
+       u8 *eap_payload_tlv;
+       size_t eap_payload_tlv_len;
+       struct eap_tlv_crypto_binding_tlv *crypto_binding;
+       size_t crypto_binding_len;
+       int iresult;
+       int result;
+       int request_action;
+       u8 *pac;
+       size_t pac_len;
+};
+
+void eap_fast_put_tlv_hdr(struct wpabuf *buf, u16 type, u16 len);
+void eap_fast_put_tlv(struct wpabuf *buf, u16 type, const void *data,
+                     u16 len);
+void eap_fast_put_tlv_buf(struct wpabuf *buf, u16 type,
+                         const struct wpabuf *data);
+struct wpabuf * eap_fast_tlv_eap_payload(struct wpabuf *buf);
+void eap_fast_derive_master_secret(const u8 *pac_key, const u8 *server_random,
+                                  const u8 *client_random, u8 *master_secret);
+u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
+                        const char *label, size_t len);
+void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
+void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
+int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
+                      int tlv_type, u8 *pos, int len);
+
+#endif /* EAP_FAST_H */
diff --git a/src/eap_common/eap_gpsk_common.c b/src/eap_common/eap_gpsk_common.c
new file mode 100644 (file)
index 0000000..4076262
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-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 "crypto/aes_wrap.h"
+#include "crypto/sha256.h"
+#include "eap_defs.h"
+#include "eap_gpsk_common.h"
+
+
+/**
+ * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: 1 if ciphersuite is support, or 0 if not
+ */
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier)
+{
+       if (vendor == EAP_GPSK_VENDOR_IETF &&
+           specifier == EAP_GPSK_CIPHER_AES)
+               return 1;
+#ifdef EAP_GPSK_SHA256
+       if (vendor == EAP_GPSK_VENDOR_IETF &&
+           specifier == EAP_GPSK_CIPHER_SHA256)
+               return 1;
+#endif /* EAP_GPSK_SHA256 */
+       return 0;
+}
+
+
+static int eap_gpsk_gkdf_cmac(const u8 *psk /* Y */,
+                             const u8 *data /* Z */, size_t data_len,
+                             u8 *buf, size_t len /* X */)
+{
+       u8 *opos;
+       size_t i, n, hashlen, left, clen;
+       u8 ibuf[2], hash[16];
+       const u8 *addr[2];
+       size_t vlen[2];
+
+       hashlen = sizeof(hash);
+       /* M_i = MAC_Y (i || Z); (MAC = AES-CMAC-128) */
+       addr[0] = ibuf;
+       vlen[0] = sizeof(ibuf);
+       addr[1] = data;
+       vlen[1] = data_len;
+
+       opos = buf;
+       left = len;
+       n = (len + hashlen - 1) / hashlen;
+       for (i = 1; i <= n; i++) {
+               WPA_PUT_BE16(ibuf, i);
+               if (omac1_aes_128_vector(psk, 2, addr, vlen, hash))
+                       return -1;
+               clen = left > hashlen ? hashlen : left;
+               os_memcpy(opos, hash, clen);
+               opos += clen;
+               left -= clen;
+       }
+
+       return 0;
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */,
+                               const u8 *data /* Z */, size_t data_len,
+                               u8 *buf, size_t len /* X */)
+{
+       u8 *opos;
+       size_t i, n, hashlen, left, clen;
+       u8 ibuf[2], hash[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t vlen[2];
+
+       hashlen = SHA256_MAC_LEN;
+       /* M_i = MAC_Y (i || Z); (MAC = HMAC-SHA256) */
+       addr[0] = ibuf;
+       vlen[0] = sizeof(ibuf);
+       addr[1] = data;
+       vlen[1] = data_len;
+
+       opos = buf;
+       left = len;
+       n = (len + hashlen - 1) / hashlen;
+       for (i = 1; i <= n; i++) {
+               WPA_PUT_BE16(ibuf, i);
+               hmac_sha256_vector(psk, 32, 2, addr, vlen, hash);
+               clen = left > hashlen ? hashlen : left;
+               os_memcpy(opos, hash, clen);
+               opos += clen;
+               left -= clen;
+       }
+
+       return 0;
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+static int eap_gpsk_derive_keys_helper(u32 csuite_specifier,
+                                      u8 *kdf_out, size_t kdf_out_len,
+                                      const u8 *psk, size_t psk_len,
+                                      const u8 *seed, size_t seed_len,
+                                      u8 *msk, u8 *emsk,
+                                      u8 *sk, size_t sk_len,
+                                      u8 *pk, size_t pk_len)
+{
+       u8 mk[32], *pos, *data;
+       size_t data_len, mk_len;
+       int (*gkdf)(const u8 *_psk, const u8 *_data, size_t _data_len,
+                   u8 *buf, size_t len);
+
+       gkdf = NULL;
+       switch (csuite_specifier) {
+       case EAP_GPSK_CIPHER_AES:
+               gkdf = eap_gpsk_gkdf_cmac;
+               mk_len = 16;
+               break;
+#ifdef EAP_GPSK_SHA256
+       case EAP_GPSK_CIPHER_SHA256:
+               gkdf = eap_gpsk_gkdf_sha256;
+               mk_len = SHA256_MAC_LEN;
+               break;
+#endif /* EAP_GPSK_SHA256 */
+       default:
+               return -1;
+       }
+
+       if (psk_len < mk_len)
+               return -1;
+
+       data_len = 2 + psk_len + 6 + seed_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       pos = data;
+       WPA_PUT_BE16(pos, psk_len);
+       pos += 2;
+       os_memcpy(pos, psk, psk_len);
+       pos += psk_len;
+       WPA_PUT_BE32(pos, EAP_GPSK_VENDOR_IETF); /* CSuite/Vendor = IETF */
+       pos += 4;
+       WPA_PUT_BE16(pos, csuite_specifier); /* CSuite/Specifier */
+       pos += 2;
+       os_memcpy(pos, seed, seed_len); /* inputString */
+       wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation",
+                       data, data_len);
+
+       if (gkdf(psk, data, data_len, mk, mk_len) < 0) {
+               os_free(data);
+               return -1;
+       }
+       os_free(data);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, mk_len);
+
+       if (gkdf(mk, seed, seed_len, kdf_out, kdf_out_len) < 0)
+               return -1;
+
+       pos = kdf_out;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN);
+       os_memcpy(msk, pos, EAP_MSK_LEN);
+       pos += EAP_MSK_LEN;
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN);
+       os_memcpy(emsk, pos, EAP_EMSK_LEN);
+       pos += EAP_EMSK_LEN;
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, sk_len);
+       os_memcpy(sk, pos, sk_len);
+       pos += sk_len;
+
+       if (pk) {
+               wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, pk_len);
+               os_memcpy(pk, pos, pk_len);
+       }
+
+       return 0;
+}
+
+
+static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len,
+                                   const u8 *seed, size_t seed_len,
+                                   u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+                                   u8 *pk, size_t *pk_len)
+{
+#define EAP_GPSK_SK_LEN_AES 16
+#define EAP_GPSK_PK_LEN_AES 16
+       u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES +
+                  EAP_GPSK_PK_LEN_AES];
+
+       /*
+        * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+        *            (= seed)
+        * KS = 16, PL = psk_len, CSuite_Sel = 0x00000000 0x0001
+        * MK = GKDF-16 (PSK[0..15], PL || PSK || CSuite_Sel || inputString)
+        * MSK = GKDF-160 (MK, inputString)[0..63]
+        * EMSK = GKDF-160 (MK, inputString)[64..127]
+        * SK = GKDF-160 (MK, inputString)[128..143]
+        * PK = GKDF-160 (MK, inputString)[144..159]
+        * zero = 0x00 || 0x00 || ... || 0x00 (16 times)
+        * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+        *                      CSuite_Sel || inputString)
+        */
+
+       *sk_len = EAP_GPSK_SK_LEN_AES;
+       *pk_len = EAP_GPSK_PK_LEN_AES;
+
+       return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_AES,
+                                          kdf_out, sizeof(kdf_out),
+                                          psk, psk_len, seed, seed_len,
+                                          msk, emsk, sk, *sk_len,
+                                          pk, *pk_len);
+}
+
+
+#ifdef EAP_GPSK_SHA256
+static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len,
+                                      const u8 *seed, size_t seed_len,
+                                      u8 *msk, u8 *emsk,
+                                      u8 *sk, size_t *sk_len)
+{
+#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN
+#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN
+       u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 +
+                  EAP_GPSK_PK_LEN_SHA256];
+
+       /*
+        * inputString = RAND_Peer || ID_Peer || RAND_Server || ID_Server
+        *            (= seed)
+        * KS = 32, PL = psk_len, CSuite_Sel = 0x00000000 0x0002
+        * MK = GKDF-32 (PSK[0..31], PL || PSK || CSuite_Sel || inputString)
+        * MSK = GKDF-160 (MK, inputString)[0..63]
+        * EMSK = GKDF-160 (MK, inputString)[64..127]
+        * SK = GKDF-160 (MK, inputString)[128..159]
+        * zero = 0x00 || 0x00 || ... || 0x00 (32 times)
+        * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
+        *                      CSuite_Sel || inputString)
+        */
+
+       *sk_len = EAP_GPSK_SK_LEN_SHA256;
+
+       return eap_gpsk_derive_keys_helper(EAP_GPSK_CIPHER_SHA256,
+                                          kdf_out, sizeof(kdf_out),
+                                          psk, psk_len, seed, seed_len,
+                                          msk, emsk, sk, *sk_len,
+                                          NULL, 0);
+}
+#endif /* EAP_GPSK_SHA256 */
+
+
+/**
+ * eap_gpsk_derive_keys - Derive EAP-GPSK keys
+ * @psk: Pre-shared key
+ * @psk_len: Length of psk in bytes
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @rand_peer: 32-byte RAND_Peer
+ * @rand_server: 32-byte RAND_Server
+ * @id_peer: ID_Peer
+ * @id_peer_len: Length of ID_Peer
+ * @id_server: ID_Server
+ * @id_server_len: Length of ID_Server
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes)
+ * @sk_len: Buffer for returning length of SK
+ * @pk: Buffer for PK (at least EAP_GPSK_MAX_PK_LEN bytes)
+ * @pk_len: Buffer for returning length of PK
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+                        int specifier,
+                        const u8 *rand_peer, const u8 *rand_server,
+                        const u8 *id_peer, size_t id_peer_len,
+                        const u8 *id_server, size_t id_server_len,
+                        u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+                        u8 *pk, size_t *pk_len)
+{
+       u8 *seed, *pos;
+       size_t seed_len;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
+                  vendor, specifier);
+
+       if (vendor != EAP_GPSK_VENDOR_IETF)
+               return -1;
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
+
+       /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
+       seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
+       seed = os_malloc(seed_len);
+       if (seed == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
+                          "for key derivation");
+               return -1;
+       }
+
+       pos = seed;
+       os_memcpy(pos, rand_peer, EAP_GPSK_RAND_LEN);
+       pos += EAP_GPSK_RAND_LEN;
+       os_memcpy(pos, id_peer, id_peer_len);
+       pos += id_peer_len;
+       os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN);
+       pos += EAP_GPSK_RAND_LEN;
+       os_memcpy(pos, id_server, id_server_len);
+       pos += id_server_len;
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+
+       switch (specifier) {
+       case EAP_GPSK_CIPHER_AES:
+               ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
+                                              msk, emsk, sk, sk_len,
+                                              pk, pk_len);
+               break;
+#ifdef EAP_GPSK_SHA256
+       case EAP_GPSK_CIPHER_SHA256:
+               ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
+                                                 msk, emsk, sk, sk_len);
+               break;
+#endif /* EAP_GPSK_SHA256 */
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+                          "key derivation", vendor, specifier);
+               ret = -1;
+               break;
+       }
+
+       os_free(seed);
+
+       return ret;
+}
+
+
+/**
+ * eap_gpsk_mic_len - Get the length of the MIC
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * Returns: MIC length in bytes
+ */
+size_t eap_gpsk_mic_len(int vendor, int specifier)
+{
+       if (vendor != EAP_GPSK_VENDOR_IETF)
+               return 0;
+
+       switch (specifier) {
+       case EAP_GPSK_CIPHER_AES:
+               return 16;
+#ifdef EAP_GPSK_SHA256
+       case EAP_GPSK_CIPHER_SHA256:
+               return 32;
+#endif /* EAP_GPSK_SHA256 */
+       default:
+               return 0;
+       }
+}
+
+
+static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len,
+                                   const u8 *data, size_t len, u8 *mic)
+{
+       if (sk_len != 16) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %lu for "
+                          "AES-CMAC MIC", (unsigned long) sk_len);
+               return -1;
+       }
+
+       return omac1_aes_128(sk, data, len, mic);
+}
+
+
+/**
+ * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet
+ * @sk: Session key SK from eap_gpsk_derive_keys()
+ * @sk_len: SK length in bytes from eap_gpsk_derive_keys()
+ * @vendor: CSuite/Vendor
+ * @specifier: CSuite/Specifier
+ * @data: Input data to MIC
+ * @len: Input data length in bytes
+ * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+                        int specifier, const u8 *data, size_t len, u8 *mic)
+{
+       int ret;
+
+       if (vendor != EAP_GPSK_VENDOR_IETF)
+               return -1;
+
+       switch (specifier) {
+       case EAP_GPSK_CIPHER_AES:
+               ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic);
+               break;
+#ifdef EAP_GPSK_SHA256
+       case EAP_GPSK_CIPHER_SHA256:
+               hmac_sha256(sk, sk_len, data, len, mic);
+               ret = 0;
+               break;
+#endif /* EAP_GPSK_SHA256 */
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in "
+                          "MIC computation", vendor, specifier);
+               ret = -1;
+               break;
+       }
+
+       return ret;
+}
diff --git a/src/eap_common/eap_gpsk_common.h b/src/eap_common/eap_gpsk_common.h
new file mode 100644 (file)
index 0000000..a30ab97
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * EAP server/peer: EAP-GPSK shared routines
+ * Copyright (c) 2006-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 EAP_GPSK_COMMON_H
+#define EAP_GPSK_COMMON_H
+
+#define EAP_GPSK_OPCODE_GPSK_1 1
+#define EAP_GPSK_OPCODE_GPSK_2 2
+#define EAP_GPSK_OPCODE_GPSK_3 3
+#define EAP_GPSK_OPCODE_GPSK_4 4
+#define EAP_GPSK_OPCODE_FAIL 5
+#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6
+
+/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */
+#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001
+#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002
+#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003
+
+#define EAP_GPSK_RAND_LEN 32
+#define EAP_GPSK_MAX_SK_LEN 32
+#define EAP_GPSK_MAX_PK_LEN 32
+#define EAP_GPSK_MAX_MIC_LEN 32
+
+#define EAP_GPSK_VENDOR_IETF           0x00000000
+#define EAP_GPSK_CIPHER_RESERVED       0x000000
+#define EAP_GPSK_CIPHER_AES            0x000001
+#define EAP_GPSK_CIPHER_SHA256         0x000002
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_gpsk_csuite {
+       u8 vendor[4];
+       u8 specifier[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+int eap_gpsk_supported_ciphersuite(int vendor, int specifier);
+int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
+                        int specifier,
+                        const u8 *rand_client, const u8 *rand_server,
+                        const u8 *id_client, size_t id_client_len,
+                        const u8 *id_server, size_t id_server_len,
+                        u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len,
+                        u8 *pk, size_t *pk_len);
+size_t eap_gpsk_mic_len(int vendor, int specifier);
+int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor,
+                        int specifier, const u8 *data, size_t len, u8 *mic);
+
+#endif /* EAP_GPSK_COMMON_H */
diff --git a/src/eap_common/eap_ikev2_common.c b/src/eap_common/eap_ikev2_common.c
new file mode 100644 (file)
index 0000000..e9a9c55
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * EAP-IKEv2 common routines
+ * 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"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "ikev2_common.h"
+#include "eap_ikev2_common.h"
+
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+                           const u8 *i_nonce, size_t i_nonce_len,
+                           const u8 *r_nonce, size_t r_nonce_len,
+                           u8 *keymat)
+{
+       u8 *nonces;
+       size_t nlen;
+
+       /* KEYMAT = prf+(SK_d, Ni | Nr) */
+       if (keys->SK_d == NULL || i_nonce == NULL || r_nonce == NULL)
+               return -1;
+
+       nlen = i_nonce_len + r_nonce_len;
+       nonces = os_malloc(nlen);
+       if (nonces == NULL)
+               return -1;
+       os_memcpy(nonces, i_nonce, i_nonce_len);
+       os_memcpy(nonces + i_nonce_len, r_nonce, r_nonce_len);
+
+       if (ikev2_prf_plus(prf, keys->SK_d, keys->SK_d_len, nonces, nlen,
+                          keymat, EAP_MSK_LEN + EAP_EMSK_LEN)) {
+               os_free(nonces);
+               return -1;
+       }
+       os_free(nonces);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-IKEV2: KEYMAT",
+                       keymat, EAP_MSK_LEN + EAP_EMSK_LEN);
+
+       return 0;
+}
+
+
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
+{
+       struct wpabuf *msg;
+
+#ifdef CCNS_PL
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
+                          "for fragment ack");
+               return NULL;
+       }
+       wpabuf_put_u8(msg, 0); /* Flags */
+#else /* CCNS_PL */
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
+                          "for fragment ack");
+               return NULL;
+       }
+#endif /* CCNS_PL */
+
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
+
+       return msg;
+}
+
+
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+                          int initiator, const struct wpabuf *msg,
+                          const u8 *pos, const u8 *end)
+{
+       const struct ikev2_integ_alg *integ;
+       size_t icv_len;
+       u8 icv[IKEV2_MAX_HASH_LEN];
+       const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+       integ = ikev2_get_integ(integ_alg);
+       if (integ == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+                          "transform / cannot validate ICV");
+               return -1;
+       }
+       icv_len = integ->hash_len;
+
+       if (end - pos < (int) icv_len) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Not enough room in the "
+                          "message for Integrity Checksum Data");
+               return -1;
+       }
+
+       if (SK_a == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: No SK_a for ICV validation");
+               return -1;
+       }
+
+       if (ikev2_integ_hash(integ_alg, SK_a, keys->SK_integ_len,
+                            wpabuf_head(msg),
+                            wpabuf_len(msg) - icv_len, icv) < 0) {
+               wpa_printf(MSG_INFO, "EAP-IKEV2: Could not calculate ICV");
+               return -1;
+       }
+
+       if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
+               wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
+                           icv, icv_len);
+               wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Received ICV",
+                           end - icv_len, icv_len);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Valid Integrity Checksum Data in "
+                  "the received message");
+
+       return icv_len;
+}
diff --git a/src/eap_common/eap_ikev2_common.h b/src/eap_common/eap_ikev2_common.h
new file mode 100644 (file)
index 0000000..a9fc2ca
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * EAP-IKEv2 definitions
+ * 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.
+ */
+
+#ifndef EAP_IKEV2_COMMON_H
+#define EAP_IKEV2_COMMON_H
+
+#ifdef CCNS_PL
+/* incorrect bit order */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02
+#define IKEV2_FLAGS_ICV_INCLUDED 0x04
+#else /* CCNS_PL */
+#define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
+#define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
+#define IKEV2_FLAGS_ICV_INCLUDED 0x20
+#endif /* CCNS_PL */
+
+#define IKEV2_FRAGMENT_SIZE 1400
+
+struct ikev2_keys;
+
+int eap_ikev2_derive_keymat(int prf, struct ikev2_keys *keys,
+                           const u8 *i_nonce, size_t i_nonce_len,
+                           const u8 *r_nonce, size_t r_nonce_len,
+                           u8 *keymat);
+struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code);
+int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
+                          int initiator, const struct wpabuf *msg,
+                          const u8 *pos, const u8 *end);
+
+#endif /* EAP_IKEV2_COMMON_H */
diff --git a/src/eap_common/eap_pax_common.c b/src/eap_common/eap_pax_common.c
new file mode 100644 (file)
index 0000000..32dc80c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * 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 "crypto/sha1.h"
+#include "eap_pax_common.h"
+
+
+/**
+ * eap_pax_kdf - PAX Key Derivation Function
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key (X)
+ * @key_len: Length of the secret key in bytes
+ * @identifier: Public identifier for the key (Y)
+ * @entropy: Exchanged entropy to seed the KDF (Z)
+ * @entropy_len: Length of the entropy in bytes
+ * @output_len: Output len in bytes (W)
+ * @output: Buffer for the derived key
+ * Returns: 0 on success, -1 failed
+ *
+ * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z)
+ */
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+               const char *identifier,
+               const u8 *entropy, size_t entropy_len,
+               size_t output_len, u8 *output)
+{
+       u8 mac[SHA1_MAC_LEN];
+       u8 counter, *pos;
+       const u8 *addr[3];
+       size_t len[3];
+       size_t num_blocks, left;
+
+       num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN;
+       if (identifier == NULL || num_blocks >= 255)
+               return -1;
+
+       /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+       if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+               return -1;
+
+       addr[0] = (const u8 *) identifier;
+       len[0] = os_strlen(identifier);
+       addr[1] = entropy;
+       len[1] = entropy_len;
+       addr[2] = &counter;
+       len[2] = 1;
+
+       pos = output;
+       left = output_len;
+       for (counter = 1; counter <= (u8) num_blocks; counter++) {
+               size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left;
+               hmac_sha1_vector(key, key_len, 3, addr, len, mac);
+               os_memcpy(pos, mac, clen);
+               pos += clen;
+               left -= clen;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_pax_mac - EAP-PAX MAC
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @key: Secret key
+ * @key_len: Length of the secret key in bytes
+ * @data1: Optional data, first block; %NULL if not used
+ * @data1_len: Length of data1 in bytes
+ * @data2: Optional data, second block; %NULL if not used
+ * @data2_len: Length of data2 in bytes
+ * @data3: Optional data, third block; %NULL if not used
+ * @data3_len: Length of data3 in bytes
+ * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Wrapper function to calculate EAP-PAX MAC.
+ */
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+               const u8 *data1, size_t data1_len,
+               const u8 *data2, size_t data2_len,
+               const u8 *data3, size_t data3_len,
+               u8 *mac)
+{
+       u8 hash[SHA1_MAC_LEN];
+       const u8 *addr[3];
+       size_t len[3];
+       size_t count;
+
+       /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */
+       if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128)
+               return -1;
+
+       addr[0] = data1;
+       len[0] = data1_len;
+       addr[1] = data2;
+       len[1] = data2_len;
+       addr[2] = data3;
+       len[2] = data3_len;
+
+       count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0);
+       hmac_sha1_vector(key, key_len, count, addr, len, hash);
+       os_memcpy(mac, hash, EAP_PAX_MAC_LEN);
+
+       return 0;
+}
+
+
+/**
+ * eap_pax_initial_key_derivation - EAP-PAX initial key derivation
+ * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported
+ * @ak: Authentication Key
+ * @e: Entropy
+ * @mk: Buffer for the derived Master Key
+ * @ck: Buffer for the derived Confirmation Key
+ * @ick: Buffer for the derived Integrity Check Key
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+                                  u8 *mk, u8 *ck, u8 *ick)
+{
+       wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation");
+       if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key",
+                       e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) ||
+           eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key",
+                       e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) ||
+           eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key",
+                       e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick))
+               return -1;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN);
+
+       return 0;
+}
diff --git a/src/eap_common/eap_pax_common.h b/src/eap_common/eap_pax_common.h
new file mode 100644 (file)
index 0000000..dcc171e
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * EAP server/peer: EAP-PAX shared routines
+ * Copyright (c) 2005-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 EAP_PAX_COMMON_H
+#define EAP_PAX_COMMON_H
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_pax_hdr {
+       u8 op_code;
+       u8 flags;
+       u8 mac_id;
+       u8 dh_group_id;
+       u8 public_key_id;
+       /* Followed by variable length payload and ICV */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* op_code: */
+enum {
+       EAP_PAX_OP_STD_1 = 0x01,
+       EAP_PAX_OP_STD_2 = 0x02,
+       EAP_PAX_OP_STD_3 = 0x03,
+       EAP_PAX_OP_SEC_1 = 0x11,
+       EAP_PAX_OP_SEC_2 = 0x12,
+       EAP_PAX_OP_SEC_3 = 0x13,
+       EAP_PAX_OP_SEC_4 = 0x14,
+       EAP_PAX_OP_SEC_5 = 0x15,
+       EAP_PAX_OP_ACK = 0x21
+};
+
+/* flags: */
+#define EAP_PAX_FLAGS_MF                       0x01
+#define EAP_PAX_FLAGS_CE                       0x02
+#define EAP_PAX_FLAGS_AI                       0x04
+
+/* mac_id: */
+#define EAP_PAX_MAC_HMAC_SHA1_128              0x01
+#define EAP_PAX_HMAC_SHA256_128                        0x02
+
+/* dh_group_id: */
+#define EAP_PAX_DH_GROUP_NONE                  0x00
+#define EAP_PAX_DH_GROUP_2048_MODP             0x01
+#define EAP_PAX_DH_GROUP_3072_MODP             0x02
+#define EAP_PAX_DH_GROUP_NIST_ECC_P_256                0x03
+
+/* public_key_id: */
+#define EAP_PAX_PUBLIC_KEY_NONE                        0x00
+#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP          0x01
+#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5      0x02
+#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC   0x03
+
+/* ADE type: */
+#define EAP_PAX_ADE_VENDOR_SPECIFIC            0x01
+#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING     0x02
+#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING     0x03
+
+
+#define EAP_PAX_RAND_LEN 32
+#define EAP_PAX_MAC_LEN 16
+#define EAP_PAX_ICV_LEN 16
+#define EAP_PAX_AK_LEN 16
+#define EAP_PAX_MK_LEN 16
+#define EAP_PAX_CK_LEN 16
+#define EAP_PAX_ICK_LEN 16
+
+
+int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
+               const char *identifier,
+               const u8 *entropy, size_t entropy_len,
+               size_t output_len, u8 *output);
+int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
+               const u8 *data1, size_t data1_len,
+               const u8 *data2, size_t data2_len,
+               const u8 *data3, size_t data3_len,
+               u8 *mac);
+int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
+                                  u8 *mk, u8 *ck, u8 *ick);
+
+#endif /* EAP_PAX_COMMON_H */
diff --git a/src/eap_common/eap_peap_common.c b/src/eap_common/eap_peap_common.c
new file mode 100644 (file)
index 0000000..3a64b8e
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * EAP-PEAP common routines
+ * 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 "common.h"
+#include "crypto/sha1.h"
+#include "eap_peap_common.h"
+
+void peap_prfplus(int version, const u8 *key, size_t key_len,
+                 const char *label, const u8 *seed, size_t seed_len,
+                 u8 *buf, size_t buf_len)
+{
+       unsigned char counter = 0;
+       size_t pos, plen;
+       u8 hash[SHA1_MAC_LEN];
+       size_t label_len = os_strlen(label);
+       u8 extra[2];
+       const unsigned char *addr[5];
+       size_t len[5];
+
+       addr[0] = hash;
+       len[0] = 0;
+       addr[1] = (unsigned char *) label;
+       len[1] = label_len;
+       addr[2] = seed;
+       len[2] = seed_len;
+
+       if (version == 0) {
+               /*
+                * PRF+(K, S, LEN) = T1 | T2 | ... | Tn
+                * T1 = HMAC-SHA1(K, S | 0x01 | 0x00 | 0x00)
+                * T2 = HMAC-SHA1(K, T1 | S | 0x02 | 0x00 | 0x00)
+                * ...
+                * Tn = HMAC-SHA1(K, Tn-1 | S | n | 0x00 | 0x00)
+                */
+
+               extra[0] = 0;
+               extra[1] = 0;
+
+               addr[3] = &counter;
+               len[3] = 1;
+               addr[4] = extra;
+               len[4] = 2;
+       } else {
+               /*
+                * PRF (K,S,LEN) = T1 | T2 | T3 | T4 | ... where:
+                * T1 = HMAC-SHA1(K, S | LEN | 0x01)
+                * T2 = HMAC-SHA1 (K, T1 | S | LEN | 0x02)
+                * T3 = HMAC-SHA1 (K, T2 | S | LEN | 0x03)
+                * T4 = HMAC-SHA1 (K, T3 | S | LEN | 0x04)
+                *   ...
+                */
+
+               extra[0] = buf_len & 0xff;
+
+               addr[3] = extra;
+               len[3] = 1;
+               addr[4] = &counter;
+               len[4] = 1;
+       }
+
+       pos = 0;
+       while (pos < buf_len) {
+               counter++;
+               plen = buf_len - pos;
+               hmac_sha1_vector(key, key_len, 5, addr, len, hash);
+               if (plen >= SHA1_MAC_LEN) {
+                       os_memcpy(&buf[pos], hash, SHA1_MAC_LEN);
+                       pos += SHA1_MAC_LEN;
+               } else {
+                       os_memcpy(&buf[pos], hash, plen);
+                       break;
+               }
+               len[0] = SHA1_MAC_LEN;
+       }
+}
diff --git a/src/eap_common/eap_peap_common.h b/src/eap_common/eap_peap_common.h
new file mode 100644 (file)
index 0000000..f59afb0
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * EAP-PEAP common routines
+ * 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 EAP_PEAP_COMMON_H
+#define EAP_PEAP_COMMON_H
+
+void peap_prfplus(int version, const u8 *key, size_t key_len,
+                 const char *label, const u8 *seed, size_t seed_len,
+                 u8 *buf, size_t buf_len);
+
+#endif /* EAP_PEAP_COMMON_H */
diff --git a/src/eap_common/eap_psk_common.c b/src/eap_common/eap_psk_common.c
new file mode 100644 (file)
index 0000000..7417d5c
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_defs.h"
+#include "eap_psk_common.h"
+
+#define aes_block_size 16
+
+
+int eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk)
+{
+       os_memset(ak, 0, aes_block_size);
+       if (aes_128_encrypt_block(psk, ak, ak))
+               return -1;
+       os_memcpy(kdk, ak, aes_block_size);
+       ak[aes_block_size - 1] ^= 0x01;
+       kdk[aes_block_size - 1] ^= 0x02;
+       if (aes_128_encrypt_block(psk, ak, ak) ||
+           aes_128_encrypt_block(psk, kdk, kdk))
+               return -1;
+       return 0;
+}
+
+
+int eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk,
+                       u8 *emsk)
+{
+       u8 hash[aes_block_size];
+       u8 counter = 1;
+       int i;
+
+       if (aes_128_encrypt_block(kdk, rand_p, hash))
+               return -1;
+
+       hash[aes_block_size - 1] ^= counter;
+       if (aes_128_encrypt_block(kdk, hash, tek))
+               return -1;
+       hash[aes_block_size - 1] ^= counter;
+       counter++;
+
+       for (i = 0; i < EAP_MSK_LEN / aes_block_size; i++) {
+               hash[aes_block_size - 1] ^= counter;
+               if (aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]))
+                       return -1;
+               hash[aes_block_size - 1] ^= counter;
+               counter++;
+       }
+
+       for (i = 0; i < EAP_EMSK_LEN / aes_block_size; i++) {
+               hash[aes_block_size - 1] ^= counter;
+               if (aes_128_encrypt_block(kdk, hash,
+                                         &emsk[i * aes_block_size]))
+                       return -1;
+               hash[aes_block_size - 1] ^= counter;
+               counter++;
+       }
+
+       return 0;
+}
diff --git a/src/eap_common/eap_psk_common.h b/src/eap_common/eap_psk_common.h
new file mode 100644 (file)
index 0000000..8adc054
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * EAP server/peer: EAP-PSK shared routines
+ * 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 EAP_PSK_COMMON_H
+#define EAP_PSK_COMMON_H
+
+
+#define EAP_PSK_RAND_LEN 16
+#define EAP_PSK_MAC_LEN 16
+#define EAP_PSK_TEK_LEN 16
+#define EAP_PSK_PSK_LEN 16
+#define EAP_PSK_AK_LEN 16
+#define EAP_PSK_KDK_LEN 16
+
+#define EAP_PSK_R_FLAG_CONT 1
+#define EAP_PSK_R_FLAG_DONE_SUCCESS 2
+#define EAP_PSK_R_FLAG_DONE_FAILURE 3
+#define EAP_PSK_E_FLAG 0x20
+
+#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6)
+#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+/* EAP-PSK First Message (AS -> Supplicant) */
+struct eap_psk_hdr_1 {
+       u8 flags;
+       u8 rand_s[EAP_PSK_RAND_LEN];
+       /* Followed by variable length ID_S */
+} STRUCT_PACKED;
+
+/* EAP-PSK Second Message (Supplicant -> AS) */
+struct eap_psk_hdr_2 {
+       u8 flags;
+       u8 rand_s[EAP_PSK_RAND_LEN];
+       u8 rand_p[EAP_PSK_RAND_LEN];
+       u8 mac_p[EAP_PSK_MAC_LEN];
+       /* Followed by variable length ID_P */
+} STRUCT_PACKED;
+
+/* EAP-PSK Third Message (AS -> Supplicant) */
+struct eap_psk_hdr_3 {
+       u8 flags;
+       u8 rand_s[EAP_PSK_RAND_LEN];
+       u8 mac_s[EAP_PSK_MAC_LEN];
+       /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+/* EAP-PSK Fourth Message (Supplicant -> AS) */
+struct eap_psk_hdr_4 {
+       u8 flags;
+       u8 rand_s[EAP_PSK_RAND_LEN];
+       /* Followed by variable length PCHANNEL */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+int __must_check eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk);
+int __must_check eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek,
+                                    u8 *msk, u8 *emsk);
+
+#endif /* EAP_PSK_COMMON_H */
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
new file mode 100644 (file)
index 0000000..9002b0c
--- /dev/null
@@ -0,0 +1,393 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-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 "wpabuf.h"
+#include "crypto/sha1.h"
+#include "eap_defs.h"
+#include "eap_sake_common.h"
+
+
+static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr,
+                                  const u8 *pos)
+{
+       size_t i;
+
+       switch (pos[0]) {
+       case EAP_SAKE_AT_RAND_S:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S");
+               if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with "
+                                  "invalid length %d", pos[1]);
+                       return -1;
+               }
+               attr->rand_s = pos + 2;
+               break;
+       case EAP_SAKE_AT_RAND_P:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P");
+               if (pos[1] != 2 + EAP_SAKE_RAND_LEN) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with "
+                                  "invalid length %d", pos[1]);
+                       return -1;
+               }
+               attr->rand_p = pos + 2;
+               break;
+       case EAP_SAKE_AT_MIC_S:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S");
+               if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with "
+                                  "invalid length %d", pos[1]);
+                       return -1;
+               }
+               attr->mic_s = pos + 2;
+               break;
+       case EAP_SAKE_AT_MIC_P:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P");
+               if (pos[1] != 2 + EAP_SAKE_MIC_LEN) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with "
+                                  "invalid length %d", pos[1]);
+                       return -1;
+               }
+               attr->mic_p = pos + 2;
+               break;
+       case EAP_SAKE_AT_SERVERID:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID");
+               attr->serverid = pos + 2;
+               attr->serverid_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_PEERID:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID");
+               attr->peerid = pos + 2;
+               attr->peerid_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_SPI_S:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S");
+               attr->spi_s = pos + 2;
+               attr->spi_s_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_SPI_P:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P");
+               attr->spi_p = pos + 2;
+               attr->spi_p_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_ANY_ID_REQ:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ");
+               if (pos[1] != 4) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ"
+                                  " length %d", pos[1]);
+                       return -1;
+               }
+               attr->any_id_req = pos + 2;
+               break;
+       case EAP_SAKE_AT_PERM_ID_REQ:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ");
+               if (pos[1] != 4) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+                                  "AT_PERM_ID_REQ length %d", pos[1]);
+                       return -1;
+               }
+               attr->perm_id_req = pos + 2;
+               break;
+       case EAP_SAKE_AT_ENCR_DATA:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA");
+               attr->encr_data = pos + 2;
+               attr->encr_data_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_IV:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+               attr->iv = pos + 2;
+               attr->iv_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_PADDING:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING");
+               for (i = 2; i < pos[1]; i++) {
+                       if (pos[i]) {
+                               wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING "
+                                          "with non-zero pad byte");
+                               return -1;
+                       }
+               }
+               break;
+       case EAP_SAKE_AT_NEXT_TMPID:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID");
+               attr->next_tmpid = pos + 2;
+               attr->next_tmpid_len = pos[1] - 2;
+               break;
+       case EAP_SAKE_AT_MSK_LIFE:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV");
+               if (pos[1] != 6) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid "
+                                  "AT_MSK_LIFE length %d", pos[1]);
+                       return -1;
+               }
+               attr->msk_life = pos + 2;
+               break;
+       default:
+               if (pos[0] < 128) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable"
+                                  " attribute %d", pos[0]);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable "
+                          "attribute %d", pos[0]);
+               break;
+       }
+
+       if (attr->iv && !attr->encr_data) {
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without "
+                          "AT_ENCR_DATA");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_sake_parse_attributes - Parse EAP-SAKE attributes
+ * @buf: Packet payload (starting with the first attribute)
+ * @len: Payload length
+ * @attr: Structure to be filled with found attributes
+ * Returns: 0 on success or -1 on failure
+ */
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+                             struct eap_sake_parse_attr *attr)
+{
+       const u8 *pos = buf, *end = buf + len;
+
+       os_memset(attr, 0, sizeof(*attr));
+       while (pos < end) {
+               if (end - pos < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute");
+                       return -1;
+               }
+
+               if (pos[1] < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute "
+                                  "length (%d)", pos[1]);
+                       return -1;
+               }
+
+               if (pos + pos[1] > end) {
+                       wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow");
+                       return -1;
+               }
+
+               if (eap_sake_parse_add_attr(attr, pos))
+                       return -1;
+
+               pos += pos[1];
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF)
+ * @key: Key for KDF
+ * @key_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @data: Extra data (start) to bind into the key
+ * @data_len: Length of the data
+ * @data2: Extra data (end) to bind into the key
+ * @data2_len: Length of the data2
+ * @buf: Buffer for the generated pseudo-random key
+ * @buf_len: Number of bytes of key to generate
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
+ */
+static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
+                        const u8 *data, size_t data_len,
+                        const u8 *data2, size_t data2_len,
+                        u8 *buf, size_t buf_len)
+{
+       u8 counter = 0;
+       size_t pos, plen;
+       u8 hash[SHA1_MAC_LEN];
+       size_t label_len = os_strlen(label) + 1;
+       const unsigned char *addr[4];
+       size_t len[4];
+
+       addr[0] = (u8 *) label; /* Label | Y */
+       len[0] = label_len;
+       addr[1] = data; /* Msg[start] */
+       len[1] = data_len;
+       addr[2] = data2; /* Msg[end] */
+       len[2] = data2_len;
+       addr[3] = &counter; /* Length */
+       len[3] = 1;
+
+       pos = 0;
+       while (pos < buf_len) {
+               plen = buf_len - pos;
+               if (plen >= SHA1_MAC_LEN) {
+                       hmac_sha1_vector(key, key_len, 4, addr, len,
+                                        &buf[pos]);
+                       pos += SHA1_MAC_LEN;
+               } else {
+                       hmac_sha1_vector(key, key_len, 4, addr, len,
+                                        hash);
+                       os_memcpy(&buf[pos], hash, plen);
+                       break;
+               }
+               counter++;
+       }
+}
+
+
+/**
+ * eap_sake_derive_keys - Derive EAP-SAKE keys
+ * @root_secret_a: 16-byte Root-Secret-A
+ * @root_secret_b: 16-byte Root-Secret-B
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
+ * @msk: Buffer for 64-byte MSK
+ * @emsk: Buffer for 64-byte EMSK
+ *
+ * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
+ */
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+                         const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
+                         u8 *emsk)
+{
+       u8 sms_a[EAP_SAKE_SMS_LEN];
+       u8 sms_b[EAP_SAKE_SMS_LEN];
+       u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys");
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
+                       root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
+       eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
+                    "SAKE Master Secret A",
+                    rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+                    sms_a, EAP_SAKE_SMS_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
+       eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
+                    rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+                    tek, EAP_SAKE_TEK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
+                       tek, EAP_SAKE_TEK_AUTH_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
+                       tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
+                       root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
+       eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
+                    "SAKE Master Secret B",
+                    rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+                    sms_b, EAP_SAKE_SMS_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
+       eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
+                    rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+                    key_buf, sizeof(key_buf));
+       os_memcpy(msk, key_buf, EAP_MSK_LEN);
+       os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+/**
+ * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet
+ * @tek_auth: 16-byte TEK-Auth
+ * @rand_s: 16-byte RAND_S
+ * @rand_p: 16-byte RAND_P
+ * @serverid: SERVERID
+ * @serverid_len: SERVERID length
+ * @peerid: PEERID
+ * @peerid_len: PEERID length
+ * @peer: MIC calculation for 0 = Server, 1 = Peer message
+ * @eap: EAP packet
+ * @eap_len: EAP packet length
+ * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
+ * @mic: Buffer for the computed 16-byte MIC
+ */
+int eap_sake_compute_mic(const u8 *tek_auth,
+                        const u8 *rand_s, const u8 *rand_p,
+                        const u8 *serverid, size_t serverid_len,
+                        const u8 *peerid, size_t peerid_len,
+                        int peer, const u8 *eap, size_t eap_len,
+                        const u8 *mic_pos, u8 *mic)
+{
+       u8 _rand[2 * EAP_SAKE_RAND_LEN];
+       u8 *tmp, *pos;
+       size_t tmplen;
+
+       tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
+       tmp = os_malloc(tmplen);
+       if (tmp == NULL)
+               return -1;
+       pos = tmp;
+       if (peer) {
+               if (peerid) {
+                       os_memcpy(pos, peerid, peerid_len);
+                       pos += peerid_len;
+               }
+               *pos++ = 0x00;
+               if (serverid) {
+                       os_memcpy(pos, serverid, serverid_len);
+                       pos += serverid_len;
+               }
+               *pos++ = 0x00;
+
+               os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN);
+               os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p,
+                         EAP_SAKE_RAND_LEN);
+       } else {
+               if (serverid) {
+                       os_memcpy(pos, serverid, serverid_len);
+                       pos += serverid_len;
+               }
+               *pos++ = 0x00;
+               if (peerid) {
+                       os_memcpy(pos, peerid, peerid_len);
+                       pos += peerid_len;
+               }
+               *pos++ = 0x00;
+
+               os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN);
+               os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s,
+                         EAP_SAKE_RAND_LEN);
+       }
+
+       os_memcpy(pos, eap, eap_len);
+       os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
+
+       eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
+                    peer ? "Peer MIC" : "Server MIC",
+                    _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
+                    mic, EAP_SAKE_MIC_LEN);
+
+       os_free(tmp);
+
+       return 0;
+}
+
+
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+                      size_t len)
+{
+       wpabuf_put_u8(buf, type);
+       wpabuf_put_u8(buf, 2 + len); /* Length; including attr header */
+       if (data)
+               wpabuf_put_data(buf, data, len);
+       else
+               os_memset(wpabuf_put(buf, len), 0, len);
+}
diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h
new file mode 100644 (file)
index 0000000..201e207
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * EAP server/peer: EAP-SAKE shared routines
+ * Copyright (c) 2006-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 EAP_SAKE_COMMON_H
+#define EAP_SAKE_COMMON_H
+
+#define EAP_SAKE_VERSION 2
+
+#define EAP_SAKE_SUBTYPE_CHALLENGE 1
+#define EAP_SAKE_SUBTYPE_CONFIRM 2
+#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3
+#define EAP_SAKE_SUBTYPE_IDENTITY 4
+
+#define EAP_SAKE_AT_RAND_S 1
+#define EAP_SAKE_AT_RAND_P 2
+#define EAP_SAKE_AT_MIC_S 3
+#define EAP_SAKE_AT_MIC_P 4
+#define EAP_SAKE_AT_SERVERID 5
+#define EAP_SAKE_AT_PEERID 6
+#define EAP_SAKE_AT_SPI_S 7
+#define EAP_SAKE_AT_SPI_P 8
+#define EAP_SAKE_AT_ANY_ID_REQ 9
+#define EAP_SAKE_AT_PERM_ID_REQ 10
+#define EAP_SAKE_AT_ENCR_DATA 128
+#define EAP_SAKE_AT_IV 129
+#define EAP_SAKE_AT_PADDING 130
+#define EAP_SAKE_AT_NEXT_TMPID 131
+#define EAP_SAKE_AT_MSK_LIFE 132
+
+#define EAP_SAKE_RAND_LEN 16
+#define EAP_SAKE_MIC_LEN 16
+#define EAP_SAKE_ROOT_SECRET_LEN 16
+#define EAP_SAKE_SMS_LEN 16
+#define EAP_SAKE_TEK_AUTH_LEN 16
+#define EAP_SAKE_TEK_CIPHER_LEN 16
+#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN)
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_sake_hdr {
+       u8 version; /* EAP_SAKE_VERSION */
+       u8 session_id;
+       u8 subtype;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+struct eap_sake_parse_attr {
+       const u8 *rand_s;
+       const u8 *rand_p;
+       const u8 *mic_s;
+       const u8 *mic_p;
+       const u8 *serverid;
+       size_t serverid_len;
+       const u8 *peerid;
+       size_t peerid_len;
+       const u8 *spi_s;
+       size_t spi_s_len;
+       const u8 *spi_p;
+       size_t spi_p_len;
+       const u8 *any_id_req;
+       const u8 *perm_id_req;
+       const u8 *encr_data;
+       size_t encr_data_len;
+       const u8 *iv;
+       size_t iv_len;
+       const u8 *next_tmpid;
+       size_t next_tmpid_len;
+       const u8 *msk_life;
+};
+
+int eap_sake_parse_attributes(const u8 *buf, size_t len,
+                             struct eap_sake_parse_attr *attr);
+void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+                         const u8 *rand_s, const u8 *rand_p,
+                         u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_compute_mic(const u8 *tek_auth,
+                        const u8 *rand_s, const u8 *rand_p,
+                        const u8 *serverid, size_t serverid_len,
+                        const u8 *peerid, size_t peerid_len,
+                        int peer, const u8 *eap, size_t eap_len,
+                        const u8 *mic_pos, u8 *mic);
+void eap_sake_add_attr(struct wpabuf *buf, u8 type, const u8 *data,
+                      size_t len);
+
+#endif /* EAP_SAKE_COMMON_H */
diff --git a/src/eap_common/eap_sim_common.c b/src/eap_common/eap_sim_common.c
new file mode 100644 (file)
index 0000000..56b4ded
--- /dev/null
@@ -0,0 +1,1214 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * 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 "wpabuf.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "eap_common/eap_defs.h"
+#include "eap_common/eap_sim_common.h"
+
+
+static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen)
+{
+       return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen);
+}
+
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+                      const u8 *nonce_mt, u16 selected_version,
+                      const u8 *ver_list, size_t ver_list_len,
+                      int num_chal, const u8 *kc, u8 *mk)
+{
+       u8 sel_ver[2];
+       const unsigned char *addr[5];
+       size_t len[5];
+
+       addr[0] = identity;
+       len[0] = identity_len;
+       addr[1] = kc;
+       len[1] = num_chal * EAP_SIM_KC_LEN;
+       addr[2] = nonce_mt;
+       len[2] = EAP_SIM_NONCE_MT_LEN;
+       addr[3] = ver_list;
+       len[3] = ver_list_len;
+       addr[4] = sel_ver;
+       len[4] = 2;
+
+       WPA_PUT_BE16(sel_ver, selected_version);
+
+       /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */
+       sha1_vector(5, addr, len, mk);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+}
+
+
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+                      const u8 *ik, const u8 *ck, u8 *mk)
+{
+       const u8 *addr[3];
+       size_t len[3];
+
+       addr[0] = identity;
+       len[0] = identity_len;
+       addr[1] = ik;
+       len[1] = EAP_AKA_IK_LEN;
+       addr[2] = ck;
+       len[2] = EAP_AKA_CK_LEN;
+
+       /* MK = SHA1(Identity|IK|CK) */
+       sha1_vector(3, addr, len, mk);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN);
+}
+
+
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk)
+{
+       u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN +
+              EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos;
+       if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) {
+               wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
+               return -1;
+       }
+       pos = buf;
+       os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+       pos += EAP_SIM_K_ENCR_LEN;
+       os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN);
+       pos += EAP_SIM_K_AUT_LEN;
+       os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN);
+       pos += EAP_SIM_KEYING_DATA_LEN;
+       os_memcpy(emsk, pos, EAP_EMSK_LEN);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr",
+                       k_encr, EAP_SIM_K_ENCR_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut",
+                       k_aut, EAP_SIM_K_AUT_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
+                       msk, EAP_SIM_KEYING_DATA_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
+       os_memset(buf, 0, sizeof(buf));
+
+       return 0;
+}
+
+
+int eap_sim_derive_keys_reauth(u16 _counter,
+                              const u8 *identity, size_t identity_len,
+                              const u8 *nonce_s, const u8 *mk, u8 *msk,
+                              u8 *emsk)
+{
+       u8 xkey[SHA1_MAC_LEN];
+       u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32];
+       u8 counter[2];
+       const u8 *addr[4];
+       size_t len[4];
+
+       while (identity_len > 0 && identity[identity_len - 1] == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop null "
+                          "character from the end of identity");
+               identity_len--;
+       }
+       addr[0] = identity;
+       len[0] = identity_len;
+       addr[1] = counter;
+       len[1] = 2;
+       addr[2] = nonce_s;
+       len[2] = EAP_SIM_NONCE_S_LEN;
+       addr[3] = mk;
+       len[3] = EAP_SIM_MK_LEN;
+
+       WPA_PUT_BE16(counter, _counter);
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth");
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+                         identity, identity_len);
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM: counter", counter, 2);
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM: NONCE_S", nonce_s,
+                   EAP_SIM_NONCE_S_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN);
+
+       /* XKEY' = SHA1(Identity|counter|NONCE_S|MK) */
+       sha1_vector(4, addr, len, xkey);
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN);
+
+       if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) {
+               wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys");
+               return -1;
+       }
+       if (msk) {
+               os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)",
+                           msk, EAP_SIM_KEYING_DATA_LEN);
+       }
+       if (emsk) {
+               os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN);
+       }
+       os_memset(buf, 0, sizeof(buf));
+
+       return 0;
+}
+
+
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+                      const u8 *mac, const u8 *extra, size_t extra_len)
+{
+       unsigned char hmac[SHA1_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+       u8 *tmp;
+
+       if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
+           mac < wpabuf_head_u8(req) ||
+           mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
+               return -1;
+
+       tmp = os_malloc(wpabuf_len(req));
+       if (tmp == NULL)
+               return -1;
+
+       addr[0] = tmp;
+       len[0] = wpabuf_len(req);
+       addr[1] = extra;
+       len[1] = extra_len;
+
+       /* HMAC-SHA1-128 */
+       os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
+       os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - msg",
+                   tmp, wpabuf_len(req));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data",
+                   extra, extra_len);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut",
+                       k_aut, EAP_SIM_K_AUT_LEN);
+       hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC",
+                   hmac, EAP_SIM_MAC_LEN);
+       os_free(tmp);
+
+       return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+                    const u8 *extra, size_t extra_len)
+{
+       unsigned char hmac[SHA1_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       addr[0] = msg;
+       len[0] = msg_len;
+       addr[1] = extra;
+       len[1] = extra_len;
+
+       /* HMAC-SHA1-128 */
+       os_memset(mac, 0, EAP_SIM_MAC_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data",
+                   extra, extra_len);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut",
+                       k_aut, EAP_SIM_K_AUT_LEN);
+       hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac);
+       os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC",
+                   mac, EAP_SIM_MAC_LEN);
+}
+
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+static void prf_prime(const u8 *k, const char *seed1,
+                     const u8 *seed2, size_t seed2_len,
+                     const u8 *seed3, size_t seed3_len,
+                     u8 *res, size_t res_len)
+{
+       const u8 *addr[5];
+       size_t len[5];
+       u8 hash[SHA256_MAC_LEN];
+       u8 iter;
+
+       /*
+        * PRF'(K,S) = T1 | T2 | T3 | T4 | ...
+        * T1 = HMAC-SHA-256 (K, S | 0x01)
+        * T2 = HMAC-SHA-256 (K, T1 | S | 0x02)
+        * T3 = HMAC-SHA-256 (K, T2 | S | 0x03)
+        * T4 = HMAC-SHA-256 (K, T3 | S | 0x04)
+        * ...
+        */
+
+       addr[0] = hash;
+       len[0] = 0;
+       addr[1] = (const u8 *) seed1;
+       len[1] = os_strlen(seed1);
+       addr[2] = seed2;
+       len[2] = seed2_len;
+       addr[3] = seed3;
+       len[3] = seed3_len;
+       addr[4] = &iter;
+       len[4] = 1;
+
+       iter = 0;
+       while (res_len) {
+               size_t hlen;
+               iter++;
+               hmac_sha256_vector(k, 32, 5, addr, len, hash);
+               len[0] = SHA256_MAC_LEN;
+               hlen = res_len > SHA256_MAC_LEN ? SHA256_MAC_LEN : res_len;
+               os_memcpy(res, hash, hlen);
+               res += hlen;
+               res_len -= hlen;
+       }
+}
+
+
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+                              const u8 *ik, const u8 *ck, u8 *k_encr,
+                              u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk)
+{
+       u8 key[EAP_AKA_IK_LEN + EAP_AKA_CK_LEN];
+       u8 keys[EAP_SIM_K_ENCR_LEN + EAP_AKA_PRIME_K_AUT_LEN +
+               EAP_AKA_PRIME_K_RE_LEN + EAP_MSK_LEN + EAP_EMSK_LEN];
+       u8 *pos;
+
+       /*
+        * MK = PRF'(IK'|CK',"EAP-AKA'"|Identity)
+        * K_encr = MK[0..127]
+        * K_aut  = MK[128..383]
+        * K_re   = MK[384..639]
+        * MSK    = MK[640..1151]
+        * EMSK   = MK[1152..1663]
+        */
+
+       os_memcpy(key, ik, EAP_AKA_IK_LEN);
+       os_memcpy(key + EAP_AKA_IK_LEN, ck, EAP_AKA_CK_LEN);
+
+       prf_prime(key, "EAP-AKA'", identity, identity_len, NULL, 0,
+                 keys, sizeof(keys));
+
+       pos = keys;
+       os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_encr",
+                       k_encr, EAP_SIM_K_ENCR_LEN);
+       pos += EAP_SIM_K_ENCR_LEN;
+
+       os_memcpy(k_aut, pos, EAP_AKA_PRIME_K_AUT_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_aut",
+                       k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+       pos += EAP_AKA_PRIME_K_AUT_LEN;
+
+       os_memcpy(k_re, pos, EAP_AKA_PRIME_K_RE_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': K_re",
+                       k_re, EAP_AKA_PRIME_K_RE_LEN);
+       pos += EAP_AKA_PRIME_K_RE_LEN;
+
+       os_memcpy(msk, pos, EAP_MSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
+       pos += EAP_MSK_LEN;
+
+       os_memcpy(emsk, pos, EAP_EMSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
+}
+
+
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+                                    const u8 *identity, size_t identity_len,
+                                    const u8 *nonce_s, u8 *msk, u8 *emsk)
+{
+       u8 seed3[2 + EAP_SIM_NONCE_S_LEN];
+       u8 keys[EAP_MSK_LEN + EAP_EMSK_LEN];
+       u8 *pos;
+
+       /*
+        * MK = PRF'(K_re,"EAP-AKA' re-auth"|Identity|counter|NONCE_S)
+        * MSK  = MK[0..511]
+        * EMSK = MK[512..1023]
+        */
+
+       WPA_PUT_BE16(seed3, counter);
+       os_memcpy(seed3 + 2, nonce_s, EAP_SIM_NONCE_S_LEN);
+
+       prf_prime(k_re, "EAP-AKA' re-auth", identity, identity_len,
+                 seed3, sizeof(seed3),
+                 keys, sizeof(keys));
+
+       pos = keys;
+       os_memcpy(msk, pos, EAP_MSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': MSK", msk, EAP_MSK_LEN);
+       pos += EAP_MSK_LEN;
+
+       os_memcpy(emsk, pos, EAP_EMSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': EMSK", emsk, EAP_EMSK_LEN);
+
+       os_memset(keys, 0, sizeof(keys));
+
+       return 0;
+}
+
+
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+                             const u8 *mac, const u8 *extra, size_t extra_len)
+{
+       unsigned char hmac[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+       u8 *tmp;
+
+       if (mac == NULL || wpabuf_len(req) < EAP_SIM_MAC_LEN ||
+           mac < wpabuf_head_u8(req) ||
+           mac > wpabuf_head_u8(req) + wpabuf_len(req) - EAP_SIM_MAC_LEN)
+               return -1;
+
+       tmp = os_malloc(wpabuf_len(req));
+       if (tmp == NULL)
+               return -1;
+
+       addr[0] = tmp;
+       len[0] = wpabuf_len(req);
+       addr[1] = extra;
+       len[1] = extra_len;
+
+       /* HMAC-SHA-256-128 */
+       os_memcpy(tmp, wpabuf_head(req), wpabuf_len(req));
+       os_memset(tmp + (mac - wpabuf_head_u8(req)), 0, EAP_SIM_MAC_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - msg",
+                   tmp, wpabuf_len(req));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC - extra data",
+                   extra, extra_len);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Verify MAC - K_aut",
+                       k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+       hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Verify MAC: MAC",
+                   hmac, EAP_SIM_MAC_LEN);
+       os_free(tmp);
+
+       return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+}
+
+
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+                           u8 *mac, const u8 *extra, size_t extra_len)
+{
+       unsigned char hmac[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       addr[0] = msg;
+       len[0] = msg_len;
+       addr[1] = extra;
+       len[1] = extra_len;
+
+       /* HMAC-SHA-256-128 */
+       os_memset(mac, 0, EAP_SIM_MAC_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - msg", msg, msg_len);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC - extra data",
+                   extra, extra_len);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA': Add MAC - K_aut",
+                       k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+       hmac_sha256_vector(k_aut, EAP_AKA_PRIME_K_AUT_LEN, 2, addr, len, hmac);
+       os_memcpy(mac, hmac, EAP_SIM_MAC_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA': Add MAC: MAC",
+                   mac, EAP_SIM_MAC_LEN);
+}
+
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+                                     const u8 *network_name,
+                                     size_t network_name_len)
+{
+       u8 key[EAP_AKA_CK_LEN + EAP_AKA_IK_LEN];
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[5];
+       size_t len[5];
+       u8 fc;
+       u8 l0[2], l1[2];
+
+       /* 3GPP TS 33.402 V8.0.0
+        * (CK', IK') = F(CK, IK, <access network identity>)
+        */
+       /* TODO: CK', IK' generation should really be moved into the actual
+        * AKA procedure with network name passed in there and option to use
+        * AMF separation bit = 1 (3GPP TS 33.401). */
+
+       /* Change Request 33.402 CR 0033 to version 8.1.1 from
+        * 3GPP TSG-SA WG3 Meeting #53 in September 2008:
+        *
+        * CK' || IK' = HMAC-SHA-256(Key, S)
+        * S = FC || P0 || L0 || P1 || L1 || ... || Pn || Ln
+        * Key = CK || IK
+        * FC = 0x20
+        * P0 = access network identity (3GPP TS 24.302)
+        * L0 = length of acceess network identity (2 octets, big endian)
+        * P1 = SQN xor AK (if AK is not used, AK is treaded as 000..0
+        * L1 = 0x00 0x06
+        */
+
+       fc = 0x20;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA': Derive (CK',IK') from (CK,IK)");
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK", ck, EAP_AKA_CK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK", ik, EAP_AKA_IK_LEN);
+       wpa_printf(MSG_DEBUG, "EAP-AKA': FC = 0x%x", fc);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': P0 = Access network identity",
+                         network_name, network_name_len);
+       wpa_hexdump(MSG_DEBUG, "EAP-AKA': P1 = SQN xor AK", sqn_ak, 6);
+
+       os_memcpy(key, ck, EAP_AKA_CK_LEN);
+       os_memcpy(key + EAP_AKA_CK_LEN, ik, EAP_AKA_IK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': Key = CK || IK",
+                       key, sizeof(key));
+
+       addr[0] = &fc;
+       len[0] = 1;
+       addr[1] = network_name;
+       len[1] = network_name_len;
+       WPA_PUT_BE16(l0, network_name_len);
+       addr[2] = l0;
+       len[2] = 2;
+       addr[3] = sqn_ak;
+       len[3] = 6;
+       WPA_PUT_BE16(l1, 6);
+       addr[4] = l1;
+       len[4] = 2;
+
+       hmac_sha256_vector(key, sizeof(key), 5, addr, len, hash);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': KDF output (CK' || IK')",
+                       hash, sizeof(hash));
+
+       os_memcpy(ck, hash, EAP_AKA_CK_LEN);
+       os_memcpy(ik, hash + EAP_AKA_CK_LEN, EAP_AKA_IK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN);
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+                      struct eap_sim_attrs *attr, int aka, int encr)
+{
+       const u8 *pos = start, *apos;
+       size_t alen, plen, i, list_len;
+
+       os_memset(attr, 0, sizeof(*attr));
+       attr->id_req = NO_ID_REQ;
+       attr->notification = -1;
+       attr->counter = -1;
+       attr->selected_version = -1;
+       attr->client_error_code = -1;
+
+       while (pos < end) {
+               if (pos + 2 > end) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow(1)");
+                       return -1;
+               }
+               wpa_printf(MSG_MSGDUMP, "EAP-SIM: Attribute: Type=%d Len=%d",
+                          pos[0], pos[1] * 4);
+               if (pos + pos[1] * 4 > end) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Attribute overflow "
+                                  "(pos=%p len=%d end=%p)",
+                                  pos, pos[1] * 4, end);
+                       return -1;
+               }
+               if (pos[1] == 0) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Attribute underflow");
+                       return -1;
+               }
+               apos = pos + 2;
+               alen = pos[1] * 4 - 2;
+               wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Attribute data",
+                           apos, alen);
+
+               switch (pos[0]) {
+               case EAP_SIM_AT_RAND:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RAND");
+                       apos += 2;
+                       alen -= 2;
+                       if ((!aka && (alen % GSM_RAND_LEN)) ||
+                           (aka && alen != EAP_AKA_RAND_LEN)) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND"
+                                          " (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->rand = apos;
+                       attr->num_chal = alen / GSM_RAND_LEN;
+                       break;
+               case EAP_SIM_AT_AUTN:
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTN");
+                       if (!aka) {
+                               wpa_printf(MSG_DEBUG, "EAP-SIM: "
+                                          "Unexpected AT_AUTN");
+                               return -1;
+                       }
+                       apos += 2;
+                       alen -= 2;
+                       if (alen != EAP_AKA_AUTN_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN"
+                                          " (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->autn = apos;
+                       break;
+               case EAP_SIM_AT_PADDING:
+                       if (!encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+                                          "AT_PADDING");
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_PADDING");
+                       for (i = 2; i < alen; i++) {
+                               if (apos[i] != 0) {
+                                       wpa_printf(MSG_INFO, "EAP-SIM: (encr) "
+                                                  "AT_PADDING used a non-zero"
+                                                  " padding byte");
+                                       wpa_hexdump(MSG_DEBUG, "EAP-SIM: "
+                                                   "(encr) padding bytes",
+                                                   apos + 2, alen - 2);
+                                       return -1;
+                               }
+                       }
+                       break;
+               case EAP_SIM_AT_NONCE_MT:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NONCE_MT");
+                       if (alen != 2 + EAP_SIM_NONCE_MT_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_NONCE_MT length");
+                               return -1;
+                       }
+                       attr->nonce_mt = apos + 2;
+                       break;
+               case EAP_SIM_AT_PERMANENT_ID_REQ:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_PERMANENT_ID_REQ");
+                       attr->id_req = PERMANENT_ID;
+                       break;
+               case EAP_SIM_AT_MAC:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_MAC");
+                       if (alen != 2 + EAP_SIM_MAC_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_MAC "
+                                          "length");
+                               return -1;
+                       }
+                       attr->mac = apos + 2;
+                       break;
+               case EAP_SIM_AT_NOTIFICATION:
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_NOTIFICATION length %lu",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->notification = apos[0] * 256 + apos[1];
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_NOTIFICATION %d",
+                                  attr->notification);
+                       break;
+               case EAP_SIM_AT_ANY_ID_REQ:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ANY_ID_REQ");
+                       attr->id_req = ANY_ID;
+                       break;
+               case EAP_SIM_AT_IDENTITY:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IDENTITY");
+                       plen = WPA_GET_BE16(apos);
+                       apos += 2;
+                       alen -= 2;
+                       if (plen > alen) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_IDENTITY (Actual Length %lu, "
+                                          "remaining length %lu)",
+                                          (unsigned long) plen,
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+
+                       attr->identity = apos;
+                       attr->identity_len = plen;
+                       break;
+               case EAP_SIM_AT_VERSION_LIST:
+                       if (aka) {
+                               wpa_printf(MSG_DEBUG, "EAP-AKA: "
+                                          "Unexpected AT_VERSION_LIST");
+                               return -1;
+                       }
+                       list_len = apos[0] * 256 + apos[1];
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST");
+                       if (list_len < 2 || list_len > alen - 2) {
+                               wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+                                          "AT_VERSION_LIST (list_len=%lu "
+                                          "attr_len=%lu)",
+                                          (unsigned long) list_len,
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->version_list = apos + 2;
+                       attr->version_list_len = list_len;
+                       break;
+               case EAP_SIM_AT_SELECTED_VERSION:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION");
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_SELECTED_VERSION length %lu",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->selected_version = apos[0] * 256 + apos[1];
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_SELECTED_VERSION "
+                                  "%d", attr->selected_version);
+                       break;
+               case EAP_SIM_AT_FULLAUTH_ID_REQ:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_FULLAUTH_ID_REQ");
+                       attr->id_req = FULLAUTH_ID;
+                       break;
+               case EAP_SIM_AT_COUNTER:
+                       if (!encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+                                          "AT_COUNTER");
+                               return -1;
+                       }
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+                                          "AT_COUNTER (alen=%lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->counter = apos[0] * 256 + apos[1];
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d",
+                                  attr->counter);
+                       break;
+               case EAP_SIM_AT_COUNTER_TOO_SMALL:
+                       if (!encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+                                          "AT_COUNTER_TOO_SMALL");
+                               return -1;
+                       }
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+                                          "AT_COUNTER_TOO_SMALL (alen=%lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+                                  "AT_COUNTER_TOO_SMALL");
+                       attr->counter_too_small = 1;
+                       break;
+               case EAP_SIM_AT_NONCE_S:
+                       if (!encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+                                          "AT_NONCE_S");
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+                                  "AT_NONCE_S");
+                       if (alen != 2 + EAP_SIM_NONCE_S_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid "
+                                          "AT_NONCE_S (alen=%lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->nonce_s = apos + 2;
+                       break;
+               case EAP_SIM_AT_CLIENT_ERROR_CODE:
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_CLIENT_ERROR_CODE length %lu",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->client_error_code = apos[0] * 256 + apos[1];
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_CLIENT_ERROR_CODE "
+                                  "%d", attr->client_error_code);
+                       break;
+               case EAP_SIM_AT_IV:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_IV");
+                       if (alen != 2 + EAP_SIM_MAC_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_IV "
+                                          "length %lu", (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->iv = apos + 2;
+                       break;
+               case EAP_SIM_AT_ENCR_DATA:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_ENCR_DATA");
+                       attr->encr_data = apos + 2;
+                       attr->encr_data_len = alen - 2;
+                       if (attr->encr_data_len % 16) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_ENCR_DATA length %lu",
+                                          (unsigned long)
+                                          attr->encr_data_len);
+                               return -1;
+                       }
+                       break;
+               case EAP_SIM_AT_NEXT_PSEUDONYM:
+                       if (!encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+                                          "AT_NEXT_PSEUDONYM");
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+                                  "AT_NEXT_PSEUDONYM");
+                       plen = apos[0] * 256 + apos[1];
+                       if (plen > alen - 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+                                          " AT_NEXT_PSEUDONYM (actual"
+                                          " len %lu, attr len %lu)",
+                                          (unsigned long) plen,
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->next_pseudonym = pos + 4;
+                       attr->next_pseudonym_len = plen;
+                       break;
+               case EAP_SIM_AT_NEXT_REAUTH_ID:
+                       if (!encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted "
+                                          "AT_NEXT_REAUTH_ID");
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) "
+                                  "AT_NEXT_REAUTH_ID");
+                       plen = apos[0] * 256 + apos[1];
+                       if (plen > alen - 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid"
+                                          " AT_NEXT_REAUTH_ID (actual"
+                                          " len %lu, attr len %lu)",
+                                          (unsigned long) plen,
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->next_reauth_id = pos + 4;
+                       attr->next_reauth_id_len = plen;
+                       break;
+               case EAP_SIM_AT_RES:
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES");
+                       attr->res_len_bits = WPA_GET_BE16(apos);
+                       apos += 2;
+                       alen -= 2;
+                       if (!aka || alen < EAP_AKA_MIN_RES_LEN ||
+                           alen > EAP_AKA_MAX_RES_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES "
+                                          "(len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->res = apos;
+                       attr->res_len = alen;
+                       break;
+               case EAP_SIM_AT_AUTS:
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS");
+                       if (!aka) {
+                               wpa_printf(MSG_DEBUG, "EAP-SIM: "
+                                          "Unexpected AT_AUTS");
+                               return -1;
+                       }
+                       if (alen != EAP_AKA_AUTS_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS"
+                                          " (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->auts = apos;
+                       break;
+               case EAP_SIM_AT_CHECKCODE:
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_CHECKCODE");
+                       if (!aka) {
+                               wpa_printf(MSG_DEBUG, "EAP-SIM: "
+                                          "Unexpected AT_CHECKCODE");
+                               return -1;
+                       }
+                       apos += 2;
+                       alen -= 2;
+                       if (alen != 0 && alen != EAP_AKA_CHECKCODE_LEN &&
+                           alen != EAP_AKA_PRIME_CHECKCODE_LEN) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+                                          "AT_CHECKCODE (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->checkcode = apos;
+                       attr->checkcode_len = alen;
+                       break;
+               case EAP_SIM_AT_RESULT_IND:
+                       if (encr) {
+                               wpa_printf(MSG_ERROR, "EAP-SIM: Encrypted "
+                                          "AT_RESULT_IND");
+                               return -1;
+                       }
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Invalid "
+                                          "AT_RESULT_IND (alen=%lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND");
+                       attr->result_ind = 1;
+                       break;
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+               case EAP_SIM_AT_KDF_INPUT:
+                       if (aka != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+                                          "AT_KDF_INPUT");
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF_INPUT");
+                       plen = WPA_GET_BE16(apos);
+                       apos += 2;
+                       alen -= 2;
+                       if (plen > alen) {
+                               wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+                                          "AT_KDF_INPUT (Actual Length %lu, "
+                                          "remaining length %lu)",
+                                          (unsigned long) plen,
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->kdf_input = apos;
+                       attr->kdf_input_len = plen;
+                       break;
+               case EAP_SIM_AT_KDF:
+                       if (aka != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Unexpected "
+                                          "AT_KDF");
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_KDF");
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA': Invalid "
+                                          "AT_KDF (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
+                               wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
+                                          "AT_KDF attributes - ignore this");
+                               continue;
+                       }
+                       attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
+                       attr->kdf_count++;
+                       break;
+               case EAP_SIM_AT_BIDDING:
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: AT_BIDDING");
+                       if (alen != 2) {
+                               wpa_printf(MSG_INFO, "EAP-AKA: Invalid "
+                                          "AT_BIDDING (len %lu)",
+                                          (unsigned long) alen);
+                               return -1;
+                       }
+                       attr->bidding = apos;
+                       break;
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+               default:
+                       if (pos[0] < 128) {
+                               wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized "
+                                          "non-skippable attribute %d",
+                                          pos[0]);
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized skippable"
+                                  " attribute %d ignored", pos[0]);
+                       break;
+               }
+
+               pos += pos[1] * 4;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Attributes parsed successfully "
+                  "(aka=%d encr=%d)", aka, encr);
+
+       return 0;
+}
+
+
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+                       size_t encr_data_len, const u8 *iv,
+                       struct eap_sim_attrs *attr, int aka)
+{
+       u8 *decrypted;
+
+       if (!iv) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV");
+               return NULL;
+       }
+
+       decrypted = os_malloc(encr_data_len);
+       if (decrypted == NULL)
+               return NULL;
+       os_memcpy(decrypted, encr_data, encr_data_len);
+
+       if (aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len)) {
+               os_free(decrypted);
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA",
+                   decrypted, encr_data_len);
+
+       if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr,
+                              aka, 1)) {
+               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse "
+                          "decrypted AT_ENCR_DATA");
+               os_free(decrypted);
+               return NULL;
+       }
+
+       return decrypted;
+}
+
+
+#define EAP_SIM_INIT_LEN 128
+
+struct eap_sim_msg {
+       struct wpabuf *buf;
+       size_t mac, iv, encr; /* index from buf */
+       int type;
+};
+
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
+{
+       struct eap_sim_msg *msg;
+       struct eap_hdr *eap;
+       u8 *pos;
+
+       msg = os_zalloc(sizeof(*msg));
+       if (msg == NULL)
+               return NULL;
+
+       msg->type = type;
+       msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
+       if (msg->buf == NULL) {
+               os_free(msg);
+               return NULL;
+       }
+       eap = wpabuf_put(msg->buf, sizeof(*eap));
+       eap->code = code;
+       eap->identifier = id;
+
+       pos = wpabuf_put(msg->buf, 4);
+       *pos++ = type;
+       *pos++ = subtype;
+       *pos++ = 0; /* Reserved */
+       *pos++ = 0; /* Reserved */
+
+       return msg;
+}
+
+
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+                                  const u8 *extra, size_t extra_len)
+{
+       struct eap_hdr *eap;
+       struct wpabuf *buf;
+
+       if (msg == NULL)
+               return NULL;
+
+       eap = wpabuf_mhead(msg->buf);
+       eap->length = host_to_be16(wpabuf_len(msg->buf));
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+       if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) {
+               eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
+                                      wpabuf_len(msg->buf),
+                                      (u8 *) wpabuf_mhead(msg->buf) +
+                                      msg->mac, extra, extra_len);
+       } else
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+       if (k_aut && msg->mac) {
+               eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf),
+                               wpabuf_len(msg->buf),
+                               (u8 *) wpabuf_mhead(msg->buf) + msg->mac,
+                               extra, extra_len);
+       }
+
+       buf = msg->buf;
+       os_free(msg);
+       return buf;
+}
+
+
+void eap_sim_msg_free(struct eap_sim_msg *msg)
+{
+       if (msg) {
+               wpabuf_free(msg->buf);
+               os_free(msg);
+       }
+}
+
+
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+                         const u8 *data, size_t len)
+{
+       int attr_len = 2 + len;
+       int pad_len;
+       u8 *start;
+
+       if (msg == NULL)
+               return NULL;
+
+       pad_len = (4 - attr_len % 4) % 4;
+       attr_len += pad_len;
+       if (wpabuf_resize(&msg->buf, attr_len))
+               return NULL;
+       start = wpabuf_put(msg->buf, 0);
+       wpabuf_put_u8(msg->buf, attr);
+       wpabuf_put_u8(msg->buf, attr_len / 4);
+       wpabuf_put_data(msg->buf, data, len);
+       if (pad_len)
+               os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+       return start;
+}
+
+
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value,
+                    const u8 *data, size_t len)
+{
+       int attr_len = 4 + len;
+       int pad_len;
+       u8 *start;
+
+       if (msg == NULL)
+               return NULL;
+
+       pad_len = (4 - attr_len % 4) % 4;
+       attr_len += pad_len;
+       if (wpabuf_resize(&msg->buf, attr_len))
+               return NULL;
+       start = wpabuf_put(msg->buf, 0);
+       wpabuf_put_u8(msg->buf, attr);
+       wpabuf_put_u8(msg->buf, attr_len / 4);
+       wpabuf_put_be16(msg->buf, value);
+       if (data)
+               wpabuf_put_data(msg->buf, data, len);
+       else
+               wpabuf_put(msg->buf, len);
+       if (pad_len)
+               os_memset(wpabuf_put(msg->buf, pad_len), 0, pad_len);
+       return start;
+}
+
+
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr)
+{
+       u8 *pos = eap_sim_msg_add(msg, attr, 0, NULL, EAP_SIM_MAC_LEN);
+       if (pos)
+               msg->mac = (pos - wpabuf_head_u8(msg->buf)) + 4;
+       return pos;
+}
+
+
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+                              u8 attr_encr)
+{
+       u8 *pos = eap_sim_msg_add(msg, attr_iv, 0, NULL, EAP_SIM_IV_LEN);
+       if (pos == NULL)
+               return -1;
+       msg->iv = (pos - wpabuf_head_u8(msg->buf)) + 4;
+       if (os_get_random(wpabuf_mhead_u8(msg->buf) + msg->iv,
+                         EAP_SIM_IV_LEN)) {
+               msg->iv = 0;
+               return -1;
+       }
+
+       pos = eap_sim_msg_add(msg, attr_encr, 0, NULL, 0);
+       if (pos == NULL) {
+               msg->iv = 0;
+               return -1;
+       }
+       msg->encr = pos - wpabuf_head_u8(msg->buf);
+
+       return 0;
+}
+
+
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad)
+{
+       size_t encr_len;
+
+       if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0)
+               return -1;
+
+       encr_len = wpabuf_len(msg->buf) - msg->encr - 4;
+       if (encr_len % 16) {
+               u8 *pos;
+               int pad_len = 16 - (encr_len % 16);
+               if (pad_len < 4) {
+                       wpa_printf(MSG_WARNING, "EAP-SIM: "
+                                  "eap_sim_msg_add_encr_end - invalid pad_len"
+                                  " %d", pad_len);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "   *AT_PADDING");
+               pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4);
+               if (pos == NULL)
+                       return -1;
+               os_memset(pos + 4, 0, pad_len - 4);
+               encr_len += pad_len;
+       }
+       wpa_printf(MSG_DEBUG, "   (AT_ENCR_DATA data len %lu)",
+                  (unsigned long) encr_len);
+       wpabuf_mhead_u8(msg->buf)[msg->encr + 1] = encr_len / 4 + 1;
+       return aes_128_cbc_encrypt(k_encr, wpabuf_head_u8(msg->buf) + msg->iv,
+                                  wpabuf_mhead_u8(msg->buf) + msg->encr + 4,
+                                  encr_len);
+}
+
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       const char *type = aka ? "AKA" : "SIM";
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+       switch (notification) {
+       case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH:
+               wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+                          "notification (after authentication)", type);
+               break;
+       case EAP_SIM_TEMPORARILY_DENIED:
+               wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+                          "User has been temporarily denied access to the "
+                          "requested service", type);
+               break;
+       case EAP_SIM_NOT_SUBSCRIBED:
+               wpa_printf(MSG_WARNING, "EAP-%s: Failure notification: "
+                          "User has not subscribed to the requested service",
+                          type);
+               break;
+       case EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH:
+               wpa_printf(MSG_WARNING, "EAP-%s: General failure "
+                          "notification (before authentication)", type);
+               break;
+       case EAP_SIM_SUCCESS:
+               wpa_printf(MSG_INFO, "EAP-%s: Successful authentication "
+                          "notification", type);
+               break;
+       default:
+               if (notification >= 32768) {
+                       wpa_printf(MSG_INFO, "EAP-%s: Unrecognized "
+                                  "non-failure notification %d",
+                                  type, notification);
+               } else {
+                       wpa_printf(MSG_WARNING, "EAP-%s: Unrecognized "
+                                  "failure notification %d",
+                                  type, notification);
+               }
+       }
+}
diff --git a/src/eap_common/eap_sim_common.h b/src/eap_common/eap_sim_common.h
new file mode 100644 (file)
index 0000000..48c8eaa
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * EAP peer/server: EAP-SIM/AKA/AKA' shared routines
+ * 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 EAP_SIM_COMMON_H
+#define EAP_SIM_COMMON_H
+
+#define EAP_SIM_NONCE_S_LEN 16
+#define EAP_SIM_NONCE_MT_LEN 16
+#define EAP_SIM_MAC_LEN 16
+#define EAP_SIM_MK_LEN 20
+#define EAP_SIM_K_AUT_LEN 16
+#define EAP_SIM_K_ENCR_LEN 16
+#define EAP_SIM_KEYING_DATA_LEN 64
+#define EAP_SIM_IV_LEN 16
+#define EAP_SIM_KC_LEN 8
+#define EAP_SIM_SRES_LEN 4
+
+#define GSM_RAND_LEN 16
+
+#define EAP_SIM_VERSION 1
+
+/* EAP-SIM Subtypes */
+#define EAP_SIM_SUBTYPE_START 10
+#define EAP_SIM_SUBTYPE_CHALLENGE 11
+#define EAP_SIM_SUBTYPE_NOTIFICATION 12
+#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13
+#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0
+#define EAP_SIM_UNSUPPORTED_VERSION 1
+#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2
+#define EAP_SIM_RAND_NOT_FRESH 3
+
+#define EAP_SIM_MAX_FAST_REAUTHS 1000
+
+#define EAP_SIM_MAX_CHAL 3
+
+
+/* EAP-AKA Subtypes */
+#define EAP_AKA_SUBTYPE_CHALLENGE 1
+#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2
+#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4
+#define EAP_AKA_SUBTYPE_IDENTITY 5
+#define EAP_AKA_SUBTYPE_NOTIFICATION 12
+#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13
+#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14
+
+/* AT_CLIENT_ERROR_CODE error codes */
+#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0
+
+#define EAP_AKA_RAND_LEN 16
+#define EAP_AKA_AUTN_LEN 16
+#define EAP_AKA_AUTS_LEN 14
+#define EAP_AKA_RES_MAX_LEN 16
+#define EAP_AKA_IK_LEN 16
+#define EAP_AKA_CK_LEN 16
+#define EAP_AKA_MAX_FAST_REAUTHS 1000
+#define EAP_AKA_MIN_RES_LEN 4
+#define EAP_AKA_MAX_RES_LEN 16
+#define EAP_AKA_CHECKCODE_LEN 20
+
+#define EAP_AKA_PRIME_K_AUT_LEN 32
+#define EAP_AKA_PRIME_CHECKCODE_LEN 32
+#define EAP_AKA_PRIME_K_RE_LEN 32
+
+struct wpabuf;
+
+void eap_sim_derive_mk(const u8 *identity, size_t identity_len,
+                      const u8 *nonce_mt, u16 selected_version,
+                      const u8 *ver_list, size_t ver_list_len,
+                      int num_chal, const u8 *kc, u8 *mk);
+void eap_aka_derive_mk(const u8 *identity, size_t identity_len,
+                      const u8 *ik, const u8 *ck, u8 *mk);
+int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk,
+                       u8 *emsk);
+int eap_sim_derive_keys_reauth(u16 _counter,
+                              const u8 *identity, size_t identity_len,
+                              const u8 *nonce_s, const u8 *mk, u8 *msk,
+                              u8 *emsk);
+int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
+                      const u8 *mac, const u8 *extra, size_t extra_len);
+void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac,
+                    const u8 *extra, size_t extra_len);
+
+#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
+void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len,
+                              const u8 *ik, const u8 *ck, u8 *k_encr,
+                              u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk);
+int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+                                    const u8 *identity, size_t identity_len,
+                                    const u8 *nonce_s, u8 *msk, u8 *emsk);
+int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
+                             const u8 *mac, const u8 *extra,
+                             size_t extra_len);
+void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len,
+                           u8 *mac, const u8 *extra, size_t extra_len);
+
+void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak,
+                                     const u8 *network_name,
+                                     size_t network_name_len);
+#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+static inline void eap_aka_prime_derive_keys(const u8 *identity,
+                                            size_t identity_len,
+                                            const u8 *ik, const u8 *ck,
+                                            u8 *k_encr, u8 *k_aut, u8 *k_re,
+                                            u8 *msk, u8 *emsk)
+{
+}
+
+static inline int eap_aka_prime_derive_keys_reauth(const u8 *k_re, u16 counter,
+                                                  const u8 *identity,
+                                                  size_t identity_len,
+                                                  const u8 *nonce_s, u8 *msk,
+                                                  u8 *emsk)
+{
+       return -1;
+}
+
+static inline int eap_sim_verify_mac_sha256(const u8 *k_aut,
+                                           const struct wpabuf *req,
+                                           const u8 *mac, const u8 *extra,
+                                           size_t extra_len)
+{
+       return -1;
+}
+#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */
+
+
+/* EAP-SIM/AKA Attributes (0..127 non-skippable) */
+#define EAP_SIM_AT_RAND 1
+#define EAP_SIM_AT_AUTN 2 /* only AKA */
+#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */
+#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */
+#define EAP_SIM_AT_PADDING 6 /* only encrypted */
+#define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */
+#define EAP_SIM_AT_PERMANENT_ID_REQ 10
+#define EAP_SIM_AT_MAC 11
+#define EAP_SIM_AT_NOTIFICATION 12
+#define EAP_SIM_AT_ANY_ID_REQ 13
+#define EAP_SIM_AT_IDENTITY 14 /* only send */
+#define EAP_SIM_AT_VERSION_LIST 15 /* only SIM */
+#define EAP_SIM_AT_SELECTED_VERSION 16 /* only SIM */
+#define EAP_SIM_AT_FULLAUTH_ID_REQ 17
+#define EAP_SIM_AT_COUNTER 19 /* only encrypted */
+#define EAP_SIM_AT_COUNTER_TOO_SMALL 20 /* only encrypted */
+#define EAP_SIM_AT_NONCE_S 21 /* only encrypted */
+#define EAP_SIM_AT_CLIENT_ERROR_CODE 22 /* only send */
+#define EAP_SIM_AT_KDF_INPUT 23 /* only AKA' */
+#define EAP_SIM_AT_KDF 24 /* only AKA' */
+#define EAP_SIM_AT_IV 129
+#define EAP_SIM_AT_ENCR_DATA 130
+#define EAP_SIM_AT_NEXT_PSEUDONYM 132 /* only encrypted */
+#define EAP_SIM_AT_NEXT_REAUTH_ID 133 /* only encrypted */
+#define EAP_SIM_AT_CHECKCODE 134 /* only AKA */
+#define EAP_SIM_AT_RESULT_IND 135
+#define EAP_SIM_AT_BIDDING 136
+
+/* AT_NOTIFICATION notification code values */
+#define EAP_SIM_GENERAL_FAILURE_AFTER_AUTH 0
+#define EAP_SIM_TEMPORARILY_DENIED 1026
+#define EAP_SIM_NOT_SUBSCRIBED 1031
+#define EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH 16384
+#define EAP_SIM_SUCCESS 32768
+
+/* EAP-AKA' AT_KDF Key Derivation Function values */
+#define EAP_AKA_PRIME_KDF 1
+
+/* AT_BIDDING flags */
+#define EAP_AKA_BIDDING_FLAG_D 0x8000
+
+
+enum eap_sim_id_req {
+       NO_ID_REQ, ANY_ID, FULLAUTH_ID, PERMANENT_ID
+};
+
+
+struct eap_sim_attrs {
+       const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s;
+       const u8 *next_pseudonym, *next_reauth_id;
+       const u8 *nonce_mt, *identity, *res, *auts;
+       const u8 *checkcode;
+       const u8 *kdf_input;
+       const u8 *bidding;
+       size_t num_chal, version_list_len, encr_data_len;
+       size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len;
+       size_t res_len_bits;
+       size_t checkcode_len;
+       size_t kdf_input_len;
+       enum eap_sim_id_req id_req;
+       int notification, counter, selected_version, client_error_code;
+       int counter_too_small;
+       int result_ind;
+#define EAP_AKA_PRIME_KDF_MAX 10
+       u16 kdf[EAP_AKA_PRIME_KDF_MAX];
+       size_t kdf_count;
+};
+
+int eap_sim_parse_attr(const u8 *start, const u8 *end,
+                      struct eap_sim_attrs *attr, int aka, int encr);
+u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
+                       size_t encr_data_len, const u8 *iv,
+                       struct eap_sim_attrs *attr, int aka);
+
+
+struct eap_sim_msg;
+
+struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+                                  const u8 *extra, size_t extra_len);
+void eap_sim_msg_free(struct eap_sim_msg *msg);
+u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
+                         const u8 *data, size_t len);
+u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr,
+                    u16 value, const u8 *data, size_t len);
+u8 * eap_sim_msg_add_mac(struct eap_sim_msg *msg, u8 attr);
+int eap_sim_msg_add_encr_start(struct eap_sim_msg *msg, u8 attr_iv,
+                              u8 attr_encr);
+int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr,
+                            int attr_pad);
+
+void eap_sim_report_notification(void *msg_ctx, int notification, int aka);
+
+#endif /* EAP_SIM_COMMON_H */
diff --git a/src/eap_common/eap_tlv_common.h b/src/eap_common/eap_tlv_common.h
new file mode 100644 (file)
index 0000000..f86015d
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * EAP-TLV definitions (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * 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 EAP_TLV_COMMON_H
+#define EAP_TLV_COMMON_H
+
+/* EAP-TLV TLVs (draft-josefsson-ppext-eap-tls-eap-10.txt) */
+#define EAP_TLV_RESULT_TLV 3 /* Acknowledged Result */
+#define EAP_TLV_NAK_TLV 4
+#define EAP_TLV_ERROR_CODE_TLV 5
+#define EAP_TLV_CONNECTION_BINDING_TLV 6
+#define EAP_TLV_VENDOR_SPECIFIC_TLV 7
+#define EAP_TLV_URI_TLV 8
+#define EAP_TLV_EAP_PAYLOAD_TLV 9
+#define EAP_TLV_INTERMEDIATE_RESULT_TLV 10
+#define EAP_TLV_PAC_TLV 11 /* RFC 5422, Section 4.2 */
+#define EAP_TLV_CRYPTO_BINDING_TLV 12
+#define EAP_TLV_CALLING_STATION_ID_TLV 13
+#define EAP_TLV_CALLED_STATION_ID_TLV 14
+#define EAP_TLV_NAS_PORT_TYPE_TLV 15
+#define EAP_TLV_SERVER_IDENTIFIER_TLV 16
+#define EAP_TLV_IDENTITY_TYPE_TLV 17
+#define EAP_TLV_SERVER_TRUSTED_ROOT_TLV 18
+#define EAP_TLV_REQUEST_ACTION_TLV 19
+#define EAP_TLV_PKCS7_TLV 20
+
+#define EAP_TLV_RESULT_SUCCESS 1
+#define EAP_TLV_RESULT_FAILURE 2
+
+#define EAP_TLV_TYPE_MANDATORY 0x8000
+#define EAP_TLV_TYPE_MASK 0x3fff
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_tlv_hdr {
+       be16 tlv_type;
+       be16 length;
+} STRUCT_PACKED;
+
+struct eap_tlv_nak_tlv {
+       be16 tlv_type;
+       be16 length;
+       be32 vendor_id;
+       be16 nak_type;
+} STRUCT_PACKED;
+
+struct eap_tlv_result_tlv {
+       be16 tlv_type;
+       be16 length;
+       be16 status;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.7 - Intermediate-Result TLV */
+struct eap_tlv_intermediate_result_tlv {
+       be16 tlv_type;
+       be16 length;
+       be16 status;
+       /* Followed by optional TLVs */
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.8 - Crypto-Binding TLV */
+struct eap_tlv_crypto_binding_tlv {
+       be16 tlv_type;
+       be16 length;
+       u8 reserved;
+       u8 version;
+       u8 received_version;
+       u8 subtype;
+       u8 nonce[32];
+       u8 compound_mac[20];
+} STRUCT_PACKED;
+
+struct eap_tlv_pac_ack_tlv {
+       be16 tlv_type;
+       be16 length;
+       be16 pac_type;
+       be16 pac_len;
+       be16 result;
+} STRUCT_PACKED;
+
+/* RFC 4851, Section 4.2.9 - Request-Action TLV */
+struct eap_tlv_request_action_tlv {
+       be16 tlv_type;
+       be16 length;
+       be16 action;
+} STRUCT_PACKED;
+
+/* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+struct eap_tlv_pac_type_tlv {
+       be16 tlv_type; /* PAC_TYPE_PAC_TYPE */
+       be16 length;
+       be16 pac_type;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST 0
+#define EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE 1
+
+#define EAP_TLV_ACTION_PROCESS_TLV 1
+#define EAP_TLV_ACTION_NEGOTIATE_EAP 2
+
+#endif /* EAP_TLV_COMMON_H */
diff --git a/src/eap_common/eap_ttls.h b/src/eap_common/eap_ttls.h
new file mode 100644 (file)
index 0000000..797d084
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * EAP server/peer: EAP-TTLS (RFC 5281)
+ * 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 EAP_TTLS_H
+#define EAP_TTLS_H
+
+struct ttls_avp {
+       be32 avp_code;
+       be32 avp_length; /* 8-bit flags, 24-bit length;
+                         * length includes AVP header */
+       /* optional 32-bit Vendor-ID */
+       /* Data */
+};
+
+struct ttls_avp_vendor {
+       be32 avp_code;
+       be32 avp_length; /* 8-bit flags, 24-bit length;
+                         * length includes AVP header */
+       be32 vendor_id;
+       /* Data */
+};
+
+#define AVP_FLAGS_VENDOR 0x80
+#define AVP_FLAGS_MANDATORY 0x40
+
+#define AVP_PAD(start, pos) \
+do { \
+       int __pad; \
+       __pad = (4 - (((pos) - (start)) & 3)) & 3; \
+       os_memset((pos), 0, __pad); \
+       pos += __pad; \
+} while (0)
+
+
+/* RFC 2865 */
+#define RADIUS_ATTR_USER_NAME 1
+#define RADIUS_ATTR_USER_PASSWORD 2
+#define RADIUS_ATTR_CHAP_PASSWORD 3
+#define RADIUS_ATTR_REPLY_MESSAGE 18
+#define RADIUS_ATTR_CHAP_CHALLENGE 60
+#define RADIUS_ATTR_EAP_MESSAGE 79
+
+/* RFC 2548 */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+#define RADIUS_ATTR_MS_CHAP_RESPONSE 1
+#define RADIUS_ATTR_MS_CHAP_ERROR 2
+#define RADIUS_ATTR_MS_CHAP_NT_ENC_PW 6
+#define RADIUS_ATTR_MS_CHAP_CHALLENGE 11
+#define RADIUS_ATTR_MS_CHAP2_RESPONSE 25
+#define RADIUS_ATTR_MS_CHAP2_SUCCESS 26
+#define RADIUS_ATTR_MS_CHAP2_CPW 27
+
+#define EAP_TTLS_MSCHAPV2_CHALLENGE_LEN 16
+#define EAP_TTLS_MSCHAPV2_RESPONSE_LEN 50
+#define EAP_TTLS_MSCHAP_CHALLENGE_LEN 8
+#define EAP_TTLS_MSCHAP_RESPONSE_LEN 50
+#define EAP_TTLS_CHAP_CHALLENGE_LEN 16
+#define EAP_TTLS_CHAP_PASSWORD_LEN 16
+
+#endif /* EAP_TTLS_H */
diff --git a/src/eap_common/eap_wsc_common.c b/src/eap_common/eap_wsc_common.c
new file mode 100644 (file)
index 0000000..5d4e8cc
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * EAP-WSC common routines for Wi-Fi Protected Setup
+ * 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"
+
+#include "common.h"
+#include "eap_defs.h"
+#include "eap_common.h"
+#include "wps/wps.h"
+#include "eap_wsc_common.h"
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code)
+{
+       struct wpabuf *msg;
+
+       msg = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2, code, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+                          "FRAG_ACK");
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/FRAG_ACK");
+       wpabuf_put_u8(msg, WSC_FRAG_ACK); /* Op-Code */
+       wpabuf_put_u8(msg, 0); /* Flags */
+
+       return msg;
+}
diff --git a/src/eap_common/eap_wsc_common.h b/src/eap_common/eap_wsc_common.h
new file mode 100644 (file)
index 0000000..fdf61d3
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * EAP-WSC definitions for Wi-Fi Protected Setup
+ * 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.
+ */
+
+#ifndef EAP_WSC_COMMON_H
+#define EAP_WSC_COMMON_H
+
+#define EAP_VENDOR_TYPE_WSC 1
+
+#define WSC_FLAGS_MF 0x01
+#define WSC_FLAGS_LF 0x02
+
+#define WSC_ID_REGISTRAR "WFA-SimpleConfig-Registrar-1-0"
+#define WSC_ID_REGISTRAR_LEN 30
+#define WSC_ID_ENROLLEE "WFA-SimpleConfig-Enrollee-1-0"
+#define WSC_ID_ENROLLEE_LEN 29
+
+#define WSC_FRAGMENT_SIZE 1400
+
+
+struct wpabuf * eap_wsc_build_frag_ack(u8 id, u8 code);
+
+#endif /* EAP_WSC_COMMON_H */
diff --git a/src/eap_common/ikev2_common.c b/src/eap_common/ikev2_common.c
new file mode 100644 (file)
index 0000000..67754d8
--- /dev/null
@@ -0,0 +1,796 @@
+/*
+ * IKEv2 common routines for initiator and responder
+ * 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"
+
+#include "common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "ikev2_common.h"
+
+
+static struct ikev2_integ_alg ikev2_integ_algs[] = {
+       { AUTH_HMAC_SHA1_96, 20, 12 },
+       { AUTH_HMAC_MD5_96, 16, 12 }
+};
+
+#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0]))
+
+
+static struct ikev2_prf_alg ikev2_prf_algs[] = {
+       { PRF_HMAC_SHA1, 20, 20 },
+       { PRF_HMAC_MD5, 16, 16 }
+};
+
+#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0]))
+
+
+static struct ikev2_encr_alg ikev2_encr_algs[] = {
+       { ENCR_AES_CBC, 16, 16 }, /* only 128-bit keys supported for now */
+       { ENCR_3DES, 24, 8 }
+};
+
+#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0]))
+
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id)
+{
+       size_t i;
+
+       for (i = 0; i < NUM_INTEG_ALGS; i++) {
+               if (ikev2_integ_algs[i].id == id)
+                       return &ikev2_integ_algs[i];
+       }
+
+       return NULL;
+}
+
+
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+                    size_t data_len, u8 *hash)
+{
+       u8 tmphash[IKEV2_MAX_HASH_LEN];
+
+       switch (alg) {
+       case AUTH_HMAC_SHA1_96:
+               if (key_len != 20)
+                       return -1;
+               hmac_sha1(key, key_len, data, data_len, tmphash);
+               os_memcpy(hash, tmphash, 12);
+               break;
+       case AUTH_HMAC_MD5_96:
+               if (key_len != 16)
+                       return -1;
+               hmac_md5(key, key_len, data, data_len, tmphash);
+               os_memcpy(hash, tmphash, 12);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+const struct ikev2_prf_alg * ikev2_get_prf(int id)
+{
+       size_t i;
+
+       for (i = 0; i < NUM_PRF_ALGS; i++) {
+               if (ikev2_prf_algs[i].id == id)
+                       return &ikev2_prf_algs[i];
+       }
+
+       return NULL;
+}
+
+
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+                  size_t num_elem, const u8 *addr[], const size_t *len,
+                  u8 *hash)
+{
+       switch (alg) {
+       case PRF_HMAC_SHA1:
+               hmac_sha1_vector(key, key_len, num_elem, addr, len, hash);
+               break;
+       case PRF_HMAC_MD5:
+               hmac_md5_vector(key, key_len, num_elem, addr, len, hash);
+               break;
+       default:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+                  const u8 *data, size_t data_len,
+                  u8 *out, size_t out_len)
+{
+       u8 hash[IKEV2_MAX_HASH_LEN];
+       size_t hash_len;
+       u8 iter, *pos, *end;
+       const u8 *addr[3];
+       size_t len[3];
+       const struct ikev2_prf_alg *prf;
+       int res;
+
+       prf = ikev2_get_prf(alg);
+       if (prf == NULL)
+               return -1;
+       hash_len = prf->hash_len;
+
+       addr[0] = hash;
+       len[0] = hash_len;
+       addr[1] = data;
+       len[1] = data_len;
+       addr[2] = &iter;
+       len[2] = 1;
+
+       pos = out;
+       end = out + out_len;
+       iter = 1;
+       while (pos < end) {
+               size_t clen;
+               if (iter == 1)
+                       res = ikev2_prf_hash(alg, key, key_len, 2, &addr[1],
+                                            &len[1], hash);
+               else
+                       res = ikev2_prf_hash(alg, key, key_len, 3, addr, len,
+                                            hash);
+               if (res < 0)
+                       return -1;
+               clen = hash_len;
+               if ((int) clen > end - pos)
+                       clen = end - pos;
+               os_memcpy(pos, hash, clen);
+               pos += clen;
+               iter++;
+       }
+
+       return 0;
+}
+
+
+const struct ikev2_encr_alg * ikev2_get_encr(int id)
+{
+       size_t i;
+
+       for (i = 0; i < NUM_ENCR_ALGS; i++) {
+               if (ikev2_encr_algs[i].id == id)
+                       return &ikev2_encr_algs[i];
+       }
+
+       return NULL;
+}
+
+
+#ifdef CCNS_PL
+/* from des.c */
+struct des3_key_s {
+       u32 ek[3][32];
+       u32 dk[3][32];
+};
+
+void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
+void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
+void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
+#endif /* CCNS_PL */
+
+
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+                      const u8 *plain, u8 *crypt, size_t len)
+{
+       struct crypto_cipher *cipher;
+       int encr_alg;
+
+#ifdef CCNS_PL
+       if (alg == ENCR_3DES) {
+               struct des3_key_s des3key;
+               size_t i, blocks;
+               u8 *pos;
+
+               /* ECB mode is used incorrectly for 3DES!? */
+               if (key_len != 24) {
+                       wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
+                       return -1;
+               }
+               des3_key_setup(key, &des3key);
+
+               blocks = len / 8;
+               pos = crypt;
+               for (i = 0; i < blocks; i++) {
+                       des3_encrypt(pos, &des3key, pos);
+                       pos += 8;
+               }
+       } else {
+#endif /* CCNS_PL */
+       switch (alg) {
+       case ENCR_3DES:
+               encr_alg = CRYPTO_CIPHER_ALG_3DES;
+               break;
+       case ENCR_AES_CBC:
+               encr_alg = CRYPTO_CIPHER_ALG_AES;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+               return -1;
+       }
+
+       cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+       if (cipher == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+               return -1;
+       }
+
+       if (crypto_cipher_encrypt(cipher, plain, crypt, len) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Encryption failed");
+               crypto_cipher_deinit(cipher);
+               return -1;
+       }
+       crypto_cipher_deinit(cipher);
+#ifdef CCNS_PL
+       }
+#endif /* CCNS_PL */
+
+       return 0;
+}
+
+
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+                      const u8 *crypt, u8 *plain, size_t len)
+{
+       struct crypto_cipher *cipher;
+       int encr_alg;
+
+#ifdef CCNS_PL
+       if (alg == ENCR_3DES) {
+               struct des3_key_s des3key;
+               size_t i, blocks;
+
+               /* ECB mode is used incorrectly for 3DES!? */
+               if (key_len != 24) {
+                       wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
+                       return -1;
+               }
+               des3_key_setup(key, &des3key);
+
+               if (len % 8) {
+                       wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted "
+                                  "length");
+                       return -1;
+               }
+               blocks = len / 8;
+               for (i = 0; i < blocks; i++) {
+                       des3_decrypt(crypt, &des3key, plain);
+                       plain += 8;
+                       crypt += 8;
+               }
+       } else {
+#endif /* CCNS_PL */
+       switch (alg) {
+       case ENCR_3DES:
+               encr_alg = CRYPTO_CIPHER_ALG_3DES;
+               break;
+       case ENCR_AES_CBC:
+               encr_alg = CRYPTO_CIPHER_ALG_AES;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "IKEV2: Unsupported encr alg %d", alg);
+               return -1;
+       }
+
+       cipher = crypto_cipher_init(encr_alg, iv, key, key_len);
+       if (cipher == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Failed to initialize cipher");
+               return -1;
+       }
+
+       if (crypto_cipher_decrypt(cipher, crypt, plain, len) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Decryption failed");
+               crypto_cipher_deinit(cipher);
+               return -1;
+       }
+       crypto_cipher_deinit(cipher);
+#ifdef CCNS_PL
+       }
+#endif /* CCNS_PL */
+
+       return 0;
+}
+
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+                        u8 next_payload, const u8 *pos, const u8 *end)
+{
+       const struct ikev2_payload_hdr *phdr;
+
+       os_memset(payloads, 0, sizeof(*payloads));
+
+       while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
+               int plen, pdatalen;
+               const u8 *pdata;
+               wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
+                          next_payload);
+               if (end - pos < (int) sizeof(*phdr)) {
+                       wpa_printf(MSG_INFO, "IKEV2:   Too short message for "
+                                  "payload header (left=%ld)",
+                                  (long) (end - pos));
+               }
+               phdr = (const struct ikev2_payload_hdr *) pos;
+               plen = WPA_GET_BE16(phdr->payload_length);
+               if (plen < (int) sizeof(*phdr) || pos + plen > end) {
+                       wpa_printf(MSG_INFO, "IKEV2:   Invalid payload header "
+                                  "length %d", plen);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Flags: 0x%x"
+                          "  Payload Length: %d",
+                          phdr->next_payload, phdr->flags, plen);
+
+               pdata = (const u8 *) (phdr + 1);
+               pdatalen = plen - sizeof(*phdr);
+
+               switch (next_payload) {
+               case IKEV2_PAYLOAD_SA:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Security "
+                                  "Association");
+                       payloads->sa = pdata;
+                       payloads->sa_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_KEY_EXCHANGE:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Key "
+                                  "Exchange");
+                       payloads->ke = pdata;
+                       payloads->ke_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_IDi:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: IDi");
+                       payloads->idi = pdata;
+                       payloads->idi_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_IDr:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: IDr");
+                       payloads->idr = pdata;
+                       payloads->idr_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_CERTIFICATE:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Certificate");
+                       payloads->cert = pdata;
+                       payloads->cert_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_AUTHENTICATION:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: "
+                                  "Authentication");
+                       payloads->auth = pdata;
+                       payloads->auth_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_NONCE:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Nonce");
+                       payloads->nonce = pdata;
+                       payloads->nonce_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_ENCRYPTED:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: Encrypted");
+                       payloads->encrypted = pdata;
+                       payloads->encrypted_len = pdatalen;
+                       break;
+               case IKEV2_PAYLOAD_NOTIFICATION:
+                       wpa_printf(MSG_DEBUG, "IKEV2:   Payload: "
+                                  "Notification");
+                       payloads->notification = pdata;
+                       payloads->notification_len = pdatalen;
+                       break;
+               default:
+                       if (phdr->flags & IKEV2_PAYLOAD_FLAGS_CRITICAL) {
+                               wpa_printf(MSG_INFO, "IKEV2:   Unsupported "
+                                          "critical payload %u - reject the "
+                                          "entire message", next_payload);
+                               return -1;
+                       } else {
+                               wpa_printf(MSG_DEBUG, "IKEV2:   Skipped "
+                                          "unsupported payload %u",
+                                          next_payload);
+                       }
+               }
+
+               if (next_payload == IKEV2_PAYLOAD_ENCRYPTED &&
+                   pos + plen == end) {
+                       /*
+                        * Next Payload in the case of Encrypted Payload is
+                        * actually the payload type for the first embedded
+                        * payload.
+                        */
+                       payloads->encr_next_payload = phdr->next_payload;
+                       next_payload = IKEV2_PAYLOAD_NO_NEXT_PAYLOAD;
+               } else
+                       next_payload = phdr->next_payload;
+
+               pos += plen;
+       }
+
+       if (pos != end) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected extra data after "
+                          "payloads");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+                          const u8 *ID, size_t ID_len, u8 ID_type,
+                          struct ikev2_keys *keys, int initiator,
+                          const u8 *shared_secret, size_t shared_secret_len,
+                          const u8 *nonce, size_t nonce_len,
+                          const u8 *key_pad, size_t key_pad_len,
+                          u8 *auth_data)
+{
+       size_t sign_len, buf_len;
+       u8 *sign_data, *pos, *buf, hash[IKEV2_MAX_HASH_LEN];
+       const struct ikev2_prf_alg *prf;
+       const u8 *SK_p = initiator ? keys->SK_pi : keys->SK_pr;
+
+       prf = ikev2_get_prf(prf_alg);
+       if (sign_msg == NULL || ID == NULL || SK_p == NULL ||
+           shared_secret == NULL || nonce == NULL || prf == NULL)
+               return -1;
+
+       /* prf(SK_pi/r,IDi/r') */
+       buf_len = 4 + ID_len;
+       buf = os_zalloc(buf_len);
+       if (buf == NULL)
+               return -1;
+       buf[0] = ID_type;
+       os_memcpy(buf + 4, ID, ID_len);
+       if (ikev2_prf_hash(prf->id, SK_p, keys->SK_prf_len,
+                          1, (const u8 **) &buf, &buf_len, hash) < 0) {
+               os_free(buf);
+               return -1;
+       }
+       os_free(buf);
+
+       /* sign_data = msg | Nr/i | prf(SK_pi/r,IDi/r') */
+       sign_len = wpabuf_len(sign_msg) + nonce_len + prf->hash_len;
+       sign_data = os_malloc(sign_len);
+       if (sign_data == NULL)
+               return -1;
+       pos = sign_data;
+       os_memcpy(pos, wpabuf_head(sign_msg), wpabuf_len(sign_msg));
+       pos += wpabuf_len(sign_msg);
+       os_memcpy(pos, nonce, nonce_len);
+       pos += nonce_len;
+       os_memcpy(pos, hash, prf->hash_len);
+
+       /* AUTH = prf(prf(Shared Secret, key pad, sign_data) */
+       if (ikev2_prf_hash(prf->id, shared_secret, shared_secret_len, 1,
+                          &key_pad, &key_pad_len, hash) < 0 ||
+           ikev2_prf_hash(prf->id, hash, prf->hash_len, 1,
+                          (const u8 **) &sign_data, &sign_len, auth_data) < 0)
+       {
+               os_free(sign_data);
+               return -1;
+       }
+       os_free(sign_data);
+
+       return 0;
+}
+
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id,
+                          struct ikev2_keys *keys, int initiator,
+                          const struct ikev2_hdr *hdr,
+                          const u8 *encrypted, size_t encrypted_len,
+                          size_t *res_len)
+{
+       size_t iv_len;
+       const u8 *pos, *end, *iv, *integ;
+       u8 hash[IKEV2_MAX_HASH_LEN], *decrypted;
+       size_t decrypted_len, pad_len;
+       const struct ikev2_integ_alg *integ_alg;
+       const struct ikev2_encr_alg *encr_alg;
+       const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+       const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+       if (encrypted == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No Encrypted payload in SA_AUTH");
+               return NULL;
+       }
+
+       encr_alg = ikev2_get_encr(encr_id);
+       if (encr_alg == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+               return NULL;
+       }
+       iv_len = encr_alg->block_size;
+
+       integ_alg = ikev2_get_integ(integ_id);
+       if (integ_alg == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+               return NULL;
+       }
+
+       if (encrypted_len < iv_len + 1 + integ_alg->hash_len) {
+               wpa_printf(MSG_INFO, "IKEV2: No room for IV or Integrity "
+                         "Checksum");
+               return NULL;
+       }
+
+       iv = encrypted;
+       pos = iv + iv_len;
+       end = encrypted + encrypted_len;
+       integ = end - integ_alg->hash_len;
+
+       if (SK_a == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+               return NULL;
+       }
+       if (ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+                            (const u8 *) hdr,
+                            integ - (const u8 *) hdr, hash) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Failed to calculate integrity "
+                          "hash");
+               return NULL;
+       }
+       if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
+                          "Data");
+               return NULL;
+       }
+
+       if (SK_e == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+               return NULL;
+       }
+
+       decrypted_len = integ - pos;
+       decrypted = os_malloc(decrypted_len);
+       if (decrypted == NULL)
+               return NULL;
+
+       if (ikev2_encr_decrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv, pos,
+                              decrypted, decrypted_len) < 0) {
+               os_free(decrypted);
+               return NULL;
+       }
+
+       pad_len = decrypted[decrypted_len - 1];
+       if (decrypted_len < pad_len + 1) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid padding in encrypted "
+                          "payload");
+               os_free(decrypted);
+               return NULL;
+       }
+
+       decrypted_len -= pad_len + 1;
+
+       *res_len = decrypted_len;
+       return decrypted;
+}
+
+
+void ikev2_update_hdr(struct wpabuf *msg)
+{
+       struct ikev2_hdr *hdr;
+
+       /* Update lenth field in HDR */
+       hdr = wpabuf_mhead(msg);
+       WPA_PUT_BE32(hdr->length, wpabuf_len(msg));
+}
+
+
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+                         int initiator, struct wpabuf *msg,
+                         struct wpabuf *plain, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       size_t iv_len, pad_len;
+       u8 *icv, *iv;
+       const struct ikev2_integ_alg *integ_alg;
+       const struct ikev2_encr_alg *encr_alg;
+       const u8 *SK_e = initiator ? keys->SK_ei : keys->SK_er;
+       const u8 *SK_a = initiator ? keys->SK_ai : keys->SK_ar;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding Encrypted payload");
+
+       /* Encr - RFC 4306, Sect. 3.14 */
+
+       encr_alg = ikev2_get_encr(encr_id);
+       if (encr_alg == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported encryption type");
+               return -1;
+       }
+       iv_len = encr_alg->block_size;
+
+       integ_alg = ikev2_get_integ(integ_id);
+       if (integ_alg == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported intergrity type");
+               return -1;
+       }
+
+       if (SK_e == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No SK_e available");
+               return -1;
+       }
+
+       if (SK_a == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No SK_a available");
+               return -1;
+       }
+
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+
+       iv = wpabuf_put(msg, iv_len);
+       if (os_get_random(iv, iv_len)) {
+               wpa_printf(MSG_INFO, "IKEV2: Could not generate IV");
+               return -1;
+       }
+
+       pad_len = iv_len - (wpabuf_len(plain) + 1) % iv_len;
+       if (pad_len == iv_len)
+               pad_len = 0;
+       wpabuf_put(plain, pad_len);
+       wpabuf_put_u8(plain, pad_len);
+
+       if (ikev2_encr_encrypt(encr_alg->id, SK_e, keys->SK_encr_len, iv,
+                              wpabuf_head(plain), wpabuf_mhead(plain),
+                              wpabuf_len(plain)) < 0)
+               return -1;
+
+       wpabuf_put_buf(msg, plain);
+
+       /* Need to update all headers (Length fields) prior to hash func */
+       icv = wpabuf_put(msg, integ_alg->hash_len);
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+
+       ikev2_update_hdr(msg);
+
+       return ikev2_integ_hash(integ_id, SK_a, keys->SK_integ_len,
+                               wpabuf_head(msg),
+                               wpabuf_len(msg) - integ_alg->hash_len, icv);
+
+       return 0;
+}
+
+
+int ikev2_keys_set(struct ikev2_keys *keys)
+{
+       return keys->SK_d && keys->SK_ai && keys->SK_ar && keys->SK_ei &&
+               keys->SK_er && keys->SK_pi && keys->SK_pr;
+}
+
+
+void ikev2_free_keys(struct ikev2_keys *keys)
+{
+       os_free(keys->SK_d);
+       os_free(keys->SK_ai);
+       os_free(keys->SK_ar);
+       os_free(keys->SK_ei);
+       os_free(keys->SK_er);
+       os_free(keys->SK_pi);
+       os_free(keys->SK_pr);
+       keys->SK_d = keys->SK_ai = keys->SK_ar = keys->SK_ei = keys->SK_er =
+               keys->SK_pi = keys->SK_pr = NULL;
+}
+
+
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+                        const struct ikev2_integ_alg *integ,
+                        const struct ikev2_encr_alg *encr,
+                        const u8 *skeyseed, const u8 *data, size_t data_len,
+                        struct ikev2_keys *keys)
+{
+       u8 *keybuf, *pos;
+       size_t keybuf_len;
+
+       /*
+        * {SK_d | SK_ai | SK_ar | SK_ei | SK_er | SK_pi | SK_pr } =
+        *      prf+(SKEYSEED, Ni | Nr | SPIi | SPIr )
+        */
+       ikev2_free_keys(keys);
+       keys->SK_d_len = prf->key_len;
+       keys->SK_integ_len = integ->key_len;
+       keys->SK_encr_len = encr->key_len;
+       keys->SK_prf_len = prf->key_len;
+#ifdef CCNS_PL
+       /* Uses encryption key length for SK_d; should be PRF length */
+       keys->SK_d_len = keys->SK_encr_len;
+#endif /* CCNS_PL */
+
+       keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len +
+               2 * keys->SK_encr_len + 2 * keys->SK_prf_len;
+       keybuf = os_malloc(keybuf_len);
+       if (keybuf == NULL)
+               return -1;
+
+       if (ikev2_prf_plus(prf->id, skeyseed, prf->hash_len,
+                          data, data_len, keybuf, keybuf_len)) {
+               os_free(keybuf);
+               return -1;
+       }
+
+       pos = keybuf;
+
+       keys->SK_d = os_malloc(keys->SK_d_len);
+       if (keys->SK_d) {
+               os_memcpy(keys->SK_d, pos, keys->SK_d_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_d",
+                               keys->SK_d, keys->SK_d_len);
+       }
+       pos += keys->SK_d_len;
+
+       keys->SK_ai = os_malloc(keys->SK_integ_len);
+       if (keys->SK_ai) {
+               os_memcpy(keys->SK_ai, pos, keys->SK_integ_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ai",
+                               keys->SK_ai, keys->SK_integ_len);
+       }
+       pos += keys->SK_integ_len;
+
+       keys->SK_ar = os_malloc(keys->SK_integ_len);
+       if (keys->SK_ar) {
+               os_memcpy(keys->SK_ar, pos, keys->SK_integ_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ar",
+                               keys->SK_ar, keys->SK_integ_len);
+       }
+       pos += keys->SK_integ_len;
+
+       keys->SK_ei = os_malloc(keys->SK_encr_len);
+       if (keys->SK_ei) {
+               os_memcpy(keys->SK_ei, pos, keys->SK_encr_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_ei",
+                               keys->SK_ei, keys->SK_encr_len);
+       }
+       pos += keys->SK_encr_len;
+
+       keys->SK_er = os_malloc(keys->SK_encr_len);
+       if (keys->SK_er) {
+               os_memcpy(keys->SK_er, pos, keys->SK_encr_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_er",
+                               keys->SK_er, keys->SK_encr_len);
+       }
+       pos += keys->SK_encr_len;
+
+       keys->SK_pi = os_malloc(keys->SK_prf_len);
+       if (keys->SK_pi) {
+               os_memcpy(keys->SK_pi, pos, keys->SK_prf_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pi",
+                               keys->SK_pi, keys->SK_prf_len);
+       }
+       pos += keys->SK_prf_len;
+
+       keys->SK_pr = os_malloc(keys->SK_prf_len);
+       if (keys->SK_pr) {
+               os_memcpy(keys->SK_pr, pos, keys->SK_prf_len);
+               wpa_hexdump_key(MSG_DEBUG, "IKEV2: SK_pr",
+                               keys->SK_pr, keys->SK_prf_len);
+       }
+
+       os_free(keybuf);
+
+       if (!ikev2_keys_set(keys)) {
+               ikev2_free_keys(keys);
+               return -1;
+       }
+
+       return 0;
+}
diff --git a/src/eap_common/ikev2_common.h b/src/eap_common/ikev2_common.h
new file mode 100644 (file)
index 0000000..c96a070
--- /dev/null
@@ -0,0 +1,344 @@
+/*
+ * IKEv2 definitions
+ * 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.
+ */
+
+#ifndef IKEV2_COMMON_H
+#define IKEV2_COMMON_H
+
+/*
+ * Nonce length must be at least 16 octets. It must also be at least half the
+ * key size of the negotiated PRF.
+ */
+#define IKEV2_NONCE_MIN_LEN 16
+#define IKEV2_NONCE_MAX_LEN 256
+
+/* IKE Header - RFC 4306, Sect. 3.1 */
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#define IKEV2_SPI_LEN 8
+
+struct ikev2_hdr {
+       u8 i_spi[IKEV2_SPI_LEN]; /* IKE_SA Initiator's SPI */
+       u8 r_spi[IKEV2_SPI_LEN]; /* IKE_SA Responder's SPI */
+       u8 next_payload;
+       u8 version; /* MjVer | MnVer */
+       u8 exchange_type;
+       u8 flags;
+       u8 message_id[4];
+       u8 length[4]; /* total length of HDR + payloads */
+} STRUCT_PACKED;
+
+struct ikev2_payload_hdr {
+       u8 next_payload;
+       u8 flags;
+       u8 payload_length[2]; /* this payload, including the payload header */
+} STRUCT_PACKED;
+
+struct ikev2_proposal {
+       u8 type; /* 0 (last) or 2 (more) */
+       u8 reserved;
+       u8 proposal_length[2]; /* including all transform and attributes */
+       u8 proposal_num;
+       u8 protocol_id; /* IKEV2_PROTOCOL_* */
+       u8 spi_size;
+       u8 num_transforms;
+       /* SPI of spi_size octets */
+       /* Transforms */
+} STRUCT_PACKED;
+
+struct ikev2_transform {
+       u8 type; /* 0 (last) or 3 (more) */
+       u8 reserved;
+       u8 transform_length[2]; /* including Header and Attributes */
+       u8 transform_type;
+       u8 reserved2;
+       u8 transform_id[2];
+       /* Transform Attributes */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+/* Current IKEv2 version from RFC 4306 */
+#define IKEV2_MjVer 2
+#define IKEV2_MnVer 0
+#ifdef CCNS_PL
+#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4))
+#else /* CCNS_PL */
+#define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer))
+#endif /* CCNS_PL */
+
+/* IKEv2 Exchange Types */
+enum {
+       /* 0-33 RESERVED */
+       IKE_SA_INIT = 34,
+       IKE_SA_AUTH = 35,
+       CREATE_CHILD_SA = 36,
+       INFORMATION = 37
+       /* 38-239 RESERVED TO IANA */
+       /* 240-255 Reserved for private use */
+};
+
+/* IKEv2 Flags */
+#define IKEV2_HDR_INITIATOR    0x08
+#define IKEV2_HDR_VERSION      0x10
+#define IKEV2_HDR_RESPONSE     0x20
+
+/* Payload Header Flags */
+#define IKEV2_PAYLOAD_FLAGS_CRITICAL 0x01
+
+
+/* EAP-IKEv2 Payload Types (in Next Payload Type field)
+ * http://www.iana.org/assignments/eap-ikev2-payloads */
+enum {
+       IKEV2_PAYLOAD_NO_NEXT_PAYLOAD = 0,
+       IKEV2_PAYLOAD_SA = 33,
+       IKEV2_PAYLOAD_KEY_EXCHANGE = 34,
+       IKEV2_PAYLOAD_IDi = 35,
+       IKEV2_PAYLOAD_IDr = 36,
+       IKEV2_PAYLOAD_CERTIFICATE = 37,
+       IKEV2_PAYLOAD_CERT_REQ = 38,
+       IKEV2_PAYLOAD_AUTHENTICATION = 39,
+       IKEV2_PAYLOAD_NONCE = 40,
+       IKEV2_PAYLOAD_NOTIFICATION = 41,
+       IKEV2_PAYLOAD_VENDOD_ID = 43,
+       IKEV2_PAYLOAD_ENCRYPTED = 46,
+       IKEV2_PAYLOAD_NEXT_FAST_ID = 121
+};
+
+
+/* IKEv2 Proposal - Protocol ID */
+enum {
+       IKEV2_PROTOCOL_RESERVED = 0,
+       IKEV2_PROTOCOL_IKE = 1, /* IKE is the only one allowed for EAP-IKEv2 */
+       IKEV2_PROTOCOL_AH = 2,
+       IKEV2_PROTOCOL_ESP = 3
+};
+
+
+/* IKEv2 Transform Types */
+enum {
+       IKEV2_TRANSFORM_ENCR = 1,
+       IKEV2_TRANSFORM_PRF = 2,
+       IKEV2_TRANSFORM_INTEG = 3,
+       IKEV2_TRANSFORM_DH = 4,
+       IKEV2_TRANSFORM_ESN = 5
+};
+
+/* IKEv2 Tranform Type 1 (Encryption Algorithm) */
+enum {
+       ENCR_DES_IV64 = 1,
+       ENCR_DES = 2,
+       ENCR_3DES = 3,
+       ENCR_RC5 = 4,
+       ENCR_IDEA = 5,
+       ENCR_CAST = 6,
+       ENCR_BLOWFISH = 7,
+       ENCR_3IDEA = 8,
+       ENCR_DES_IV32 = 9,
+       ENCR_NULL = 11,
+       ENCR_AES_CBC = 12,
+       ENCR_AES_CTR = 13
+};
+
+/* IKEv2 Transform Type 2 (Pseudo-random Function) */
+enum {
+       PRF_HMAC_MD5 = 1,
+       PRF_HMAC_SHA1 = 2,
+       PRF_HMAC_TIGER = 3,
+       PRF_AES128_XCBC = 4
+};
+
+/* IKEv2 Transform Type 3 (Integrity Algorithm) */
+enum {
+       AUTH_HMAC_MD5_96 = 1,
+       AUTH_HMAC_SHA1_96 = 2,
+       AUTH_DES_MAC = 3,
+       AUTH_KPDK_MD5 = 4,
+       AUTH_AES_XCBC_96 = 5
+};
+
+/* IKEv2 Transform Type 4 (Diffie-Hellman Group) */
+enum {
+       DH_GROUP1_768BIT_MODP = 1, /* RFC 4306 */
+       DH_GROUP2_1024BIT_MODP = 2, /* RFC 4306 */
+       DH_GROUP5_1536BIT_MODP = 5, /* RFC 3526 */
+       DH_GROUP5_2048BIT_MODP = 14, /* RFC 3526 */
+       DH_GROUP5_3072BIT_MODP = 15, /* RFC 3526 */
+       DH_GROUP5_4096BIT_MODP = 16, /* RFC 3526 */
+       DH_GROUP5_6144BIT_MODP = 17, /* RFC 3526 */
+       DH_GROUP5_8192BIT_MODP = 18 /* RFC 3526 */
+};
+
+
+/* Identification Data Types (RFC 4306, Sect. 3.5) */
+enum {
+       ID_IPV4_ADDR = 1,
+       ID_FQDN = 2,
+       ID_RFC822_ADDR = 3,
+       ID_IPV6_ADDR = 5,
+       ID_DER_ASN1_DN = 9,
+       ID_DER_ASN1_GN= 10,
+       ID_KEY_ID = 11
+};
+
+
+/* Certificate Encoding (RFC 4306, Sect. 3.6) */
+enum {
+       CERT_ENCODING_PKCS7_X509 = 1,
+       CERT_ENCODING_PGP_CERT = 2,
+       CERT_ENCODING_DNS_SIGNED_KEY = 3,
+       /* X.509 Certificate - Signature: DER encoded X.509 certificate whose
+        * public key is used to validate the sender's AUTH payload */
+       CERT_ENCODING_X509_CERT_SIGN = 4,
+       CERT_ENCODING_KERBEROS_TOKEN = 6,
+       /* DER encoded X.509 certificate revocation list */
+       CERT_ENCODING_CRL = 7,
+       CERT_ENCODING_ARL = 8,
+       CERT_ENCODING_SPKI_CERT = 9,
+       CERT_ENCODING_X509_CERT_ATTR = 10,
+       /* PKCS #1 encoded RSA key */
+       CERT_ENCODING_RAW_RSA_KEY = 11,
+       CERT_ENCODING_HASH_AND_URL_X509_CERT = 12,
+       CERT_ENCODING_HASH_AND_URL_X509_BUNDLE = 13
+};
+
+
+/* Authentication Method (RFC 4306, Sect. 3.8) */
+enum {
+       AUTH_RSA_SIGN = 1,
+       AUTH_SHARED_KEY_MIC = 2,
+       AUTH_DSS_SIGN = 3
+};
+
+
+/* Notify Message Types (RFC 4306, Sect. 3.10.1) */
+enum {
+       UNSUPPORTED_CRITICAL_PAYLOAD = 1,
+       INVALID_IKE_SPI = 4,
+       INVALID_MAJOR_VERSION = 5,
+       INVALID_SYNTAX = 7,
+       INVALID_MESSAGE_ID = 9,
+       INVALID_SPI = 11,
+       NO_PROPOSAL_CHOSEN = 14,
+       INVALID_KE_PAYLOAD = 17,
+       AUTHENTICATION_FAILED = 24,
+       SINGLE_PAIR_REQUIRED = 34,
+       NO_ADDITIONAL_SAS = 35,
+       INTERNAL_ADDRESS_FAILURE = 36,
+       FAILED_CP_REQUIRED = 37,
+       TS_UNACCEPTABLE = 38,
+       INVALID_SELECTORS = 39
+};
+
+
+struct ikev2_keys {
+       u8 *SK_d, *SK_ai, *SK_ar, *SK_ei, *SK_er, *SK_pi, *SK_pr;
+       size_t SK_d_len, SK_integ_len, SK_encr_len, SK_prf_len;
+};
+
+
+int ikev2_keys_set(struct ikev2_keys *keys);
+void ikev2_free_keys(struct ikev2_keys *keys);
+
+
+/* Maximum hash length for supported hash algorithms */
+#define IKEV2_MAX_HASH_LEN 20
+
+struct ikev2_integ_alg {
+       int id;
+       size_t key_len;
+       size_t hash_len;
+};
+
+struct ikev2_prf_alg {
+       int id;
+       size_t key_len;
+       size_t hash_len;
+};
+
+struct ikev2_encr_alg {
+       int id;
+       size_t key_len;
+       size_t block_size;
+};
+
+const struct ikev2_integ_alg * ikev2_get_integ(int id);
+int ikev2_integ_hash(int alg, const u8 *key, size_t key_len, const u8 *data,
+                    size_t data_len, u8 *hash);
+const struct ikev2_prf_alg * ikev2_get_prf(int id);
+int ikev2_prf_hash(int alg, const u8 *key, size_t key_len,
+                  size_t num_elem, const u8 *addr[], const size_t *len,
+                  u8 *hash);
+int ikev2_prf_plus(int alg, const u8 *key, size_t key_len,
+                  const u8 *data, size_t data_len,
+                  u8 *out, size_t out_len);
+const struct ikev2_encr_alg * ikev2_get_encr(int id);
+int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+                      const u8 *plain, u8 *crypt, size_t len);
+int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
+                      const u8 *crypt, u8 *plain, size_t len);
+
+int ikev2_derive_auth_data(int prf_alg, const struct wpabuf *sign_msg,
+                          const u8 *ID, size_t ID_len, u8 ID_type,
+                          struct ikev2_keys *keys, int initiator,
+                          const u8 *shared_secret, size_t shared_secret_len,
+                          const u8 *nonce, size_t nonce_len,
+                          const u8 *key_pad, size_t key_pad_len,
+                          u8 *auth_data);
+
+
+struct ikev2_payloads {
+       const u8 *sa;
+       size_t sa_len;
+       const u8 *ke;
+       size_t ke_len;
+       const u8 *idi;
+       size_t idi_len;
+       const u8 *idr;
+       size_t idr_len;
+       const u8 *cert;
+       size_t cert_len;
+       const u8 *auth;
+       size_t auth_len;
+       const u8 *nonce;
+       size_t nonce_len;
+       const u8 *encrypted;
+       size_t encrypted_len;
+       u8 encr_next_payload;
+       const u8 *notification;
+       size_t notification_len;
+};
+
+int ikev2_parse_payloads(struct ikev2_payloads *payloads,
+                        u8 next_payload, const u8 *pos, const u8 *end);
+
+u8 * ikev2_decrypt_payload(int encr_id, int integ_id, struct ikev2_keys *keys,
+                          int initiator, const struct ikev2_hdr *hdr,
+                          const u8 *encrypted, size_t encrypted_len,
+                          size_t *res_len);
+void ikev2_update_hdr(struct wpabuf *msg);
+int ikev2_build_encrypted(int encr_id, int integ_id, struct ikev2_keys *keys,
+                         int initiator, struct wpabuf *msg,
+                         struct wpabuf *plain, u8 next_payload);
+int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
+                        const struct ikev2_integ_alg *integ,
+                        const struct ikev2_encr_alg *encr,
+                        const u8 *skeyseed, const u8 *data, size_t data_len,
+                        struct ikev2_keys *keys);
+
+#endif /* IKEV2_COMMON_H */
diff --git a/src/eap_peer/Makefile b/src/eap_peer/Makefile
new file mode 100644 (file)
index 0000000..3651056
--- /dev/null
@@ -0,0 +1,11 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.so *.d
+
+install:
+       if ls *.so >/dev/null 2>&1; then \
+               install -d $(DESTDIR)$(LIBDIR)/wpa_supplicant && \
+               cp *.so $(DESTDIR)$(LIBDIR)/wpa_supplicant \
+       ; fi
diff --git a/src/eap_peer/eap.c b/src/eap_peer/eap.c
new file mode 100644 (file)
index 0000000..b9f186b
--- /dev/null
@@ -0,0 +1,2140 @@
+/*
+ * EAP peer state machines (RFC 4137)
+ * Copyright (c) 2004-2010, 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.
+ *
+ * This file implements the Peer State Machine as defined in RFC 4137. The used
+ * states and state transitions match mostly with the RFC. However, there are
+ * couple of additional transitions for working around small issues noticed
+ * during testing. These exceptions are explained in comments within the
+ * functions in this file. The method functions, m.func(), are similar to the
+ * ones used in RFC 4137, but some small changes have used here to optimize
+ * operations and to add functionality needed for fast re-authentication
+ * (session resumption).
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "pcsc_funcs.h"
+#include "state_machine.h"
+#include "crypto/crypto.h"
+#include "crypto/tls.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+                                 EapType method);
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id);
+static void eap_sm_processIdentity(struct eap_sm *sm,
+                                  const struct wpabuf *req);
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req);
+static struct wpabuf * eap_sm_buildNotify(int id);
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req);
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state);
+static const char * eap_sm_decision_txt(EapDecision decision);
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+
+static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var)
+{
+       return sm->eapol_cb->get_bool(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_bool(struct eap_sm *sm, enum eapol_bool_var var,
+                          Boolean value)
+{
+       sm->eapol_cb->set_bool(sm->eapol_ctx, var, value);
+}
+
+
+static unsigned int eapol_get_int(struct eap_sm *sm, enum eapol_int_var var)
+{
+       return sm->eapol_cb->get_int(sm->eapol_ctx, var);
+}
+
+
+static void eapol_set_int(struct eap_sm *sm, enum eapol_int_var var,
+                         unsigned int value)
+{
+       sm->eapol_cb->set_int(sm->eapol_ctx, var, value);
+}
+
+
+static struct wpabuf * eapol_get_eapReqData(struct eap_sm *sm)
+{
+       return sm->eapol_cb->get_eapReqData(sm->eapol_ctx);
+}
+
+
+static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
+{
+       if (sm->m == NULL || sm->eap_method_priv == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP: deinitialize previously used EAP method "
+                  "(%d, %s) at %s", sm->selectedMethod, sm->m->name, txt);
+       sm->m->deinit(sm, sm->eap_method_priv);
+       sm->eap_method_priv = NULL;
+       sm->m = NULL;
+}
+
+
+/**
+ * eap_allowed_method - Check whether EAP method is allowed
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @vendor: Vendor-Id for expanded types or 0 = IETF for legacy types
+ * @method: EAP type
+ * Returns: 1 = allowed EAP method, 0 = not allowed
+ */
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       int i;
+       struct eap_method_type *m;
+
+       if (config == NULL || config->eap_methods == NULL)
+               return 1;
+
+       m = config->eap_methods;
+       for (i = 0; m[i].vendor != EAP_VENDOR_IETF ||
+                    m[i].method != EAP_TYPE_NONE; i++) {
+               if (m[i].vendor == vendor && m[i].method == method)
+                       return 1;
+       }
+       return 0;
+}
+
+
+/*
+ * This state initializes state machine variables when the machine is
+ * activated (portEnabled = TRUE). This is also used when re-starting
+ * authentication (eapRestart == TRUE).
+ */
+SM_STATE(EAP, INITIALIZE)
+{
+       SM_ENTRY(EAP, INITIALIZE);
+       if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
+           sm->m->has_reauth_data(sm, sm->eap_method_priv) &&
+           !sm->prev_failure) {
+               wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
+                          "fast reauthentication");
+               sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
+       } else {
+               eap_deinit_prev_method(sm, "INITIALIZE");
+       }
+       sm->selectedMethod = EAP_TYPE_NONE;
+       sm->methodState = METHOD_NONE;
+       sm->allowNotifications = TRUE;
+       sm->decision = DECISION_FAIL;
+       eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+       eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+       eapol_set_bool(sm, EAPOL_eapFail, FALSE);
+       os_free(sm->eapKeyData);
+       sm->eapKeyData = NULL;
+       sm->eapKeyAvailable = FALSE;
+       eapol_set_bool(sm, EAPOL_eapRestart, FALSE);
+       sm->lastId = -1; /* new session - make sure this does not match with
+                         * the first EAP-Packet */
+       /*
+        * RFC 4137 does not reset eapResp and eapNoResp here. However, this
+        * seemed to be able to trigger cases where both were set and if EAPOL
+        * state machine uses eapNoResp first, it may end up not sending a real
+        * reply correctly. This occurred when the workaround in FAIL state set
+        * eapNoResp = TRUE.. Maybe that workaround needs to be fixed to do
+        * something else(?)
+        */
+       eapol_set_bool(sm, EAPOL_eapResp, FALSE);
+       eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
+       sm->num_rounds = 0;
+       sm->prev_failure = 0;
+}
+
+
+/*
+ * This state is reached whenever service from the lower layer is interrupted
+ * or unavailable (portEnabled == FALSE). Immediate transition to INITIALIZE
+ * occurs when the port becomes enabled.
+ */
+SM_STATE(EAP, DISABLED)
+{
+       SM_ENTRY(EAP, DISABLED);
+       sm->num_rounds = 0;
+}
+
+
+/*
+ * The state machine spends most of its time here, waiting for something to
+ * happen. This state is entered unconditionally from INITIALIZE, DISCARD, and
+ * SEND_RESPONSE states.
+ */
+SM_STATE(EAP, IDLE)
+{
+       SM_ENTRY(EAP, IDLE);
+}
+
+
+/*
+ * This state is entered when an EAP packet is received (eapReq == TRUE) to
+ * parse the packet header.
+ */
+SM_STATE(EAP, RECEIVED)
+{
+       const struct wpabuf *eapReqData;
+
+       SM_ENTRY(EAP, RECEIVED);
+       eapReqData = eapol_get_eapReqData(sm);
+       /* parse rxReq, rxSuccess, rxFailure, reqId, reqMethod */
+       eap_sm_parseEapReq(sm, eapReqData);
+       sm->num_rounds++;
+}
+
+
+/*
+ * This state is entered when a request for a new type comes in. Either the
+ * correct method is started, or a Nak response is built.
+ */
+SM_STATE(EAP, GET_METHOD)
+{
+       int reinit;
+       EapType method;
+
+       SM_ENTRY(EAP, GET_METHOD);
+
+       if (sm->reqMethod == EAP_TYPE_EXPANDED)
+               method = sm->reqVendorMethod;
+       else
+               method = sm->reqMethod;
+
+       if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) {
+               wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed",
+                          sm->reqVendor, method);
+               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+                       "vendor=%u method=%u -> NAK",
+                       sm->reqVendor, method);
+               goto nak;
+       }
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+               "vendor=%u method=%u", sm->reqVendor, method);
+
+       /*
+        * RFC 4137 does not define specific operation for fast
+        * re-authentication (session resumption). The design here is to allow
+        * the previously used method data to be maintained for
+        * re-authentication if the method support session resumption.
+        * Otherwise, the previously used method data is freed and a new method
+        * is allocated here.
+        */
+       if (sm->fast_reauth &&
+           sm->m && sm->m->vendor == sm->reqVendor &&
+           sm->m->method == method &&
+           sm->m->has_reauth_data &&
+           sm->m->has_reauth_data(sm, sm->eap_method_priv)) {
+               wpa_printf(MSG_DEBUG, "EAP: Using previous method data"
+                          " for fast re-authentication");
+               reinit = 1;
+       } else {
+               eap_deinit_prev_method(sm, "GET_METHOD");
+               reinit = 0;
+       }
+
+       sm->selectedMethod = sm->reqMethod;
+       if (sm->m == NULL)
+               sm->m = eap_peer_get_eap_method(sm->reqVendor, method);
+       if (!sm->m) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not find selected method: "
+                          "vendor %d method %d",
+                          sm->reqVendor, method);
+               goto nak;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP: Initialize selected EAP method: "
+                  "vendor %u method %u (%s)",
+                  sm->reqVendor, method, sm->m->name);
+       if (reinit)
+               sm->eap_method_priv = sm->m->init_for_reauth(
+                       sm, sm->eap_method_priv);
+       else
+               sm->eap_method_priv = sm->m->init(sm);
+
+       if (sm->eap_method_priv == NULL) {
+               struct eap_peer_config *config = eap_get_config(sm);
+               wpa_msg(sm->msg_ctx, MSG_INFO,
+                       "EAP: Failed to initialize EAP method: vendor %u "
+                       "method %u (%s)",
+                       sm->reqVendor, method, sm->m->name);
+               sm->m = NULL;
+               sm->methodState = METHOD_NONE;
+               sm->selectedMethod = EAP_TYPE_NONE;
+               if (sm->reqMethod == EAP_TYPE_TLS && config &&
+                   (config->pending_req_pin ||
+                    config->pending_req_passphrase)) {
+                       /*
+                        * Return without generating Nak in order to allow
+                        * entering of PIN code or passphrase to retry the
+                        * current EAP packet.
+                        */
+                       wpa_printf(MSG_DEBUG, "EAP: Pending PIN/passphrase "
+                                  "request - skip Nak");
+                       return;
+               }
+
+               goto nak;
+       }
+
+       sm->methodState = METHOD_INIT;
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_METHOD
+               "EAP vendor %u method %u (%s) selected",
+               sm->reqVendor, method, sm->m->name);
+       return;
+
+nak:
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = NULL;
+       sm->eapRespData = eap_sm_buildNak(sm, sm->reqId);
+}
+
+
+/*
+ * The method processing happens here. The request from the authenticator is
+ * processed, and an appropriate response packet is built.
+ */
+SM_STATE(EAP, METHOD)
+{
+       struct wpabuf *eapReqData;
+       struct eap_method_ret ret;
+
+       SM_ENTRY(EAP, METHOD);
+       if (sm->m == NULL) {
+               wpa_printf(MSG_WARNING, "EAP::METHOD - method not selected");
+               return;
+       }
+
+       eapReqData = eapol_get_eapReqData(sm);
+
+       /*
+        * Get ignore, methodState, decision, allowNotifications, and
+        * eapRespData. RFC 4137 uses three separate method procedure (check,
+        * process, and buildResp) in this state. These have been combined into
+        * a single function call to m->process() in order to optimize EAP
+        * method implementation interface a bit. These procedures are only
+        * used from within this METHOD state, so there is no need to keep
+        * these as separate C functions.
+        *
+        * The RFC 4137 procedures return values as follows:
+        * ignore = m.check(eapReqData)
+        * (methodState, decision, allowNotifications) = m.process(eapReqData)
+        * eapRespData = m.buildResp(reqId)
+        */
+       os_memset(&ret, 0, sizeof(ret));
+       ret.ignore = sm->ignore;
+       ret.methodState = sm->methodState;
+       ret.decision = sm->decision;
+       ret.allowNotifications = sm->allowNotifications;
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = NULL;
+       sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
+                                        eapReqData);
+       wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
+                  "methodState=%s decision=%s",
+                  ret.ignore ? "TRUE" : "FALSE",
+                  eap_sm_method_state_txt(ret.methodState),
+                  eap_sm_decision_txt(ret.decision));
+
+       sm->ignore = ret.ignore;
+       if (sm->ignore)
+               return;
+       sm->methodState = ret.methodState;
+       sm->decision = ret.decision;
+       sm->allowNotifications = ret.allowNotifications;
+
+       if (sm->m->isKeyAvailable && sm->m->getKey &&
+           sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
+               os_free(sm->eapKeyData);
+               sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
+                                              &sm->eapKeyDataLen);
+       }
+}
+
+
+/*
+ * This state signals the lower layer that a response packet is ready to be
+ * sent.
+ */
+SM_STATE(EAP, SEND_RESPONSE)
+{
+       SM_ENTRY(EAP, SEND_RESPONSE);
+       wpabuf_free(sm->lastRespData);
+       if (sm->eapRespData) {
+               if (sm->workaround)
+                       os_memcpy(sm->last_md5, sm->req_md5, 16);
+               sm->lastId = sm->reqId;
+               sm->lastRespData = wpabuf_dup(sm->eapRespData);
+               eapol_set_bool(sm, EAPOL_eapResp, TRUE);
+       } else
+               sm->lastRespData = NULL;
+       eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+       eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+}
+
+
+/*
+ * This state signals the lower layer that the request was discarded, and no
+ * response packet will be sent at this time.
+ */
+SM_STATE(EAP, DISCARD)
+{
+       SM_ENTRY(EAP, DISCARD);
+       eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+       eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+}
+
+
+/*
+ * Handles requests for Identity method and builds a response.
+ */
+SM_STATE(EAP, IDENTITY)
+{
+       const struct wpabuf *eapReqData;
+
+       SM_ENTRY(EAP, IDENTITY);
+       eapReqData = eapol_get_eapReqData(sm);
+       eap_sm_processIdentity(sm, eapReqData);
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = NULL;
+       sm->eapRespData = eap_sm_buildIdentity(sm, sm->reqId, 0);
+}
+
+
+/*
+ * Handles requests for Notification method and builds a response.
+ */
+SM_STATE(EAP, NOTIFICATION)
+{
+       const struct wpabuf *eapReqData;
+
+       SM_ENTRY(EAP, NOTIFICATION);
+       eapReqData = eapol_get_eapReqData(sm);
+       eap_sm_processNotify(sm, eapReqData);
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = NULL;
+       sm->eapRespData = eap_sm_buildNotify(sm->reqId);
+}
+
+
+/*
+ * This state retransmits the previous response packet.
+ */
+SM_STATE(EAP, RETRANSMIT)
+{
+       SM_ENTRY(EAP, RETRANSMIT);
+       wpabuf_free(sm->eapRespData);
+       if (sm->lastRespData)
+               sm->eapRespData = wpabuf_dup(sm->lastRespData);
+       else
+               sm->eapRespData = NULL;
+}
+
+
+/*
+ * This state is entered in case of a successful completion of authentication
+ * and state machine waits here until port is disabled or EAP authentication is
+ * restarted.
+ */
+SM_STATE(EAP, SUCCESS)
+{
+       SM_ENTRY(EAP, SUCCESS);
+       if (sm->eapKeyData != NULL)
+               sm->eapKeyAvailable = TRUE;
+       eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+
+       /*
+        * RFC 4137 does not clear eapReq here, but this seems to be required
+        * to avoid processing the same request twice when state machine is
+        * initialized.
+        */
+       eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+       /*
+        * RFC 4137 does not set eapNoResp here, but this seems to be required
+        * to get EAPOL Supplicant backend state machine into SUCCESS state. In
+        * addition, either eapResp or eapNoResp is required to be set after
+        * processing the received EAP frame.
+        */
+       eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+               "EAP authentication completed successfully");
+}
+
+
+/*
+ * This state is entered in case of a failure and state machine waits here
+ * until port is disabled or EAP authentication is restarted.
+ */
+SM_STATE(EAP, FAILURE)
+{
+       SM_ENTRY(EAP, FAILURE);
+       eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+
+       /*
+        * RFC 4137 does not clear eapReq here, but this seems to be required
+        * to avoid processing the same request twice when state machine is
+        * initialized.
+        */
+       eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+
+       /*
+        * RFC 4137 does not set eapNoResp here. However, either eapResp or
+        * eapNoResp is required to be set after processing the received EAP
+        * frame.
+        */
+       eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+               "EAP authentication failed");
+
+       sm->prev_failure = 1;
+}
+
+
+static int eap_success_workaround(struct eap_sm *sm, int reqId, int lastId)
+{
+       /*
+        * At least Microsoft IAS and Meetinghouse Aegis seem to be sending
+        * EAP-Success/Failure with lastId + 1 even though RFC 3748 and
+        * RFC 4137 require that reqId == lastId. In addition, it looks like
+        * Ringmaster v2.1.2.0 would be using lastId + 2 in EAP-Success.
+        *
+        * Accept this kind of Id if EAP workarounds are enabled. These are
+        * unauthenticated plaintext messages, so this should have minimal
+        * security implications (bit easier to fake EAP-Success/Failure).
+        */
+       if (sm->workaround && (reqId == ((lastId + 1) & 0xff) ||
+                              reqId == ((lastId + 2) & 0xff))) {
+               wpa_printf(MSG_DEBUG, "EAP: Workaround for unexpected "
+                          "identifier field in EAP Success: "
+                          "reqId=%d lastId=%d (these are supposed to be "
+                          "same)", reqId, lastId);
+               return 1;
+       }
+       wpa_printf(MSG_DEBUG, "EAP: EAP-Success Id mismatch - reqId=%d "
+                  "lastId=%d", reqId, lastId);
+       return 0;
+}
+
+
+/*
+ * RFC 4137 - Appendix A.1: EAP Peer State Machine - State transitions
+ */
+
+static void eap_peer_sm_step_idle(struct eap_sm *sm)
+{
+       /*
+        * The first three transitions are from RFC 4137. The last two are
+        * local additions to handle special cases with LEAP and PEAP server
+        * not sending EAP-Success in some cases.
+        */
+       if (eapol_get_bool(sm, EAPOL_eapReq))
+               SM_ENTER(EAP, RECEIVED);
+       else if ((eapol_get_bool(sm, EAPOL_altAccept) &&
+                 sm->decision != DECISION_FAIL) ||
+                (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+                 sm->decision == DECISION_UNCOND_SUCC))
+               SM_ENTER(EAP, SUCCESS);
+       else if (eapol_get_bool(sm, EAPOL_altReject) ||
+                (eapol_get_int(sm, EAPOL_idleWhile) == 0 &&
+                 sm->decision != DECISION_UNCOND_SUCC) ||
+                (eapol_get_bool(sm, EAPOL_altAccept) &&
+                 sm->methodState != METHOD_CONT &&
+                 sm->decision == DECISION_FAIL))
+               SM_ENTER(EAP, FAILURE);
+       else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+                sm->leap_done && sm->decision != DECISION_FAIL &&
+                sm->methodState == METHOD_DONE)
+               SM_ENTER(EAP, SUCCESS);
+       else if (sm->selectedMethod == EAP_TYPE_PEAP &&
+                sm->peap_done && sm->decision != DECISION_FAIL &&
+                sm->methodState == METHOD_DONE)
+               SM_ENTER(EAP, SUCCESS);
+}
+
+
+static int eap_peer_req_is_duplicate(struct eap_sm *sm)
+{
+       int duplicate;
+
+       duplicate = (sm->reqId == sm->lastId) && sm->rxReq;
+       if (sm->workaround && duplicate &&
+           os_memcmp(sm->req_md5, sm->last_md5, 16) != 0) {
+               /*
+                * RFC 4137 uses (reqId == lastId) as the only verification for
+                * duplicate EAP requests. However, this misses cases where the
+                * AS is incorrectly using the same id again; and
+                * unfortunately, such implementations exist. Use MD5 hash as
+                * an extra verification for the packets being duplicate to
+                * workaround these issues.
+                */
+               wpa_printf(MSG_DEBUG, "EAP: AS used the same Id again, but "
+                          "EAP packets were not identical");
+               wpa_printf(MSG_DEBUG, "EAP: workaround - assume this is not a "
+                          "duplicate packet");
+               duplicate = 0;
+       }
+
+       return duplicate;
+}
+
+
+static void eap_peer_sm_step_received(struct eap_sm *sm)
+{
+       int duplicate = eap_peer_req_is_duplicate(sm);
+
+       /*
+        * Two special cases below for LEAP are local additions to work around
+        * odd LEAP behavior (EAP-Success in the middle of authentication and
+        * then swapped roles). Other transitions are based on RFC 4137.
+        */
+       if (sm->rxSuccess && sm->decision != DECISION_FAIL &&
+           (sm->reqId == sm->lastId ||
+            eap_success_workaround(sm, sm->reqId, sm->lastId)))
+               SM_ENTER(EAP, SUCCESS);
+       else if (sm->methodState != METHOD_CONT &&
+                ((sm->rxFailure &&
+                  sm->decision != DECISION_UNCOND_SUCC) ||
+                 (sm->rxSuccess && sm->decision == DECISION_FAIL &&
+                  (sm->selectedMethod != EAP_TYPE_LEAP ||
+                   sm->methodState != METHOD_MAY_CONT))) &&
+                (sm->reqId == sm->lastId ||
+                 eap_success_workaround(sm, sm->reqId, sm->lastId)))
+               SM_ENTER(EAP, FAILURE);
+       else if (sm->rxReq && duplicate)
+               SM_ENTER(EAP, RETRANSMIT);
+       else if (sm->rxReq && !duplicate &&
+                sm->reqMethod == EAP_TYPE_NOTIFICATION &&
+                sm->allowNotifications)
+               SM_ENTER(EAP, NOTIFICATION);
+       else if (sm->rxReq && !duplicate &&
+                sm->selectedMethod == EAP_TYPE_NONE &&
+                sm->reqMethod == EAP_TYPE_IDENTITY)
+               SM_ENTER(EAP, IDENTITY);
+       else if (sm->rxReq && !duplicate &&
+                sm->selectedMethod == EAP_TYPE_NONE &&
+                sm->reqMethod != EAP_TYPE_IDENTITY &&
+                sm->reqMethod != EAP_TYPE_NOTIFICATION)
+               SM_ENTER(EAP, GET_METHOD);
+       else if (sm->rxReq && !duplicate &&
+                sm->reqMethod == sm->selectedMethod &&
+                sm->methodState != METHOD_DONE)
+               SM_ENTER(EAP, METHOD);
+       else if (sm->selectedMethod == EAP_TYPE_LEAP &&
+                (sm->rxSuccess || sm->rxResp))
+               SM_ENTER(EAP, METHOD);
+       else
+               SM_ENTER(EAP, DISCARD);
+}
+
+
+static void eap_peer_sm_step_local(struct eap_sm *sm)
+{
+       switch (sm->EAP_state) {
+       case EAP_INITIALIZE:
+               SM_ENTER(EAP, IDLE);
+               break;
+       case EAP_DISABLED:
+               if (eapol_get_bool(sm, EAPOL_portEnabled) &&
+                   !sm->force_disabled)
+                       SM_ENTER(EAP, INITIALIZE);
+               break;
+       case EAP_IDLE:
+               eap_peer_sm_step_idle(sm);
+               break;
+       case EAP_RECEIVED:
+               eap_peer_sm_step_received(sm);
+               break;
+       case EAP_GET_METHOD:
+               if (sm->selectedMethod == sm->reqMethod)
+                       SM_ENTER(EAP, METHOD);
+               else
+                       SM_ENTER(EAP, SEND_RESPONSE);
+               break;
+       case EAP_METHOD:
+               if (sm->ignore)
+                       SM_ENTER(EAP, DISCARD);
+               else
+                       SM_ENTER(EAP, SEND_RESPONSE);
+               break;
+       case EAP_SEND_RESPONSE:
+               SM_ENTER(EAP, IDLE);
+               break;
+       case EAP_DISCARD:
+               SM_ENTER(EAP, IDLE);
+               break;
+       case EAP_IDENTITY:
+               SM_ENTER(EAP, SEND_RESPONSE);
+               break;
+       case EAP_NOTIFICATION:
+               SM_ENTER(EAP, SEND_RESPONSE);
+               break;
+       case EAP_RETRANSMIT:
+               SM_ENTER(EAP, SEND_RESPONSE);
+               break;
+       case EAP_SUCCESS:
+               break;
+       case EAP_FAILURE:
+               break;
+       }
+}
+
+
+SM_STEP(EAP)
+{
+       /* Global transitions */
+       if (eapol_get_bool(sm, EAPOL_eapRestart) &&
+           eapol_get_bool(sm, EAPOL_portEnabled))
+               SM_ENTER_GLOBAL(EAP, INITIALIZE);
+       else if (!eapol_get_bool(sm, EAPOL_portEnabled) || sm->force_disabled)
+               SM_ENTER_GLOBAL(EAP, DISABLED);
+       else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+               /* RFC 4137 does not place any limit on number of EAP messages
+                * in an authentication session. However, some error cases have
+                * ended up in a state were EAP messages were sent between the
+                * peer and server in a loop (e.g., TLS ACK frame in both
+                * direction). Since this is quite undesired outcome, limit the
+                * total number of EAP round-trips and abort authentication if
+                * this limit is exceeded.
+                */
+               if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+                       wpa_msg(sm->msg_ctx, MSG_INFO, "EAP: more than %d "
+                               "authentication rounds - abort",
+                               EAP_MAX_AUTH_ROUNDS);
+                       sm->num_rounds++;
+                       SM_ENTER_GLOBAL(EAP, FAILURE);
+               }
+       } else {
+               /* Local transitions */
+               eap_peer_sm_step_local(sm);
+       }
+}
+
+
+static Boolean eap_sm_allowMethod(struct eap_sm *sm, int vendor,
+                                 EapType method)
+{
+       if (!eap_allowed_method(sm, vendor, method)) {
+               wpa_printf(MSG_DEBUG, "EAP: configuration does not allow: "
+                          "vendor %u method %u", vendor, method);
+               return FALSE;
+       }
+       if (eap_peer_get_eap_method(vendor, method))
+               return TRUE;
+       wpa_printf(MSG_DEBUG, "EAP: not included in build: "
+                  "vendor %u method %u", vendor, method);
+       return FALSE;
+}
+
+
+static struct wpabuf * eap_sm_build_expanded_nak(
+       struct eap_sm *sm, int id, const struct eap_method *methods,
+       size_t count)
+{
+       struct wpabuf *resp;
+       int found = 0;
+       const struct eap_method *m;
+
+       wpa_printf(MSG_DEBUG, "EAP: Building expanded EAP-Nak");
+
+       /* RFC 3748 - 5.3.2: Expanded Nak */
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EXPANDED,
+                            8 + 8 * (count + 1), EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+       wpabuf_put_be32(resp, EAP_TYPE_NAK);
+
+       for (m = methods; m; m = m->next) {
+               if (sm->reqVendor == m->vendor &&
+                   sm->reqVendorMethod == m->method)
+                       continue; /* do not allow the current method again */
+               if (eap_allowed_method(sm, m->vendor, m->method)) {
+                       wpa_printf(MSG_DEBUG, "EAP: allowed type: "
+                                  "vendor=%u method=%u",
+                                  m->vendor, m->method);
+                       wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+                       wpabuf_put_be24(resp, m->vendor);
+                       wpabuf_put_be32(resp, m->method);
+
+                       found++;
+               }
+       }
+       if (!found) {
+               wpa_printf(MSG_DEBUG, "EAP: no more allowed methods");
+               wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+               wpabuf_put_be24(resp, EAP_VENDOR_IETF);
+               wpabuf_put_be32(resp, EAP_TYPE_NONE);
+       }
+
+       eap_update_len(resp);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_sm_buildNak(struct eap_sm *sm, int id)
+{
+       struct wpabuf *resp;
+       u8 *start;
+       int found = 0, expanded_found = 0;
+       size_t count;
+       const struct eap_method *methods, *m;
+
+       wpa_printf(MSG_DEBUG, "EAP: Building EAP-Nak (requested type %u "
+                  "vendor=%u method=%u not allowed)", sm->reqMethod,
+                  sm->reqVendor, sm->reqVendorMethod);
+       methods = eap_peer_get_methods(&count);
+       if (methods == NULL)
+               return NULL;
+       if (sm->reqMethod == EAP_TYPE_EXPANDED)
+               return eap_sm_build_expanded_nak(sm, id, methods, count);
+
+       /* RFC 3748 - 5.3.1: Legacy Nak */
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK,
+                            sizeof(struct eap_hdr) + 1 + count + 1,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       start = wpabuf_put(resp, 0);
+       for (m = methods; m; m = m->next) {
+               if (m->vendor == EAP_VENDOR_IETF && m->method == sm->reqMethod)
+                       continue; /* do not allow the current method again */
+               if (eap_allowed_method(sm, m->vendor, m->method)) {
+                       if (m->vendor != EAP_VENDOR_IETF) {
+                               if (expanded_found)
+                                       continue;
+                               expanded_found = 1;
+                               wpabuf_put_u8(resp, EAP_TYPE_EXPANDED);
+                       } else
+                               wpabuf_put_u8(resp, m->method);
+                       found++;
+               }
+       }
+       if (!found)
+               wpabuf_put_u8(resp, EAP_TYPE_NONE);
+       wpa_hexdump(MSG_DEBUG, "EAP: allowed methods", start, found);
+
+       eap_update_len(resp);
+
+       return resp;
+}
+
+
+static void eap_sm_processIdentity(struct eap_sm *sm, const struct wpabuf *req)
+{
+       const struct eap_hdr *hdr = wpabuf_head(req);
+       const u8 *pos = (const u8 *) (hdr + 1);
+       pos++;
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+               "EAP authentication started");
+
+       /*
+        * RFC 3748 - 5.1: Identity
+        * Data field may contain a displayable message in UTF-8. If this
+        * includes NUL-character, only the data before that should be
+        * displayed. Some EAP implementasitons may piggy-back additional
+        * options after the NUL.
+        */
+       /* TODO: could save displayable message so that it can be shown to the
+        * user in case of interaction is required */
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Identity data",
+                         pos, be_to_host16(hdr->length) - 5);
+}
+
+
+#ifdef PCSC_FUNCS
+static int eap_sm_imsi_identity(struct eap_sm *sm,
+                               struct eap_peer_config *conf)
+{
+       int aka = 0;
+       char imsi[100];
+       size_t imsi_len;
+       struct eap_method_type *m = conf->eap_methods;
+       int i;
+
+       imsi_len = sizeof(imsi);
+       if (scard_get_imsi(sm->scard_ctx, imsi, &imsi_len)) {
+               wpa_printf(MSG_WARNING, "Failed to get IMSI from SIM");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "IMSI", (u8 *) imsi, imsi_len);
+
+       for (i = 0; m && (m[i].vendor != EAP_VENDOR_IETF ||
+                         m[i].method != EAP_TYPE_NONE); i++) {
+               if (m[i].vendor == EAP_VENDOR_IETF &&
+                   m[i].method == EAP_TYPE_AKA) {
+                       aka = 1;
+                       break;
+               }
+       }
+
+       os_free(conf->identity);
+       conf->identity = os_malloc(1 + imsi_len);
+       if (conf->identity == NULL) {
+               wpa_printf(MSG_WARNING, "Failed to allocate buffer for "
+                          "IMSI-based identity");
+               return -1;
+       }
+
+       conf->identity[0] = aka ? '0' : '1';
+       os_memcpy(conf->identity + 1, imsi, imsi_len);
+       conf->identity_len = 1 + imsi_len;
+
+       return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
+static int eap_sm_set_scard_pin(struct eap_sm *sm,
+                               struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+       if (scard_set_pin(sm->scard_ctx, conf->pin)) {
+               /*
+                * Make sure the same PIN is not tried again in order to avoid
+                * blocking SIM.
+                */
+               os_free(conf->pin);
+               conf->pin = NULL;
+
+               wpa_printf(MSG_WARNING, "PIN validation failed");
+               eap_sm_request_pin(sm);
+               return -1;
+       }
+       return 0;
+#else /* PCSC_FUNCS */
+       return -1;
+#endif /* PCSC_FUNCS */
+}
+
+static int eap_sm_get_scard_identity(struct eap_sm *sm,
+                                    struct eap_peer_config *conf)
+{
+#ifdef PCSC_FUNCS
+       if (eap_sm_set_scard_pin(sm, conf))
+               return -1;
+
+       return eap_sm_imsi_identity(sm, conf);
+#else /* PCSC_FUNCS */
+       return -1;
+#endif /* PCSC_FUNCS */
+}
+
+
+/**
+ * eap_sm_buildIdentity - Build EAP-Identity/Response for the current network
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @id: EAP identifier for the packet
+ * @encrypted: Whether the packet is for encrypted tunnel (EAP phase 2)
+ * Returns: Pointer to the allocated EAP-Identity/Response packet or %NULL on
+ * failure
+ *
+ * This function allocates and builds an EAP-Identity/Response packet for the
+ * current network. The caller is responsible for freeing the returned data.
+ */
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       struct wpabuf *resp;
+       const u8 *identity;
+       size_t identity_len;
+
+       if (config == NULL) {
+               wpa_printf(MSG_WARNING, "EAP: buildIdentity: configuration "
+                          "was not available");
+               return NULL;
+       }
+
+       if (sm->m && sm->m->get_identity &&
+           (identity = sm->m->get_identity(sm, sm->eap_method_priv,
+                                           &identity_len)) != NULL) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP: using method re-auth "
+                                 "identity", identity, identity_len);
+       } else if (!encrypted && config->anonymous_identity) {
+               identity = config->anonymous_identity;
+               identity_len = config->anonymous_identity_len;
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP: using anonymous identity",
+                                 identity, identity_len);
+       } else {
+               identity = config->identity;
+               identity_len = config->identity_len;
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP: using real identity",
+                                 identity, identity_len);
+       }
+
+       if (identity == NULL) {
+               wpa_printf(MSG_WARNING, "EAP: buildIdentity: identity "
+                          "configuration was not available");
+               if (config->pcsc) {
+                       if (eap_sm_get_scard_identity(sm, config) < 0)
+                               return NULL;
+                       identity = config->identity;
+                       identity_len = config->identity_len;
+                       wpa_hexdump_ascii(MSG_DEBUG, "permanent identity from "
+                                         "IMSI", identity, identity_len);
+               } else {
+                       eap_sm_request_identity(sm);
+                       return NULL;
+               }
+       } else if (config->pcsc) {
+               if (eap_sm_set_scard_pin(sm, config) < 0)
+                       return NULL;
+       }
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, identity_len,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_data(resp, identity, identity_len);
+
+       return resp;
+}
+
+
+static void eap_sm_processNotify(struct eap_sm *sm, const struct wpabuf *req)
+{
+       const u8 *pos;
+       char *msg;
+       size_t i, msg_len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, req,
+                              &msg_len);
+       if (pos == NULL)
+               return;
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Request Notification data",
+                         pos, msg_len);
+
+       msg = os_malloc(msg_len + 1);
+       if (msg == NULL)
+               return;
+       for (i = 0; i < msg_len; i++)
+               msg[i] = isprint(pos[i]) ? (char) pos[i] : '_';
+       msg[msg_len] = '\0';
+       wpa_msg(sm->msg_ctx, MSG_INFO, "%s%s",
+               WPA_EVENT_EAP_NOTIFICATION, msg);
+       os_free(msg);
+}
+
+
+static struct wpabuf * eap_sm_buildNotify(int id)
+{
+       struct wpabuf *resp;
+
+       wpa_printf(MSG_DEBUG, "EAP: Generating EAP-Response Notification");
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NOTIFICATION, 0,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       return resp;
+}
+
+
+static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
+{
+       const struct eap_hdr *hdr;
+       size_t plen;
+       const u8 *pos;
+
+       sm->rxReq = sm->rxResp = sm->rxSuccess = sm->rxFailure = FALSE;
+       sm->reqId = 0;
+       sm->reqMethod = EAP_TYPE_NONE;
+       sm->reqVendor = EAP_VENDOR_IETF;
+       sm->reqVendorMethod = EAP_TYPE_NONE;
+
+       if (req == NULL || wpabuf_len(req) < sizeof(*hdr))
+               return;
+
+       hdr = wpabuf_head(req);
+       plen = be_to_host16(hdr->length);
+       if (plen > wpabuf_len(req)) {
+               wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+                          "(len=%lu plen=%lu)",
+                          (unsigned long) wpabuf_len(req),
+                          (unsigned long) plen);
+               return;
+       }
+
+       sm->reqId = hdr->identifier;
+
+       if (sm->workaround) {
+               const u8 *addr[1];
+               addr[0] = wpabuf_head(req);
+               md5_vector(1, addr, &plen, sm->req_md5);
+       }
+
+       switch (hdr->code) {
+       case EAP_CODE_REQUEST:
+               if (plen < sizeof(*hdr) + 1) {
+                       wpa_printf(MSG_DEBUG, "EAP: Too short EAP-Request - "
+                                  "no Type field");
+                       return;
+               }
+               sm->rxReq = TRUE;
+               pos = (const u8 *) (hdr + 1);
+               sm->reqMethod = *pos++;
+               if (sm->reqMethod == EAP_TYPE_EXPANDED) {
+                       if (plen < sizeof(*hdr) + 8) {
+                               wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+                                          "expanded EAP-Packet (plen=%lu)",
+                                          (unsigned long) plen);
+                               return;
+                       }
+                       sm->reqVendor = WPA_GET_BE24(pos);
+                       pos += 3;
+                       sm->reqVendorMethod = WPA_GET_BE32(pos);
+               }
+               wpa_printf(MSG_DEBUG, "EAP: Received EAP-Request id=%d "
+                          "method=%u vendor=%u vendorMethod=%u",
+                          sm->reqId, sm->reqMethod, sm->reqVendor,
+                          sm->reqVendorMethod);
+               break;
+       case EAP_CODE_RESPONSE:
+               if (sm->selectedMethod == EAP_TYPE_LEAP) {
+                       /*
+                        * LEAP differs from RFC 4137 by using reversed roles
+                        * for mutual authentication and because of this, we
+                        * need to accept EAP-Response frames if LEAP is used.
+                        */
+                       if (plen < sizeof(*hdr) + 1) {
+                               wpa_printf(MSG_DEBUG, "EAP: Too short "
+                                          "EAP-Response - no Type field");
+                               return;
+                       }
+                       sm->rxResp = TRUE;
+                       pos = (const u8 *) (hdr + 1);
+                       sm->reqMethod = *pos;
+                       wpa_printf(MSG_DEBUG, "EAP: Received EAP-Response for "
+                                  "LEAP method=%d id=%d",
+                                  sm->reqMethod, sm->reqId);
+                       break;
+               }
+               wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Response");
+               break;
+       case EAP_CODE_SUCCESS:
+               wpa_printf(MSG_DEBUG, "EAP: Received EAP-Success");
+               sm->rxSuccess = TRUE;
+               break;
+       case EAP_CODE_FAILURE:
+               wpa_printf(MSG_DEBUG, "EAP: Received EAP-Failure");
+               sm->rxFailure = TRUE;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
+                          "code %d", hdr->code);
+               break;
+       }
+}
+
+
+static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
+                                 union tls_event_data *data)
+{
+       struct eap_sm *sm = ctx;
+       char *hash_hex = NULL;
+       char *cert_hex = NULL;
+
+       switch (ev) {
+       case TLS_CERT_CHAIN_FAILURE:
+               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR
+                       "reason=%d depth=%d subject='%s' err='%s'",
+                       data->cert_fail.reason,
+                       data->cert_fail.depth,
+                       data->cert_fail.subject,
+                       data->cert_fail.reason_txt);
+               break;
+       case TLS_PEER_CERTIFICATE:
+               if (data->peer_cert.hash) {
+                       size_t len = data->peer_cert.hash_len * 2 + 1;
+                       hash_hex = os_malloc(len);
+                       if (hash_hex) {
+                               wpa_snprintf_hex(hash_hex, len,
+                                                data->peer_cert.hash,
+                                                data->peer_cert.hash_len);
+                       }
+               }
+               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
+                       "depth=%d subject='%s'%s%s",
+                       data->peer_cert.depth, data->peer_cert.subject,
+                       hash_hex ? " hash=" : "", hash_hex ? hash_hex : "");
+
+               if (data->peer_cert.cert) {
+                       size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1;
+                       cert_hex = os_malloc(len);
+                       if (cert_hex == NULL)
+                               break;
+                       wpa_snprintf_hex(cert_hex, len,
+                                        wpabuf_head(data->peer_cert.cert),
+                                        wpabuf_len(data->peer_cert.cert));
+                       wpa_msg_ctrl(sm->msg_ctx, MSG_INFO,
+                                    WPA_EVENT_EAP_PEER_CERT
+                                    "depth=%d subject='%s' cert=%s",
+                                    data->peer_cert.depth,
+                                    data->peer_cert.subject,
+                                    cert_hex);
+               }
+               break;
+       }
+
+       os_free(hash_hex);
+       os_free(cert_hex);
+}
+
+
+/**
+ * eap_peer_sm_init - Allocate and initialize EAP peer state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @msg_ctx: Context data for wpa_msg() calls
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine. In addition,
+ * this initializes TLS library for the new EAP state machine. eapol_cb pointer
+ * will be in use until eap_peer_sm_deinit() is used to deinitialize this EAP
+ * state machine. Consequently, the caller must make sure that this data
+ * structure remains alive while the EAP state machine is active.
+ */
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+                                struct eapol_callbacks *eapol_cb,
+                                void *msg_ctx, struct eap_config *conf)
+{
+       struct eap_sm *sm;
+       struct tls_config tlsconf;
+
+       sm = os_zalloc(sizeof(*sm));
+       if (sm == NULL)
+               return NULL;
+       sm->eapol_ctx = eapol_ctx;
+       sm->eapol_cb = eapol_cb;
+       sm->msg_ctx = msg_ctx;
+       sm->ClientTimeout = 60;
+       sm->wps = conf->wps;
+
+       os_memset(&tlsconf, 0, sizeof(tlsconf));
+       tlsconf.opensc_engine_path = conf->opensc_engine_path;
+       tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
+       tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+#ifdef CONFIG_FIPS
+       tlsconf.fips_mode = 1;
+#endif /* CONFIG_FIPS */
+       tlsconf.event_cb = eap_peer_sm_tls_event;
+       tlsconf.cb_ctx = sm;
+       sm->ssl_ctx = tls_init(&tlsconf);
+       if (sm->ssl_ctx == NULL) {
+               wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS "
+                          "context.");
+               os_free(sm);
+               return NULL;
+       }
+
+       return sm;
+}
+
+
+/**
+ * eap_peer_sm_deinit - Deinitialize and free an EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_peer_sm_deinit(struct eap_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       eap_deinit_prev_method(sm, "EAP deinit");
+       eap_sm_abort(sm);
+       tls_deinit(sm->ssl_ctx);
+       os_free(sm);
+}
+
+
+/**
+ * eap_peer_sm_step - Step EAP peer state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_peer_sm_step(struct eap_sm *sm)
+{
+       int res = 0;
+       do {
+               sm->changed = FALSE;
+               SM_STEP_RUN(EAP);
+               if (sm->changed)
+                       res = 1;
+       } while (sm->changed);
+       return res;
+}
+
+
+/**
+ * eap_sm_abort - Abort EAP authentication
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Release system resources that have been allocated for the authentication
+ * session without fully deinitializing the EAP state machine.
+ */
+void eap_sm_abort(struct eap_sm *sm)
+{
+       wpabuf_free(sm->lastRespData);
+       sm->lastRespData = NULL;
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = NULL;
+       os_free(sm->eapKeyData);
+       sm->eapKeyData = NULL;
+
+       /* This is not clearly specified in the EAP statemachines draft, but
+        * it seems necessary to make sure that some of the EAPOL variables get
+        * cleared for the next authentication. */
+       eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char * eap_sm_state_txt(int state)
+{
+       switch (state) {
+       case EAP_INITIALIZE:
+               return "INITIALIZE";
+       case EAP_DISABLED:
+               return "DISABLED";
+       case EAP_IDLE:
+               return "IDLE";
+       case EAP_RECEIVED:
+               return "RECEIVED";
+       case EAP_GET_METHOD:
+               return "GET_METHOD";
+       case EAP_METHOD:
+               return "METHOD";
+       case EAP_SEND_RESPONSE:
+               return "SEND_RESPONSE";
+       case EAP_DISCARD:
+               return "DISCARD";
+       case EAP_IDENTITY:
+               return "IDENTITY";
+       case EAP_NOTIFICATION:
+               return "NOTIFICATION";
+       case EAP_RETRANSMIT:
+               return "RETRANSMIT";
+       case EAP_SUCCESS:
+               return "SUCCESS";
+       case EAP_FAILURE:
+               return "FAILURE";
+       default:
+               return "UNKNOWN";
+       }
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eap_sm_method_state_txt(EapMethodState state)
+{
+       switch (state) {
+       case METHOD_NONE:
+               return "NONE";
+       case METHOD_INIT:
+               return "INIT";
+       case METHOD_CONT:
+               return "CONT";
+       case METHOD_MAY_CONT:
+               return "MAY_CONT";
+       case METHOD_DONE:
+               return "DONE";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+static const char * eap_sm_decision_txt(EapDecision decision)
+{
+       switch (decision) {
+       case DECISION_FAIL:
+               return "FAIL";
+       case DECISION_COND_SUCC:
+               return "COND_SUCC";
+       case DECISION_UNCOND_SUCC:
+               return "UNCOND_SUCC";
+       default:
+               return "UNKNOWN";
+       }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+/**
+ * eap_sm_get_status - Get EAP state machine status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAP state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
+{
+       int len, ret;
+
+       if (sm == NULL)
+               return 0;
+
+       len = os_snprintf(buf, buflen,
+                         "EAP state=%s\n",
+                         eap_sm_state_txt(sm->EAP_state));
+       if (len < 0 || (size_t) len >= buflen)
+               return 0;
+
+       if (sm->selectedMethod != EAP_TYPE_NONE) {
+               const char *name;
+               if (sm->m) {
+                       name = sm->m->name;
+               } else {
+                       const struct eap_method *m =
+                               eap_peer_get_eap_method(EAP_VENDOR_IETF,
+                                                       sm->selectedMethod);
+                       if (m)
+                               name = m->name;
+                       else
+                               name = "?";
+               }
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "selectedMethod=%d (EAP-%s)\n",
+                                 sm->selectedMethod, name);
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+
+               if (sm->m && sm->m->get_status) {
+                       len += sm->m->get_status(sm, sm->eap_method_priv,
+                                                buf + len, buflen - len,
+                                                verbose);
+               }
+       }
+
+       if (verbose) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "reqMethod=%d\n"
+                                 "methodState=%s\n"
+                                 "decision=%s\n"
+                                 "ClientTimeout=%d\n",
+                                 sm->reqMethod,
+                                 eap_sm_method_state_txt(sm->methodState),
+                                 eap_sm_decision_txt(sm->decision),
+                                 sm->ClientTimeout);
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+       }
+
+       return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+typedef enum {
+       TYPE_IDENTITY, TYPE_PASSWORD, TYPE_OTP, TYPE_PIN, TYPE_NEW_PASSWORD,
+       TYPE_PASSPHRASE
+} eap_ctrl_req_type;
+
+static void eap_sm_request(struct eap_sm *sm, eap_ctrl_req_type type,
+                          const char *msg, size_t msglen)
+{
+       struct eap_peer_config *config;
+       char *field, *txt, *tmp;
+
+       if (sm == NULL)
+               return;
+       config = eap_get_config(sm);
+       if (config == NULL)
+               return;
+
+       switch (type) {
+       case TYPE_IDENTITY:
+               field = "IDENTITY";
+               txt = "Identity";
+               config->pending_req_identity++;
+               break;
+       case TYPE_PASSWORD:
+               field = "PASSWORD";
+               txt = "Password";
+               config->pending_req_password++;
+               break;
+       case TYPE_NEW_PASSWORD:
+               field = "NEW_PASSWORD";
+               txt = "New Password";
+               config->pending_req_new_password++;
+               break;
+       case TYPE_PIN:
+               field = "PIN";
+               txt = "PIN";
+               config->pending_req_pin++;
+               break;
+       case TYPE_OTP:
+               field = "OTP";
+               if (msg) {
+                       tmp = os_malloc(msglen + 3);
+                       if (tmp == NULL)
+                               return;
+                       tmp[0] = '[';
+                       os_memcpy(tmp + 1, msg, msglen);
+                       tmp[msglen + 1] = ']';
+                       tmp[msglen + 2] = '\0';
+                       txt = tmp;
+                       os_free(config->pending_req_otp);
+                       config->pending_req_otp = tmp;
+                       config->pending_req_otp_len = msglen + 3;
+               } else {
+                       if (config->pending_req_otp == NULL)
+                               return;
+                       txt = config->pending_req_otp;
+               }
+               break;
+       case TYPE_PASSPHRASE:
+               field = "PASSPHRASE";
+               txt = "Private key passphrase";
+               config->pending_req_passphrase++;
+               break;
+       default:
+               return;
+       }
+
+       if (sm->eapol_cb->eap_param_needed)
+               sm->eapol_cb->eap_param_needed(sm->eapol_ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eap_sm_request(sm, type, msg, msglen) do { } while (0)
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+/**
+ * eap_sm_request_identity - Request identity from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request identity information for the
+ * current network. This is normally called when the identity is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+       eap_sm_request(sm, TYPE_IDENTITY, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_password - Request password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request password information for the
+ * current network. This is normally called when the password is not included
+ * in the network configuration. The request will be sent to monitor programs
+ * through the control interface.
+ */
+void eap_sm_request_password(struct eap_sm *sm)
+{
+       eap_sm_request(sm, TYPE_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_new_password - Request new password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request new password information for
+ * the current network. This is normally called when the EAP method indicates
+ * that the current password has expired and password change is required. The
+ * request will be sent to monitor programs through the control interface.
+ */
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+       eap_sm_request(sm, TYPE_NEW_PASSWORD, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_pin - Request SIM or smart card PIN from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request SIM or smart card PIN
+ * information for the current network. This is normally called when the PIN is
+ * not included in the network configuration. The request will be sent to
+ * monitor programs through the control interface.
+ */
+void eap_sm_request_pin(struct eap_sm *sm)
+{
+       eap_sm_request(sm, TYPE_PIN, NULL, 0);
+}
+
+
+/**
+ * eap_sm_request_otp - Request one time password from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @msg: Message to be displayed to the user when asking for OTP
+ * @msg_len: Length of the user displayable message
+ *
+ * EAP methods can call this function to request open time password (OTP) for
+ * the current network. The request will be sent to monitor programs through
+ * the control interface.
+ */
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len)
+{
+       eap_sm_request(sm, TYPE_OTP, msg, msg_len);
+}
+
+
+/**
+ * eap_sm_request_passphrase - Request passphrase from user (ctrl_iface)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * EAP methods can call this function to request passphrase for a private key
+ * for the current network. This is normally called when the passphrase is not
+ * included in the network configuration. The request will be sent to monitor
+ * programs through the control interface.
+ */
+void eap_sm_request_passphrase(struct eap_sm *sm)
+{
+       eap_sm_request(sm, TYPE_PASSPHRASE, NULL, 0);
+}
+
+
+/**
+ * eap_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (config == NULL)
+               return;
+
+       /* Re-send any pending requests for user data since a new control
+        * interface was added. This handles cases where the EAP authentication
+        * starts immediately after system startup when the user interface is
+        * not yet running. */
+       if (config->pending_req_identity)
+               eap_sm_request_identity(sm);
+       if (config->pending_req_password)
+               eap_sm_request_password(sm);
+       if (config->pending_req_new_password)
+               eap_sm_request_new_password(sm);
+       if (config->pending_req_otp)
+               eap_sm_request_otp(sm, NULL, 0);
+       if (config->pending_req_pin)
+               eap_sm_request_pin(sm);
+       if (config->pending_req_passphrase)
+               eap_sm_request_passphrase(sm);
+}
+
+
+static int eap_allowed_phase2_type(int vendor, int type)
+{
+       if (vendor != EAP_VENDOR_IETF)
+               return 0;
+       return type != EAP_TYPE_PEAP && type != EAP_TYPE_TTLS &&
+               type != EAP_TYPE_FAST;
+}
+
+
+/**
+ * eap_get_phase2_type - Get EAP type for the given EAP phase 2 method name
+ * @name: EAP method name, e.g., MD5
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers that are allowed for
+ * Phase 2, i.e., for tunneled authentication. Phase 2 is used, e.g., with
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+u32 eap_get_phase2_type(const char *name, int *vendor)
+{
+       int v;
+       u8 type = eap_peer_get_type(name, &v);
+       if (eap_allowed_phase2_type(v, type)) {
+               *vendor = v;
+               return type;
+       }
+       *vendor = EAP_VENDOR_IETF;
+       return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_phase2_types - Get list of allowed EAP phase 2 types
+ * @config: Pointer to a network configuration
+ * @count: Pointer to a variable to be filled with number of returned EAP types
+ * Returns: Pointer to allocated type list or %NULL on failure
+ *
+ * This function generates an array of allowed EAP phase 2 (tunneled) types for
+ * the given network configuration.
+ */
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+                                             size_t *count)
+{
+       struct eap_method_type *buf;
+       u32 method;
+       int vendor;
+       size_t mcount;
+       const struct eap_method *methods, *m;
+
+       methods = eap_peer_get_methods(&mcount);
+       if (methods == NULL)
+               return NULL;
+       *count = 0;
+       buf = os_malloc(mcount * sizeof(struct eap_method_type));
+       if (buf == NULL)
+               return NULL;
+
+       for (m = methods; m; m = m->next) {
+               vendor = m->vendor;
+               method = m->method;
+               if (eap_allowed_phase2_type(vendor, method)) {
+                       if (vendor == EAP_VENDOR_IETF &&
+                           method == EAP_TYPE_TLS && config &&
+                           config->private_key2 == NULL)
+                               continue;
+                       buf[*count].vendor = vendor;
+                       buf[*count].method = method;
+                       (*count)++;
+               }
+       }
+
+       return buf;
+}
+
+
+/**
+ * eap_set_fast_reauth - Update fast_reauth setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @enabled: 1 = Fast reauthentication is enabled, 0 = Disabled
+ */
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled)
+{
+       sm->fast_reauth = enabled;
+}
+
+
+/**
+ * eap_set_workaround - Update EAP workarounds setting
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @workaround: 1 = Enable EAP workarounds, 0 = Disable EAP workarounds
+ */
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround)
+{
+       sm->workaround = workaround;
+}
+
+
+/**
+ * eap_get_config - Get current network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the current network configuration or %NULL if not found
+ *
+ * EAP peer methods should avoid using this function if they can use other
+ * access functions, like eap_get_config_identity() and
+ * eap_get_config_password(), that do not require direct access to
+ * struct eap_peer_config.
+ */
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+       return sm->eapol_cb->get_config(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_get_config_identity - Get identity from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the identity
+ * Returns: Pointer to the identity or %NULL if not found
+ */
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->identity_len;
+       return config->identity;
+}
+
+
+/**
+ * eap_get_config_password - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->password_len;
+       return config->password;
+}
+
+
+/**
+ * eap_get_config_password2 - Get password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the password
+ * @hash: Buffer for returning whether the password is stored as a
+ * NtPasswordHash instead of plaintext password; can be %NULL if this
+ * information is not needed
+ * Returns: Pointer to the password or %NULL if not found
+ */
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->password_len;
+       if (hash)
+               *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+       return config->password;
+}
+
+
+/**
+ * eap_get_config_new_password - Get new password from network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the new password
+ * Returns: Pointer to the new password or %NULL if not found
+ */
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->new_password_len;
+       return config->new_password;
+}
+
+
+/**
+ * eap_get_config_otp - Get one-time password from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Buffer for the length of the one-time password
+ * Returns: Pointer to the one-time password or %NULL if not found
+ */
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       *len = config->otp_len;
+       return config->otp;
+}
+
+
+/**
+ * eap_clear_config_otp - Clear used one-time password
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function clears a used one-time password (OTP) from the current network
+ * configuration. This should be called when the OTP has been used and is not
+ * needed anymore.
+ */
+void eap_clear_config_otp(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return;
+       os_memset(config->otp, 0, config->otp_len);
+       os_free(config->otp);
+       config->otp = NULL;
+       config->otp_len = 0;
+}
+
+
+/**
+ * eap_get_config_phase1 - Get phase1 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase1(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       return config->phase1;
+}
+
+
+/**
+ * eap_get_config_phase2 - Get phase2 data from the network configuration
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the phase1 data or %NULL if not found
+ */
+const char * eap_get_config_phase2(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL)
+               return NULL;
+       return config->phase2;
+}
+
+
+/**
+ * eap_key_available - Get key availability (eapKeyAvailable variable)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: 1 if EAP keying material is available, 0 if not
+ */
+int eap_key_available(struct eap_sm *sm)
+{
+       return sm ? sm->eapKeyAvailable : 0;
+}
+
+
+/**
+ * eap_notify_success - Notify EAP state machine about external success trigger
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * This function is called when external event, e.g., successful completion of
+ * WPA-PSK key handshake, is indicating that EAP state machine should move to
+ * success state. This is mainly used with security modes that do not use EAP
+ * state machine (e.g., WPA-PSK).
+ */
+void eap_notify_success(struct eap_sm *sm)
+{
+       if (sm) {
+               sm->decision = DECISION_COND_SUCC;
+               sm->EAP_state = EAP_SUCCESS;
+       }
+}
+
+
+/**
+ * eap_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * Notify EAP state machines that a lower layer has detected a successful
+ * authentication. This is used to recover from dropped EAP-Success messages.
+ */
+void eap_notify_lower_layer_success(struct eap_sm *sm)
+{
+       if (sm == NULL)
+               return;
+
+       if (eapol_get_bool(sm, EAPOL_eapSuccess) ||
+           sm->decision == DECISION_FAIL ||
+           (sm->methodState != METHOD_MAY_CONT &&
+            sm->methodState != METHOD_DONE))
+               return;
+
+       if (sm->eapKeyData != NULL)
+               sm->eapKeyAvailable = TRUE;
+       eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+               "EAP authentication completed successfully (based on lower "
+               "layer success)");
+}
+
+
+/**
+ * eap_get_eapKeyData - Get master session key (MSK) from EAP state machine
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the key
+ * Returns: Pointer to the EAP keying data or %NULL on failure
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from the EAP state machine. The
+ * key is available only after a successful authentication. EAP state machine
+ * continues to manage the key data and the caller must not change or free the
+ * returned data.
+ */
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len)
+{
+       if (sm == NULL || sm->eapKeyData == NULL) {
+               *len = 0;
+               return NULL;
+       }
+
+       *len = sm->eapKeyDataLen;
+       return sm->eapKeyData;
+}
+
+
+/**
+ * eap_get_eapKeyData - Get EAP response data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * Returns: Pointer to the EAP response (eapRespData) or %NULL on failure
+ *
+ * Fetch EAP response (eapRespData) from the EAP state machine. This data is
+ * available when EAP state machine has processed an incoming EAP request. The
+ * EAP state machine does not maintain a reference to the response after this
+ * function is called and the caller is responsible for freeing the data.
+ */
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm)
+{
+       struct wpabuf *resp;
+
+       if (sm == NULL || sm->eapRespData == NULL)
+               return NULL;
+
+       resp = sm->eapRespData;
+       sm->eapRespData = NULL;
+
+       return resp;
+}
+
+
+/**
+ * eap_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAP state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx)
+{
+       if (sm)
+               sm->scard_ctx = ctx;
+}
+
+
+/**
+ * eap_set_config_blob - Set or add a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       sm->eapol_cb->set_config_blob(sm->eapol_ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_get_config_blob - Get a named configuration blob
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * eap_get_config_blob(struct eap_sm *sm,
+                                                  const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       return sm->eapol_cb->get_config_blob(sm->eapol_ctx, name);
+#else /* CONFIG_NO_CONFIG_BLOBS */
+       return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+/**
+ * eap_set_force_disabled - Set force_disabled flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @disabled: 1 = EAP disabled, 0 = EAP enabled
+ *
+ * This function is used to force EAP state machine to be disabled when it is
+ * not in use (e.g., with WPA-PSK or plaintext connections).
+ */
+void eap_set_force_disabled(struct eap_sm *sm, int disabled)
+{
+       sm->force_disabled = disabled;
+}
+
+
+ /**
+ * eap_notify_pending - Notify that EAP method is ready to re-process a request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ *
+ * An EAP method can perform a pending operation (e.g., to get a response from
+ * an external process). Once the response is available, this function can be
+ * used to request EAPOL state machine to retry delivering the previously
+ * received (and still unanswered) EAP request to EAP state machine.
+ */
+void eap_notify_pending(struct eap_sm *sm)
+{
+       sm->eapol_cb->notify_pending(sm->eapol_ctx);
+}
+
+
+/**
+ * eap_invalidate_cached_session - Mark cached session data invalid
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ */
+void eap_invalidate_cached_session(struct eap_sm *sm)
+{
+       if (sm)
+               eap_deinit_prev_method(sm, "invalidate");
+}
+
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf)
+{
+       if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+           os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+               return 0; /* Not a WPS Enrollee */
+
+       if (conf->phase1 == NULL || os_strstr(conf->phase1, "pbc=1") == NULL)
+               return 0; /* Not using PBC */
+
+       return 1;
+}
+
+
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf)
+{
+       if (conf->identity_len != WSC_ID_ENROLLEE_LEN ||
+           os_memcmp(conf->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN))
+               return 0; /* Not a WPS Enrollee */
+
+       if (conf->phase1 == NULL || os_strstr(conf->phase1, "pin=") == NULL)
+               return 0; /* Not using PIN */
+
+       return 1;
+}
diff --git a/src/eap_peer/eap.h b/src/eap_peer/eap.h
new file mode 100644 (file)
index 0000000..40d0b69
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * EAP peer state machine functions (RFC 4137)
+ * 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 EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_peer/eap_methods.h"
+
+struct eap_sm;
+struct wpa_config_blob;
+struct wpabuf;
+
+struct eap_method_type {
+       int vendor;
+       u32 method;
+};
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * enum eapol_bool_var - EAPOL boolean state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_bool_var {
+       /**
+        * EAPOL_eapSuccess - EAP SUCCESS state reached
+        *
+        * EAP state machine reads and writes this value.
+        */
+       EAPOL_eapSuccess,
+
+       /**
+        * EAPOL_eapRestart - Lower layer request to restart authentication
+        *
+        * Set to TRUE in lower layer, FALSE in EAP state machine.
+        */
+       EAPOL_eapRestart,
+
+       /**
+        * EAPOL_eapFail - EAP FAILURE state reached
+        *
+        * EAP state machine writes this value.
+        */
+       EAPOL_eapFail,
+
+       /**
+        * EAPOL_eapResp - Response to send
+        *
+        * Set to TRUE in EAP state machine, FALSE in lower layer.
+        */
+       EAPOL_eapResp,
+
+       /**
+        * EAPOL_eapNoResp - Request has been process; no response to send
+        *
+        * Set to TRUE in EAP state machine, FALSE in lower layer.
+        */
+       EAPOL_eapNoResp,
+
+       /**
+        * EAPOL_eapReq - EAP request available from lower layer
+        *
+        * Set to TRUE in lower layer, FALSE in EAP state machine.
+        */
+       EAPOL_eapReq,
+
+       /**
+        * EAPOL_portEnabled - Lower layer is ready for communication
+        *
+        * EAP state machines reads this value.
+        */
+       EAPOL_portEnabled,
+
+       /**
+        * EAPOL_altAccept - Alternate indication of success (RFC3748)
+        *
+        * EAP state machines reads this value.
+        */
+       EAPOL_altAccept,
+
+       /**
+        * EAPOL_altReject - Alternate indication of failure (RFC3748)
+        *
+        * EAP state machines reads this value.
+        */
+       EAPOL_altReject
+};
+
+/**
+ * enum eapol_int_var - EAPOL integer state variables for EAP state machine
+ *
+ * These variables are used in the interface between EAP peer state machine and
+ * lower layer. These are defined in RFC 4137, Sect. 4.1. Lower layer code is
+ * expected to maintain these variables and register a callback functions for
+ * EAP state machine to get and set the variables.
+ */
+enum eapol_int_var {
+       /**
+        * EAPOL_idleWhile - Outside time for EAP peer timeout
+        *
+        * This integer variable is used to provide an outside timer that the
+        * external (to EAP state machine) code must decrement by one every
+        * second until the value reaches zero. This is used in the same way as
+        * EAPOL state machine timers. EAP state machine reads and writes this
+        * value.
+        */
+       EAPOL_idleWhile
+};
+
+/**
+ * struct eapol_callbacks - Callback functions from EAP to lower layer
+ *
+ * This structure defines the callback functions that EAP state machine
+ * requires from the lower layer (usually EAPOL state machine) for updating
+ * state variables and requesting information. eapol_ctx from
+ * eap_peer_sm_init() call will be used as the ctx parameter for these
+ * callback functions.
+ */
+struct eapol_callbacks {
+       /**
+        * get_config - Get pointer to the current network configuration
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        */
+       struct eap_peer_config * (*get_config)(void *ctx);
+
+       /**
+        * get_bool - Get a boolean EAPOL state variable
+        * @variable: EAPOL boolean variable to get
+        * Returns: Value of the EAPOL variable
+        */
+       Boolean (*get_bool)(void *ctx, enum eapol_bool_var variable);
+
+       /**
+        * set_bool - Set a boolean EAPOL state variable
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @variable: EAPOL boolean variable to set
+        * @value: Value for the EAPOL variable
+        */
+       void (*set_bool)(void *ctx, enum eapol_bool_var variable,
+                        Boolean value);
+
+       /**
+        * get_int - Get an integer EAPOL state variable
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @variable: EAPOL integer variable to get
+        * Returns: Value of the EAPOL variable
+        */
+       unsigned int (*get_int)(void *ctx, enum eapol_int_var variable);
+
+       /**
+        * set_int - Set an integer EAPOL state variable
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @variable: EAPOL integer variable to set
+        * @value: Value for the EAPOL variable
+        */
+       void (*set_int)(void *ctx, enum eapol_int_var variable,
+                       unsigned int value);
+
+       /**
+        * get_eapReqData - Get EAP-Request data
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @len: Pointer to variable that will be set to eapReqDataLen
+        * Returns: Reference to eapReqData (EAP state machine will not free
+        * this) or %NULL if eapReqData not available.
+        */
+       struct wpabuf * (*get_eapReqData)(void *ctx);
+
+       /**
+        * set_config_blob - Set named configuration blob
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @blob: New value for the blob
+        *
+        * Adds a new configuration blob or replaces the current value of an
+        * existing blob.
+        */
+       void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+       /**
+        * get_config_blob - Get a named configuration blob
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @name: Name of the blob
+        * Returns: Pointer to blob data or %NULL if not found
+        */
+       const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+                                                         const char *name);
+
+       /**
+        * notify_pending - Notify that a pending request can be retried
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        *
+        * An EAP method can perform a pending operation (e.g., to get a
+        * response from an external process). Once the response is available,
+        * this callback function can be used to request EAPOL state machine to
+        * retry delivering the previously received (and still unanswered) EAP
+        * request to EAP state machine.
+        */
+       void (*notify_pending)(void *ctx);
+
+       /**
+        * eap_param_needed - Notify that EAP parameter is needed
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        * @field: Field name (e.g., "IDENTITY")
+        * @txt: User readable text describing the required parameter
+        */
+       void (*eap_param_needed)(void *ctx, const char *field,
+                                const char *txt);
+};
+
+/**
+ * struct eap_config - Configuration for EAP state machine
+ */
+struct eap_config {
+       /**
+        * opensc_engine_path - OpenSC engine for OpenSSL engine support
+        *
+        * Usually, path to engine_opensc.so.
+        */
+       const char *opensc_engine_path;
+       /**
+        * pkcs11_engine_path - PKCS#11 engine for OpenSSL engine support
+        *
+        * Usually, path to engine_pkcs11.so.
+        */
+       const char *pkcs11_engine_path;
+       /**
+        * pkcs11_module_path - OpenSC PKCS#11 module for OpenSSL engine
+        *
+        * Usually, path to opensc-pkcs11.so.
+        */
+       const char *pkcs11_module_path;
+       /**
+        * wps - WPS context data
+        *
+        * This is only used by EAP-WSC and can be left %NULL if not available.
+        */
+       struct wps_context *wps;
+};
+
+struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
+                                struct eapol_callbacks *eapol_cb,
+                                void *msg_ctx, struct eap_config *conf);
+void eap_peer_sm_deinit(struct eap_sm *sm);
+int eap_peer_sm_step(struct eap_sm *sm);
+void eap_sm_abort(struct eap_sm *sm);
+int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen,
+                     int verbose);
+struct wpabuf * eap_sm_buildIdentity(struct eap_sm *sm, int id, int encrypted);
+void eap_sm_request_identity(struct eap_sm *sm);
+void eap_sm_request_password(struct eap_sm *sm);
+void eap_sm_request_new_password(struct eap_sm *sm);
+void eap_sm_request_pin(struct eap_sm *sm);
+void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
+void eap_sm_request_passphrase(struct eap_sm *sm);
+void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
+u32 eap_get_phase2_type(const char *name, int *vendor);
+struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
+                                             size_t *count);
+void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
+void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
+void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+int eap_key_available(struct eap_sm *sm);
+void eap_notify_success(struct eap_sm *sm);
+void eap_notify_lower_layer_success(struct eap_sm *sm);
+const u8 * eap_get_eapKeyData(struct eap_sm *sm, size_t *len);
+struct wpabuf * eap_get_eapRespData(struct eap_sm *sm);
+void eap_register_scard_ctx(struct eap_sm *sm, void *ctx);
+void eap_invalidate_cached_session(struct eap_sm *sm);
+
+int eap_is_wps_pbc_enrollee(struct eap_peer_config *conf);
+int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
+
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAP_H */
diff --git a/src/eap_peer/eap_aka.c b/src/eap_peer/eap_aka.c
new file mode 100644 (file)
index 0000000..182f01a
--- /dev/null
@@ -0,0 +1,1389 @@
+/*
+ * EAP peer method: EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
+ * 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 "pcsc_funcs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "crypto/milenage.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+
+
+struct eap_aka_data {
+       u8 ik[EAP_AKA_IK_LEN], ck[EAP_AKA_CK_LEN], res[EAP_AKA_RES_MAX_LEN];
+       size_t res_len;
+       u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+       u8 mk[EAP_SIM_MK_LEN];
+       u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+       u8 k_encr[EAP_SIM_K_ENCR_LEN];
+       u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
+       u8 msk[EAP_SIM_KEYING_DATA_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 rand[EAP_AKA_RAND_LEN], autn[EAP_AKA_AUTN_LEN];
+       u8 auts[EAP_AKA_AUTS_LEN];
+
+       int num_id_req, num_notification;
+       u8 *pseudonym;
+       size_t pseudonym_len;
+       u8 *reauth_id;
+       size_t reauth_id_len;
+       int reauth;
+       unsigned int counter, counter_too_small;
+       u8 *last_eap_identity;
+       size_t last_eap_identity_len;
+       enum {
+               CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+       } state;
+
+       struct wpabuf *id_msgs;
+       int prev_id;
+       int result_ind, use_result_ind;
+       u8 eap_method;
+       u8 *network_name;
+       size_t network_name_len;
+       u16 kdf;
+       int kdf_negotiation;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_aka_state_txt(int state)
+{
+       switch (state) {
+       case CONTINUE:
+               return "CONTINUE";
+       case RESULT_SUCCESS:
+               return "RESULT_SUCCESS";
+       case RESULT_FAILURE:
+               return "RESULT_FAILURE";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+                  eap_aka_state_txt(data->state),
+                  eap_aka_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+       struct eap_aka_data *data;
+       const char *phase1 = eap_get_config_phase1(sm);
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->eap_method = EAP_TYPE_AKA;
+
+       eap_aka_state(data, CONTINUE);
+       data->prev_id = -1;
+
+       data->result_ind = phase1 && os_strstr(phase1, "result_ind=1") != NULL;
+
+       return data;
+}
+
+
+#ifdef EAP_AKA_PRIME
+static void * eap_aka_prime_init(struct eap_sm *sm)
+{
+       struct eap_aka_data *data = eap_aka_init(sm);
+       if (data == NULL)
+               return NULL;
+       data->eap_method = EAP_TYPE_AKA_PRIME;
+       return data;
+}
+#endif /* EAP_AKA_PRIME */
+
+
+static void eap_aka_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       if (data) {
+               os_free(data->pseudonym);
+               os_free(data->reauth_id);
+               os_free(data->last_eap_identity);
+               wpabuf_free(data->id_msgs);
+               os_free(data->network_name);
+               os_free(data);
+       }
+}
+
+
+static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
+{
+       struct eap_peer_config *conf;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: UMTS authentication algorithm");
+
+       conf = eap_get_config(sm);
+       if (conf == NULL)
+               return -1;
+       if (conf->pcsc) {
+               return scard_umts_auth(sm->scard_ctx, data->rand,
+                                      data->autn, data->res, &data->res_len,
+                                      data->ik, data->ck, data->auts);
+       }
+
+#ifdef CONFIG_USIM_SIMULATOR
+       if (conf->password) {
+               u8 opc[16], k[16], sqn[6];
+               const char *pos;
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Use internal Milenage "
+                          "implementation for UMTS authentication");
+               if (conf->password_len < 78) {
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: invalid Milenage "
+                                  "password");
+                       return -1;
+               }
+               pos = (const char *) conf->password;
+               if (hexstr2bin(pos, k, 16))
+                       return -1;
+               pos += 32;
+               if (*pos != ':')
+                       return -1;
+               pos++;
+
+               if (hexstr2bin(pos, opc, 16))
+                       return -1;
+               pos += 32;
+               if (*pos != ':')
+                       return -1;
+               pos++;
+
+               if (hexstr2bin(pos, sqn, 6))
+                       return -1;
+
+               return milenage_check(opc, k, sqn, data->rand, data->autn,
+                                     data->ik, data->ck,
+                                     data->res, &data->res_len, data->auts);
+       }
+#endif /* CONFIG_USIM_SIMULATOR */
+
+#ifdef CONFIG_USIM_HARDCODED
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Use hardcoded Kc and SRES values for "
+                  "testing");
+
+       /* These hardcoded Kc and SRES values are used for testing.
+        * Could consider making them configurable. */
+       os_memset(data->res, '2', EAP_AKA_RES_MAX_LEN);
+       data->res_len = EAP_AKA_RES_MAX_LEN;
+       os_memset(data->ik, '3', EAP_AKA_IK_LEN);
+       os_memset(data->ck, '4', EAP_AKA_CK_LEN);
+       {
+               u8 autn[EAP_AKA_AUTN_LEN];
+               os_memset(autn, '1', EAP_AKA_AUTN_LEN);
+               if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
+                                  "with expected value");
+                       return -1;
+               }
+       }
+#if 0
+       {
+               static int test_resync = 1;
+               if (test_resync) {
+                       /* Test Resynchronization */
+                       test_resync = 0;
+                       return -2;
+               }
+       }
+#endif
+       return 0;
+
+#else /* CONFIG_USIM_HARDCODED */
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith "
+                  "enabled");
+       return -1;
+
+#endif /* CONFIG_USIM_HARDCODED */
+}
+
+
+#define CLEAR_PSEUDONYM        0x01
+#define CLEAR_REAUTH_ID        0x02
+#define CLEAR_EAP_ID   0x04
+
+static void eap_aka_clear_identities(struct eap_aka_data *data, int id)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: forgetting old%s%s%s",
+                  id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+                  id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+                  id & CLEAR_EAP_ID ? " eap_id" : "");
+       if (id & CLEAR_PSEUDONYM) {
+               os_free(data->pseudonym);
+               data->pseudonym = NULL;
+               data->pseudonym_len = 0;
+       }
+       if (id & CLEAR_REAUTH_ID) {
+               os_free(data->reauth_id);
+               data->reauth_id = NULL;
+               data->reauth_id_len = 0;
+       }
+       if (id & CLEAR_EAP_ID) {
+               os_free(data->last_eap_identity);
+               data->last_eap_identity = NULL;
+               data->last_eap_identity_len = 0;
+       }
+}
+
+
+static int eap_aka_learn_ids(struct eap_aka_data *data,
+                            struct eap_sim_attrs *attr)
+{
+       if (attr->next_pseudonym) {
+               os_free(data->pseudonym);
+               data->pseudonym = os_malloc(attr->next_pseudonym_len);
+               if (data->pseudonym == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+                                  "next pseudonym");
+                       return -1;
+               }
+               os_memcpy(data->pseudonym, attr->next_pseudonym,
+                         attr->next_pseudonym_len);
+               data->pseudonym_len = attr->next_pseudonym_len;
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP-AKA: (encr) AT_NEXT_PSEUDONYM",
+                                 data->pseudonym,
+                                 data->pseudonym_len);
+       }
+
+       if (attr->next_reauth_id) {
+               os_free(data->reauth_id);
+               data->reauth_id = os_malloc(attr->next_reauth_id_len);
+               if (data->reauth_id == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-AKA: (encr) No memory for "
+                                  "next reauth_id");
+                       return -1;
+               }
+               os_memcpy(data->reauth_id, attr->next_reauth_id,
+                         attr->next_reauth_id_len);
+               data->reauth_id_len = attr->next_reauth_id_len;
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP-AKA: (encr) AT_NEXT_REAUTH_ID",
+                                 data->reauth_id,
+                                 data->reauth_id_len);
+       }
+
+       return 0;
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+                             const struct wpabuf *msg)
+{
+       if (msg == NULL)
+               return -1;
+
+       if (data->id_msgs == NULL) {
+               data->id_msgs = wpabuf_dup(msg);
+               return data->id_msgs == NULL ? -1 : 0;
+       }
+
+       if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+               return -1;
+       wpabuf_put_buf(data->id_msgs, msg);
+
+       return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+                                 struct eap_sim_msg *msg)
+{
+       const u8 *addr;
+       size_t len;
+       u8 hash[SHA256_MAC_LEN];
+
+       wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
+
+       if (data->id_msgs == NULL) {
+               /*
+                * No EAP-AKA/Identity packets were exchanged - send empty
+                * checkcode.
+                */
+               eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+               return;
+       }
+
+       /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
+       addr = wpabuf_head(data->id_msgs);
+       len = wpabuf_len(data->id_msgs);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+#ifdef EAP_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA_PRIME)
+               sha256_vector(1, &addr, &len, hash);
+       else
+#endif /* EAP_AKA_PRIME */
+               sha1_vector(1, &addr, &len, hash);
+
+       eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+                       data->eap_method == EAP_TYPE_AKA_PRIME ?
+                       EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+                                   const u8 *checkcode, size_t checkcode_len)
+{
+       const u8 *addr;
+       size_t len;
+       u8 hash[SHA256_MAC_LEN];
+       size_t hash_len;
+
+       if (checkcode == NULL)
+               return -1;
+
+       if (data->id_msgs == NULL) {
+               if (checkcode_len != 0) {
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+                                  "indicates that AKA/Identity messages were "
+                                  "used, but they were not");
+                       return -1;
+               }
+               return 0;
+       }
+
+       hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
+               EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
+
+       if (checkcode_len != hash_len) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from server "
+                          "indicates that AKA/Identity message were not "
+                          "used, but they were");
+               return -1;
+       }
+
+       /* Checkcode is SHA1/SHA256 hash over all EAP-AKA/Identity packets. */
+       addr = wpabuf_head(data->id_msgs);
+       len = wpabuf_len(data->id_msgs);
+#ifdef EAP_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA_PRIME)
+               sha256_vector(1, &addr, &len, hash);
+       else
+#endif /* EAP_AKA_PRIME */
+               sha1_vector(1, &addr, &len, hash);
+
+       if (os_memcmp(hash, checkcode, hash_len) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
+                                           int err)
+{
+       struct eap_sim_msg *msg;
+
+       eap_aka_state(data, FAILURE);
+       data->num_id_req = 0;
+       data->num_notification = 0;
+
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_CLIENT_ERROR);
+       eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
+                                                    u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       eap_aka_state(data, FAILURE);
+       data->num_id_req = 0;
+       data->num_notification = 0;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Authentication-Reject "
+                  "(id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_synchronization_failure(
+       struct eap_aka_data *data, u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       data->num_id_req = 0;
+       data->num_notification = 0;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Synchronization-Failure "
+                  "(id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE);
+       wpa_printf(MSG_DEBUG, "   AT_AUTS");
+       eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
+                            EAP_AKA_AUTS_LEN);
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
+                                                struct eap_aka_data *data,
+                                                u8 id,
+                                                enum eap_sim_id_req id_req)
+{
+       const u8 *identity = NULL;
+       size_t identity_len = 0;
+       struct eap_sim_msg *msg;
+
+       data->reauth = 0;
+       if (id_req == ANY_ID && data->reauth_id) {
+               identity = data->reauth_id;
+               identity_len = data->reauth_id_len;
+               data->reauth = 1;
+       } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+                  data->pseudonym) {
+               identity = data->pseudonym;
+               identity_len = data->pseudonym_len;
+               eap_aka_clear_identities(data, CLEAR_REAUTH_ID);
+       } else if (id_req != NO_ID_REQ) {
+               identity = eap_get_config_identity(sm, &identity_len);
+               if (identity) {
+                       eap_aka_clear_identities(data, CLEAR_PSEUDONYM |
+                                                CLEAR_REAUTH_ID);
+               }
+       }
+       if (id_req != NO_ID_REQ)
+               eap_aka_clear_identities(data, CLEAR_EAP_ID);
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Identity (id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_IDENTITY);
+
+       if (identity) {
+               wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
+                                 identity, identity_len);
+               eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+                               identity, identity_len);
+       }
+
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
+                                                 u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_CHALLENGE);
+       wpa_printf(MSG_DEBUG, "   AT_RES");
+       eap_sim_msg_add(msg, EAP_SIM_AT_RES, data->res_len * 8,
+                       data->res, data->res_len);
+       eap_aka_add_checkcode(data, msg);
+       if (data->use_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
+                                              u8 id, int counter_too_small,
+                                              const u8 *nonce_s)
+{
+       struct eap_sim_msg *msg;
+       unsigned int counter;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Reauthentication (id=%d)",
+                  id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_REAUTHENTICATION);
+       wpa_printf(MSG_DEBUG, "   AT_IV");
+       wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+       eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+       if (counter_too_small) {
+               wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
+               eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+               counter = data->counter_too_small;
+       } else
+               counter = data->counter;
+
+       wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
+       eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+       if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+                          "AT_ENCR_DATA");
+               eap_sim_msg_free(msg);
+               return NULL;
+       }
+       eap_aka_add_checkcode(data, msg);
+       if (data->use_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+                                 EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
+                                                    u8 id, u16 notification)
+{
+       struct eap_sim_msg *msg;
+       u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Notification (id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_NOTIFICATION);
+       if (k_aut && data->reauth) {
+               wpa_printf(MSG_DEBUG, "   AT_IV");
+               wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+               eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+                                          EAP_SIM_AT_ENCR_DATA);
+               wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
+               eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+                               NULL, 0);
+               if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+                                            EAP_SIM_AT_PADDING)) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+                                  "AT_ENCR_DATA");
+                       eap_sim_msg_free(msg);
+                       return NULL;
+               }
+       }
+       if (k_aut) {
+               wpa_printf(MSG_DEBUG, "   AT_MAC");
+               eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       }
+       return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_aka_process_identity(struct eap_sm *sm,
+                                               struct eap_aka_data *data,
+                                               u8 id,
+                                               const struct wpabuf *reqData,
+                                               struct eap_sim_attrs *attr)
+{
+       int id_error;
+       struct wpabuf *buf;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Identity");
+
+       id_error = 0;
+       switch (attr->id_req) {
+       case NO_ID_REQ:
+               break;
+       case ANY_ID:
+               if (data->num_id_req > 0)
+                       id_error++;
+               data->num_id_req++;
+               break;
+       case FULLAUTH_ID:
+               if (data->num_id_req > 1)
+                       id_error++;
+               data->num_id_req++;
+               break;
+       case PERMANENT_ID:
+               if (data->num_id_req > 2)
+                       id_error++;
+               data->num_id_req++;
+               break;
+       }
+       if (id_error) {
+               wpa_printf(MSG_INFO, "EAP-AKA: Too many ID requests "
+                          "used within one authentication");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       buf = eap_aka_response_identity(sm, data, id, attr->id_req);
+
+       if (data->prev_id != id) {
+               eap_aka_add_id_msg(data, reqData);
+               eap_aka_add_id_msg(data, buf);
+               data->prev_id = id;
+       }
+
+       return buf;
+}
+
+
+static int eap_aka_verify_mac(struct eap_aka_data *data,
+                             const struct wpabuf *req,
+                             const u8 *mac, const u8 *extra,
+                             size_t extra_len)
+{
+       if (data->eap_method == EAP_TYPE_AKA_PRIME)
+               return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
+                                                extra_len);
+       return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
+}
+
+
+#ifdef EAP_AKA_PRIME
+static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
+                                               u8 id, u16 kdf)
+{
+       struct eap_sim_msg *msg;
+
+       data->kdf_negotiation = 1;
+       data->kdf = kdf;
+       wpa_printf(MSG_DEBUG, "Generating EAP-AKA Challenge (id=%d) (KDF "
+                  "select)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_CHALLENGE);
+       wpa_printf(MSG_DEBUG, "   AT_KDF");
+       eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_prime_kdf_neg(struct eap_aka_data *data,
+                                            u8 id, struct eap_sim_attrs *attr)
+{
+       size_t i;
+
+       for (i = 0; i < attr->kdf_count; i++) {
+               if (attr->kdf[i] == EAP_AKA_PRIME_KDF)
+                       return eap_aka_prime_kdf_select(data, id,
+                                                       EAP_AKA_PRIME_KDF);
+       }
+
+       /* No matching KDF found - fail authentication as if AUTN had been
+        * incorrect */
+       return eap_aka_authentication_reject(data, id);
+}
+
+
+static int eap_aka_prime_kdf_valid(struct eap_aka_data *data,
+                                  struct eap_sim_attrs *attr)
+{
+       size_t i, j;
+
+       if (attr->kdf_count == 0)
+               return 0;
+
+       /* The only allowed (and required) duplication of a KDF is the addition
+        * of the selected KDF into the beginning of the list. */
+
+       if (data->kdf_negotiation) {
+               if (attr->kdf[0] != data->kdf) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+                                  "accept the selected KDF");
+                       return 0;
+               }
+
+               for (i = 1; i < attr->kdf_count; i++) {
+                       if (attr->kdf[i] == data->kdf)
+                               break;
+               }
+               if (i == attr->kdf_count &&
+                   attr->kdf_count < EAP_AKA_PRIME_KDF_MAX) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': The server did not "
+                                  "duplicate the selected KDF");
+                       return 0;
+               }
+
+               /* TODO: should check that the list is identical to the one
+                * used in the previous Challenge message apart from the added
+                * entry in the beginning. */
+       }
+
+       for (i = data->kdf ? 1 : 0; i < attr->kdf_count; i++) {
+               for (j = i + 1; j < attr->kdf_count; j++) {
+                       if (attr->kdf[i] == attr->kdf[j]) {
+                               wpa_printf(MSG_WARNING, "EAP-AKA': The server "
+                                          "included a duplicated KDF");
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+#endif /* EAP_AKA_PRIME */
+
+
+static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
+                                                struct eap_aka_data *data,
+                                                u8 id,
+                                                const struct wpabuf *reqData,
+                                                struct eap_sim_attrs *attr)
+{
+       const u8 *identity;
+       size_t identity_len;
+       int res;
+       struct eap_sim_attrs eattr;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Challenge");
+
+       if (attr->checkcode &&
+           eap_aka_verify_checkcode(data, attr->checkcode,
+                                    attr->checkcode_len)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+                          "message");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+#ifdef EAP_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               if (!attr->kdf_input || attr->kdf_input_len == 0) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': Challenge message "
+                                  "did not include non-empty AT_KDF_INPUT");
+                       /* Fail authentication as if AUTN had been incorrect */
+                       return eap_aka_authentication_reject(data, id);
+               }
+               os_free(data->network_name);
+               data->network_name = os_malloc(attr->kdf_input_len);
+               if (data->network_name == NULL) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': No memory for "
+                                  "storing Network Name");
+                       return eap_aka_authentication_reject(data, id);
+               }
+               os_memcpy(data->network_name, attr->kdf_input,
+                         attr->kdf_input_len);
+               data->network_name_len = attr->kdf_input_len;
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA': Network Name "
+                                 "(AT_KDF_INPUT)",
+                                 data->network_name, data->network_name_len);
+               /* TODO: check Network Name per 3GPP.33.402 */
+
+               if (!eap_aka_prime_kdf_valid(data, attr))
+                       return eap_aka_authentication_reject(data, id);
+
+               if (attr->kdf[0] != EAP_AKA_PRIME_KDF)
+                       return eap_aka_prime_kdf_neg(data, id, attr);
+
+               data->kdf = EAP_AKA_PRIME_KDF;
+               wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+       }
+
+       if (data->eap_method == EAP_TYPE_AKA && attr->bidding) {
+               u16 flags = WPA_GET_BE16(attr->bidding);
+               if ((flags & EAP_AKA_BIDDING_FLAG_D) &&
+                   eap_allowed_method(sm, EAP_VENDOR_IETF,
+                                      EAP_TYPE_AKA_PRIME)) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA: Bidding down from "
+                                  "AKA' to AKA detected");
+                       /* Fail authentication as if AUTN had been incorrect */
+                       return eap_aka_authentication_reject(data, id);
+               }
+       }
+#endif /* EAP_AKA_PRIME */
+
+       data->reauth = 0;
+       if (!attr->mac || !attr->rand || !attr->autn) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+                          "did not include%s%s%s",
+                          !attr->mac ? " AT_MAC" : "",
+                          !attr->rand ? " AT_RAND" : "",
+                          !attr->autn ? " AT_AUTN" : "");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+       os_memcpy(data->rand, attr->rand, EAP_AKA_RAND_LEN);
+       os_memcpy(data->autn, attr->autn, EAP_AKA_AUTN_LEN);
+
+       res = eap_aka_umts_auth(sm, data);
+       if (res == -1) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+                          "failed (AUTN)");
+               return eap_aka_authentication_reject(data, id);
+       } else if (res == -2) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
+                          "failed (AUTN seq# -> AUTS)");
+               return eap_aka_synchronization_failure(data, id);
+       } else if (res) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+#ifdef EAP_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
+                * needed 6-octet SQN ^ AK for CK',IK' derivation */
+               u16 amf = WPA_GET_BE16(data->autn + 6);
+               if (!(amf & 0x8000)) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': AMF separation bit "
+                                  "not set (AMF=0x%4x)", amf);
+                       return eap_aka_authentication_reject(data, id);
+               }
+               eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
+                                                data->autn,
+                                                data->network_name,
+                                                data->network_name_len);
+       }
+#endif /* EAP_AKA_PRIME */
+       if (data->last_eap_identity) {
+               identity = data->last_eap_identity;
+               identity_len = data->last_eap_identity_len;
+       } else if (data->pseudonym) {
+               identity = data->pseudonym;
+               identity_len = data->pseudonym_len;
+       } else
+               identity = eap_get_config_identity(sm, &identity_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Selected identity for MK "
+                         "derivation", identity, identity_len);
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+                                         data->ck, data->k_encr, data->k_aut,
+                                         data->k_re, data->msk, data->emsk);
+       } else {
+               eap_aka_derive_mk(identity, identity_len, data->ik, data->ck,
+                                 data->mk);
+               eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+                                   data->msk, data->emsk);
+       }
+       if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+                          "used invalid AT_MAC");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       /* Old reauthentication and pseudonym identities must not be used
+        * anymore. In other words, if no new identities are received, full
+        * authentication will be used on next reauthentication. */
+       eap_aka_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+                                CLEAR_EAP_ID);
+
+       if (attr->encr_data) {
+               u8 *decrypted;
+               decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                              attr->encr_data_len, attr->iv,
+                                              &eattr, 0);
+               if (decrypted == NULL) {
+                       return eap_aka_client_error(
+                               data, id, EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+               }
+               eap_aka_learn_ids(data, &eattr);
+               os_free(decrypted);
+       }
+
+       if (data->result_ind && attr->result_ind)
+               data->use_result_ind = 1;
+
+       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+               eap_aka_state(data, data->use_result_ind ?
+                             RESULT_SUCCESS : SUCCESS);
+       }
+
+       data->num_id_req = 0;
+       data->num_notification = 0;
+       /* RFC 4187 specifies that counter is initialized to one after
+        * fullauth, but initializing it to zero makes it easier to implement
+        * reauth verification. */
+       data->counter = 0;
+       return eap_aka_response_challenge(data, id);
+}
+
+
+static int eap_aka_process_notification_reauth(struct eap_aka_data *data,
+                                              struct eap_sim_attrs *attr)
+{
+       struct eap_sim_attrs eattr;
+       u8 *decrypted;
+
+       if (attr->encr_data == NULL || attr->iv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Notification message after "
+                          "reauth did not include encrypted data");
+               return -1;
+       }
+
+       decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                      attr->encr_data_len, attr->iv, &eattr,
+                                      0);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+                          "data from notification message");
+               return -1;
+       }
+
+       if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Counter in notification "
+                          "message does not match with counter in reauth "
+                          "message");
+               os_free(decrypted);
+               return -1;
+       }
+
+       os_free(decrypted);
+       return 0;
+}
+
+
+static int eap_aka_process_notification_auth(struct eap_aka_data *data,
+                                            const struct wpabuf *reqData,
+                                            struct eap_sim_attrs *attr)
+{
+       if (attr->mac == NULL) {
+               wpa_printf(MSG_INFO, "EAP-AKA: no AT_MAC in after_auth "
+                          "Notification message");
+               return -1;
+       }
+
+       if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Notification message "
+                          "used invalid AT_MAC");
+               return -1;
+       }
+
+       if (data->reauth &&
+           eap_aka_process_notification_reauth(data, attr)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Invalid notification "
+                          "message after reauth");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_aka_process_notification(
+       struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+       const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Notification");
+       if (data->num_notification > 0) {
+               wpa_printf(MSG_INFO, "EAP-AKA: too many notification "
+                          "rounds (only one allowed)");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+       data->num_notification++;
+       if (attr->notification == -1) {
+               wpa_printf(MSG_INFO, "EAP-AKA: no AT_NOTIFICATION in "
+                          "Notification message");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if ((attr->notification & 0x4000) == 0 &&
+           eap_aka_process_notification_auth(data, reqData, attr)) {
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       eap_sim_report_notification(sm->msg_ctx, attr->notification, 1);
+       if (attr->notification >= 0 && attr->notification < 32768) {
+               eap_aka_state(data, FAILURE);
+       } else if (attr->notification == EAP_SIM_SUCCESS &&
+                  data->state == RESULT_SUCCESS)
+               eap_aka_state(data, SUCCESS);
+       return eap_aka_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_aka_process_reauthentication(
+       struct eap_sm *sm, struct eap_aka_data *data, u8 id,
+       const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+       struct eap_sim_attrs eattr;
+       u8 *decrypted;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Reauthentication");
+
+       if (attr->checkcode &&
+           eap_aka_verify_checkcode(data, attr->checkcode,
+                                    attr->checkcode_len)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+                          "message");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (data->reauth_id == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Server is trying "
+                          "reauthentication, but no reauth_id available");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       data->reauth = 1;
+       if (eap_aka_verify_mac(data, reqData, attr->mac, (u8 *) "", 0)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+                          "did not have valid AT_MAC");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (attr->encr_data == NULL || attr->iv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+                          "message did not include encrypted data");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                      attr->encr_data_len, attr->iv, &eattr,
+                                      0);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+                          "data from reauthentication message");
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (eattr.nonce_s == NULL || eattr.counter < 0) {
+               wpa_printf(MSG_INFO, "EAP-AKA: (encr) No%s%s in reauth packet",
+                          !eattr.nonce_s ? " AT_NONCE_S" : "",
+                          eattr.counter < 0 ? " AT_COUNTER" : "");
+               os_free(decrypted);
+               return eap_aka_client_error(data, id,
+                                           EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+               struct wpabuf *res;
+               wpa_printf(MSG_INFO, "EAP-AKA: (encr) Invalid counter "
+                          "(%d <= %d)", eattr.counter, data->counter);
+               data->counter_too_small = eattr.counter;
+
+               /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+                * reauth_id must not be used to start a new reauthentication.
+                * However, since it was used in the last EAP-Response-Identity
+                * packet, it has to saved for the following fullauth to be
+                * used in MK derivation. */
+               os_free(data->last_eap_identity);
+               data->last_eap_identity = data->reauth_id;
+               data->last_eap_identity_len = data->reauth_id_len;
+               data->reauth_id = NULL;
+               data->reauth_id_len = 0;
+
+               res = eap_aka_response_reauth(data, id, 1, eattr.nonce_s);
+               os_free(decrypted);
+
+               return res;
+       }
+       data->counter = eattr.counter;
+
+       os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-AKA: (encr) AT_NONCE_S",
+                   data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
+                                                data->reauth_id,
+                                                data->reauth_id_len,
+                                                data->nonce_s,
+                                                data->msk, data->emsk);
+       } else {
+               eap_sim_derive_keys_reauth(data->counter, data->reauth_id,
+                                          data->reauth_id_len,
+                                          data->nonce_s, data->mk,
+                                          data->msk, data->emsk);
+       }
+       eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+       eap_aka_learn_ids(data, &eattr);
+
+       if (data->result_ind && attr->result_ind)
+               data->use_result_ind = 1;
+
+       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+               eap_aka_state(data, data->use_result_ind ?
+                             RESULT_SUCCESS : SUCCESS);
+       }
+
+       data->num_id_req = 0;
+       data->num_notification = 0;
+       if (data->counter > EAP_AKA_MAX_FAST_REAUTHS) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Maximum number of "
+                          "fast reauths performed - force fullauth");
+               eap_aka_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+       }
+       os_free(decrypted);
+       return eap_aka_response_reauth(data, id, 0, data->nonce_s);
+}
+
+
+static struct wpabuf * eap_aka_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_aka_data *data = priv;
+       const struct eap_hdr *req;
+       u8 subtype, id;
+       struct wpabuf *res;
+       const u8 *pos;
+       struct eap_sim_attrs attr;
+       size_t len;
+
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-AKA: EAP data", reqData);
+       if (eap_get_config_identity(sm, &len) == NULL) {
+               wpa_printf(MSG_INFO, "EAP-AKA: Identity not configured");
+               eap_sm_request_identity(sm);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, reqData,
+                              &len);
+       if (pos == NULL || len < 1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       req = wpabuf_head(reqData);
+       id = req->identifier;
+       len = be_to_host16(req->length);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       subtype = *pos++;
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Subtype=%d", subtype);
+       pos += 2; /* Reserved */
+
+       if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr,
+                              data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+                              0)) {
+               res = eap_aka_client_error(data, id,
+                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+               goto done;
+       }
+
+       switch (subtype) {
+       case EAP_AKA_SUBTYPE_IDENTITY:
+               res = eap_aka_process_identity(sm, data, id, reqData, &attr);
+               break;
+       case EAP_AKA_SUBTYPE_CHALLENGE:
+               res = eap_aka_process_challenge(sm, data, id, reqData, &attr);
+               break;
+       case EAP_AKA_SUBTYPE_NOTIFICATION:
+               res = eap_aka_process_notification(sm, data, id, reqData,
+                                                  &attr);
+               break;
+       case EAP_AKA_SUBTYPE_REAUTHENTICATION:
+               res = eap_aka_process_reauthentication(sm, data, id, reqData,
+                                                      &attr);
+               break;
+       case EAP_AKA_SUBTYPE_CLIENT_ERROR:
+               wpa_printf(MSG_DEBUG, "EAP-AKA: subtype Client-Error");
+               res = eap_aka_client_error(data, id,
+                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown subtype=%d", subtype);
+               res = eap_aka_client_error(data, id,
+                                          EAP_AKA_UNABLE_TO_PROCESS_PACKET);
+               break;
+       }
+
+done:
+       if (data->state == FAILURE) {
+               ret->decision = DECISION_FAIL;
+               ret->methodState = METHOD_DONE;
+       } else if (data->state == SUCCESS) {
+               ret->decision = data->use_result_ind ?
+                       DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+               /*
+                * It is possible for the server to reply with AKA
+                * Notification, so we must allow the method to continue and
+                * not only accept EAP-Success at this point.
+                */
+               ret->methodState = data->use_result_ind ?
+                       METHOD_DONE : METHOD_MAY_CONT;
+       } else if (data->state == RESULT_FAILURE)
+               ret->methodState = METHOD_CONT;
+       else if (data->state == RESULT_SUCCESS)
+               ret->methodState = METHOD_CONT;
+
+       if (ret->methodState == METHOD_DONE) {
+               ret->allowNotifications = FALSE;
+       }
+
+       return res;
+}
+
+
+static Boolean eap_aka_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       eap_aka_clear_identities(data, CLEAR_EAP_ID);
+       data->prev_id = -1;
+       wpabuf_free(data->id_msgs);
+       data->id_msgs = NULL;
+       data->use_result_ind = 0;
+       data->kdf_negotiation = 0;
+}
+
+
+static void * eap_aka_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       data->num_id_req = 0;
+       data->num_notification = 0;
+       eap_aka_state(data, CONTINUE);
+       return priv;
+}
+
+
+static const u8 * eap_aka_get_identity(struct eap_sm *sm, void *priv,
+                                      size_t *len)
+{
+       struct eap_aka_data *data = priv;
+
+       if (data->reauth_id) {
+               *len = data->reauth_id_len;
+               return data->reauth_id;
+       }
+
+       if (data->pseudonym) {
+               *len = data->pseudonym_len;
+               return data->pseudonym;
+       }
+
+       return NULL;
+}
+
+
+static Boolean eap_aka_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_aka_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_SIM_KEYING_DATA_LEN;
+       os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+       return key;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_aka_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+       return key;
+}
+
+
+int eap_peer_aka_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_aka_init;
+       eap->deinit = eap_aka_deinit;
+       eap->process = eap_aka_process;
+       eap->isKeyAvailable = eap_aka_isKeyAvailable;
+       eap->getKey = eap_aka_getKey;
+       eap->has_reauth_data = eap_aka_has_reauth_data;
+       eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+       eap->init_for_reauth = eap_aka_init_for_reauth;
+       eap->get_identity = eap_aka_get_identity;
+       eap->get_emsk = eap_aka_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
+
+
+#ifdef EAP_AKA_PRIME
+int eap_peer_aka_prime_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
+                                   "AKA'");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_aka_prime_init;
+       eap->deinit = eap_aka_deinit;
+       eap->process = eap_aka_process;
+       eap->isKeyAvailable = eap_aka_isKeyAvailable;
+       eap->getKey = eap_aka_getKey;
+       eap->has_reauth_data = eap_aka_has_reauth_data;
+       eap->deinit_for_reauth = eap_aka_deinit_for_reauth;
+       eap->init_for_reauth = eap_aka_init_for_reauth;
+       eap->get_identity = eap_aka_get_identity;
+       eap->get_emsk = eap_aka_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+
+       return ret;
+}
+#endif /* EAP_AKA_PRIME */
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
new file mode 100644 (file)
index 0000000..b64b68f
--- /dev/null
@@ -0,0 +1,669 @@
+/*
+ * EAP peer configuration data
+ * Copyright (c) 2003-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 EAP_CONFIG_H
+#define EAP_CONFIG_H
+
+/**
+ * struct eap_peer_config - EAP peer configuration/credentials
+ */
+struct eap_peer_config {
+       /**
+        * identity - EAP Identity
+        *
+        * This field is used to set the real user identity or NAI (for
+        * EAP-PSK/PAX/SAKE/GPSK).
+        */
+       u8 *identity;
+
+       /**
+        * identity_len - EAP Identity length
+        */
+       size_t identity_len;
+
+       /**
+        * anonymous_identity -  Anonymous EAP Identity
+        *
+        * This field is used for unencrypted use with EAP types that support
+        * different tunnelled identity, e.g., EAP-TTLS, in order to reveal the
+        * real identity (identity field) only to the authentication server.
+        *
+        * If not set, the identity field will be used for both unencrypted and
+        * protected fields.
+        */
+       u8 *anonymous_identity;
+
+       /**
+        * anonymous_identity_len - Length of anonymous_identity
+        */
+       size_t anonymous_identity_len;
+
+       /**
+        * password - Password string for EAP
+        *
+        * This field can include either the plaintext password (default
+        * option) or a NtPasswordHash (16-byte MD4 hash of the unicode
+        * presentation of the password) if flags field has
+        * EAP_CONFIG_FLAGS_PASSWORD_NTHASH bit set to 1. NtPasswordHash can
+        * only be used with authentication mechanism that use this hash as the
+        * starting point for operation: MSCHAP and MSCHAPv2 (EAP-MSCHAPv2,
+        * EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+        *
+        * In addition, this field is used to configure a pre-shared key for
+        * EAP-PSK/PAX/SAKE/GPSK. The length of the PSK must be 16 for EAP-PSK
+        * and EAP-PAX and 32 for EAP-SAKE. EAP-GPSK can use a variable length
+        * PSK.
+        */
+       u8 *password;
+
+       /**
+        * password_len - Length of password field
+        */
+       size_t password_len;
+
+       /**
+        * ca_cert - File path to CA certificate file (PEM/DER)
+        *
+        * This file can have one or more trusted CA certificates. If ca_cert
+        * and ca_path are not included, server certificate will not be
+        * verified. This is insecure and a trusted CA certificate should
+        * always be configured when using EAP-TLS/TTLS/PEAP. Full path to the
+        * file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        *
+        * Alternatively, this can be used to only perform matching of the
+        * server certificate (SHA-256 hash of the DER encoded X.509
+        * certificate). In this case, the possible CA certificates in the
+        * server certificate chain are ignored and only the server certificate
+        * is verified. This is configured with the following format:
+        * hash:://server/sha256/cert_hash_in_hex
+        * For example: "hash://server/sha256/
+        * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+        *
+        * On Windows, trusted CA certificates can be loaded from the system
+        * certificate store by setting this to cert_store://name, e.g.,
+        * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+        * Note that when running wpa_supplicant as an application, the user
+        * certificate store (My user account) is used, whereas computer store
+        * (Computer account) is used when running wpasvc as a service.
+        */
+       u8 *ca_cert;
+
+       /**
+        * ca_path - Directory path for CA certificate files (PEM)
+        *
+        * This path may contain multiple CA certificates in OpenSSL format.
+        * Common use for this is to point to system trusted CA list which is
+        * often installed into directory like /etc/ssl/certs. If configured,
+        * these certificates are added to the list of trusted CAs. ca_cert
+        * may also be included in that case, but it is not required.
+        */
+       u8 *ca_path;
+
+       /**
+        * client_cert - File path to client certificate file (PEM/DER)
+        *
+        * This field is used with EAP method that use TLS authentication.
+        * Usually, this is only configured for EAP-TLS, even though this could
+        * in theory be used with EAP-TTLS and EAP-PEAP, too. Full path to the
+        * file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *client_cert;
+
+       /**
+        * private_key - File path to client private key file (PEM/DER/PFX)
+        *
+        * When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+        * commented out. Both the private key and certificate will be read
+        * from the PKCS#12 file in this case. Full path to the file should be
+        * used since working directory may change when wpa_supplicant is run
+        * in the background.
+        *
+        * Windows certificate store can be used by leaving client_cert out and
+        * configuring private_key in one of the following formats:
+        *
+        * cert://substring_to_match
+        *
+        * hash://certificate_thumbprint_in_hex
+        *
+        * For example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+        *
+        * Note that when running wpa_supplicant as an application, the user
+        * certificate store (My user account) is used, whereas computer store
+        * (Computer account) is used when running wpasvc as a service.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *private_key;
+
+       /**
+        * private_key_passwd - Password for private key file
+        *
+        * If left out, this will be asked through control interface.
+        */
+       u8 *private_key_passwd;
+
+       /**
+        * 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. Full path to the
+        * file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *dh_file;
+
+       /**
+        * subject_match - Constraint for server certificate subject
+        *
+        * This substring is matched against the subject of the authentication
+        * server certificate. If this string is set, the server sertificate is
+        * only accepted if it contains this string in the subject. The subject
+        * string is in following format:
+        *
+        * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+        */
+       u8 *subject_match;
+
+       /**
+        * altsubject_match - Constraint for server certificate alt. subject
+        *
+        * Semicolon separated string of entries to be matched against the
+        * alternative subject name of the authentication server certificate.
+        * If this string is set, the server sertificate is only accepted if it
+        * contains one of the entries in an alternative subject name
+        * extension.
+        *
+        * altSubjectName string is in following format: TYPE:VALUE
+        *
+        * Example: EMAIL:server@example.com
+        * Example: DNS:server.example.com;DNS:server2.example.com
+        *
+        * Following types are supported: EMAIL, DNS, URI
+        */
+       u8 *altsubject_match;
+
+       /**
+        * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
+        *
+        * This file can have one or more trusted CA certificates. If ca_cert2
+        * and ca_path2 are not included, server certificate will not be
+        * verified. This is insecure and a trusted CA certificate should
+        * always be configured. Full path to the file should be used since
+        * working directory may change when wpa_supplicant is run in the
+        * background.
+        *
+        * This field is like ca_cert, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *ca_cert2;
+
+       /**
+        * ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
+        *
+        * This path may contain multiple CA certificates in OpenSSL format.
+        * Common use for this is to point to system trusted CA list which is
+        * often installed into directory like /etc/ssl/certs. If configured,
+        * these certificates are added to the list of trusted CAs. ca_cert
+        * may also be included in that case, but it is not required.
+        *
+        * This field is like ca_path, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       u8 *ca_path2;
+
+       /**
+        * client_cert2 - File path to client certificate file
+        *
+        * This field is like client_cert, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+        * file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *client_cert2;
+
+       /**
+        * private_key2 - File path to client private key file
+        *
+        * This field is like private_key, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+        * file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *private_key2;
+
+       /**
+        * private_key2_passwd -  Password for private key file
+        *
+        * This field is like private_key_passwd, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       u8 *private_key2_passwd;
+
+       /**
+        * dh_file2 - File path to DH/DSA parameters file (in PEM format)
+        *
+        * This field is like dh_file, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication. Full path to the
+        * file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        *
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       u8 *dh_file2;
+
+       /**
+        * subject_match2 - Constraint for server certificate subject
+        *
+        * This field is like subject_match, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       u8 *subject_match2;
+
+       /**
+        * altsubject_match2 - Constraint for server certificate alt. subject
+        *
+        * This field is like altsubject_match, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       u8 *altsubject_match2;
+
+       /**
+        * eap_methods - Allowed EAP methods
+        *
+        * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
+        * allowed EAP methods or %NULL if all methods are accepted.
+        */
+       struct eap_method_type *eap_methods;
+
+       /**
+        * phase1 - Phase 1 (outer authentication) parameters
+        *
+        * String with field-value pairs, e.g., "peapver=0" or
+        * "peapver=1 peaplabel=1".
+        *
+        * 'peapver' can be used to force which PEAP version (0 or 1) is used.
+        *
+        * 'peaplabel=1' can be used to force new label, "client PEAP
+        * encryption", to be used during key derivation when PEAPv1 or newer.
+        *
+        * Most existing PEAPv1 implementation seem to be using the old label,
+        * "client EAP encryption", and wpa_supplicant is now using that as the
+        * default value.
+        *
+        * Some servers, e.g., Radiator, may require peaplabel=1 configuration
+        * to interoperate with PEAPv1; see eap_testing.txt for more details.
+        *
+        * 'peap_outer_success=0' can be used to terminate PEAP authentication
+        * on tunneled EAP-Success. This is required with some RADIUS servers
+        * that implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+        * Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode).
+        *
+        * include_tls_length=1 can be used to force wpa_supplicant to include
+        * TLS Message Length field in all TLS messages even if they are not
+        * fragmented.
+        *
+        * sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+        * challenges (by default, it accepts 2 or 3).
+        *
+        * result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+        * protected result indication.
+        *
+        * fast_provisioning option can be used to enable in-line provisioning
+        * of EAP-FAST credentials (PAC):
+        * 0 = disabled,
+        * 1 = allow unauthenticated provisioning,
+        * 2 = allow authenticated provisioning,
+        * 3 = allow both unauthenticated and authenticated provisioning
+        *
+        * fast_max_pac_list_len=num option can be used to set the maximum
+        * number of PAC entries to store in a PAC list (default: 10).
+        *
+        * fast_pac_format=binary option can be used to select binary format
+        * for storing PAC entries in order to save some space (the default
+        * text format uses about 2.5 times the size of minimal binary format).
+        *
+        * crypto_binding option can be used to control PEAPv0 cryptobinding
+        * behavior:
+        * 0 = do not use cryptobinding (default)
+        * 1 = use cryptobinding if server supports it
+        * 2 = require cryptobinding
+        *
+        * EAP-WSC (WPS) uses following options: pin=Device_Password and
+        * uuid=Device_UUID
+        */
+       char *phase1;
+
+       /**
+        * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
+        *
+        * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+        * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
+        */
+       char *phase2;
+
+       /**
+        * pcsc - Parameters for PC/SC smartcard interface for USIM and GSM SIM
+        *
+        * This field is used to configure PC/SC smartcard interface.
+        * Currently, the only configuration is whether this field is %NULL (do
+        * not use PC/SC) or non-NULL (e.g., "") to enable PC/SC.
+        *
+        * This field is used for EAP-SIM and EAP-AKA.
+        */
+       char *pcsc;
+
+       /**
+        * pin - PIN for USIM, GSM SIM, and smartcards
+        *
+        * This field is used to configure PIN for SIM and smartcards for
+        * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+        * smartcard is used for private key operations.
+        *
+        * If left out, this will be asked through control interface.
+        */
+       char *pin;
+
+       /**
+        * engine - Enable OpenSSL engine (e.g., for smartcard access)
+        *
+        * This is used if private key operations for EAP-TLS are performed
+        * using a smartcard.
+        */
+       int engine;
+
+       /**
+        * engine_id - Engine ID for OpenSSL engine
+        *
+        * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+        * engine.
+        *
+        * This is used if private key operations for EAP-TLS are performed
+        * using a smartcard.
+        */
+       char *engine_id;
+
+       /**
+        * engine2 - Enable OpenSSL engine (e.g., for smartcard) (Phase 2)
+        *
+        * This is used if private key operations for EAP-TLS are performed
+        * using a smartcard.
+        *
+        * This field is like engine, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       int engine2;
+
+
+       /**
+        * pin2 - PIN for USIM, GSM SIM, and smartcards (Phase 2)
+        *
+        * This field is used to configure PIN for SIM and smartcards for
+        * EAP-SIM and EAP-AKA. In addition, this is used with EAP-TLS if a
+        * smartcard is used for private key operations.
+        *
+        * This field is like pin2, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        *
+        * If left out, this will be asked through control interface.
+        */
+       char *pin2;
+
+       /**
+        * engine2_id - Engine ID for OpenSSL engine (Phase 2)
+        *
+        * "opensc" to select OpenSC engine or "pkcs11" to select PKCS#11
+        * engine.
+        *
+        * This is used if private key operations for EAP-TLS are performed
+        * using a smartcard.
+        *
+        * This field is like engine_id, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       char *engine2_id;
+
+
+       /**
+        * key_id - Key ID for OpenSSL engine
+        *
+        * This is used if private key operations for EAP-TLS are performed
+        * using a smartcard.
+        */
+       char *key_id;
+
+       /**
+        * cert_id - Cert ID for OpenSSL engine
+        *
+        * This is used if the certificate operations for EAP-TLS are performed
+        * using a smartcard.
+        */
+       char *cert_id;
+
+       /**
+        * ca_cert_id - CA Cert ID for OpenSSL engine
+        *
+        * This is used if the CA certificate for EAP-TLS is on a smartcard.
+        */
+       char *ca_cert_id;
+
+       /**
+        * key2_id - Key ID for OpenSSL engine (phase2)
+        *
+        * This is used if private key operations for EAP-TLS are performed
+        * using a smartcard.
+        */
+       char *key2_id;
+
+       /**
+        * cert2_id - Cert ID for OpenSSL engine (phase2)
+        *
+        * This is used if the certificate operations for EAP-TLS are performed
+        * using a smartcard.
+        */
+       char *cert2_id;
+
+       /**
+        * ca_cert2_id - CA Cert ID for OpenSSL engine (phase2)
+        *
+        * This is used if the CA certificate for EAP-TLS is on a smartcard.
+        */
+       char *ca_cert2_id;
+
+       /**
+        * otp - One-time-password
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when OTP is entered through the control interface.
+        */
+       u8 *otp;
+
+       /**
+        * otp_len - Length of the otp field
+        */
+       size_t otp_len;
+
+       /**
+        * pending_req_identity - Whether there is a pending identity request
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request needed
+        * information.
+        */
+       int pending_req_identity;
+
+       /**
+        * pending_req_password - Whether there is a pending password request
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request needed
+        * information.
+        */
+       int pending_req_password;
+
+       /**
+        * pending_req_pin - Whether there is a pending PIN request
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request needed
+        * information.
+        */
+       int pending_req_pin;
+
+       /**
+        * pending_req_new_password - Pending password update request
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request needed
+        * information.
+        */
+       int pending_req_new_password;
+
+       /**
+        * pending_req_passphrase - Pending passphrase request
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request needed
+        * information.
+        */
+       int pending_req_passphrase;
+
+       /**
+        * pending_req_otp - Whether there is a pending OTP request
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request needed
+        * information.
+        */
+       char *pending_req_otp;
+
+       /**
+        * pending_req_otp_len - Length of the pending OTP request
+        */
+       size_t pending_req_otp_len;
+
+       /**
+        * pac_file - File path or blob name for the PAC entries (EAP-FAST)
+        *
+        * wpa_supplicant will need to be able to create this file and write
+        * updates to it when PAC is being provisioned or refreshed. Full path
+        * to the file should be used since working directory may change when
+        * wpa_supplicant is run in the background.
+        * Alternatively, a named configuration blob can be used by setting
+        * this to blob://blob_name.
+        */
+       char *pac_file;
+
+       /**
+        * mschapv2_retry - MSCHAPv2 retry in progress
+        *
+        * This field is used internally by EAP-MSCHAPv2 and should not be set
+        * as part of configuration.
+        */
+       int mschapv2_retry;
+
+       /**
+        * new_password - New password for password update
+        *
+        * This field is used during MSCHAPv2 password update. This is normally
+        * requested from the user through the control interface and not set
+        * from configuration.
+        */
+       u8 *new_password;
+
+       /**
+        * new_password_len - Length of new_password field
+        */
+       size_t new_password_len;
+
+       /**
+        * fragment_size - Maximum EAP fragment size in bytes (default 1398)
+        *
+        * This value limits the fragment size for EAP methods that support
+        * fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+        * small enough to make the EAP messages fit in MTU of the network
+        * interface used for EAPOL. The default value is suitable for most
+        * cases.
+        */
+       int fragment_size;
+
+#define EAP_CONFIG_FLAGS_PASSWORD_NTHASH BIT(0)
+       /**
+        * flags - Network configuration flags (bitfield)
+        *
+        * This variable is used for internal flags to describe further details
+        * for the network parameters.
+        * bit 0 = password is represented as a 16-byte NtPasswordHash value
+        *         instead of plaintext password
+        */
+       u32 flags;
+};
+
+
+/**
+ * struct wpa_config_blob - Named configuration blob
+ *
+ * This data structure is used to provide storage for binary objects to store
+ * abstract information like certificates and private keys inlined with the
+ * configuration data.
+ */
+struct wpa_config_blob {
+       /**
+        * name - Blob name
+        */
+       char *name;
+
+       /**
+        * data - Pointer to binary data
+        */
+       u8 *data;
+
+       /**
+        * len - Length of binary data
+        */
+       size_t len;
+
+       /**
+        * next - Pointer to next blob in the configuration
+        */
+       struct wpa_config_blob *next;
+};
+
+#endif /* EAP_CONFIG_H */
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
new file mode 100644 (file)
index 0000000..5d3e69d
--- /dev/null
@@ -0,0 +1,1712 @@
+/*
+ * EAP peer method: EAP-FAST (RFC 4851)
+ * 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 "crypto/tls.h"
+#include "crypto/sha1.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "eap_fast_pac.h"
+
+#ifdef EAP_FAST_DYNAMIC
+#include "eap_fast_pac.c"
+#endif /* EAP_FAST_DYNAMIC */
+
+/* TODO:
+ * - test session resumption and enable it if it interoperates
+ * - password change (pending mschapv2 packet; replay decrypted packet)
+ */
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_fast_data {
+       struct eap_ssl_data ssl;
+
+       int fast_version;
+
+       const struct eap_method *phase2_method;
+       void *phase2_priv;
+       int phase2_success;
+
+       struct eap_method_type phase2_type;
+       struct eap_method_type *phase2_types;
+       size_t num_phase2_types;
+       int resuming; /* starting a resumed session */
+       struct eap_fast_key_block_provisioning *key_block_p;
+#define EAP_FAST_PROV_UNAUTH 1
+#define EAP_FAST_PROV_AUTH 2
+       int provisioning_allowed; /* Allowed PAC provisioning modes */
+       int provisioning; /* doing PAC provisioning (not the normal auth) */
+       int anon_provisioning; /* doing anonymous (unauthenticated)
+                               * provisioning */
+       int session_ticket_used;
+
+       u8 key_data[EAP_FAST_KEY_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       int success;
+
+       struct eap_fast_pac *pac;
+       struct eap_fast_pac *current_pac;
+       size_t max_pac_list_len;
+       int use_pac_binary_format;
+
+       u8 simck[EAP_FAST_SIMCK_LEN];
+       int simck_idx;
+
+       struct wpabuf *pending_phase2_req;
+};
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+                                     const u8 *client_random,
+                                     const u8 *server_random,
+                                     u8 *master_secret)
+{
+       struct eap_fast_data *data = ctx;
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+
+       if (client_random == NULL || server_random == NULL ||
+           master_secret == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket failed - fall "
+                          "back to full TLS handshake");
+               data->session_ticket_used = 0;
+               if (data->provisioning_allowed) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Try to provision a "
+                                  "new PAC-Key");
+                       data->provisioning = 1;
+                       data->current_pac = NULL;
+               }
+               return 0;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket", ticket, len);
+
+       if (data->current_pac == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key available for "
+                          "using SessionTicket");
+               data->session_ticket_used = 0;
+               return 0;
+       }
+
+       eap_fast_derive_master_secret(data->current_pac->pac_key,
+                                     server_random, client_random,
+                                     master_secret);
+
+       data->session_ticket_used = 1;
+
+       return 1;
+}
+
+
+static int eap_fast_parse_phase1(struct eap_fast_data *data,
+                                const char *phase1)
+{
+       const char *pos;
+
+       pos = os_strstr(phase1, "fast_provisioning=");
+       if (pos) {
+               data->provisioning_allowed = atoi(pos + 18);
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Automatic PAC provisioning "
+                          "mode: %d", data->provisioning_allowed);
+       }
+
+       pos = os_strstr(phase1, "fast_max_pac_list_len=");
+       if (pos) {
+               data->max_pac_list_len = atoi(pos + 22);
+               if (data->max_pac_list_len == 0)
+                       data->max_pac_list_len = 1;
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Maximum PAC list length: %lu",
+                          (unsigned long) data->max_pac_list_len);
+       }
+
+       pos = os_strstr(phase1, "fast_pac_format=binary");
+       if (pos) {
+               data->use_pac_binary_format = 1;
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Using binary format for PAC "
+                          "list");
+       }
+
+       return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+       struct eap_fast_data *data;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->fast_version = EAP_FAST_VERSION;
+       data->max_pac_list_len = 10;
+
+       if (config && config->phase1 &&
+           eap_fast_parse_phase1(data, config->phase1) < 0) {
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
+       if (eap_peer_select_phase2_methods(config, "auth=",
+                                          &data->phase2_types,
+                                          &data->num_phase2_types) < 0) {
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
+       data->phase2_type.vendor = EAP_VENDOR_IETF;
+       data->phase2_type.method = EAP_TYPE_NONE;
+
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
+       if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+                                                eap_fast_session_ticket_cb,
+                                                data) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+                          "callback");
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
+       /*
+        * The local RADIUS server in a Cisco AP does not seem to like empty
+        * fragments before data, so disable that workaround for CBC.
+        * TODO: consider making this configurable
+        */
+       if (tls_connection_enable_workaround(sm->ssl_ctx, data->ssl.conn)) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to enable TLS "
+                          "workarounds");
+       }
+
+       if (data->use_pac_binary_format &&
+           eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
+       if (!data->use_pac_binary_format &&
+           eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) {
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+       eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+
+       if (data->pac == NULL && !data->provisioning_allowed) {
+               wpa_printf(MSG_INFO, "EAP-FAST: No PAC configured and "
+                          "provisioning disabled");
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_fast_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       struct eap_fast_pac *pac, *prev;
+
+       if (data == NULL)
+               return;
+       if (data->phase2_priv && data->phase2_method)
+               data->phase2_method->deinit(sm, data->phase2_priv);
+       os_free(data->phase2_types);
+       os_free(data->key_block_p);
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);
+
+       pac = data->pac;
+       prev = NULL;
+       while (pac) {
+               prev = pac;
+               pac = pac->next;
+               eap_fast_free_pac(prev);
+       }
+       wpabuf_free(data->pending_phase2_req);
+       os_free(data);
+}
+
+
+static int eap_fast_derive_msk(struct eap_fast_data *data)
+{
+       eap_fast_derive_eap_msk(data->simck, data->key_data);
+       eap_fast_derive_eap_emsk(data->simck, data->emsk);
+       data->success = 1;
+       return 0;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+                                    struct eap_fast_data *data)
+{
+       u8 *sks;
+
+       /* RFC 4851, Section 5.1:
+        * Extra key material after TLS key_block: session_key_seed[40]
+        */
+
+       sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+                                 EAP_FAST_SKS_LEN);
+       if (sks == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+                          "session_key_seed");
+               return;
+       }
+
+       /*
+        * RFC 4851, Section 5.2:
+        * S-IMCK[0] = session_key_seed
+        */
+       wpa_hexdump_key(MSG_DEBUG,
+                       "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+                       sks, EAP_FAST_SKS_LEN);
+       data->simck_idx = 0;
+       os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+       os_free(sks);
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+                                            struct eap_fast_data *data)
+{
+       os_free(data->key_block_p);
+       data->key_block_p = (struct eap_fast_key_block_provisioning *)
+               eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+                                   "key expansion",
+                                   sizeof(*data->key_block_p));
+       if (data->key_block_p == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+               return;
+       }
+       /*
+        * RFC 4851, Section 5.2:
+        * S-IMCK[0] = session_key_seed
+        */
+       wpa_hexdump_key(MSG_DEBUG,
+                       "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+                       data->key_block_p->session_key_seed,
+                       sizeof(data->key_block_p->session_key_seed));
+       data->simck_idx = 0;
+       os_memcpy(data->simck, data->key_block_p->session_key_seed,
+                 EAP_FAST_SIMCK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+                       data->key_block_p->server_challenge,
+                       sizeof(data->key_block_p->server_challenge));
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+                       data->key_block_p->client_challenge,
+                       sizeof(data->key_block_p->client_challenge));
+}
+
+
+static void eap_fast_derive_keys(struct eap_sm *sm, struct eap_fast_data *data)
+{
+       if (data->anon_provisioning)
+               eap_fast_derive_key_provisioning(sm, data);
+       else
+               eap_fast_derive_key_auth(sm, data);
+}
+
+
+static int eap_fast_init_phase2_method(struct eap_sm *sm,
+                                      struct eap_fast_data *data)
+{
+       data->phase2_method =
+               eap_peer_get_eap_method(data->phase2_type.vendor,
+                                       data->phase2_type.method);
+       if (data->phase2_method == NULL)
+               return -1;
+
+       if (data->key_block_p) {
+               sm->auth_challenge = data->key_block_p->server_challenge;
+               sm->peer_challenge = data->key_block_p->client_challenge;
+       }
+       sm->init_phase2 = 1;
+       data->phase2_priv = data->phase2_method->init(sm);
+       sm->init_phase2 = 0;
+       sm->auth_challenge = NULL;
+       sm->peer_challenge = NULL;
+
+       return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static int eap_fast_select_phase2_method(struct eap_fast_data *data, u8 type)
+{
+       size_t i;
+
+       /* TODO: TNC with anonymous provisioning; need to require both
+        * completed MSCHAPv2 and TNC */
+
+       if (data->anon_provisioning && type != EAP_TYPE_MSCHAPV2) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Only EAP-MSCHAPv2 is allowed "
+                          "during unauthenticated provisioning; reject phase2"
+                          " type %d", type);
+               return -1;
+       }
+
+#ifdef EAP_TNC
+       if (type == EAP_TYPE_TNC) {
+               data->phase2_type.vendor = EAP_VENDOR_IETF;
+               data->phase2_type.method = EAP_TYPE_TNC;
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+                          "vendor %d method %d for TNC",
+                          data->phase2_type.vendor,
+                          data->phase2_type.method);
+               return 0;
+       }
+#endif /* EAP_TNC */
+
+       for (i = 0; i < data->num_phase2_types; i++) {
+               if (data->phase2_types[i].vendor != EAP_VENDOR_IETF ||
+                   data->phase2_types[i].method != type)
+                       continue;
+
+               data->phase2_type.vendor = data->phase2_types[i].vendor;
+               data->phase2_type.method = data->phase2_types[i].method;
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Selected Phase 2 EAP "
+                          "vendor %d method %d",
+                          data->phase2_type.vendor,
+                          data->phase2_type.method);
+               break;
+       }
+
+       if (type != data->phase2_type.method || type == EAP_TYPE_NONE)
+               return -1;
+
+       return 0;
+}
+
+
+static int eap_fast_phase2_request(struct eap_sm *sm,
+                                  struct eap_fast_data *data,
+                                  struct eap_method_ret *ret,
+                                  struct eap_hdr *hdr,
+                                  struct wpabuf **resp)
+{
+       size_t len = be_to_host16(hdr->length);
+       u8 *pos;
+       struct eap_method_ret iret;
+       struct eap_peer_config *config = eap_get_config(sm);
+       struct wpabuf msg;
+
+       if (len <= sizeof(struct eap_hdr)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: too short "
+                          "Phase 2 request (len=%lu)", (unsigned long) len);
+               return -1;
+       }
+       pos = (u8 *) (hdr + 1);
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 Request: type=%d", *pos);
+       if (*pos == EAP_TYPE_IDENTITY) {
+               *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+               return 0;
+       }
+
+       if (data->phase2_priv && data->phase2_method &&
+           *pos != data->phase2_type.method) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 EAP sequence - "
+                          "deinitialize previous method");
+               data->phase2_method->deinit(sm, data->phase2_priv);
+               data->phase2_method = NULL;
+               data->phase2_priv = NULL;
+               data->phase2_type.vendor = EAP_VENDOR_IETF;
+               data->phase2_type.method = EAP_TYPE_NONE;
+       }
+
+       if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+           data->phase2_type.method == EAP_TYPE_NONE &&
+           eap_fast_select_phase2_method(data, *pos) < 0) {
+               if (eap_peer_tls_phase2_nak(data->phase2_types,
+                                           data->num_phase2_types,
+                                           hdr, resp))
+                       return -1;
+               return 0;
+       }
+
+       if (data->phase2_priv == NULL &&
+           eap_fast_init_phase2_method(sm, data) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize "
+                          "Phase 2 EAP method %d", *pos);
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return -1;
+       }
+
+       os_memset(&iret, 0, sizeof(iret));
+       wpabuf_set(&msg, hdr, len);
+       *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+                                            &msg);
+       if (*resp == NULL ||
+           (iret.methodState == METHOD_DONE &&
+            iret.decision == DECISION_FAIL)) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+       } else if ((iret.methodState == METHOD_DONE ||
+                   iret.methodState == METHOD_MAY_CONT) &&
+                  (iret.decision == DECISION_UNCOND_SUCC ||
+                   iret.decision == DECISION_COND_SUCC)) {
+               data->phase2_success = 1;
+       }
+
+       if (*resp == NULL && config &&
+           (config->pending_req_identity || config->pending_req_password ||
+            config->pending_req_otp || config->pending_req_new_password)) {
+               wpabuf_free(data->pending_phase2_req);
+               data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+       } else if (*resp == NULL)
+               return -1;
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_fast_tlv_nak(int vendor_id, int tlv_type)
+{
+       struct wpabuf *buf;
+       struct eap_tlv_nak_tlv *nak;
+       buf = wpabuf_alloc(sizeof(*nak));
+       if (buf == NULL)
+               return NULL;
+       nak = wpabuf_put(buf, sizeof(*nak));
+       nak->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY | EAP_TLV_NAK_TLV);
+       nak->length = host_to_be16(6);
+       nak->vendor_id = host_to_be32(vendor_id);
+       nak->nak_type = host_to_be16(tlv_type);
+       return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_result(int status, int intermediate)
+{
+       struct wpabuf *buf;
+       struct eap_tlv_intermediate_result_tlv *result;
+       buf = wpabuf_alloc(sizeof(*result));
+       if (buf == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Add %sResult TLV(status=%d)",
+                  intermediate ? "Intermediate " : "", status);
+       result = wpabuf_put(buf, sizeof(*result));
+       result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                       (intermediate ?
+                                        EAP_TLV_INTERMEDIATE_RESULT_TLV :
+                                        EAP_TLV_RESULT_TLV));
+       result->length = host_to_be16(2);
+       result->status = host_to_be16(status);
+       return buf;
+}
+
+
+static struct wpabuf * eap_fast_tlv_pac_ack(void)
+{
+       struct wpabuf *buf;
+       struct eap_tlv_result_tlv *res;
+       struct eap_tlv_pac_ack_tlv *ack;
+
+       buf = wpabuf_alloc(sizeof(*res) + sizeof(*ack));
+       if (buf == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV (ack)");
+       ack = wpabuf_put(buf, sizeof(*ack));
+       ack->tlv_type = host_to_be16(EAP_TLV_PAC_TLV |
+                                    EAP_TLV_TYPE_MANDATORY);
+       ack->length = host_to_be16(sizeof(*ack) - sizeof(struct eap_tlv_hdr));
+       ack->pac_type = host_to_be16(PAC_TYPE_PAC_ACKNOWLEDGEMENT);
+       ack->pac_len = host_to_be16(2);
+       ack->result = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+
+       return buf;
+}
+
+
+static struct wpabuf * eap_fast_process_eap_payload_tlv(
+       struct eap_sm *sm, struct eap_fast_data *data,
+       struct eap_method_ret *ret, const struct eap_hdr *req,
+       u8 *eap_payload_tlv, size_t eap_payload_tlv_len)
+{
+       struct eap_hdr *hdr;
+       struct wpabuf *resp = NULL;
+
+       if (eap_payload_tlv_len < sizeof(*hdr)) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: too short EAP "
+                          "Payload TLV (len=%lu)",
+                          (unsigned long) eap_payload_tlv_len);
+               return NULL;
+       }
+
+       hdr = (struct eap_hdr *) eap_payload_tlv;
+       if (be_to_host16(hdr->length) > eap_payload_tlv_len) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: EAP packet overflow in "
+                          "EAP Payload TLV");
+               return NULL;
+       }
+
+       if (hdr->code != EAP_CODE_REQUEST) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+                          "Phase 2 EAP header", hdr->code);
+               return NULL;
+       }
+
+       if (eap_fast_phase2_request(sm, data, ret, hdr, &resp)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Phase2 Request processing "
+                          "failed");
+               return NULL;
+       }
+
+       return eap_fast_tlv_eap_payload(resp);
+}
+
+
+static int eap_fast_validate_crypto_binding(
+       struct eap_tlv_crypto_binding_tlv *_bind)
+{
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV: Version %d "
+                  "Received Version %d SubType %d",
+                  _bind->version, _bind->received_version, _bind->subtype);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+                   _bind->nonce, sizeof(_bind->nonce));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+                   _bind->compound_mac, sizeof(_bind->compound_mac));
+
+       if (_bind->version != EAP_FAST_VERSION ||
+           _bind->received_version != EAP_FAST_VERSION ||
+           _bind->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Invalid version/subtype in "
+                          "Crypto-Binding TLV: Version %d "
+                          "Received Version %d SubType %d",
+                          _bind->version, _bind->received_version,
+                          _bind->subtype);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void eap_fast_write_crypto_binding(
+       struct eap_tlv_crypto_binding_tlv *rbind,
+       struct eap_tlv_crypto_binding_tlv *_bind, const u8 *cmk)
+{
+       rbind->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                      EAP_TLV_CRYPTO_BINDING_TLV);
+       rbind->length = host_to_be16(sizeof(*rbind) -
+                                    sizeof(struct eap_tlv_hdr));
+       rbind->version = EAP_FAST_VERSION;
+       rbind->received_version = _bind->version;
+       rbind->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE;
+       os_memcpy(rbind->nonce, _bind->nonce, sizeof(_bind->nonce));
+       inc_byte_array(rbind->nonce, sizeof(rbind->nonce));
+       hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) rbind, sizeof(*rbind),
+                 rbind->compound_mac);
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: Version %d "
+                  "Received Version %d SubType %d",
+                  rbind->version, rbind->received_version, rbind->subtype);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+                   rbind->nonce, sizeof(rbind->nonce));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+                   rbind->compound_mac, sizeof(rbind->compound_mac));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+                                  struct eap_fast_data *data,
+                                  u8 *isk, size_t isk_len)
+{
+       u8 *key;
+       size_t key_len;
+
+       os_memset(isk, 0, isk_len);
+
+       if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+                          "available");
+               return -1;
+       }
+
+       if (data->phase2_method->isKeyAvailable == NULL ||
+           data->phase2_method->getKey == NULL)
+               return 0;
+
+       if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+           (key = data->phase2_method->getKey(sm, data->phase2_priv,
+                                              &key_len)) == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+                          "from Phase 2");
+               return -1;
+       }
+
+       if (key_len > isk_len)
+               key_len = isk_len;
+       if (key_len == 32 &&
+           data->phase2_method->vendor == EAP_VENDOR_IETF &&
+           data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+               /*
+                * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+                * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+                * ISK for EAP-FAST cryptobinding.
+                */
+               os_memcpy(isk, key + 16, 16);
+               os_memcpy(isk + 16, key, 16);
+       } else
+               os_memcpy(isk, key, key_len);
+       os_free(key);
+
+       return 0;
+}
+
+
+static int eap_fast_get_cmk(struct eap_sm *sm, struct eap_fast_data *data,
+                           u8 *cmk)
+{
+       u8 isk[32], imck[60];
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Determining CMK[%d] for Compound MIC "
+                  "calculation", data->simck_idx + 1);
+
+       /*
+        * RFC 4851, Section 5.2:
+        * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+        *                 MSK[j], 60)
+        * S-IMCK[j] = first 40 octets of IMCK[j]
+        * CMK[j] = last 20 octets of IMCK[j]
+        */
+
+       if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+               return -1;
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+       sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+                  "Inner Methods Compound Keys",
+                  isk, sizeof(isk), imck, sizeof(imck));
+       data->simck_idx++;
+       os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+                       data->simck, EAP_FAST_SIMCK_LEN);
+       os_memcpy(cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+                       cmk, EAP_FAST_CMK_LEN);
+
+       return 0;
+}
+
+
+static u8 * eap_fast_write_pac_request(u8 *pos, u16 pac_type)
+{
+       struct eap_tlv_hdr *pac;
+       struct eap_tlv_request_action_tlv *act;
+       struct eap_tlv_pac_type_tlv *type;
+
+       act = (struct eap_tlv_request_action_tlv *) pos;
+       act->tlv_type = host_to_be16(EAP_TLV_REQUEST_ACTION_TLV);
+       act->length = host_to_be16(2);
+       act->action = host_to_be16(EAP_TLV_ACTION_PROCESS_TLV);
+
+       pac = (struct eap_tlv_hdr *) (act + 1);
+       pac->tlv_type = host_to_be16(EAP_TLV_PAC_TLV);
+       pac->length = host_to_be16(sizeof(*type));
+
+       type = (struct eap_tlv_pac_type_tlv *) (pac + 1);
+       type->tlv_type = host_to_be16(PAC_TYPE_PAC_TYPE);
+       type->length = host_to_be16(2);
+       type->pac_type = host_to_be16(pac_type);
+
+       return (u8 *) (type + 1);
+}
+
+
+static struct wpabuf * eap_fast_process_crypto_binding(
+       struct eap_sm *sm, struct eap_fast_data *data,
+       struct eap_method_ret *ret,
+       struct eap_tlv_crypto_binding_tlv *_bind, size_t bind_len)
+{
+       struct wpabuf *resp;
+       u8 *pos;
+       u8 cmk[EAP_FAST_CMK_LEN], cmac[SHA1_MAC_LEN];
+       int res;
+       size_t len;
+
+       if (eap_fast_validate_crypto_binding(_bind) < 0)
+               return NULL;
+
+       if (eap_fast_get_cmk(sm, data, cmk) < 0)
+               return NULL;
+
+       /* Validate received Compound MAC */
+       os_memcpy(cmac, _bind->compound_mac, sizeof(cmac));
+       os_memset(_bind->compound_mac, 0, sizeof(cmac));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for Compound "
+                   "MAC calculation", (u8 *) _bind, bind_len);
+       hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
+                 _bind->compound_mac);
+       res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
+                   cmac, sizeof(cmac));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
+                   _bind->compound_mac, sizeof(cmac));
+       if (res != 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not match");
+               os_memcpy(_bind->compound_mac, cmac, sizeof(cmac));
+               return NULL;
+       }
+
+       /*
+        * Compound MAC was valid, so authentication succeeded. Reply with
+        * crypto binding to allow server to complete authentication.
+        */
+
+       len = sizeof(struct eap_tlv_crypto_binding_tlv);
+       resp = wpabuf_alloc(len);
+       if (resp == NULL)
+               return NULL;
+
+       if (!data->anon_provisioning && data->phase2_success &&
+           eap_fast_derive_msk(data) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to generate MSK");
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               data->phase2_success = 0;
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       pos = wpabuf_put(resp, sizeof(struct eap_tlv_crypto_binding_tlv));
+       eap_fast_write_crypto_binding((struct eap_tlv_crypto_binding_tlv *)
+                                     pos, _bind, cmk);
+
+       return resp;
+}
+
+
+static void eap_fast_parse_pac_tlv(struct eap_fast_pac *entry, int type,
+                                  u8 *pos, size_t len, int *pac_key_found)
+{
+       switch (type & 0x7fff) {
+       case PAC_TYPE_PAC_KEY:
+               wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key", pos, len);
+               if (len != EAP_FAST_PAC_KEY_LEN) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid PAC-Key "
+                                  "length %lu", (unsigned long) len);
+                       break;
+               }
+               *pac_key_found = 1;
+               os_memcpy(entry->pac_key, pos, len);
+               break;
+       case PAC_TYPE_PAC_OPAQUE:
+               wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque", pos, len);
+               entry->pac_opaque = pos;
+               entry->pac_opaque_len = len;
+               break;
+       case PAC_TYPE_PAC_INFO:
+               wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info", pos, len);
+               entry->pac_info = pos;
+               entry->pac_info_len = len;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC type %d",
+                          type);
+               break;
+       }
+}
+
+
+static int eap_fast_process_pac_tlv(struct eap_fast_pac *entry,
+                                   u8 *pac, size_t pac_len)
+{
+       struct pac_tlv_hdr *hdr;
+       u8 *pos;
+       size_t left, len;
+       int type, pac_key_found = 0;
+
+       pos = pac;
+       left = pac_len;
+
+       while (left > sizeof(*hdr)) {
+               hdr = (struct pac_tlv_hdr *) pos;
+               type = be_to_host16(hdr->type);
+               len = be_to_host16(hdr->len);
+               pos += sizeof(*hdr);
+               left -= sizeof(*hdr);
+               if (len > left) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV overrun "
+                                  "(type=%d len=%lu left=%lu)",
+                                  type, (unsigned long) len,
+                                  (unsigned long) left);
+                       return -1;
+               }
+
+               eap_fast_parse_pac_tlv(entry, type, pos, len, &pac_key_found);
+
+               pos += len;
+               left -= len;
+       }
+
+       if (!pac_key_found || !entry->pac_opaque || !entry->pac_info) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV does not include "
+                          "all the required fields");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_parse_pac_info(struct eap_fast_pac *entry, int type,
+                                  u8 *pos, size_t len)
+{
+       u16 pac_type;
+       u32 lifetime;
+       struct os_time now;
+
+       switch (type & 0x7fff) {
+       case PAC_TYPE_CRED_LIFETIME:
+               if (len != 4) {
+                       wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Info - "
+                                   "Invalid CRED_LIFETIME length - ignored",
+                                   pos, len);
+                       return 0;
+               }
+
+               /*
+                * This is not currently saved separately in PAC files since
+                * the server can automatically initiate PAC update when
+                * needed. Anyway, the information is available from PAC-Info
+                * dump if it is needed for something in the future.
+                */
+               lifetime = WPA_GET_BE32(pos);
+               os_get_time(&now);
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - CRED_LIFETIME %d "
+                          "(%d days)",
+                          lifetime, (lifetime - (u32) now.sec) / 86400);
+               break;
+       case PAC_TYPE_A_ID:
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID",
+                                 pos, len);
+               entry->a_id = pos;
+               entry->a_id_len = len;
+               break;
+       case PAC_TYPE_I_ID:
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - I-ID",
+                                 pos, len);
+               entry->i_id = pos;
+               entry->i_id_len = len;
+               break;
+       case PAC_TYPE_A_ID_INFO:
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: PAC-Info - A-ID-Info",
+                                 pos, len);
+               entry->a_id_info = pos;
+               entry->a_id_info_len = len;
+               break;
+       case PAC_TYPE_PAC_TYPE:
+               /* RFC 5422, Section 4.2.6 - PAC-Type TLV */
+               if (len != 2) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC-Type "
+                                  "length %lu (expected 2)",
+                                  (unsigned long) len);
+                       wpa_hexdump_ascii(MSG_DEBUG,
+                                         "EAP-FAST: PAC-Info - PAC-Type",
+                                         pos, len);
+                       return -1;
+               }
+               pac_type = WPA_GET_BE16(pos);
+               if (pac_type != PAC_TYPE_TUNNEL_PAC &&
+                   pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+                   pac_type != PAC_TYPE_MACHINE_AUTHENTICATION) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: Unsupported PAC Type "
+                                  "%d", pac_type);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info - PAC-Type %d",
+                          pac_type);
+               entry->pac_type = pac_type;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored unknown PAC-Info "
+                          "type %d", type);
+               break;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_process_pac_info(struct eap_fast_pac *entry)
+{
+       struct pac_tlv_hdr *hdr;
+       u8 *pos;
+       size_t left, len;
+       int type;
+
+       /* RFC 5422, Section 4.2.4 */
+
+       /* PAC-Type defaults to Tunnel PAC (Type 1) */
+       entry->pac_type = PAC_TYPE_TUNNEL_PAC;
+
+       pos = entry->pac_info;
+       left = entry->pac_info_len;
+       while (left > sizeof(*hdr)) {
+               hdr = (struct pac_tlv_hdr *) pos;
+               type = be_to_host16(hdr->type);
+               len = be_to_host16(hdr->len);
+               pos += sizeof(*hdr);
+               left -= sizeof(*hdr);
+               if (len > left) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info overrun "
+                                  "(type=%d len=%lu left=%lu)",
+                                  type, (unsigned long) len,
+                                  (unsigned long) left);
+                       return -1;
+               }
+
+               if (eap_fast_parse_pac_info(entry, type, pos, len) < 0)
+                       return -1;
+
+               pos += len;
+               left -= len;
+       }
+
+       if (entry->a_id == NULL || entry->a_id_info == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Info does not include "
+                          "all the required fields");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
+                                           struct eap_fast_data *data,
+                                           struct eap_method_ret *ret,
+                                           u8 *pac, size_t pac_len)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       struct eap_fast_pac entry;
+
+       os_memset(&entry, 0, sizeof(entry));
+       if (eap_fast_process_pac_tlv(&entry, pac, pac_len) ||
+           eap_fast_process_pac_info(&entry))
+               return NULL;
+
+       eap_fast_add_pac(&data->pac, &data->current_pac, &entry);
+       eap_fast_pac_list_truncate(data->pac, data->max_pac_list_len);
+       if (data->use_pac_binary_format)
+               eap_fast_save_pac_bin(sm, data->pac, config->pac_file);
+       else
+               eap_fast_save_pac(sm, data->pac, config->pac_file);
+
+       if (data->provisioning) {
+               if (data->anon_provisioning) {
+                       /*
+                        * Unauthenticated provisioning does not provide keying
+                        * material and must end with an EAP-Failure.
+                        * Authentication will be done separately after this.
+                        */
+                       data->success = 0;
+                       ret->decision = DECISION_FAIL;
+               } else {
+                       /*
+                        * Server may or may not allow authenticated
+                        * provisioning also for key generation.
+                        */
+                       ret->decision = DECISION_COND_SUCC;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+                          "- Provisioning completed successfully");
+       } else {
+               /*
+                * This is PAC refreshing, i.e., normal authentication that is
+                * expected to be completed with an EAP-Success.
+                */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
+                          "- PAC refreshing completed successfully");
+               ret->decision = DECISION_UNCOND_SUCC;
+       }
+       ret->methodState = METHOD_DONE;
+       return eap_fast_tlv_pac_ack();
+}
+
+
+static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
+                                   struct eap_fast_tlv_parse *tlv,
+                                   struct wpabuf **resp)
+{
+       int mandatory, tlv_type, len, res;
+       u8 *pos, *end;
+
+       os_memset(tlv, 0, sizeof(*tlv));
+
+       /* Parse TLVs from the decrypted Phase 2 data */
+       pos = wpabuf_mhead(decrypted);
+       end = pos + wpabuf_len(decrypted);
+       while (pos + 4 < end) {
+               mandatory = pos[0] & 0x80;
+               tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+               pos += 2;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + len > end) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+                          "TLV type %d length %d%s",
+                          tlv_type, len, mandatory ? " (mandatory)" : "");
+
+               res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
+               if (res == -2)
+                       break;
+               if (res < 0) {
+                       if (mandatory) {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+                                          "mandatory TLV type %d", tlv_type);
+                               *resp = eap_fast_tlv_nak(0, tlv_type);
+                               break;
+                       } else {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: ignored "
+                                          "unknown optional TLV type %d",
+                                          tlv_type);
+                       }
+               }
+
+               pos += len;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_encrypt_response(struct eap_sm *sm,
+                                    struct eap_fast_data *data,
+                                    struct wpabuf *resp,
+                                    u8 identifier, struct wpabuf **out_data)
+{
+       if (resp == NULL)
+               return 0;
+
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 data",
+                       resp);
+       if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+                                data->fast_version, identifier,
+                                resp, out_data)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
+                          "frame");
+       }
+       wpabuf_free(resp);
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_fast_pac_request(void)
+{
+       struct wpabuf *tmp;
+       u8 *pos, *pos2;
+
+       tmp = wpabuf_alloc(sizeof(struct eap_tlv_hdr) +
+                          sizeof(struct eap_tlv_request_action_tlv) +
+                          sizeof(struct eap_tlv_pac_type_tlv));
+       if (tmp == NULL)
+               return NULL;
+
+       pos = wpabuf_put(tmp, 0);
+       pos2 = eap_fast_write_pac_request(pos, PAC_TYPE_TUNNEL_PAC);
+       wpabuf_put(tmp, pos2 - pos);
+       return tmp;
+}
+
+
+static int eap_fast_process_decrypted(struct eap_sm *sm,
+                                     struct eap_fast_data *data,
+                                     struct eap_method_ret *ret,
+                                     const struct eap_hdr *req,
+                                     struct wpabuf *decrypted,
+                                     struct wpabuf **out_data)
+{
+       struct wpabuf *resp = NULL, *tmp;
+       struct eap_fast_tlv_parse tlv;
+       int failed = 0;
+
+       if (eap_fast_parse_decrypted(decrypted, &tlv, &resp) < 0)
+               return 0;
+       if (resp)
+               return eap_fast_encrypt_response(sm, data, resp,
+                                                req->identifier, out_data);
+
+       if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+               resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+               return eap_fast_encrypt_response(sm, data, resp,
+                                                req->identifier, out_data);
+       }
+
+       if (tlv.iresult == EAP_TLV_RESULT_FAILURE) {
+               resp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 1);
+               return eap_fast_encrypt_response(sm, data, resp,
+                                                req->identifier, out_data);
+       }
+
+       if (tlv.crypto_binding) {
+               tmp = eap_fast_process_crypto_binding(sm, data, ret,
+                                                     tlv.crypto_binding,
+                                                     tlv.crypto_binding_len);
+               if (tmp == NULL)
+                       failed = 1;
+               else
+                       resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (tlv.iresult == EAP_TLV_RESULT_SUCCESS) {
+               tmp = eap_fast_tlv_result(failed ? EAP_TLV_RESULT_FAILURE :
+                                         EAP_TLV_RESULT_SUCCESS, 1);
+               resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (tlv.eap_payload_tlv) {
+               tmp = eap_fast_process_eap_payload_tlv(
+                       sm, data, ret, req, tlv.eap_payload_tlv,
+                       tlv.eap_payload_tlv_len);
+               resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (tlv.pac && tlv.result != EAP_TLV_RESULT_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV without Result TLV "
+                          "acknowledging success");
+               failed = 1;
+       } else if (tlv.pac && tlv.result == EAP_TLV_RESULT_SUCCESS) {
+               tmp = eap_fast_process_pac(sm, data, ret, tlv.pac,
+                                          tlv.pac_len);
+               resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (data->current_pac == NULL && data->provisioning &&
+           !data->anon_provisioning && !tlv.pac &&
+           (tlv.iresult == EAP_TLV_RESULT_SUCCESS ||
+            tlv.result == EAP_TLV_RESULT_SUCCESS)) {
+               /*
+                * Need to request Tunnel PAC when using authenticated
+                * provisioning.
+                */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Request Tunnel PAC");
+               tmp = eap_fast_pac_request();
+               resp = wpabuf_concat(resp, tmp);
+       }
+
+       if (tlv.result == EAP_TLV_RESULT_SUCCESS && !failed) {
+               tmp = eap_fast_tlv_result(EAP_TLV_RESULT_SUCCESS, 0);
+               resp = wpabuf_concat(tmp, resp);
+       } else if (failed) {
+               tmp = eap_fast_tlv_result(EAP_TLV_RESULT_FAILURE, 0);
+               resp = wpabuf_concat(tmp, resp);
+       }
+
+       if (resp && tlv.result == EAP_TLV_RESULT_SUCCESS && !failed &&
+           tlv.crypto_binding && data->phase2_success) {
+               if (data->anon_provisioning) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Unauthenticated "
+                                  "provisioning completed successfully.");
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+                                  "completed successfully.");
+                       if (data->provisioning)
+                               ret->methodState = METHOD_MAY_CONT;
+                       else
+                               ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_UNCOND_SUCC;
+               }
+       }
+
+       if (resp == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: No recognized TLVs - send "
+                          "empty response packet");
+               resp = wpabuf_alloc(1);
+       }
+
+       return eap_fast_encrypt_response(sm, data, resp, req->identifier,
+                                        out_data);
+}
+
+
+static int eap_fast_decrypt(struct eap_sm *sm, struct eap_fast_data *data,
+                           struct eap_method_ret *ret,
+                           const struct eap_hdr *req,
+                           const struct wpabuf *in_data,
+                           struct wpabuf **out_data)
+{
+       struct wpabuf *in_decrypted;
+       int res;
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+                  " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+       if (data->pending_phase2_req) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Pending Phase 2 request - "
+                          "skip decryption and use old data");
+               /* Clear TLS reassembly state. */
+               eap_peer_tls_reset_input(&data->ssl);
+
+               in_decrypted = data->pending_phase2_req;
+               data->pending_phase2_req = NULL;
+               goto continue_req;
+       }
+
+       if (wpabuf_len(in_data) == 0) {
+               /* Received TLS ACK - requesting more fragments */
+               return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_FAST,
+                                           data->fast_version,
+                                           req->identifier, NULL, out_data);
+       }
+
+       res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+       if (res)
+               return res;
+
+continue_req:
+       wpa_hexdump_buf(MSG_MSGDUMP, "EAP-FAST: Decrypted Phase 2 TLV(s)",
+                       in_decrypted);
+
+       if (wpabuf_len(in_decrypted) < 4) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+                          "TLV frame (len=%lu)",
+                          (unsigned long) wpabuf_len(in_decrypted));
+               wpabuf_free(in_decrypted);
+               return -1;
+       }
+
+       res = eap_fast_process_decrypted(sm, data, ret, req,
+                                        in_decrypted, out_data);
+
+       wpabuf_free(in_decrypted);
+
+       return res;
+}
+
+
+static const u8 * eap_fast_get_a_id(const u8 *buf, size_t len, size_t *id_len)
+{
+       const u8 *a_id;
+       struct pac_tlv_hdr *hdr;
+
+       /*
+        * Parse authority identity (A-ID) from the EAP-FAST/Start. This
+        * supports both raw A-ID and one inside an A-ID TLV.
+        */
+       a_id = buf;
+       *id_len = len;
+       if (len > sizeof(*hdr)) {
+               int tlen;
+               hdr = (struct pac_tlv_hdr *) buf;
+               tlen = be_to_host16(hdr->len);
+               if (be_to_host16(hdr->type) == PAC_TYPE_A_ID &&
+                   sizeof(*hdr) + tlen <= len) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: A-ID was in TLV "
+                                  "(Start)");
+                       a_id = (u8 *) (hdr + 1);
+                       *id_len = tlen;
+               }
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: A-ID", a_id, *id_len);
+
+       return a_id;
+}
+
+
+static void eap_fast_select_pac(struct eap_fast_data *data,
+                               const u8 *a_id, size_t a_id_len)
+{
+       data->current_pac = eap_fast_get_pac(data->pac, a_id, a_id_len,
+                                            PAC_TYPE_TUNNEL_PAC);
+       if (data->current_pac == NULL) {
+               /*
+                * Tunnel PAC was not available for this A-ID. Try to use
+                * Machine Authentication PAC, if one is available.
+                */
+               data->current_pac = eap_fast_get_pac(
+                       data->pac, a_id, a_id_len,
+                       PAC_TYPE_MACHINE_AUTHENTICATION);
+       }
+
+       if (data->current_pac) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC found for this A-ID "
+                          "(PAC-Type %d)", data->current_pac->pac_type);
+               wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-FAST: A-ID-Info",
+                                 data->current_pac->a_id_info,
+                                 data->current_pac->a_id_info_len);
+       }
+}
+
+
+static int eap_fast_use_pac_opaque(struct eap_sm *sm,
+                                  struct eap_fast_data *data,
+                                  struct eap_fast_pac *pac)
+{
+       u8 *tlv;
+       size_t tlv_len, olen;
+       struct eap_tlv_hdr *ehdr;
+
+       olen = pac->pac_opaque_len;
+       tlv_len = sizeof(*ehdr) + olen;
+       tlv = os_malloc(tlv_len);
+       if (tlv) {
+               ehdr = (struct eap_tlv_hdr *) tlv;
+               ehdr->tlv_type = host_to_be16(PAC_TYPE_PAC_OPAQUE);
+               ehdr->length = host_to_be16(olen);
+               os_memcpy(ehdr + 1, pac->pac_opaque, olen);
+       }
+       if (tlv == NULL ||
+           tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+                                           TLS_EXT_PAC_OPAQUE,
+                                           tlv, tlv_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to add PAC-Opaque TLS "
+                          "extension");
+               os_free(tlv);
+               return -1;
+       }
+       os_free(tlv);
+
+       return 0;
+}
+
+
+static int eap_fast_clear_pac_opaque_ext(struct eap_sm *sm,
+                                        struct eap_fast_data *data)
+{
+       if (tls_connection_client_hello_ext(sm->ssl_ctx, data->ssl.conn,
+                                           TLS_EXT_PAC_OPAQUE, NULL, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to remove PAC-Opaque "
+                          "TLS extension");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int eap_fast_set_provisioning_ciphers(struct eap_sm *sm,
+                                            struct eap_fast_data *data)
+{
+       u8 ciphers[5];
+       int count = 0;
+
+       if (data->provisioning_allowed & EAP_FAST_PROV_UNAUTH) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling unauthenticated "
+                          "provisioning TLS cipher suites");
+               ciphers[count++] = TLS_CIPHER_ANON_DH_AES128_SHA;
+       }
+
+       if (data->provisioning_allowed & EAP_FAST_PROV_AUTH) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Enabling authenticated "
+                          "provisioning TLS cipher suites");
+               ciphers[count++] = TLS_CIPHER_RSA_DHE_AES128_SHA;
+               ciphers[count++] = TLS_CIPHER_AES128_SHA;
+               ciphers[count++] = TLS_CIPHER_RC4_SHA;
+       }
+
+       ciphers[count++] = TLS_CIPHER_NONE;
+
+       if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+                                          ciphers)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Could not configure TLS "
+                          "cipher suites for provisioning");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_process_start(struct eap_sm *sm,
+                                 struct eap_fast_data *data, u8 flags,
+                                 const u8 *pos, size_t left)
+{
+       const u8 *a_id;
+       size_t a_id_len;
+
+       /* EAP-FAST Version negotiation (section 3.1) */
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)",
+                  flags & EAP_TLS_VERSION_MASK, data->fast_version);
+       if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version)
+               data->fast_version = flags & EAP_TLS_VERSION_MASK;
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d",
+                  data->fast_version);
+
+       a_id = eap_fast_get_a_id(pos, left, &a_id_len);
+       eap_fast_select_pac(data, a_id, a_id_len);
+
+       if (data->resuming && data->current_pac) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Trying to resume session - "
+                          "do not add PAC-Opaque to TLS ClientHello");
+               if (eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+                       return -1;
+       } else if (data->current_pac) {
+               /*
+                * PAC found for the A-ID and we are not resuming an old
+                * session, so add PAC-Opaque extension to ClientHello.
+                */
+               if (eap_fast_use_pac_opaque(sm, data, data->current_pac) < 0)
+                       return -1;
+       } else {
+               /* No PAC found, so we must provision one. */
+               if (!data->provisioning_allowed) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found and "
+                                  "provisioning disabled");
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC found - "
+                          "starting provisioning");
+               if (eap_fast_set_provisioning_ciphers(sm, data) < 0 ||
+                   eap_fast_clear_pac_opaque_ext(sm, data) < 0)
+                       return -1;
+               data->provisioning = 1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
+                                       struct eap_method_ret *ret,
+                                       const struct wpabuf *reqData)
+{
+       const struct eap_hdr *req;
+       size_t left;
+       int res;
+       u8 flags, id;
+       struct wpabuf *resp;
+       const u8 *pos;
+       struct eap_fast_data *data = priv;
+
+       pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_FAST, ret,
+                                       reqData, &left, &flags);
+       if (pos == NULL)
+               return NULL;
+
+       req = wpabuf_head(reqData);
+       id = req->identifier;
+
+       if (flags & EAP_TLS_FLAGS_START) {
+               if (eap_fast_process_start(sm, data, flags, pos, left) < 0)
+                       return NULL;
+
+               left = 0; /* A-ID is not used in further packet processing */
+       }
+
+       resp = NULL;
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+           !data->resuming) {
+               /* Process tunneled (encrypted) phase 2 data. */
+               struct wpabuf msg;
+               wpabuf_set(&msg, pos, left);
+               res = eap_fast_decrypt(sm, data, ret, req, &msg, &resp);
+               if (res < 0) {
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       /*
+                        * Ack possible Alert that may have caused failure in
+                        * decryption.
+                        */
+                       res = 1;
+               }
+       } else {
+               /* Continue processing TLS handshake (phase 1). */
+               res = eap_peer_tls_process_helper(sm, &data->ssl,
+                                                 EAP_TYPE_FAST,
+                                                 data->fast_version, id, pos,
+                                                 left, &resp);
+
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+                       char cipher[80];
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-FAST: TLS done, proceed to Phase 2");
+                       if (data->provisioning &&
+                           (!(data->provisioning_allowed &
+                              EAP_FAST_PROV_AUTH) ||
+                            tls_get_cipher(sm->ssl_ctx, data->ssl.conn,
+                                           cipher, sizeof(cipher)) < 0 ||
+                            os_strstr(cipher, "ADH-") ||
+                            os_strstr(cipher, "anon"))) {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Using "
+                                          "anonymous (unauthenticated) "
+                                          "provisioning");
+                               data->anon_provisioning = 1;
+                       } else
+                               data->anon_provisioning = 0;
+                       data->resuming = 0;
+                       eap_fast_derive_keys(sm, data);
+               }
+
+               if (res == 2) {
+                       struct wpabuf msg;
+                       /*
+                        * Application data included in the handshake message.
+                        */
+                       wpabuf_free(data->pending_phase2_req);
+                       data->pending_phase2_req = resp;
+                       resp = NULL;
+                       wpabuf_set(&msg, pos, left);
+                       res = eap_fast_decrypt(sm, data, ret, req, &msg,
+                                              &resp);
+               }
+       }
+
+       if (res == 1) {
+               wpabuf_free(resp);
+               return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
+                                             data->fast_version);
+       }
+
+       return resp;
+}
+
+
+#if 0 /* FIX */
+static Boolean eap_fast_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       os_free(data->key_block_p);
+       data->key_block_p = NULL;
+       wpabuf_free(data->pending_phase2_req);
+       data->pending_phase2_req = NULL;
+}
+
+
+static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+               os_free(data);
+               return NULL;
+       }
+       if (data->phase2_priv && data->phase2_method &&
+           data->phase2_method->init_for_reauth)
+               data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+       data->phase2_success = 0;
+       data->resuming = 1;
+       data->provisioning = 0;
+       data->anon_provisioning = 0;
+       data->simck_idx = 0;
+       return priv;
+}
+#endif
+
+
+static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
+                              size_t buflen, int verbose)
+{
+       struct eap_fast_data *data = priv;
+       int len, ret;
+
+       len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+       if (data->phase2_method) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "EAP-FAST Phase2 method=%s\n",
+                                 data->phase2_method->name);
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+       }
+       return len;
+}
+
+
+static Boolean eap_fast_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       return data->success;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_fast_data *data = priv;
+       u8 *key;
+
+       if (!data->success)
+               return NULL;
+
+       key = os_malloc(EAP_FAST_KEY_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_FAST_KEY_LEN;
+       os_memcpy(key, data->key_data, EAP_FAST_KEY_LEN);
+
+       return key;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_fast_data *data = priv;
+       u8 *key;
+
+       if (!data->success)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+       return key;
+}
+
+
+int eap_peer_fast_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_fast_init;
+       eap->deinit = eap_fast_deinit;
+       eap->process = eap_fast_process;
+       eap->isKeyAvailable = eap_fast_isKeyAvailable;
+       eap->getKey = eap_fast_getKey;
+       eap->get_status = eap_fast_get_status;
+#if 0
+       eap->has_reauth_data = eap_fast_has_reauth_data;
+       eap->deinit_for_reauth = eap_fast_deinit_for_reauth;
+       eap->init_for_reauth = eap_fast_init_for_reauth;
+#endif
+       eap->get_emsk = eap_fast_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_fast_pac.c b/src/eap_peer/eap_fast_pac.c
new file mode 100644 (file)
index 0000000..541cce5
--- /dev/null
@@ -0,0 +1,922 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_config.h"
+#include "eap_i.h"
+#include "eap_fast_pac.h"
+
+/* TODO: encrypt PAC-Key in the PAC file */
+
+
+/* Text data format */
+static const char *pac_file_hdr =
+       "wpa_supplicant EAP-FAST PAC file - version 1";
+
+/*
+ * Binary data format
+ * 4-octet magic value: 6A E4 92 0C
+ * 2-octet version (big endian)
+ * <version specific data>
+ *
+ * version=0:
+ * Sequence of PAC entries:
+ *   2-octet PAC-Type (big endian)
+ *   32-octet PAC-Key
+ *   2-octet PAC-Opaque length (big endian)
+ *   <variable len> PAC-Opaque data (length bytes)
+ *   2-octet PAC-Info length (big endian)
+ *   <variable len> PAC-Info data (length bytes)
+ */
+
+#define EAP_FAST_PAC_BINARY_MAGIC 0x6ae4920c
+#define EAP_FAST_PAC_BINARY_FORMAT_VERSION 0
+
+
+/**
+ * eap_fast_free_pac - Free PAC data
+ * @pac: Pointer to the PAC entry
+ *
+ * Note that the PAC entry must not be in a list since this function does not
+ * remove the list links.
+ */
+void eap_fast_free_pac(struct eap_fast_pac *pac)
+{
+       os_free(pac->pac_opaque);
+       os_free(pac->pac_info);
+       os_free(pac->a_id);
+       os_free(pac->i_id);
+       os_free(pac->a_id_info);
+       os_free(pac);
+}
+
+
+/**
+ * eap_fast_get_pac - Get a PAC entry based on A-ID
+ * @pac_root: Pointer to root of the PAC list
+ * @a_id: A-ID to search for
+ * @a_id_len: Length of A-ID
+ * @pac_type: PAC-Type to search for
+ * Returns: Pointer to the PAC entry, or %NULL if A-ID not found
+ */
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+                                      const u8 *a_id, size_t a_id_len,
+                                      u16 pac_type)
+{
+       struct eap_fast_pac *pac = pac_root;
+
+       while (pac) {
+               if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+                   os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+                       return pac;
+               }
+               pac = pac->next;
+       }
+       return NULL;
+}
+
+
+static void eap_fast_remove_pac(struct eap_fast_pac **pac_root,
+                               struct eap_fast_pac **pac_current,
+                               const u8 *a_id, size_t a_id_len, u16 pac_type)
+{
+       struct eap_fast_pac *pac, *prev;
+
+       pac = *pac_root;
+       prev = NULL;
+
+       while (pac) {
+               if (pac->pac_type == pac_type && pac->a_id_len == a_id_len &&
+                   os_memcmp(pac->a_id, a_id, a_id_len) == 0) {
+                       if (prev == NULL)
+                               *pac_root = pac->next;
+                       else
+                               prev->next = pac->next;
+                       if (*pac_current == pac)
+                               *pac_current = NULL;
+                       eap_fast_free_pac(pac);
+                       break;
+               }
+               prev = pac;
+               pac = pac->next;
+       }
+}
+
+
+static int eap_fast_copy_buf(u8 **dst, size_t *dst_len,
+                            const u8 *src, size_t src_len)
+{
+       if (src) {
+               *dst = os_malloc(src_len);
+               if (*dst == NULL)
+                       return -1;
+               os_memcpy(*dst, src, src_len);
+               *dst_len = src_len;
+       }
+       return 0;
+}
+
+
+/**
+ * eap_fast_add_pac - Add a copy of a PAC entry to a list
+ * @pac_root: Pointer to PAC list root pointer
+ * @pac_current: Pointer to the current PAC pointer
+ * @entry: New entry to clone and add to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function makes a clone of the given PAC entry and adds this copied
+ * entry to the list (pac_root). If an old entry for the same A-ID is found,
+ * it will be removed from the PAC list and in this case, pac_current entry
+ * is set to %NULL if it was the removed entry.
+ */
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+                    struct eap_fast_pac **pac_current,
+                    struct eap_fast_pac *entry)
+{
+       struct eap_fast_pac *pac;
+
+       if (entry == NULL || entry->a_id == NULL)
+               return -1;
+
+       /* Remove a possible old entry for the matching A-ID. */
+       eap_fast_remove_pac(pac_root, pac_current,
+                           entry->a_id, entry->a_id_len, entry->pac_type);
+
+       /* Allocate a new entry and add it to the list of PACs. */
+       pac = os_zalloc(sizeof(*pac));
+       if (pac == NULL)
+               return -1;
+
+       pac->pac_type = entry->pac_type;
+       os_memcpy(pac->pac_key, entry->pac_key, EAP_FAST_PAC_KEY_LEN);
+       if (eap_fast_copy_buf(&pac->pac_opaque, &pac->pac_opaque_len,
+                             entry->pac_opaque, entry->pac_opaque_len) < 0 ||
+           eap_fast_copy_buf(&pac->pac_info, &pac->pac_info_len,
+                             entry->pac_info, entry->pac_info_len) < 0 ||
+           eap_fast_copy_buf(&pac->a_id, &pac->a_id_len,
+                             entry->a_id, entry->a_id_len) < 0 ||
+           eap_fast_copy_buf(&pac->i_id, &pac->i_id_len,
+                             entry->i_id, entry->i_id_len) < 0 ||
+           eap_fast_copy_buf(&pac->a_id_info, &pac->a_id_info_len,
+                             entry->a_id_info, entry->a_id_info_len) < 0) {
+               eap_fast_free_pac(pac);
+               return -1;
+       }
+
+       pac->next = *pac_root;
+       *pac_root = pac;
+
+       return 0;
+}
+
+
+struct eap_fast_read_ctx {
+       FILE *f;
+       const char *pos;
+       const char *end;
+       int line;
+       char *buf;
+       size_t buf_len;
+};
+
+static int eap_fast_read_line(struct eap_fast_read_ctx *rc, char **value)
+{
+       char *pos;
+
+       rc->line++;
+       if (rc->f) {
+               if (fgets(rc->buf, rc->buf_len, rc->f) == NULL)
+                       return -1;
+       } else {
+               const char *l_end;
+               size_t len;
+               if (rc->pos >= rc->end)
+                       return -1;
+               l_end = rc->pos;
+               while (l_end < rc->end && *l_end != '\n')
+                       l_end++;
+               len = l_end - rc->pos;
+               if (len >= rc->buf_len)
+                       len = rc->buf_len - 1;
+               os_memcpy(rc->buf, rc->pos, len);
+               rc->buf[len] = '\0';
+               rc->pos = l_end + 1;
+       }
+
+       rc->buf[rc->buf_len - 1] = '\0';
+       pos = rc->buf;
+       while (*pos != '\0') {
+               if (*pos == '\n' || *pos == '\r') {
+                       *pos = '\0';
+                       break;
+               }
+               pos++;
+       }
+
+       pos = os_strchr(rc->buf, '=');
+       if (pos)
+               *pos++ = '\0';
+       *value = pos;
+
+       return 0;
+}
+
+
+static u8 * eap_fast_parse_hex(const char *value, size_t *len)
+{
+       int hlen;
+       u8 *buf;
+
+       if (value == NULL)
+               return NULL;
+       hlen = os_strlen(value);
+       if (hlen & 1)
+               return NULL;
+       *len = hlen / 2;
+       buf = os_malloc(*len);
+       if (buf == NULL)
+               return NULL;
+       if (hexstr2bin(value, buf, *len)) {
+               os_free(buf);
+               return NULL;
+       }
+       return buf;
+}
+
+
+static int eap_fast_init_pac_data(struct eap_sm *sm, const char *pac_file,
+                                 struct eap_fast_read_ctx *rc)
+{
+       os_memset(rc, 0, sizeof(*rc));
+
+       rc->buf_len = 2048;
+       rc->buf = os_malloc(rc->buf_len);
+       if (rc->buf == NULL)
+               return -1;
+
+       if (os_strncmp(pac_file, "blob://", 7) == 0) {
+               const struct wpa_config_blob *blob;
+               blob = eap_get_config_blob(sm, pac_file + 7);
+               if (blob == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+                                  "assume no PAC entries have been "
+                                  "provisioned", pac_file + 7);
+                       os_free(rc->buf);
+                       return -1;
+               }
+               rc->pos = (char *) blob->data;
+               rc->end = (char *) blob->data + blob->len;
+       } else {
+               rc->f = fopen(pac_file, "rb");
+               if (rc->f == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+                                  "assume no PAC entries have been "
+                                  "provisioned", pac_file);
+                       os_free(rc->buf);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+static void eap_fast_deinit_pac_data(struct eap_fast_read_ctx *rc)
+{
+       os_free(rc->buf);
+       if (rc->f)
+               fclose(rc->f);
+}
+
+
+static const char * eap_fast_parse_start(struct eap_fast_pac **pac)
+{
+       if (*pac)
+               return "START line without END";
+
+       *pac = os_zalloc(sizeof(struct eap_fast_pac));
+       if (*pac == NULL)
+               return "No memory for PAC entry";
+       (*pac)->pac_type = PAC_TYPE_TUNNEL_PAC;
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
+                                      struct eap_fast_pac **pac)
+{
+       if (*pac == NULL)
+               return "END line without START";
+       if (*pac_root) {
+               struct eap_fast_pac *end = *pac_root;
+               while (end->next)
+                       end = end->next;
+               end->next = *pac;
+       } else
+               *pac_root = *pac;
+
+       *pac = NULL;
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
+                                           char *pos)
+{
+       pac->pac_type = atoi(pos);
+       if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
+           pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
+           pac->pac_type != PAC_TYPE_MACHINE_AUTHENTICATION)
+               return "Unrecognized PAC-Type";
+
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_key(struct eap_fast_pac *pac, char *pos)
+{
+       u8 *key;
+       size_t key_len;
+
+       key = eap_fast_parse_hex(pos, &key_len);
+       if (key == NULL || key_len != EAP_FAST_PAC_KEY_LEN) {
+               os_free(key);
+               return "Invalid PAC-Key";
+       }
+
+       os_memcpy(pac->pac_key, key, EAP_FAST_PAC_KEY_LEN);
+       os_free(key);
+
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_pac_opaque(struct eap_fast_pac *pac,
+                                             char *pos)
+{
+       os_free(pac->pac_opaque);
+       pac->pac_opaque = eap_fast_parse_hex(pos, &pac->pac_opaque_len);
+       if (pac->pac_opaque == NULL)
+               return "Invalid PAC-Opaque";
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id(struct eap_fast_pac *pac, char *pos)
+{
+       os_free(pac->a_id);
+       pac->a_id = eap_fast_parse_hex(pos, &pac->a_id_len);
+       if (pac->a_id == NULL)
+               return "Invalid A-ID";
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_i_id(struct eap_fast_pac *pac, char *pos)
+{
+       os_free(pac->i_id);
+       pac->i_id = eap_fast_parse_hex(pos, &pac->i_id_len);
+       if (pac->i_id == NULL)
+               return "Invalid I-ID";
+       return NULL;
+}
+
+
+static const char * eap_fast_parse_a_id_info(struct eap_fast_pac *pac,
+                                            char *pos)
+{
+       os_free(pac->a_id_info);
+       pac->a_id_info = eap_fast_parse_hex(pos, &pac->a_id_info_len);
+       if (pac->a_id_info == NULL)
+               return "Invalid A-ID-Info";
+       return NULL;
+}
+
+
+/**
+ * eap_fast_load_pac - Load PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+                     const char *pac_file)
+{
+       struct eap_fast_read_ctx rc;
+       struct eap_fast_pac *pac = NULL;
+       int count = 0;
+       char *pos;
+       const char *err = NULL;
+
+       if (pac_file == NULL)
+               return -1;
+
+       if (eap_fast_init_pac_data(sm, pac_file, &rc) < 0)
+               return 0;
+
+       if (eap_fast_read_line(&rc, &pos) < 0 ||
+           os_strcmp(pac_file_hdr, rc.buf) != 0)
+               err = "Unrecognized header line";
+
+       while (!err && eap_fast_read_line(&rc, &pos) == 0) {
+               if (os_strcmp(rc.buf, "START") == 0)
+                       err = eap_fast_parse_start(&pac);
+               else if (os_strcmp(rc.buf, "END") == 0) {
+                       err = eap_fast_parse_end(pac_root, &pac);
+                       count++;
+               } else if (!pac)
+                       err = "Unexpected line outside START/END block";
+               else if (os_strcmp(rc.buf, "PAC-Type") == 0)
+                       err = eap_fast_parse_pac_type(pac, pos);
+               else if (os_strcmp(rc.buf, "PAC-Key") == 0)
+                       err = eap_fast_parse_pac_key(pac, pos);
+               else if (os_strcmp(rc.buf, "PAC-Opaque") == 0)
+                       err = eap_fast_parse_pac_opaque(pac, pos);
+               else if (os_strcmp(rc.buf, "A-ID") == 0)
+                       err = eap_fast_parse_a_id(pac, pos);
+               else if (os_strcmp(rc.buf, "I-ID") == 0)
+                       err = eap_fast_parse_i_id(pac, pos);
+               else if (os_strcmp(rc.buf, "A-ID-Info") == 0)
+                       err = eap_fast_parse_a_id_info(pac, pos);
+       }
+
+       if (pac) {
+               err = "PAC block not terminated with END";
+               eap_fast_free_pac(pac);
+       }
+
+       eap_fast_deinit_pac_data(&rc);
+
+       if (err) {
+               wpa_printf(MSG_INFO, "EAP-FAST: %s in '%s:%d'",
+                          err, pac_file, rc.line);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Read %d PAC entries from '%s'",
+                  count, pac_file);
+
+       return 0;
+}
+
+
+static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
+                          const char *field, const u8 *data,
+                          size_t len, int txt)
+{
+       size_t i, need;
+       int ret;
+       char *end;
+
+       if (data == NULL || buf == NULL || *buf == NULL ||
+           pos == NULL || *pos == NULL || *pos < *buf)
+               return;
+
+       need = os_strlen(field) + len * 2 + 30;
+       if (txt)
+               need += os_strlen(field) + len + 20;
+
+       if (*pos - *buf + need > *buf_len) {
+               char *nbuf = os_realloc(*buf, *buf_len + need);
+               if (nbuf == NULL) {
+                       os_free(*buf);
+                       *buf = NULL;
+                       return;
+               }
+               *buf = nbuf;
+               *buf_len += need;
+       }
+       end = *buf + *buf_len;
+
+       ret = os_snprintf(*pos, end - *pos, "%s=", field);
+       if (ret < 0 || ret >= end - *pos)
+               return;
+       *pos += ret;
+       *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
+       ret = os_snprintf(*pos, end - *pos, "\n");
+       if (ret < 0 || ret >= end - *pos)
+               return;
+       *pos += ret;
+
+       if (txt) {
+               ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
+               if (ret < 0 || ret >= end - *pos)
+                       return;
+               *pos += ret;
+               for (i = 0; i < len; i++) {
+                       ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
+                       if (ret < 0 || ret >= end - *pos)
+                               return;
+                       *pos += ret;
+               }
+               ret = os_snprintf(*pos, end - *pos, "\n");
+               if (ret < 0 || ret >= end - *pos)
+                       return;
+               *pos += ret;
+       }
+}
+
+
+static int eap_fast_write_pac(struct eap_sm *sm, const char *pac_file,
+                             char *buf, size_t len)
+{
+       if (os_strncmp(pac_file, "blob://", 7) == 0) {
+               struct wpa_config_blob *blob;
+               blob = os_zalloc(sizeof(*blob));
+               if (blob == NULL)
+                       return -1;
+               blob->data = (u8 *) buf;
+               blob->len = len;
+               buf = NULL;
+               blob->name = os_strdup(pac_file + 7);
+               if (blob->name == NULL) {
+                       os_free(blob);
+                       return -1;
+               }
+               eap_set_config_blob(sm, blob);
+       } else {
+               FILE *f;
+               f = fopen(pac_file, "wb");
+               if (f == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: Failed to open PAC "
+                                  "file '%s' for writing", pac_file);
+                       return -1;
+               }
+               if (fwrite(buf, 1, len, f) != len) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: Failed to write all "
+                                  "PACs into '%s'", pac_file);
+                       fclose(f);
+                       return -1;
+               }
+               os_free(buf);
+               fclose(f);
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
+                                char **pos, size_t *buf_len)
+{
+       int ret;
+
+       ret = os_snprintf(*pos, *buf + *buf_len - *pos,
+                         "START\nPAC-Type=%d\n", pac->pac_type);
+       if (ret < 0 || ret >= *buf + *buf_len - *pos)
+               return -1;
+
+       *pos += ret;
+       eap_fast_write(buf, pos, buf_len, "PAC-Key",
+                      pac->pac_key, EAP_FAST_PAC_KEY_LEN, 0);
+       eap_fast_write(buf, pos, buf_len, "PAC-Opaque",
+                      pac->pac_opaque, pac->pac_opaque_len, 0);
+       eap_fast_write(buf, pos, buf_len, "PAC-Info",
+                      pac->pac_info, pac->pac_info_len, 0);
+       eap_fast_write(buf, pos, buf_len, "A-ID",
+                      pac->a_id, pac->a_id_len, 0);
+       eap_fast_write(buf, pos, buf_len, "I-ID",
+                      pac->i_id, pac->i_id_len, 1);
+       eap_fast_write(buf, pos, buf_len, "A-ID-Info",
+                      pac->a_id_info, pac->a_id_info_len, 1);
+       if (*buf == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: No memory for PAC "
+                          "data");
+               return -1;
+       }
+       ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
+       if (ret < 0 || ret >= *buf + *buf_len - *pos)
+               return -1;
+       *pos += ret;
+
+       return 0;
+}
+
+
+/**
+ * eap_fast_save_pac - Save PAC entries (text format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+                     const char *pac_file)
+{
+       struct eap_fast_pac *pac;
+       int ret, count = 0;
+       char *buf, *pos;
+       size_t buf_len;
+
+       if (pac_file == NULL)
+               return -1;
+
+       buf_len = 1024;
+       pos = buf = os_malloc(buf_len);
+       if (buf == NULL)
+               return -1;
+
+       ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
+       if (ret < 0 || ret >= buf + buf_len - pos) {
+               os_free(buf);
+               return -1;
+       }
+       pos += ret;
+
+       pac = pac_root;
+       while (pac) {
+               if (eap_fast_add_pac_data(pac, &buf, &pos, &buf_len)) {
+                       os_free(buf);
+                       return -1;
+               }
+               count++;
+               pac = pac->next;
+       }
+
+       if (eap_fast_write_pac(sm, pac_file, buf, pos - buf)) {
+               os_free(buf);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %d PAC entries into '%s'",
+                  count, pac_file);
+
+       return 0;
+}
+
+
+/**
+ * eap_fast_pac_list_truncate - Truncate a PAC list to the given length
+ * @pac_root: Root of the PAC list
+ * @max_len: Maximum length of the list (>= 1)
+ * Returns: Number of PAC entries removed
+ */
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+                                 size_t max_len)
+{
+       struct eap_fast_pac *pac, *prev;
+       size_t count;
+
+       pac = pac_root;
+       prev = NULL;
+       count = 0;
+
+       while (pac) {
+               count++;
+               if (count > max_len)
+                       break;
+               prev = pac;
+               pac = pac->next;
+       }
+
+       if (count <= max_len || prev == NULL)
+               return 0;
+
+       count = 0;
+       prev->next = NULL;
+
+       while (pac) {
+               prev = pac;
+               pac = pac->next;
+               eap_fast_free_pac(prev);
+               count++;
+       }
+
+       return count;
+}
+
+
+static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
+{
+       u8 *pos, *end;
+       u16 type, len;
+
+       pos = pac->pac_info;
+       end = pos + pac->pac_info_len;
+
+       while (pos + 4 < end) {
+               type = WPA_GET_BE16(pos);
+               pos += 2;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + len > end)
+                       break;
+
+               if (type == PAC_TYPE_A_ID) {
+                       os_free(pac->a_id);
+                       pac->a_id = os_malloc(len);
+                       if (pac->a_id == NULL)
+                               break;
+                       os_memcpy(pac->a_id, pos, len);
+                       pac->a_id_len = len;
+               }
+
+               if (type == PAC_TYPE_A_ID_INFO) {
+                       os_free(pac->a_id_info);
+                       pac->a_id_info = os_malloc(len);
+                       if (pac->a_id_info == NULL)
+                               break;
+                       os_memcpy(pac->a_id_info, pos, len);
+                       pac->a_id_info_len = len;
+               }
+
+               pos += len;
+       }
+}
+
+
+/**
+ * eap_fast_load_pac_bin - Load PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Pointer to root of the PAC list (to be filled)
+ * @pac_file: Name of the PAC file/blob to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+                         const char *pac_file)
+{
+       const struct wpa_config_blob *blob = NULL;
+       u8 *buf, *end, *pos;
+       size_t len, count = 0;
+       struct eap_fast_pac *pac, *prev;
+
+       *pac_root = NULL;
+
+       if (pac_file == NULL)
+               return -1;
+
+       if (os_strncmp(pac_file, "blob://", 7) == 0) {
+               blob = eap_get_config_blob(sm, pac_file + 7);
+               if (blob == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: No PAC blob '%s' - "
+                                  "assume no PAC entries have been "
+                                  "provisioned", pac_file + 7);
+                       return 0;
+               }
+               buf = blob->data;
+               len = blob->len;
+       } else {
+               buf = (u8 *) os_readfile(pac_file, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: No PAC file '%s' - "
+                                  "assume no PAC entries have been "
+                                  "provisioned", pac_file);
+                       return 0;
+               }
+       }
+
+       if (len == 0) {
+               if (blob == NULL)
+                       os_free(buf);
+               return 0;
+       }
+
+       if (len < 6 || WPA_GET_BE32(buf) != EAP_FAST_PAC_BINARY_MAGIC ||
+           WPA_GET_BE16(buf + 4) != EAP_FAST_PAC_BINARY_FORMAT_VERSION) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Invalid PAC file '%s' (bin)",
+                          pac_file);
+               if (blob == NULL)
+                       os_free(buf);
+               return -1;
+       }
+
+       pac = prev = NULL;
+       pos = buf + 6;
+       end = buf + len;
+       while (pos < end) {
+               if (end - pos < 2 + 32 + 2 + 2)
+                       goto parse_fail;
+
+               pac = os_zalloc(sizeof(*pac));
+               if (pac == NULL)
+                       goto parse_fail;
+
+               pac->pac_type = WPA_GET_BE16(pos);
+               pos += 2;
+               os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
+               pos += EAP_FAST_PAC_KEY_LEN;
+               pac->pac_opaque_len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + pac->pac_opaque_len + 2 > end)
+                       goto parse_fail;
+               pac->pac_opaque = os_malloc(pac->pac_opaque_len);
+               if (pac->pac_opaque == NULL)
+                       goto parse_fail;
+               os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
+               pos += pac->pac_opaque_len;
+               pac->pac_info_len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + pac->pac_info_len > end)
+                       goto parse_fail;
+               pac->pac_info = os_malloc(pac->pac_info_len);
+               if (pac->pac_info == NULL)
+                       goto parse_fail;
+               os_memcpy(pac->pac_info, pos, pac->pac_info_len);
+               pos += pac->pac_info_len;
+               eap_fast_pac_get_a_id(pac);
+
+               count++;
+               if (prev)
+                       prev->next = pac;
+               else
+                       *pac_root = pac;
+               prev = pac;
+       }
+
+       if (blob == NULL)
+               os_free(buf);
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Read %lu PAC entries from '%s' (bin)",
+                  (unsigned long) count, pac_file);
+
+       return 0;
+
+parse_fail:
+       wpa_printf(MSG_INFO, "EAP-FAST: Failed to parse PAC file '%s' (bin)",
+                  pac_file);
+       if (blob == NULL)
+               os_free(buf);
+       if (pac)
+               eap_fast_free_pac(pac);
+       return -1;
+}
+
+
+/**
+ * eap_fast_save_pac_bin - Save PAC entries (binary format)
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @pac_root: Root of the PAC list
+ * @pac_file: Name of the PAC file/blob
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+                         const char *pac_file)
+{
+       size_t len, count = 0;
+       struct eap_fast_pac *pac;
+       u8 *buf, *pos;
+
+       len = 6;
+       pac = pac_root;
+       while (pac) {
+               if (pac->pac_opaque_len > 65535 ||
+                   pac->pac_info_len > 65535)
+                       return -1;
+               len += 2 + EAP_FAST_PAC_KEY_LEN + 2 + pac->pac_opaque_len +
+                       2 + pac->pac_info_len;
+               pac = pac->next;
+       }
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       pos = buf;
+       WPA_PUT_BE32(pos, EAP_FAST_PAC_BINARY_MAGIC);
+       pos += 4;
+       WPA_PUT_BE16(pos, EAP_FAST_PAC_BINARY_FORMAT_VERSION);
+       pos += 2;
+
+       pac = pac_root;
+       while (pac) {
+               WPA_PUT_BE16(pos, pac->pac_type);
+               pos += 2;
+               os_memcpy(pos, pac->pac_key, EAP_FAST_PAC_KEY_LEN);
+               pos += EAP_FAST_PAC_KEY_LEN;
+               WPA_PUT_BE16(pos, pac->pac_opaque_len);
+               pos += 2;
+               os_memcpy(pos, pac->pac_opaque, pac->pac_opaque_len);
+               pos += pac->pac_opaque_len;
+               WPA_PUT_BE16(pos, pac->pac_info_len);
+               pos += 2;
+               os_memcpy(pos, pac->pac_info, pac->pac_info_len);
+               pos += pac->pac_info_len;
+
+               pac = pac->next;
+               count++;
+       }
+
+       if (eap_fast_write_pac(sm, pac_file, (char *) buf, len)) {
+               os_free(buf);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Wrote %lu PAC entries into '%s' "
+                  "(bin)", (unsigned long) count, pac_file);
+
+       return 0;
+}
diff --git a/src/eap_peer/eap_fast_pac.h b/src/eap_peer/eap_fast_pac.h
new file mode 100644 (file)
index 0000000..9483f96
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * EAP peer method: EAP-FAST PAC file processing
+ * 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 EAP_FAST_PAC_H
+#define EAP_FAST_PAC_H
+
+#include "eap_common/eap_fast_common.h"
+
+struct eap_fast_pac {
+       struct eap_fast_pac *next;
+
+       u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+       u8 *pac_opaque;
+       size_t pac_opaque_len;
+       u8 *pac_info;
+       size_t pac_info_len;
+       u8 *a_id;
+       size_t a_id_len;
+       u8 *i_id;
+       size_t i_id_len;
+       u8 *a_id_info;
+       size_t a_id_info_len;
+       u16 pac_type;
+};
+
+
+void eap_fast_free_pac(struct eap_fast_pac *pac);
+struct eap_fast_pac * eap_fast_get_pac(struct eap_fast_pac *pac_root,
+                                      const u8 *a_id, size_t a_id_len,
+                                      u16 pac_type);
+int eap_fast_add_pac(struct eap_fast_pac **pac_root,
+                    struct eap_fast_pac **pac_current,
+                    struct eap_fast_pac *entry);
+int eap_fast_load_pac(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+                     const char *pac_file);
+int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+                     const char *pac_file);
+size_t eap_fast_pac_list_truncate(struct eap_fast_pac *pac_root,
+                                 size_t max_len);
+int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
+                         const char *pac_file);
+int eap_fast_save_pac_bin(struct eap_sm *sm, struct eap_fast_pac *pac_root,
+                         const char *pac_file);
+
+#endif /* EAP_FAST_PAC_H */
diff --git a/src/eap_peer/eap_gpsk.c b/src/eap_peer/eap_gpsk.c
new file mode 100644 (file)
index 0000000..f6a1955
--- /dev/null
@@ -0,0 +1,737 @@
+/*
+ * EAP peer method: EAP-GPSK (RFC 5433)
+ * 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 "eap_peer/eap_i.h"
+#include "eap_common/eap_gpsk_common.h"
+
+struct eap_gpsk_data {
+       enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
+       u8 rand_server[EAP_GPSK_RAND_LEN];
+       u8 rand_peer[EAP_GPSK_RAND_LEN];
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 sk[EAP_GPSK_MAX_SK_LEN];
+       size_t sk_len;
+       u8 pk[EAP_GPSK_MAX_PK_LEN];
+       size_t pk_len;
+       u8 session_id;
+       int session_id_set;
+       u8 *id_peer;
+       size_t id_peer_len;
+       u8 *id_server;
+       size_t id_server_len;
+       int vendor; /* CSuite/Specifier */
+       int specifier; /* CSuite/Specifier */
+       u8 *psk;
+       size_t psk_len;
+};
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+                                           u8 identifier,
+                                           const u8 *csuite_list,
+                                           size_t csuite_list_len);
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+                                           u8 identifier);
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_gpsk_state_txt(int state)
+{
+       switch (state) {
+       case GPSK_1:
+               return "GPSK-1";
+       case GPSK_3:
+               return "GPSK-3";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+                  eap_gpsk_state_txt(data->state),
+                  eap_gpsk_state_txt(state));
+       data->state = state;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+       struct eap_gpsk_data *data;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: No key (password) configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = GPSK_1;
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity) {
+               data->id_peer = os_malloc(identity_len);
+               if (data->id_peer == NULL) {
+                       eap_gpsk_deinit(sm, data);
+                       return NULL;
+               }
+               os_memcpy(data->id_peer, identity, identity_len);
+               data->id_peer_len = identity_len;
+       }
+
+       data->psk = os_malloc(password_len);
+       if (data->psk == NULL) {
+               eap_gpsk_deinit(sm, data);
+               return NULL;
+       }
+       os_memcpy(data->psk, password, password_len);
+       data->psk_len = password_len;
+
+       return data;
+}
+
+
+static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_gpsk_data *data = priv;
+       os_free(data->id_server);
+       os_free(data->id_peer);
+       os_free(data->psk);
+       os_free(data);
+}
+
+
+static const u8 * eap_gpsk_process_id_server(struct eap_gpsk_data *data,
+                                            const u8 *pos, const u8 *end)
+{
+       u16 alen;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+               return NULL;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server overflow");
+               return NULL;
+       }
+       os_free(data->id_server);
+       data->id_server = os_malloc(alen);
+       if (data->id_server == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: No memory for ID_Server");
+               return NULL;
+       }
+       os_memcpy(data->id_server, pos, alen);
+       data->id_server_len = alen;
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server",
+                         data->id_server, data->id_server_len);
+       pos += alen;
+
+       return pos;
+}
+
+
+static const u8 * eap_gpsk_process_rand_server(struct eap_gpsk_data *data,
+                                              const u8 *pos, const u8 *end)
+{
+       if (pos == NULL)
+               return NULL;
+
+       if (end - pos < EAP_GPSK_RAND_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server overflow");
+               return NULL;
+       }
+       os_memcpy(data->rand_server, pos, EAP_GPSK_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server",
+                   data->rand_server, EAP_GPSK_RAND_LEN);
+       pos += EAP_GPSK_RAND_LEN;
+
+       return pos;
+}
+
+
+static int eap_gpsk_select_csuite(struct eap_sm *sm,
+                                 struct eap_gpsk_data *data,
+                                 const u8 *csuite_list,
+                                 size_t csuite_list_len)
+{
+       struct eap_gpsk_csuite *csuite;
+       int i, count;
+
+       count = csuite_list_len / sizeof(struct eap_gpsk_csuite);
+       data->vendor = EAP_GPSK_VENDOR_IETF;
+       data->specifier = EAP_GPSK_CIPHER_RESERVED;
+       csuite = (struct eap_gpsk_csuite *) csuite_list;
+       for (i = 0; i < count; i++) {
+               int vendor, specifier;
+               vendor = WPA_GET_BE32(csuite->vendor);
+               specifier = WPA_GET_BE16(csuite->specifier);
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite[%d]: %d:%d",
+                          i, vendor, specifier);
+               if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+                   data->specifier == EAP_GPSK_CIPHER_RESERVED &&
+                   eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+                       data->vendor = vendor;
+                       data->specifier = specifier;
+               }
+               csuite++;
+       }
+       if (data->vendor == EAP_GPSK_VENDOR_IETF &&
+           data->specifier == EAP_GPSK_CIPHER_RESERVED) {
+               wpa_msg(sm->msg_ctx, MSG_INFO, "EAP-GPSK: No supported "
+                       "ciphersuite found");
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Selected ciphersuite %d:%d",
+                  data->vendor, data->specifier);
+
+       return 0;
+}
+
+
+static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
+                                              struct eap_gpsk_data *data,
+                                              const u8 **list,
+                                              size_t *list_len,
+                                              const u8 *pos, const u8 *end)
+{
+       if (pos == NULL)
+               return NULL;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
+               return NULL;
+       }
+       *list_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < (int) *list_len) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
+               return NULL;
+       }
+       if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
+                          (unsigned long) *list_len);
+               return NULL;
+       }
+       *list = pos;
+       pos += *list_len;
+
+       if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+               return NULL;
+
+       return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
+                                              struct eap_gpsk_data *data,
+                                              struct eap_method_ret *ret,
+                                              const struct wpabuf *reqData,
+                                              const u8 *payload,
+                                              size_t payload_len)
+{
+       size_t csuite_list_len;
+       const u8 *csuite_list, *pos, *end;
+       struct wpabuf *resp;
+
+       if (data->state != GPSK_1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-1");
+
+       end = payload + payload_len;
+
+       pos = eap_gpsk_process_id_server(data, payload, end);
+       pos = eap_gpsk_process_rand_server(data, pos, end);
+       pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
+                                          &csuite_list_len, pos, end);
+       if (pos == NULL) {
+               eap_gpsk_state(data, FAILURE);
+               return NULL;
+       }
+
+       resp = eap_gpsk_send_gpsk_2(data, eap_get_id(reqData),
+                                   csuite_list, csuite_list_len);
+       if (resp == NULL)
+               return NULL;
+
+       eap_gpsk_state(data, GPSK_3);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_2(struct eap_gpsk_data *data,
+                                           u8 identifier,
+                                           const u8 *csuite_list,
+                                           size_t csuite_list_len)
+{
+       struct wpabuf *resp;
+       size_t len, miclen;
+       u8 *rpos, *start;
+       struct eap_gpsk_csuite *csuite;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-2");
+
+       miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+       len = 1 + 2 + data->id_peer_len + 2 + data->id_server_len +
+               2 * EAP_GPSK_RAND_LEN + 2 + csuite_list_len +
+               sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+                            EAP_CODE_RESPONSE, identifier);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_2);
+       start = wpabuf_put(resp, 0);
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+                         data->id_peer, data->id_peer_len);
+       wpabuf_put_be16(resp, data->id_peer_len);
+       wpabuf_put_data(resp, data->id_peer, data->id_peer_len);
+
+       wpabuf_put_be16(resp, data->id_server_len);
+       wpabuf_put_data(resp, data->id_server, data->id_server_len);
+
+       if (os_get_random(data->rand_peer, EAP_GPSK_RAND_LEN)) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to get random data "
+                          "for RAND_Peer");
+               eap_gpsk_state(data, FAILURE);
+               wpabuf_free(resp);
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
+                   data->rand_peer, EAP_GPSK_RAND_LEN);
+       wpabuf_put_data(resp, data->rand_peer, EAP_GPSK_RAND_LEN);
+       wpabuf_put_data(resp, data->rand_server, EAP_GPSK_RAND_LEN);
+
+       wpabuf_put_be16(resp, csuite_list_len);
+       wpabuf_put_data(resp, csuite_list, csuite_list_len);
+
+       csuite = wpabuf_put(resp, sizeof(*csuite));
+       WPA_PUT_BE32(csuite->vendor, data->vendor);
+       WPA_PUT_BE16(csuite->specifier, data->specifier);
+
+       if (eap_gpsk_derive_keys(data->psk, data->psk_len,
+                                data->vendor, data->specifier,
+                                data->rand_peer, data->rand_server,
+                                data->id_peer, data->id_peer_len,
+                                data->id_server, data->id_server_len,
+                                data->msk, data->emsk,
+                                data->sk, &data->sk_len,
+                                data->pk, &data->pk_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
+               eap_gpsk_state(data, FAILURE);
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       /* No PD_Payload_1 */
+       wpabuf_put_be16(resp, 0);
+
+       rpos = wpabuf_put(resp, miclen);
+       if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+                                data->specifier, start, rpos - start, rpos) <
+           0) {
+               eap_gpsk_state(data, FAILURE);
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       return resp;
+}
+
+
+static const u8 * eap_gpsk_validate_rand(struct eap_gpsk_data *data,
+                                        const u8 *pos, const u8 *end)
+{
+       if (end - pos < EAP_GPSK_RAND_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "RAND_Peer");
+               return NULL;
+       }
+       if (os_memcmp(pos, data->rand_peer, EAP_GPSK_RAND_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2 and "
+                          "GPSK-3 did not match");
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-2",
+                           data->rand_peer, EAP_GPSK_RAND_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer in GPSK-3",
+                           pos, EAP_GPSK_RAND_LEN);
+               return NULL;
+       }
+       pos += EAP_GPSK_RAND_LEN;
+
+       if (end - pos < EAP_GPSK_RAND_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "RAND_Server");
+               return NULL;
+       }
+       if (os_memcmp(pos, data->rand_server, EAP_GPSK_RAND_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+                          "GPSK-3 did not match");
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+                           data->rand_server, EAP_GPSK_RAND_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-3",
+                           pos, EAP_GPSK_RAND_LEN);
+               return NULL;
+       }
+       pos += EAP_GPSK_RAND_LEN;
+
+       return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_id_server(struct eap_gpsk_data *data,
+                                             const u8 *pos, const u8 *end)
+{
+       size_t len;
+
+       if (pos == NULL)
+               return NULL;
+
+       if (end - pos < (int) 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "length(ID_Server)");
+               return NULL;
+       }
+
+       len = WPA_GET_BE16(pos);
+       pos += 2;
+
+       if (end - pos < (int) len) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "ID_Server");
+               return NULL;
+       }
+
+       if (len != data->id_server_len ||
+           os_memcmp(pos, data->id_server, len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: ID_Server did not match with "
+                          "the one used in GPSK-1");
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1",
+                                 data->id_server, data->id_server_len);
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-3",
+                                 pos, len);
+               return NULL;
+       }
+
+       pos += len;
+
+       return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_csuite(struct eap_gpsk_data *data,
+                                          const u8 *pos, const u8 *end)
+{
+       int vendor, specifier;
+       const struct eap_gpsk_csuite *csuite;
+
+       if (pos == NULL)
+               return NULL;
+
+       if (end - pos < (int) sizeof(*csuite)) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "CSuite_Sel");
+               return NULL;
+       }
+       csuite = (const struct eap_gpsk_csuite *) pos;
+       vendor = WPA_GET_BE32(csuite->vendor);
+       specifier = WPA_GET_BE16(csuite->specifier);
+       pos += sizeof(*csuite);
+       if (vendor != data->vendor || specifier != data->specifier) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel (%d:%d) does not "
+                          "match with the one sent in GPSK-2 (%d:%d)",
+                          vendor, specifier, data->vendor, data->specifier);
+               return NULL;
+       }
+
+       return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_pd_payload_2(struct eap_gpsk_data *data,
+                                                const u8 *pos, const u8 *end)
+{
+       u16 alen;
+
+       if (pos == NULL)
+               return NULL;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "PD_Payload_2 length");
+               return NULL;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for "
+                          "%d-octet PD_Payload_2", alen);
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_2", pos, alen);
+       pos += alen;
+
+       return pos;
+}
+
+
+static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
+                                              const u8 *payload,
+                                              const u8 *pos, const u8 *end)
+{
+       size_t miclen;
+       u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+       if (pos == NULL)
+               return NULL;
+
+       miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+       if (end - pos < (int) miclen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+                          "(left=%lu miclen=%lu)",
+                          (unsigned long) (end - pos),
+                          (unsigned long) miclen);
+               return NULL;
+       }
+       if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+                                data->specifier, payload, pos - payload, mic)
+           < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+               return NULL;
+       }
+       if (os_memcmp(mic, pos, miclen) != 0) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+               return NULL;
+       }
+       pos += miclen;
+
+       return pos;
+}
+
+
+static struct wpabuf * eap_gpsk_process_gpsk_3(struct eap_sm *sm,
+                                              struct eap_gpsk_data *data,
+                                              struct eap_method_ret *ret,
+                                              const struct wpabuf *reqData,
+                                              const u8 *payload,
+                                              size_t payload_len)
+{
+       struct wpabuf *resp;
+       const u8 *pos, *end;
+
+       if (data->state != GPSK_3) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Request/GPSK-3");
+
+       end = payload + payload_len;
+
+       pos = eap_gpsk_validate_rand(data, payload, end);
+       pos = eap_gpsk_validate_id_server(data, pos, end);
+       pos = eap_gpsk_validate_csuite(data, pos, end);
+       pos = eap_gpsk_validate_pd_payload_2(data, pos, end);
+       pos = eap_gpsk_validate_gpsk_3_mic(data, payload, pos, end);
+
+       if (pos == NULL) {
+               eap_gpsk_state(data, FAILURE);
+               return NULL;
+       }
+       if (pos != end) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+                          "data in the end of GPSK-2",
+                          (unsigned long) (end - pos));
+       }
+
+       resp = eap_gpsk_send_gpsk_4(data, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+
+       eap_gpsk_state(data, SUCCESS);
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_send_gpsk_4(struct eap_gpsk_data *data,
+                                           u8 identifier)
+{
+       struct wpabuf *resp;
+       u8 *rpos, *start;
+       size_t mlen;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Sending Response/GPSK-4");
+
+       mlen = eap_gpsk_mic_len(data->vendor, data->specifier);
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, 1 + 2 + mlen,
+                            EAP_CODE_RESPONSE, identifier);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, EAP_GPSK_OPCODE_GPSK_4);
+       start = wpabuf_put(resp, 0);
+
+       /* No PD_Payload_3 */
+       wpabuf_put_be16(resp, 0);
+
+       rpos = wpabuf_put(resp, mlen);
+       if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+                                data->specifier, start, rpos - start, rpos) <
+           0) {
+               eap_gpsk_state(data, FAILURE);
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_gpsk_process(struct eap_sm *sm, void *priv,
+                                       struct eap_method_ret *ret,
+                                       const struct wpabuf *reqData)
+{
+       struct eap_gpsk_data *data = priv;
+       struct wpabuf *resp;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqData, &len);
+       if (pos == NULL || len < 1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode %d", *pos);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = FALSE;
+
+       switch (*pos) {
+       case EAP_GPSK_OPCODE_GPSK_1:
+               resp = eap_gpsk_process_gpsk_1(sm, data, ret, reqData,
+                                              pos + 1, len - 1);
+               break;
+       case EAP_GPSK_OPCODE_GPSK_3:
+               resp = eap_gpsk_process_gpsk_3(sm, data, ret, reqData,
+                                              pos + 1, len - 1);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignoring message with "
+                          "unknown opcode %d", *pos);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       return resp;
+}
+
+
+static Boolean eap_gpsk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_gpsk_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_gpsk_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_gpsk_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+int eap_peer_gpsk_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_gpsk_init;
+       eap->deinit = eap_gpsk_deinit;
+       eap->process = eap_gpsk_process;
+       eap->isKeyAvailable = eap_gpsk_isKeyAvailable;
+       eap->getKey = eap_gpsk_getKey;
+       eap->get_emsk = eap_gpsk_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_gtc.c b/src/eap_peer/eap_gtc.c
new file mode 100644 (file)
index 0000000..b2b554b
--- /dev/null
@@ -0,0 +1,151 @@
+/*
+ * EAP peer method: EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+       int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+       struct eap_gtc_data *data;
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+           sm->m->method == EAP_TYPE_FAST) {
+               wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+                          "with challenge/response");
+               data->prefix = 1;
+       }
+       return data;
+}
+
+
+static void eap_gtc_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_gtc_data *data = priv;
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_gtc_data *data = priv;
+       struct wpabuf *resp;
+       const u8 *pos, *password, *identity;
+       size_t password_len, identity_len, len, plen;
+       int otp;
+       u8 id;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqData, &len);
+       if (pos == NULL) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       id = eap_get_id(reqData);
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Request message", pos, len);
+       if (data->prefix &&
+           (len < 10 || os_memcmp(pos, "CHALLENGE=", 10) != 0)) {
+               wpa_printf(MSG_DEBUG, "EAP-GTC: Challenge did not start with "
+                          "expected prefix");
+
+               /* Send an empty response in order to allow tunneled
+                * acknowledgement of the failure. This will also cover the
+                * error case which seems to use EAP-MSCHAPv2 like error
+                * reporting with EAP-GTC inside EAP-FAST tunnel. */
+               resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC,
+                                    0, EAP_CODE_RESPONSE, id);
+               return resp;
+       }
+
+       password = eap_get_config_otp(sm, &password_len);
+       if (password)
+               otp = 1;
+       else {
+               password = eap_get_config_password(sm, &password_len);
+               otp = 0;
+       }
+
+       if (password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-GTC: Password not configured");
+               eap_sm_request_otp(sm, (const char *) pos, len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       ret->ignore = FALSE;
+
+       ret->methodState = data->prefix ? METHOD_MAY_CONT : METHOD_DONE;
+       ret->decision = DECISION_COND_SUCC;
+       ret->allowNotifications = FALSE;
+
+       plen = password_len;
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity == NULL)
+               return NULL;
+       if (data->prefix)
+               plen += 9 + identity_len + 1;
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, plen,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+       if (data->prefix) {
+               wpabuf_put_data(resp, "RESPONSE=", 9);
+               wpabuf_put_data(resp, identity, identity_len);
+               wpabuf_put_u8(resp, '\0');
+       }
+       wpabuf_put_data(resp, password, password_len);
+       wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response",
+                             wpabuf_head_u8(resp) + sizeof(struct eap_hdr) +
+                             1, plen);
+
+       if (otp) {
+               wpa_printf(MSG_DEBUG, "EAP-GTC: Forgetting used password");
+               eap_clear_config_otp(sm);
+       }
+
+       return resp;
+}
+
+
+int eap_peer_gtc_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_gtc_init;
+       eap->deinit = eap_gtc_deinit;
+       eap->process = eap_gtc_process;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_i.h b/src/eap_peer/eap_i.h
new file mode 100644 (file)
index 0000000..e7c826e
--- /dev/null
@@ -0,0 +1,355 @@
+/*
+ * EAP peer state machines internal structures (RFC 4137)
+ * 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 EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "eap_peer/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Peer state machine */
+
+typedef enum {
+       DECISION_FAIL, DECISION_COND_SUCC, DECISION_UNCOND_SUCC
+} EapDecision;
+
+typedef enum {
+       METHOD_NONE, METHOD_INIT, METHOD_CONT, METHOD_MAY_CONT, METHOD_DONE
+} EapMethodState;
+
+/**
+ * struct eap_method_ret - EAP return values from struct eap_method::process()
+ *
+ * These structure contains OUT variables for the interface between peer state
+ * machine and methods (RFC 4137, Sect. 4.2). eapRespData will be returned as
+ * the return value of struct eap_method::process() so it is not included in
+ * this structure.
+ */
+struct eap_method_ret {
+       /**
+        * ignore - Whether method decided to drop the current packed (OUT)
+        */
+       Boolean ignore;
+
+       /**
+        * methodState - Method-specific state (IN/OUT)
+        */
+       EapMethodState methodState;
+
+       /**
+        * decision - Authentication decision (OUT)
+        */
+       EapDecision decision;
+
+       /**
+        * allowNotifications - Whether method allows notifications (OUT)
+        */
+       Boolean allowNotifications;
+};
+
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 4.4 of RFC 4137.
+ */
+struct eap_method {
+       /**
+        * vendor - EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+        */
+       int vendor;
+
+       /**
+        * method - EAP type number (EAP_TYPE_*)
+        */
+       EapType method;
+
+       /**
+        * name - Name of the method (e.g., "TLS")
+        */
+       const char *name;
+
+       /**
+        * init - Initialize an EAP method
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * Returns: Pointer to allocated private data, or %NULL on failure
+        *
+        * This function is used to initialize the EAP method explicitly
+        * instead of using METHOD_INIT state as specific in RFC 4137. The
+        * method is expected to initialize it method-specific state and return
+        * a pointer that will be used as the priv argument to other calls.
+        */
+       void * (*init)(struct eap_sm *sm);
+
+       /**
+        * deinit - Deinitialize an EAP method
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        *
+        * Deinitialize the EAP method and free any allocated private data.
+        */
+       void (*deinit)(struct eap_sm *sm, void *priv);
+
+       /**
+        * process - Process an EAP request
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @ret: Return values from EAP request validation and processing
+        * @reqData: EAP request to be processed (eapReqData)
+        * Returns: Pointer to allocated EAP response packet (eapRespData)
+        *
+        * This function is a combination of m.check(), m.process(), and
+        * m.buildResp() procedures defined in section 4.4 of RFC 4137 In other
+        * words, this function validates the incoming request, processes it,
+        * and build a response packet. m.check() and m.process() return values
+        * are returned through struct eap_method_ret *ret variable. Caller is
+        * responsible for freeing the returned EAP response packet.
+        */
+       struct wpabuf * (*process)(struct eap_sm *sm, void *priv,
+                                  struct eap_method_ret *ret,
+                                  const struct wpabuf *reqData);
+
+       /**
+        * isKeyAvailable - Find out whether EAP method has keying material
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * Returns: %TRUE if key material (eapKeyData) is available
+        */
+       Boolean (*isKeyAvailable)(struct eap_sm *sm, void *priv);
+
+       /**
+        * getKey - Get EAP method specific keying material (eapKeyData)
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @len: Pointer to variable to store key length (eapKeyDataLen)
+        * Returns: Keying material (eapKeyData) or %NULL if not available
+        *
+        * This function can be used to get the keying material from the EAP
+        * method. The key may already be stored in the method-specific private
+        * data or this function may derive the key.
+        */
+       u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+
+       /**
+        * get_status - Get EAP method status
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @buf: Buffer for status information
+        * @buflen: Maximum buffer length
+        * @verbose: Whether to include verbose status information
+        * Returns: Number of bytes written to buf
+        *
+        * Query EAP method for status information. This function fills in a
+        * text area with current status information from the EAP method. If
+        * the buffer (buf) is not large enough, status information will be
+        * truncated to fit the buffer.
+        */
+       int (*get_status)(struct eap_sm *sm, void *priv, char *buf,
+                         size_t buflen, int verbose);
+
+       /**
+        * has_reauth_data - Whether method is ready for fast reauthentication
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * Returns: %TRUE or %FALSE based on whether fast reauthentication is
+        * possible
+        *
+        * This function is an optional handler that only EAP methods
+        * supporting fast re-authentication need to implement.
+        */
+       Boolean (*has_reauth_data)(struct eap_sm *sm, void *priv);
+
+       /**
+        * deinit_for_reauth - Release data that is not needed for fast re-auth
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        *
+        * This function is an optional handler that only EAP methods
+        * supporting fast re-authentication need to implement. This is called
+        * when authentication has been completed and EAP state machine is
+        * requesting that enough state information is maintained for fast
+        * re-authentication
+        */
+       void (*deinit_for_reauth)(struct eap_sm *sm, void *priv);
+
+       /**
+        * init_for_reauth - Prepare for start of fast re-authentication
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        *
+        * This function is an optional handler that only EAP methods
+        * supporting fast re-authentication need to implement. This is called
+        * when EAP authentication is started and EAP state machine is
+        * requesting fast re-authentication to be used.
+        */
+       void * (*init_for_reauth)(struct eap_sm *sm, void *priv);
+
+       /**
+        * get_identity - Get method specific identity for re-authentication
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @len: Length of the returned identity
+        * Returns: Pointer to the method specific identity or %NULL if default
+        * identity is to be used
+        *
+        * This function is an optional handler that only EAP methods
+        * that use method specific identity need to implement.
+        */
+       const u8 * (*get_identity)(struct eap_sm *sm, void *priv, size_t *len);
+
+       /**
+        * free - Free EAP method data
+        * @method: Pointer to the method data registered with
+        * eap_peer_method_register().
+        *
+        * This function will be called when the EAP method is being
+        * unregistered. If the EAP method allocated resources during
+        * registration (e.g., allocated struct eap_method), they should be
+        * freed in this function. No other method functions will be called
+        * after this call. If this function is not defined (i.e., function
+        * pointer is %NULL), a default handler is used to release the method
+        * data with free(method). This is suitable for most cases.
+        */
+       void (*free)(struct eap_method *method);
+
+#define EAP_PEER_METHOD_INTERFACE_VERSION 1
+       /**
+        * version - Version of the EAP peer method interface
+        *
+        * The EAP peer method implementation should set this variable to
+        * EAP_PEER_METHOD_INTERFACE_VERSION. This is used to verify that the
+        * EAP method is using supported API version when using dynamically
+        * loadable EAP methods.
+        */
+       int version;
+
+       /**
+        * next - Pointer to the next EAP method
+        *
+        * This variable is used internally in the EAP method registration code
+        * to create a linked list of registered EAP methods.
+        */
+       struct eap_method *next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+       /**
+        * dl_handle - Handle for the dynamic library
+        *
+        * This variable is used internally in the EAP method registration code
+        * to store a handle for the dynamic library. If the method is linked
+        * in statically, this is %NULL.
+        */
+       void *dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+       /**
+        * get_emsk - Get EAP method specific keying extended material (EMSK)
+        * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @len: Pointer to a variable to store EMSK length
+        * Returns: EMSK or %NULL if not available
+        *
+        * This function can be used to get the extended keying material from
+        * the EAP method. The key may already be stored in the method-specific
+        * private data or this function may derive the key.
+        */
+       u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+
+/**
+ * struct eap_sm - EAP state machine data
+ */
+struct eap_sm {
+       enum {
+               EAP_INITIALIZE, EAP_DISABLED, EAP_IDLE, EAP_RECEIVED,
+               EAP_GET_METHOD, EAP_METHOD, EAP_SEND_RESPONSE, EAP_DISCARD,
+               EAP_IDENTITY, EAP_NOTIFICATION, EAP_RETRANSMIT, EAP_SUCCESS,
+               EAP_FAILURE
+       } EAP_state;
+       /* Long-term local variables */
+       EapType selectedMethod;
+       EapMethodState methodState;
+       int lastId;
+       struct wpabuf *lastRespData;
+       EapDecision decision;
+       /* Short-term local variables */
+       Boolean rxReq;
+       Boolean rxSuccess;
+       Boolean rxFailure;
+       int reqId;
+       EapType reqMethod;
+       int reqVendor;
+       u32 reqVendorMethod;
+       Boolean ignore;
+       /* Constants */
+       int ClientTimeout;
+
+       /* Miscellaneous variables */
+       Boolean allowNotifications; /* peer state machine <-> methods */
+       struct wpabuf *eapRespData; /* peer to lower layer */
+       Boolean eapKeyAvailable; /* peer to lower layer */
+       u8 *eapKeyData; /* peer to lower layer */
+       size_t eapKeyDataLen; /* peer to lower layer */
+       const struct eap_method *m; /* selected EAP method */
+       /* not defined in RFC 4137 */
+       Boolean changed;
+       void *eapol_ctx;
+       struct eapol_callbacks *eapol_cb;
+       void *eap_method_priv;
+       int init_phase2;
+       int fast_reauth;
+
+       Boolean rxResp /* LEAP only */;
+       Boolean leap_done;
+       Boolean peap_done;
+       u8 req_md5[16]; /* MD5() of the current EAP packet */
+       u8 last_md5[16]; /* MD5() of the previously received EAP packet; used
+                         * in duplicate request detection. */
+
+       void *msg_ctx;
+       void *scard_ctx;
+       void *ssl_ctx;
+
+       unsigned int workaround;
+
+       /* Optional challenges generated in Phase 1 (EAP-FAST) */
+       u8 *peer_challenge, *auth_challenge;
+
+       int num_rounds;
+       int force_disabled;
+
+       struct wps_context *wps;
+
+       int prev_failure;
+};
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash);
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len);
+const u8 * eap_get_config_otp(struct eap_sm *sm, size_t *len);
+void eap_clear_config_otp(struct eap_sm *sm);
+const char * eap_get_config_phase1(struct eap_sm *sm);
+const char * eap_get_config_phase2(struct eap_sm *sm);
+struct eap_peer_config * eap_get_config(struct eap_sm *sm);
+void eap_set_config_blob(struct eap_sm *sm, struct wpa_config_blob *blob);
+const struct wpa_config_blob *
+eap_get_config_blob(struct eap_sm *sm, const char *name);
+void eap_notify_pending(struct eap_sm *sm);
+int eap_allowed_method(struct eap_sm *sm, int vendor, u32 method);
+
+#endif /* EAP_I_H */
diff --git a/src/eap_peer/eap_ikev2.c b/src/eap_peer/eap_ikev2.c
new file mode 100644 (file)
index 0000000..bb49a66
--- /dev/null
@@ -0,0 +1,506 @@
+/*
+ * EAP-IKEv2 peer (RFC 5106)
+ * 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"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+       struct ikev2_responder_data ikev2;
+       enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+       struct wpabuf *in_buf;
+       struct wpabuf *out_buf;
+       size_t out_used;
+       size_t fragment_size;
+       int keys_ready;
+       u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+       int keymat_ok;
+};
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+       switch (state) {
+       case WAIT_START:
+               return "WAIT_START";
+       case PROC_MSG:
+               return "PROC_MSG";
+       case WAIT_FRAG_ACK:
+               return "WAIT_FRAG_ACK";
+       case DONE:
+               return "DONE";
+       case FAIL:
+               return "FAIL";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+                  eap_ikev2_state_txt(data->state),
+                  eap_ikev2_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+       struct eap_ikev2_data *data;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity == NULL) {
+               wpa_printf(MSG_INFO, "EAP-IKEV2: No identity available");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = WAIT_START;
+       data->fragment_size = IKEV2_FRAGMENT_SIZE;
+       data->ikev2.state = SA_INIT;
+       data->ikev2.peer_auth = PEER_AUTH_SECRET;
+       data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
+       if (data->ikev2.key_pad == NULL)
+               goto failed;
+       data->ikev2.key_pad_len = 21;
+       data->ikev2.IDr = os_malloc(identity_len);
+       if (data->ikev2.IDr == NULL)
+               goto failed;
+       os_memcpy(data->ikev2.IDr, identity, identity_len);
+       data->ikev2.IDr_len = identity_len;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (password) {
+               data->ikev2.shared_secret = os_malloc(password_len);
+               if (data->ikev2.shared_secret == NULL)
+                       goto failed;
+               os_memcpy(data->ikev2.shared_secret, password, password_len);
+               data->ikev2.shared_secret_len = password_len;
+       }
+
+       return data;
+
+failed:
+       ikev2_responder_deinit(&data->ikev2);
+       os_free(data);
+       return NULL;
+}
+
+
+static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_ikev2_data *data = priv;
+       wpabuf_free(data->in_buf);
+       wpabuf_free(data->out_buf);
+       ikev2_responder_deinit(&data->ikev2);
+       os_free(data);
+}
+
+
+static int eap_ikev2_peer_keymat(struct eap_ikev2_data *data)
+{
+       if (eap_ikev2_derive_keymat(
+                   data->ikev2.proposal.prf, &data->ikev2.keys,
+                   data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+                   data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+                   data->keymat) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+                          "derive key material");
+               return -1;
+       }
+       data->keymat_ok = 1;
+       return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
+                                          struct eap_method_ret *ret, u8 id)
+{
+       struct wpabuf *resp;
+       u8 flags;
+       size_t send_len, plen, icv_len = 0;
+
+       ret->ignore = FALSE;
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Response");
+       ret->allowNotifications = TRUE;
+
+       flags = 0;
+       send_len = wpabuf_len(data->out_buf) - data->out_used;
+       if (1 + send_len > data->fragment_size) {
+               send_len = data->fragment_size - 1;
+               flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+               if (data->out_used == 0) {
+                       flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+                       send_len -= 4;
+               }
+       }
+#ifdef CCNS_PL
+       /* Some issues figuring out the length of the message if Message Length
+        * field not included?! */
+       if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED))
+               flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+#endif /* CCNS_PL */
+
+       plen = 1 + send_len;
+       if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+               plen += 4;
+       if (data->keys_ready) {
+               const struct ikev2_integ_alg *integ;
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+                          "Data");
+               flags |= IKEV2_FLAGS_ICV_INCLUDED;
+               integ = ikev2_get_integ(data->ikev2.proposal.integ);
+               if (integ == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+                                  "transform / cannot generate ICV");
+                       return NULL;
+               }
+               icv_len = integ->hash_len;
+
+               plen += icv_len;
+       }
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, flags); /* Flags */
+       if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+               wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+       wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+                       send_len);
+       data->out_used += send_len;
+
+       if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+               const u8 *msg = wpabuf_head(resp);
+               size_t len = wpabuf_len(resp);
+               ikev2_integ_hash(data->ikev2.proposal.integ,
+                                data->ikev2.keys.SK_ar,
+                                data->ikev2.keys.SK_integ_len,
+                                msg, len, wpabuf_put(resp, icv_len));
+       }
+
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+
+       if (data->out_used == wpabuf_len(data->out_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->out_buf);
+               data->out_buf = NULL;
+               data->out_used = 0;
+               switch (data->ikev2.state) {
+               case SA_AUTH:
+                       /* SA_INIT was sent out, so message have to be
+                        * integrity protected from now on. */
+                       data->keys_ready = 1;
+                       break;
+               case IKEV2_DONE:
+                       ret->methodState = METHOD_DONE;
+                       if (data->state == FAIL)
+                               break;
+                       ret->decision = DECISION_COND_SUCC;
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+                                  "completed successfully");
+                       if (eap_ikev2_peer_keymat(data))
+                               break;
+                       eap_ikev2_state(data, DONE);
+                       break;
+               case IKEV2_FAILED:
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication "
+                                  "failed");
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       break;
+               default:
+                       break;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->out_buf) -
+                          data->out_used);
+               eap_ikev2_state(data, WAIT_FRAG_ACK);
+       }
+
+       return resp;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+                                const struct wpabuf *reqData,
+                                u8 flags, const u8 *pos, const u8 **end)
+{
+       if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+               int icv_len = eap_ikev2_validate_icv(
+                       data->ikev2.proposal.integ, &data->ikev2.keys, 1,
+                       reqData, pos, *end);
+               if (icv_len < 0)
+                       return -1;
+               /* Hide Integrity Checksum Data from further processing */
+               *end -= icv_len;
+       } else if (data->keys_ready) {
+               wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+                          "included integrity checksum");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+                                 const u8 *buf, size_t len)
+{
+       /* Process continuation of a pending message */
+       if (len > wpabuf_tailroom(data->in_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+               eap_ikev2_state(data, FAIL);
+               return -1;
+       }
+
+       wpabuf_put_data(data->in_buf, buf, len);
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting "
+                  "for %lu bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->in_buf));
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+                                                 struct eap_method_ret *ret,
+                                                 u8 id, u8 flags,
+                                                 u32 message_length,
+                                                 const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+                          "a fragmented packet");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->in_buf == NULL) {
+               /* First fragment of the message */
+               data->in_buf = wpabuf_alloc(message_length);
+               if (data->in_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+                                  "message");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               wpabuf_put_data(data->in_buf, buf, len);
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+                          "fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->in_buf));
+       }
+
+       return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
+                                        struct eap_method_ret *ret,
+                                        const struct wpabuf *reqData)
+{
+       struct eap_ikev2_data *data = priv;
+       const u8 *start, *pos, *end;
+       size_t len;
+       u8 flags, id;
+       u32 message_length = 0;
+       struct wpabuf tmpbuf;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, reqData, &len);
+       if (pos == NULL) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       id = eap_get_id(reqData);
+
+       start = pos;
+       end = start + len;
+
+       if (len == 0)
+               flags = 0; /* fragment ack */
+       else
+               flags = *pos++;
+
+       if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+               if (end - pos < 4) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               message_length = WPA_GET_BE32(pos);
+               pos += 4;
+
+               if (message_length < (u32) (end - pos)) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+                                  "Length (%d; %ld remaining in this msg)",
+                                  message_length, (long) (end - pos));
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+                  "Message Length %u", flags, message_length);
+
+       if (data->state == WAIT_FRAG_ACK) {
+#ifdef CCNS_PL
+               if (len > 1) /* Empty Flags field included in ACK */
+#else /* CCNS_PL */
+               if (len != 0)
+#endif /* CCNS_PL */
+               {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+                                  "in WAIT_FRAG_ACK state");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+               eap_ikev2_state(data, PROC_MSG);
+               return eap_ikev2_build_msg(data, ret, id);
+       }
+
+       if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+               
+       if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+               return eap_ikev2_process_fragment(data, ret, id, flags,
+                                                 message_length, pos,
+                                                 end - pos);
+       }
+
+       if (data->in_buf == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&tmpbuf, pos, end - pos);
+               data->in_buf = &tmpbuf;
+       }
+
+       if (ikev2_responder_process(&data->ikev2, data->in_buf) < 0) {
+               if (data->in_buf == &tmpbuf)
+                       data->in_buf = NULL;
+               eap_ikev2_state(data, FAIL);
+               return NULL;
+       }
+
+       if (data->in_buf != &tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+
+       if (data->out_buf == NULL) {
+               data->out_buf = ikev2_responder_build(&data->ikev2);
+               if (data->out_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to generate "
+                                  "IKEv2 message");
+                       return NULL;
+               }
+               data->out_used = 0;
+       }
+
+       eap_ikev2_state(data, PROC_MSG);
+       return eap_ikev2_build_msg(data, ret, id);
+}
+
+
+static Boolean eap_ikev2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_ikev2_data *data = priv;
+       return data->state == DONE && data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ikev2_data *data = priv;
+       u8 *key;
+
+       if (data->state != DONE || !data->keymat_ok)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key) {
+               os_memcpy(key, data->keymat, EAP_MSK_LEN);
+               *len = EAP_MSK_LEN;
+       }
+
+       return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ikev2_data *data = priv;
+       u8 *key;
+
+       if (data->state != DONE || !data->keymat_ok)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key) {
+               os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+               *len = EAP_EMSK_LEN;
+       }
+
+       return key;
+}
+
+
+int eap_peer_ikev2_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+                                   "IKEV2");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_ikev2_init;
+       eap->deinit = eap_ikev2_deinit;
+       eap->process = eap_ikev2_process;
+       eap->isKeyAvailable = eap_ikev2_isKeyAvailable;
+       eap->getKey = eap_ikev2_getKey;
+       eap->get_emsk = eap_ikev2_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_leap.c b/src/eap_peer/eap_leap.c
new file mode 100644 (file)
index 0000000..a7c94a4
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * EAP peer method: LEAP
+ * 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 "crypto/ms_funcs.h"
+#include "crypto/crypto.h"
+#include "eap_i.h"
+
+#define LEAP_VERSION 1
+#define LEAP_CHALLENGE_LEN 8
+#define LEAP_RESPONSE_LEN 24
+#define LEAP_KEY_LEN 16
+
+
+struct eap_leap_data {
+       enum {
+               LEAP_WAIT_CHALLENGE,
+               LEAP_WAIT_SUCCESS,
+               LEAP_WAIT_RESPONSE,
+               LEAP_DONE
+       } state;
+
+       u8 peer_challenge[LEAP_CHALLENGE_LEN];
+       u8 peer_response[LEAP_RESPONSE_LEN];
+
+       u8 ap_challenge[LEAP_CHALLENGE_LEN];
+       u8 ap_response[LEAP_RESPONSE_LEN];
+};
+
+
+static void * eap_leap_init(struct eap_sm *sm)
+{
+       struct eap_leap_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = LEAP_WAIT_CHALLENGE;
+
+       sm->leap_done = FALSE;
+       return data;
+}
+
+
+static void eap_leap_deinit(struct eap_sm *sm, void *priv)
+{
+       os_free(priv);
+}
+
+
+static struct wpabuf * eap_leap_process_request(struct eap_sm *sm, void *priv,
+                                               struct eap_method_ret *ret,
+                                               const struct wpabuf *reqData)
+{
+       struct eap_leap_data *data = priv;
+       struct wpabuf *resp;
+       const u8 *pos, *challenge, *identity, *password;
+       u8 challenge_len, *rpos;
+       size_t identity_len, password_len, len;
+       int pwhash;
+
+       wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Request");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (identity == NULL || password == NULL)
+               return NULL;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+       if (pos == NULL || len < 3) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Request frame");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (*pos != LEAP_VERSION) {
+               wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+                          "%d", *pos);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       pos++;
+
+       pos++; /* skip unused byte */
+
+       challenge_len = *pos++;
+       if (challenge_len != LEAP_CHALLENGE_LEN || challenge_len > len - 3) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: Invalid challenge "
+                          "(challenge_len=%d reqDataLen=%lu)",
+                          challenge_len, (unsigned long) wpabuf_len(reqData));
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       challenge = pos;
+       os_memcpy(data->peer_challenge, challenge, LEAP_CHALLENGE_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge from AP",
+                   challenge, LEAP_CHALLENGE_LEN);
+
+       wpa_printf(MSG_DEBUG, "EAP-LEAP: Generating Challenge Response");
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+                            3 + LEAP_RESPONSE_LEN + identity_len,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+       wpabuf_put_u8(resp, LEAP_VERSION);
+       wpabuf_put_u8(resp, 0); /* unused */
+       wpabuf_put_u8(resp, LEAP_RESPONSE_LEN);
+       rpos = wpabuf_put(resp, LEAP_RESPONSE_LEN);
+       if (pwhash)
+               challenge_response(challenge, password, rpos);
+       else
+               nt_challenge_response(challenge, password, password_len, rpos);
+       os_memcpy(data->peer_response, rpos, LEAP_RESPONSE_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Response",
+                   rpos, LEAP_RESPONSE_LEN);
+       wpabuf_put_data(resp, identity, identity_len);
+
+       data->state = LEAP_WAIT_SUCCESS;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_success(struct eap_sm *sm, void *priv,
+                                               struct eap_method_ret *ret,
+                                               const struct wpabuf *reqData)
+{
+       struct eap_leap_data *data = priv;
+       struct wpabuf *resp;
+       u8 *pos;
+       const u8 *identity;
+       size_t identity_len;
+
+       wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Success");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity == NULL)
+               return NULL;
+
+       if (data->state != LEAP_WAIT_SUCCESS) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: EAP-Success received in "
+                          "unexpected state (%d) - ignored", data->state);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_LEAP,
+                            3 + LEAP_CHALLENGE_LEN + identity_len,
+                            EAP_CODE_REQUEST, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+       wpabuf_put_u8(resp, LEAP_VERSION);
+       wpabuf_put_u8(resp, 0); /* unused */
+       wpabuf_put_u8(resp, LEAP_CHALLENGE_LEN);
+       pos = wpabuf_put(resp, LEAP_CHALLENGE_LEN);
+       if (os_get_random(pos, LEAP_CHALLENGE_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-LEAP: Failed to read random data "
+                          "for challenge");
+               wpabuf_free(resp);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       os_memcpy(data->ap_challenge, pos, LEAP_CHALLENGE_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-LEAP: Challenge to AP/AS", pos,
+                   LEAP_CHALLENGE_LEN);
+       wpabuf_put_data(resp, identity, identity_len);
+
+       data->state = LEAP_WAIT_RESPONSE;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
+                                                struct eap_method_ret *ret,
+                                                const struct wpabuf *reqData)
+{
+       struct eap_leap_data *data = priv;
+       const u8 *pos, *password;
+       u8 response_len, pw_hash[16], pw_hash_hash[16],
+               expected[LEAP_RESPONSE_LEN];
+       size_t password_len, len;
+       int pwhash;
+
+       wpa_printf(MSG_DEBUG, "EAP-LEAP: Processing EAP-Response");
+
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (password == NULL)
+               return NULL;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_LEAP, reqData, &len);
+       if (pos == NULL || len < 3) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: Invalid EAP-Response frame");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (*pos != LEAP_VERSION) {
+               wpa_printf(MSG_WARNING, "EAP-LEAP: Unsupported LEAP version "
+                          "%d", *pos);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       pos++;
+
+       pos++; /* skip unused byte */
+
+       response_len = *pos++;
+       if (response_len != LEAP_RESPONSE_LEN || response_len > len - 3) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: Invalid response "
+                          "(response_len=%d reqDataLen=%lu)",
+                          response_len, (unsigned long) wpabuf_len(reqData));
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Response from AP",
+                   pos, LEAP_RESPONSE_LEN);
+       os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN);
+
+       if (pwhash) {
+               if (hash_nt_password_hash(password, pw_hash_hash)) {
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+       } else {
+               if (nt_password_hash(password, password_len, pw_hash) ||
+                   hash_nt_password_hash(pw_hash, pw_hash_hash)) {
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+       }
+       challenge_response(data->ap_challenge, pw_hash_hash, expected);
+
+       ret->methodState = METHOD_DONE;
+       ret->allowNotifications = FALSE;
+
+       if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+               wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
+                          "response - authentication failed");
+               wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
+                           expected, LEAP_RESPONSE_LEN);
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+
+       ret->decision = DECISION_UNCOND_SUCC;
+
+       /* LEAP is somewhat odd method since it sends EAP-Success in the middle
+        * of the authentication. Use special variable to transit EAP state
+        * machine to SUCCESS state. */
+       sm->leap_done = TRUE;
+       data->state = LEAP_DONE;
+
+       /* No more authentication messages expected; AP will send EAPOL-Key
+        * frames if encryption is enabled. */
+       return NULL;
+}
+
+
+static struct wpabuf * eap_leap_process(struct eap_sm *sm, void *priv,
+                                       struct eap_method_ret *ret,
+                                       const struct wpabuf *reqData)
+{
+       const struct eap_hdr *eap;
+       size_t password_len;
+       const u8 *password;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: Password not configured");
+               eap_sm_request_password(sm);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       /*
+        * LEAP needs to be able to handle EAP-Success frame which does not
+        * include Type field. Consequently, eap_hdr_validate() cannot be used
+        * here. This validation will be done separately for EAP-Request and
+        * EAP-Response frames.
+        */
+       eap = wpabuf_head(reqData);
+       if (wpabuf_len(reqData) < sizeof(*eap) ||
+           be_to_host16(eap->length) > wpabuf_len(reqData)) {
+               wpa_printf(MSG_INFO, "EAP-LEAP: Invalid frame");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       ret->ignore = FALSE;
+       ret->allowNotifications = TRUE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+
+       sm->leap_done = FALSE;
+
+       switch (eap->code) {
+       case EAP_CODE_REQUEST:
+               return eap_leap_process_request(sm, priv, ret, reqData);
+       case EAP_CODE_SUCCESS:
+               return eap_leap_process_success(sm, priv, ret, reqData);
+       case EAP_CODE_RESPONSE:
+               return eap_leap_process_response(sm, priv, ret, reqData);
+       default:
+               wpa_printf(MSG_INFO, "EAP-LEAP: Unexpected EAP code (%d) - "
+                          "ignored", eap->code);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+}
+
+
+static Boolean eap_leap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_leap_data *data = priv;
+       return data->state == LEAP_DONE;
+}
+
+
+static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_leap_data *data = priv;
+       u8 *key, pw_hash_hash[16], pw_hash[16];
+       const u8 *addr[5], *password;
+       size_t elen[5], password_len;
+       int pwhash;
+
+       if (data->state != LEAP_DONE)
+               return NULL;
+
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (password == NULL)
+               return NULL;
+
+       key = os_malloc(LEAP_KEY_LEN);
+       if (key == NULL)
+               return NULL;
+
+       if (pwhash) {
+               if (hash_nt_password_hash(password, pw_hash_hash)) {
+                       os_free(key);
+                       return NULL;
+               }
+       } else {
+               if (nt_password_hash(password, password_len, pw_hash) ||
+                   hash_nt_password_hash(pw_hash, pw_hash_hash)) {
+                       os_free(key);
+                       return NULL;
+               }
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash",
+                       pw_hash_hash, 16);
+       wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_challenge",
+                   data->peer_challenge, LEAP_CHALLENGE_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-LEAP: peer_response",
+                   data->peer_response, LEAP_RESPONSE_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_challenge",
+                   data->ap_challenge, LEAP_CHALLENGE_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-LEAP: ap_response",
+                   data->ap_response, LEAP_RESPONSE_LEN);
+
+       addr[0] = pw_hash_hash;
+       elen[0] = 16;
+       addr[1] = data->ap_challenge;
+       elen[1] = LEAP_CHALLENGE_LEN;
+       addr[2] = data->ap_response;
+       elen[2] = LEAP_RESPONSE_LEN;
+       addr[3] = data->peer_challenge;
+       elen[3] = LEAP_CHALLENGE_LEN;
+       addr[4] = data->peer_response;
+       elen[4] = LEAP_RESPONSE_LEN;
+       md5_vector(5, addr, elen, key);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
+       *len = LEAP_KEY_LEN;
+
+       return key;
+}
+
+
+int eap_peer_leap_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_LEAP, "LEAP");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_leap_init;
+       eap->deinit = eap_leap_deinit;
+       eap->process = eap_leap_process;
+       eap->isKeyAvailable = eap_leap_isKeyAvailable;
+       eap->getKey = eap_leap_getKey;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_md5.c b/src/eap_peer/eap_md5.c
new file mode 100644 (file)
index 0000000..0edbae8
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * EAP peer method: EAP-MD5 (RFC 3748 and RFC 1994)
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/chap.h"
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+       /* No need for private data. However, must return non-NULL to indicate
+        * success. */
+       return (void *) 1;
+}
+
+
+static void eap_md5_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct wpabuf *resp;
+       const u8 *pos, *challenge, *password;
+       u8 *rpos, id;
+       size_t len, challenge_len, password_len;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-MD5: Password not configured");
+               eap_sm_request_password(sm);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqData, &len);
+       if (pos == NULL || len == 0) {
+               wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame (pos=%p len=%lu)",
+                          pos, (unsigned long) len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       /*
+        * CHAP Challenge:
+        * Value-Size (1 octet) | Value(Challenge) | Name(optional)
+        */
+       challenge_len = *pos++;
+       if (challenge_len == 0 || challenge_len > len - 1) {
+               wpa_printf(MSG_INFO, "EAP-MD5: Invalid challenge "
+                          "(challenge_len=%lu len=%lu)",
+                          (unsigned long) challenge_len, (unsigned long) len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       ret->ignore = FALSE;
+       challenge = pos;
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge",
+                   challenge, challenge_len);
+
+       wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response");
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_COND_SUCC;
+       ret->allowNotifications = TRUE;
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+
+       /*
+        * CHAP Response:
+        * Value-Size (1 octet) | Value(Response) | Name(optional)
+        */
+       wpabuf_put_u8(resp, CHAP_MD5_LEN);
+
+       id = eap_get_id(resp);
+       rpos = wpabuf_put(resp, CHAP_MD5_LEN);
+       chap_md5(id, password, password_len, challenge, challenge_len, rpos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", rpos, CHAP_MD5_LEN);
+
+       return resp;
+}
+
+
+int eap_peer_md5_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_md5_init;
+       eap->deinit = eap_md5_deinit;
+       eap->process = eap_md5_process;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_methods.c b/src/eap_peer/eap_methods.c
new file mode 100644 (file)
index 0000000..3b0af05
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * EAP peer: Method registration
+ * 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_DYNAMIC_EAP_METHODS
+#include <dlfcn.h>
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods = NULL;
+
+
+/**
+ * eap_peer_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (m->vendor == vendor && m->method == method)
+                       return m;
+       }
+       return NULL;
+}
+
+
+/**
+ * eap_peer_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_peer_get_type(const char *name, int *vendor)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (os_strcmp(m->name, name) == 0) {
+                       *vendor = m->vendor;
+                       return m->method;
+               }
+       }
+       *vendor = EAP_VENDOR_IETF;
+       return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_get_name(int vendor, EapType type)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (m->vendor == vendor && m->method == type)
+                       return m->name;
+       }
+       return NULL;
+}
+
+
+/**
+ * eap_get_names - Get space separated list of names for supported EAP methods
+ * @buf: Buffer for names
+ * @buflen: Buffer length
+ * Returns: Number of characters written into buf (not including nul
+ * termination)
+ */
+size_t eap_get_names(char *buf, size_t buflen)
+{
+       char *pos, *end;
+       struct eap_method *m;
+       int ret;
+
+       if (buflen == 0)
+               return 0;
+
+       pos = buf;
+       end = pos + buflen;
+
+       for (m = eap_methods; m; m = m->next) {
+               ret = os_snprintf(pos, end - pos, "%s%s",
+                                 m == eap_methods ? "" : " ", m->name);
+               if (ret < 0 || ret >= end - pos)
+                       break;
+               pos += ret;
+       }
+       buf[buflen - 1] = '\0';
+
+       return pos - buf;
+}
+
+
+/**
+ * eap_get_names_as_string_array - Get supported EAP methods as string array
+ * @num: Buffer for returning the number of items in array, not including %NULL
+ * terminator. This parameter can be %NULL if the length is not needed.
+ * Returns: A %NULL-terminated array of strings, or %NULL on error.
+ *
+ * This function returns the list of names for all supported EAP methods as an
+ * array of strings. The caller must free the returned array items and the
+ * array.
+ */
+char ** eap_get_names_as_string_array(size_t *num)
+{
+       struct eap_method *m;
+       size_t array_len = 0;
+       char **array;
+       int i = 0, j;
+
+       for (m = eap_methods; m; m = m->next)
+               array_len++;
+
+       array = os_zalloc(sizeof(char *) * (array_len + 1));
+       if (array == NULL)
+               return NULL;
+
+       for (m = eap_methods; m; m = m->next) {
+               array[i++] = os_strdup(m->name);
+               if (array[i - 1] == NULL) {
+                       for (j = 0; j < i; j++)
+                               os_free(array[j]);
+                       os_free(array);
+                       return NULL;
+               }
+       }
+       array[i] = NULL;
+
+       if (num)
+               *num = array_len;
+
+       return array;
+}
+
+
+/**
+ * eap_peer_get_methods - Get a list of enabled EAP peer methods
+ * @count: Set to number of available methods
+ * Returns: List of enabled EAP peer methods
+ */
+const struct eap_method * eap_peer_get_methods(size_t *count)
+{
+       int c = 0;
+       struct eap_method *m;
+
+       for (m = eap_methods; m; m = m->next)
+               c++;
+       
+       *count = c;
+       return eap_methods;
+}
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+/**
+ * eap_peer_method_load - Load a dynamic EAP method library (shared object)
+ * @so: File path for the shared object file to load
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_method_load(const char *so)
+{
+       void *handle;
+       int (*dyn_init)(void);
+       int ret;
+
+       handle = dlopen(so, RTLD_LAZY);
+       if (handle == NULL) {
+               wpa_printf(MSG_ERROR, "EAP: Failed to open dynamic EAP method "
+                          "'%s': %s", so, dlerror());
+               return -1;
+       }
+
+       dyn_init = dlsym(handle, "eap_peer_method_dynamic_init");
+       if (dyn_init == NULL) {
+               dlclose(handle);
+               wpa_printf(MSG_ERROR, "EAP: Invalid EAP method '%s' - no "
+                          "eap_peer_method_dynamic_init()", so);
+               return -1;
+       }
+
+       ret = dyn_init();
+       if (ret) {
+               dlclose(handle);
+               wpa_printf(MSG_ERROR, "EAP: Failed to add EAP method '%s' - "
+                          "ret %d", so, ret);
+               return ret;
+       }
+
+       /* Store the handle for this shared object. It will be freed with
+        * dlclose() when the EAP method is unregistered. */
+       eap_methods->dl_handle = handle;
+
+       wpa_printf(MSG_DEBUG, "EAP: Loaded dynamic EAP method: '%s'", so);
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_method_unload - Unload a dynamic EAP method library (shared object)
+ * @method: Pointer to the dynamically loaded EAP method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to unload EAP methods that have been previously
+ * loaded with eap_peer_method_load(). Before unloading the method, all
+ * references to the method must be removed to make sure that no dereferences
+ * of freed memory will occur after unloading.
+ */
+int eap_peer_method_unload(struct eap_method *method)
+{
+       struct eap_method *m, *prev;
+       void *handle;
+
+       m = eap_methods;
+       prev = NULL;
+       while (m) {
+               if (m == method)
+                       break;
+               prev = m;
+               m = m->next;
+       }
+
+       if (m == NULL || m->dl_handle == NULL)
+               return -1;
+
+       if (prev)
+               prev->next = m->next;
+       else
+               eap_methods = m->next;
+
+       handle = m->dl_handle;
+
+       if (m->free)
+               m->free(m);
+       else
+               eap_peer_method_free(m);
+
+       dlclose(handle);
+
+       return 0;
+}
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+
+/**
+ * eap_peer_method_alloc - Allocate EAP peer method structure
+ * @version: Version of the EAP peer method interface (set to
+ * EAP_PEER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_peer_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+                                         EapType method, const char *name)
+{
+       struct eap_method *eap;
+       eap = os_zalloc(sizeof(*eap));
+       if (eap == NULL)
+               return NULL;
+       eap->version = version;
+       eap->vendor = vendor;
+       eap->method = method;
+       eap->name = name;
+       return eap;
+}
+
+
+/**
+ * eap_peer_method_free - Free EAP peer method structure
+ * @method: Method structure allocated with eap_peer_method_alloc()
+ */
+void eap_peer_method_free(struct eap_method *method)
+{
+       os_free(method);
+}
+
+
+/**
+ * eap_peer_method_register - Register an EAP peer method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP peer method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_peer_method_register(struct eap_method *method)
+{
+       struct eap_method *m, *last = NULL;
+
+       if (method == NULL || method->name == NULL ||
+           method->version != EAP_PEER_METHOD_INTERFACE_VERSION)
+               return -1;
+
+       for (m = eap_methods; m; m = m->next) {
+               if ((m->vendor == method->vendor &&
+                    m->method == method->method) ||
+                   os_strcmp(m->name, method->name) == 0)
+                       return -2;
+               last = m;
+       }
+
+       if (last)
+               last->next = method;
+       else
+               eap_methods = method;
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_unregister_methods - Unregister EAP peer methods
+ *
+ * This function is called at program termination to unregister all EAP peer
+ * methods.
+ */
+void eap_peer_unregister_methods(void)
+{
+       struct eap_method *m;
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+       void *handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+       while (eap_methods) {
+               m = eap_methods;
+               eap_methods = eap_methods->next;
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+               handle = m->dl_handle;
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+               if (m->free)
+                       m->free(m);
+               else
+                       eap_peer_method_free(m);
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+               if (handle)
+                       dlclose(handle);
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+       }
+}
diff --git a/src/eap_peer/eap_methods.h b/src/eap_peer/eap_methods.h
new file mode 100644 (file)
index 0000000..384c61b
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * EAP peer: Method registration
+ * 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 EAP_METHODS_H
+#define EAP_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_peer_get_eap_method(int vendor, EapType method);
+const struct eap_method * eap_peer_get_methods(size_t *count);
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+                                         EapType method, const char *name);
+void eap_peer_method_free(struct eap_method *method);
+int eap_peer_method_register(struct eap_method *method);
+
+
+#ifdef IEEE8021X_EAPOL
+
+EapType eap_peer_get_type(const char *name, int *vendor);
+const char * eap_get_name(int vendor, EapType type);
+size_t eap_get_names(char *buf, size_t buflen);
+char ** eap_get_names_as_string_array(size_t *num);
+void eap_peer_unregister_methods(void);
+
+#else /* IEEE8021X_EAPOL */
+
+static inline EapType eap_peer_get_type(const char *name, int *vendor)
+{
+       *vendor = EAP_VENDOR_IETF;
+       return EAP_TYPE_NONE;
+}
+
+static inline const char * eap_get_name(int vendor, EapType type)
+{
+       return NULL;
+}
+
+static inline size_t eap_get_names(char *buf, size_t buflen)
+{
+       return 0;
+}
+
+static inline int eap_peer_register_methods(void)
+{
+       return 0;
+}
+
+static inline void eap_peer_unregister_methods(void)
+{
+}
+
+static inline char ** eap_get_names_as_string_array(size_t *num)
+{
+       return NULL;
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_DYNAMIC_EAP_METHODS
+
+int eap_peer_method_load(const char *so);
+int eap_peer_method_unload(struct eap_method *method);
+
+#else /* CONFIG_DYNAMIC_EAP_METHODS */
+
+static inline int eap_peer_method_load(const char *so)
+{
+       return 0;
+}
+
+static inline int eap_peer_method_unload(struct eap_method *method)
+{
+       return 0;
+}
+
+#endif /* CONFIG_DYNAMIC_EAP_METHODS */
+
+/* EAP peer method registration calls for statically linked in methods */
+int eap_peer_md5_register(void);
+int eap_peer_tls_register(void);
+int eap_peer_mschapv2_register(void);
+int eap_peer_peap_register(void);
+int eap_peer_ttls_register(void);
+int eap_peer_gtc_register(void);
+int eap_peer_otp_register(void);
+int eap_peer_sim_register(void);
+int eap_peer_leap_register(void);
+int eap_peer_psk_register(void);
+int eap_peer_aka_register(void);
+int eap_peer_aka_prime_register(void);
+int eap_peer_fast_register(void);
+int eap_peer_pax_register(void);
+int eap_peer_sake_register(void);
+int eap_peer_gpsk_register(void);
+int eap_peer_wsc_register(void);
+int eap_peer_ikev2_register(void);
+int eap_peer_vendor_test_register(void);
+int eap_peer_tnc_register(void);
+
+#endif /* EAP_METHODS_H */
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
new file mode 100644 (file)
index 0000000..cd410d9
--- /dev/null
@@ -0,0 +1,882 @@
+/*
+ * EAP peer method: EAP-MSCHAPV2 (draft-kamath-pppext-eap-mschapv2-00.txt)
+ * 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.
+ *
+ * This file implements EAP peer part of EAP-MSCHAPV2 method (EAP type 26).
+ * draft-kamath-pppext-eap-mschapv2-00.txt defines the Microsoft EAP CHAP
+ * Extensions Protocol, Version 2, for mutual authentication and key
+ * derivation. This encapsulates MS-CHAP-v2 protocol which is defined in
+ * RFC 2759. Use of EAP-MSCHAPV2 derived keys with MPPE cipher is described in
+ * RFC 3079.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/ms_funcs.h"
+#include "common/wpa_ctrl.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_config.h"
+
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct eap_mschapv2_hdr {
+       u8 op_code; /* MSCHAPV2_OP_* */
+       u8 mschapv2_id; /* usually same as EAP identifier; must be changed
+                        * for challenges, but not for success/failure */
+       u8 ms_length[2]; /* Note: misaligned; length - 5 */
+       /* followed by data */
+} STRUCT_PACKED;
+
+/* Response Data field */
+struct ms_response {
+       u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+       u8 reserved[8];
+       u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+       u8 flags;
+} STRUCT_PACKED;
+
+/* Change-Password Data field */
+struct ms_change_password {
+       u8 encr_password[516];
+       u8 encr_hash[16];
+       u8 peer_challenge[MSCHAPV2_CHAL_LEN];
+       u8 reserved[8];
+       u8 nt_response[MSCHAPV2_NT_RESPONSE_LEN];
+       u8 flags[2];
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+struct eap_mschapv2_data {
+       u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+       int auth_response_valid;
+
+       int prev_error;
+       u8 passwd_change_challenge[PASSWD_CHANGE_CHAL_LEN];
+       int passwd_change_challenge_valid;
+       int passwd_change_version;
+
+       /* Optional challenge values generated in EAP-FAST Phase 1 negotiation
+        */
+       u8 *peer_challenge;
+       u8 *auth_challenge;
+
+       int phase2;
+       u8 master_key[MSCHAPV2_MASTER_KEY_LEN];
+       int master_key_valid;
+       int success;
+
+       struct wpabuf *prev_challenge;
+};
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+       struct eap_mschapv2_data *data;
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       if (sm->peer_challenge) {
+               data->peer_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+               if (data->peer_challenge == NULL) {
+                       eap_mschapv2_deinit(sm, data);
+                       return NULL;
+               }
+               os_memcpy(data->peer_challenge, sm->peer_challenge,
+                         MSCHAPV2_CHAL_LEN);
+       }
+
+       if (sm->auth_challenge) {
+               data->auth_challenge = os_malloc(MSCHAPV2_CHAL_LEN);
+               if (data->auth_challenge == NULL) {
+                       eap_mschapv2_deinit(sm, data);
+                       return NULL;
+               }
+               os_memcpy(data->auth_challenge, sm->auth_challenge,
+                         MSCHAPV2_CHAL_LEN);
+       }
+
+       data->phase2 = sm->init_phase2;
+
+       return data;
+}
+
+
+static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_mschapv2_data *data = priv;
+       os_free(data->peer_challenge);
+       os_free(data->auth_challenge);
+       wpabuf_free(data->prev_challenge);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_mschapv2_challenge_reply(
+       struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id,
+       u8 mschapv2_id, const u8 *auth_challenge)
+{
+       struct wpabuf *resp;
+       struct eap_mschapv2_hdr *ms;
+       u8 *peer_challenge;
+       int ms_len;
+       struct ms_response *r;
+       size_t identity_len, password_len;
+       const u8 *identity, *password;
+       int pwhash;
+
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Generating Challenge Response");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (identity == NULL || password == NULL)
+               return NULL;
+
+       ms_len = sizeof(*ms) + 1 + sizeof(*r) + identity_len;
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       ms = wpabuf_put(resp, sizeof(*ms));
+       ms->op_code = MSCHAPV2_OP_RESPONSE;
+       ms->mschapv2_id = mschapv2_id;
+       if (data->prev_error) {
+               /*
+                * TODO: this does not seem to be enough when processing two
+                * or more failure messages. IAS did not increment mschapv2_id
+                * in its own packets, but it seemed to expect the peer to
+                * increment this for all packets(?).
+                */
+               ms->mschapv2_id++;
+       }
+       WPA_PUT_BE16(ms->ms_length, ms_len);
+
+       wpabuf_put_u8(resp, sizeof(*r)); /* Value-Size */
+
+       /* Response */
+       r = wpabuf_put(resp, sizeof(*r));
+       peer_challenge = r->peer_challenge;
+       if (data->peer_challenge) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge generated "
+                          "in Phase 1");
+               peer_challenge = data->peer_challenge;
+               os_memset(r->peer_challenge, 0, MSCHAPV2_CHAL_LEN);
+       } else if (os_get_random(peer_challenge, MSCHAPV2_CHAL_LEN)) {
+               wpabuf_free(resp);
+               return NULL;
+       }
+       os_memset(r->reserved, 0, 8);
+       if (data->auth_challenge) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge generated "
+                          "in Phase 1");
+               auth_challenge = data->auth_challenge;
+       }
+       if (mschapv2_derive_response(identity, identity_len, password,
+                                    password_len, pwhash, auth_challenge,
+                                    peer_challenge, r->nt_response,
+                                    data->auth_response, data->master_key)) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive "
+                          "response");
+               wpabuf_free(resp);
+               return NULL;
+       }
+       data->auth_response_valid = 1;
+       data->master_key_valid = 1;
+
+       r->flags = 0; /* reserved, must be zero */
+
+       wpabuf_put_data(resp, identity, identity_len);
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+                  "(response)", id, ms->mschapv2_id);
+       return resp;
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 challenge message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in the request
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_challenge(
+       struct eap_sm *sm, struct eap_mschapv2_data *data,
+       struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req,
+       size_t req_len, u8 id)
+{
+       size_t len, challenge_len;
+       const u8 *pos, *challenge;
+
+       if (eap_get_config_identity(sm, &len) == NULL ||
+           eap_get_config_password(sm, &len) == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received challenge");
+       if (req_len < sizeof(*req) + 1) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge data "
+                          "(len %lu)", (unsigned long) req_len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       pos = (const u8 *) (req + 1);
+       challenge_len = *pos++;
+       len = req_len - sizeof(*req) - 1;
+       if (challenge_len != MSCHAPV2_CHAL_LEN) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid challenge length "
+                          "%lu", (unsigned long) challenge_len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (len < challenge_len) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Too short challenge"
+                          " packet: len=%lu challenge_len=%lu",
+                          (unsigned long) len, (unsigned long) challenge_len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->passwd_change_challenge_valid) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using challenge from the "
+                          "failure message");
+               challenge = data->passwd_change_challenge;
+       } else
+               challenge = pos;
+       pos += challenge_len;
+       len -= challenge_len;
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Authentication Servername",
+                   pos, len);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       return eap_mschapv2_challenge_reply(sm, data, id, req->mschapv2_id,
+                                           challenge);
+}
+
+
+static void eap_mschapv2_password_changed(struct eap_sm *sm,
+                                         struct eap_mschapv2_data *data)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config && config->new_password) {
+               wpa_msg(sm->msg_ctx, MSG_INFO,
+                       WPA_EVENT_PASSWORD_CHANGED
+                       "EAP-MSCHAPV2: Password changed successfully");
+               data->prev_error = 0;
+               os_free(config->password);
+               if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
+                       config->password = os_malloc(16);
+                       config->password_len = 16;
+                       if (config->password) {
+                               nt_password_hash(config->new_password,
+                                                config->new_password_len,
+                                                config->password);
+                       }
+                       os_free(config->new_password);
+               } else {
+                       config->password = config->new_password;
+                       config->password_len = config->new_password_len;
+               }
+               config->new_password = NULL;
+               config->new_password_len = 0;
+       }
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 success message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_success(struct eap_sm *sm,
+                                           struct eap_mschapv2_data *data,
+                                           struct eap_method_ret *ret,
+                                           const struct eap_mschapv2_hdr *req,
+                                           size_t req_len, u8 id)
+{
+       struct wpabuf *resp;
+       const u8 *pos;
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received success");
+       len = req_len - sizeof(*req);
+       pos = (const u8 *) (req + 1);
+       if (!data->auth_response_valid ||
+           mschapv2_verify_auth_response(data->auth_response, pos, len)) {
+               wpa_printf(MSG_WARNING, "EAP-MSCHAPV2: Invalid authenticator "
+                          "response in success request");
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       pos += 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+       len -= 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN;
+       while (len > 0 && *pos == ' ') {
+               pos++;
+               len--;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Success message",
+                         pos, len);
+       wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Authentication succeeded");
+
+       /* Note: Only op_code of the EAP-MSCHAPV2 header is included in success
+        * message. */
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Failed to allocate "
+                          "buffer for success response");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpabuf_put_u8(resp, MSCHAPV2_OP_SUCCESS); /* op_code */
+
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       ret->allowNotifications = FALSE;
+       data->success = 1;
+
+       if (data->prev_error == ERROR_PASSWD_EXPIRED)
+               eap_mschapv2_password_changed(sm, data);
+
+       return resp;
+}
+
+
+static int eap_mschapv2_failure_txt(struct eap_sm *sm,
+                                   struct eap_mschapv2_data *data, char *txt)
+{
+       char *pos, *msg = "";
+       int retry = 1;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       /* For example:
+        * E=691 R=1 C=<32 octets hex challenge> V=3 M=Authentication Failure
+        */
+
+       pos = txt;
+
+       if (pos && os_strncmp(pos, "E=", 2) == 0) {
+               pos += 2;
+               data->prev_error = atoi(pos);
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: error %d",
+                          data->prev_error);
+               pos = os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       }
+
+       if (pos && os_strncmp(pos, "R=", 2) == 0) {
+               pos += 2;
+               retry = atoi(pos);
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: retry is %sallowed",
+                          retry == 1 ? "" : "not ");
+               pos = os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       }
+
+       if (pos && os_strncmp(pos, "C=", 2) == 0) {
+               int hex_len;
+               pos += 2;
+               hex_len = os_strchr(pos, ' ') - (char *) pos;
+               if (hex_len == PASSWD_CHANGE_CHAL_LEN * 2) {
+                       if (hexstr2bin(pos, data->passwd_change_challenge,
+                                      PASSWD_CHANGE_CHAL_LEN)) {
+                               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid "
+                                          "failure challenge");
+                       } else {
+                               wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: failure "
+                                           "challenge",
+                                           data->passwd_change_challenge,
+                                           PASSWD_CHANGE_CHAL_LEN);
+                               data->passwd_change_challenge_valid = 1;
+                       }
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: invalid failure "
+                                  "challenge len %d", hex_len);
+               }
+               pos = os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: required challenge field "
+                          "was not present in failure message");
+       }
+
+       if (pos && os_strncmp(pos, "V=", 2) == 0) {
+               pos += 2;
+               data->passwd_change_version = atoi(pos);
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: password changing "
+                          "protocol version %d", data->passwd_change_version);
+               pos = os_strchr(pos, ' ');
+               if (pos)
+                       pos++;
+       }
+
+       if (pos && os_strncmp(pos, "M=", 2) == 0) {
+               pos += 2;
+               msg = pos;
+       }
+       wpa_msg(sm->msg_ctx, MSG_WARNING,
+               "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
+               "%d)",
+               msg, retry == 1 ? "" : "not ", data->prev_error);
+       if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+           data->passwd_change_version == 3 && config) {
+               if (config->new_password == NULL) {
+                       wpa_msg(sm->msg_ctx, MSG_INFO,
+                               "EAP-MSCHAPV2: Password expired - password "
+                               "change required");
+                       eap_sm_request_new_password(sm);
+               }
+       } else if (retry == 1 && config) {
+               /* TODO: could prevent the current password from being used
+                * again at least for some period of time */
+               if (!config->mschapv2_retry)
+                       eap_sm_request_identity(sm);
+               eap_sm_request_password(sm);
+               config->mschapv2_retry = 1;
+       } else if (config) {
+               /* TODO: prevent retries using same username/password */
+               config->mschapv2_retry = 0;
+       }
+
+       return retry == 1;
+}
+
+
+static struct wpabuf * eap_mschapv2_change_password(
+       struct eap_sm *sm, struct eap_mschapv2_data *data,
+       struct eap_method_ret *ret, const struct eap_mschapv2_hdr *req, u8 id)
+{
+       struct wpabuf *resp;
+       int ms_len;
+       const u8 *username, *password, *new_password;
+       size_t username_len, password_len, new_password_len;
+       struct eap_mschapv2_hdr *ms;
+       struct ms_change_password *cp;
+       u8 password_hash[16], password_hash_hash[16];
+       int pwhash;
+
+       username = eap_get_config_identity(sm, &username_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       new_password = eap_get_config_new_password(sm, &new_password_len);
+       if (username == NULL || password == NULL || new_password == NULL)
+               return NULL;
+
+       username = mschapv2_remove_domain(username, &username_len);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_COND_SUCC;
+       ret->allowNotifications = TRUE;
+
+       ms_len = sizeof(*ms) + sizeof(*cp);
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       ms = wpabuf_put(resp, sizeof(*ms));
+       ms->op_code = MSCHAPV2_OP_CHANGE_PASSWORD;
+       ms->mschapv2_id = req->mschapv2_id + 1;
+       WPA_PUT_BE16(ms->ms_length, ms_len);
+       cp = wpabuf_put(resp, sizeof(*cp));
+
+       /* Encrypted-Password */
+       if (pwhash) {
+               if (encrypt_pw_block_with_password_hash(
+                           new_password, new_password_len,
+                           password, cp->encr_password))
+                       goto fail;
+       } else {
+               if (new_password_encrypted_with_old_nt_password_hash(
+                           new_password, new_password_len,
+                           password, password_len, cp->encr_password))
+                       goto fail;
+       }
+
+       /* Encrypted-Hash */
+       if (pwhash) {
+               u8 new_password_hash[16];
+               nt_password_hash(new_password, new_password_len,
+                                new_password_hash);
+               nt_password_hash_encrypted_with_block(password,
+                                                     new_password_hash,
+                                                     cp->encr_hash);
+       } else {
+               old_nt_password_hash_encrypted_with_new_nt_password_hash(
+                       new_password, new_password_len,
+                       password, password_len, cp->encr_hash);
+       }
+
+       /* Peer-Challenge */
+       if (os_get_random(cp->peer_challenge, MSCHAPV2_CHAL_LEN))
+               goto fail;
+
+       /* Reserved, must be zero */
+       os_memset(cp->reserved, 0, 8);
+
+       /* NT-Response */
+       wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: auth_challenge",
+                   data->passwd_change_challenge, PASSWD_CHANGE_CHAL_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: peer_challenge",
+                   cp->peer_challenge, MSCHAPV2_CHAL_LEN);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: username",
+                         username, username_len);
+       wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-MSCHAPV2: new password",
+                             new_password, new_password_len);
+       generate_nt_response(data->passwd_change_challenge, cp->peer_challenge,
+                            username, username_len,
+                            new_password, new_password_len,
+                            cp->nt_response);
+       wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: NT-Response",
+                   cp->nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+
+       /* Authenticator response is not really needed yet, but calculate it
+        * here so that challenges need not be saved. */
+       generate_authenticator_response(new_password, new_password_len,
+                                       cp->peer_challenge,
+                                       data->passwd_change_challenge,
+                                       username, username_len,
+                                       cp->nt_response, data->auth_response);
+       data->auth_response_valid = 1;
+
+       /* Likewise, generate master_key here since we have the needed data
+        * available. */
+       nt_password_hash(new_password, new_password_len, password_hash);
+       hash_nt_password_hash(password_hash, password_hash_hash);
+       get_master_key(password_hash_hash, cp->nt_response, data->master_key);
+       data->master_key_valid = 1;
+
+       /* Flags */
+       os_memset(cp->flags, 0, 2);
+
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: TX identifier %d mschapv2_id %d "
+                  "(change pw)", id, ms->mschapv2_id);
+
+       return resp;
+
+fail:
+       wpabuf_free(resp);
+       return NULL;
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 failure message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: Pointer to EAP-MSCHAPv2 header from the request
+ * @req_len: Length of the EAP-MSCHAPv2 data
+ * @id: EAP identifier used in th erequest
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_failure(struct eap_sm *sm,
+                                           struct eap_mschapv2_data *data,
+                                           struct eap_method_ret *ret,
+                                           const struct eap_mschapv2_hdr *req,
+                                           size_t req_len, u8 id)
+{
+       struct wpabuf *resp;
+       const u8 *msdata = (const u8 *) (req + 1);
+       char *buf;
+       size_t len = req_len - sizeof(*req);
+       int retry = 0;
+
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received failure");
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Failure data",
+                         msdata, len);
+       /*
+        * eap_mschapv2_failure_txt() expects a nul terminated string, so we
+        * must allocate a large enough temporary buffer to create that since
+        * the received message does not include nul termination.
+        */
+       buf = os_malloc(len + 1);
+       if (buf) {
+               os_memcpy(buf, msdata, len);
+               buf[len] = '\0';
+               retry = eap_mschapv2_failure_txt(sm, data, buf);
+               os_free(buf);
+       }
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = FALSE;
+
+       if (data->prev_error == ERROR_PASSWD_EXPIRED &&
+           data->passwd_change_version == 3) {
+               struct eap_peer_config *config = eap_get_config(sm);
+               if (config && config->new_password)
+                       return eap_mschapv2_change_password(sm, data, ret, req,
+                                                           id);
+               if (config && config->pending_req_new_password)
+                       return NULL;
+       } else if (retry && data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+               /* TODO: could try to retry authentication, e.g, after having
+                * changed the username/password. In this case, EAP MS-CHAP-v2
+                * Failure Response would not be sent here. */
+               return NULL;
+       }
+
+       /* Note: Only op_code of the EAP-MSCHAPV2 header is included in failure
+        * message. */
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, 1,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, MSCHAPV2_OP_FAILURE); /* op_code */
+
+       return resp;
+}
+
+
+static int eap_mschapv2_check_config(struct eap_sm *sm)
+{
+       size_t len;
+
+       if (eap_get_config_identity(sm, &len) == NULL) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Identity not configured");
+               eap_sm_request_identity(sm);
+               return -1;
+       }
+
+       if (eap_get_config_password(sm, &len) == NULL) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+               eap_sm_request_password(sm);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_mschapv2_check_mslen(struct eap_sm *sm, size_t len,
+                                   const struct eap_mschapv2_hdr *ms)
+{
+       size_t ms_len = WPA_GET_BE16(ms->ms_length);
+
+       if (ms_len == len)
+               return 0;
+
+       wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid header: len=%lu "
+                  "ms_len=%lu", (unsigned long) len, (unsigned long) ms_len);
+       if (sm->workaround) {
+               /* Some authentication servers use invalid ms_len,
+                * ignore it for interoperability. */
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: workaround, ignore"
+                          " invalid ms_len %lu (len %lu)",
+                          (unsigned long) ms_len,
+                          (unsigned long) len);
+               return 0;
+       }
+
+       return -1;
+}
+
+
+static void eap_mschapv2_copy_challenge(struct eap_mschapv2_data *data,
+                                       const struct wpabuf *reqData)
+{
+       /*
+        * Store a copy of the challenge message, so that it can be processed
+        * again in case retry is allowed after a possible failure.
+        */
+       wpabuf_free(data->prev_challenge);
+       data->prev_challenge = wpabuf_dup(reqData);
+}
+
+
+/**
+ * eap_mschapv2_process - Process an EAP-MSCHAPv2 request
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @priv: Pointer to private EAP method data from eap_mschapv2_init()
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * Returns: Pointer to allocated EAP response packet (eapRespData) or %NULL if
+ * no reply available
+ */
+static struct wpabuf * eap_mschapv2_process(struct eap_sm *sm, void *priv,
+                                           struct eap_method_ret *ret,
+                                           const struct wpabuf *reqData)
+{
+       struct eap_mschapv2_data *data = priv;
+       struct eap_peer_config *config = eap_get_config(sm);
+       const struct eap_mschapv2_hdr *ms;
+       int using_prev_challenge = 0;
+       const u8 *pos;
+       size_t len;
+       u8 id;
+
+       if (eap_mschapv2_check_config(sm)) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (config->mschapv2_retry && data->prev_challenge &&
+           data->prev_error == ERROR_AUTHENTICATION_FAILURE) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Replacing pending packet "
+                          "with the previous challenge");
+
+               reqData = data->prev_challenge;
+               using_prev_challenge = 1;
+               config->mschapv2_retry = 0;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqData,
+                              &len);
+       if (pos == NULL || len < sizeof(*ms) + 1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       ms = (const struct eap_mschapv2_hdr *) pos;
+       if (eap_mschapv2_check_mslen(sm, len, ms)) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       id = eap_get_id(reqData);
+       wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: RX identifier %d mschapv2_id %d",
+                  id, ms->mschapv2_id);
+
+       switch (ms->op_code) {
+       case MSCHAPV2_OP_CHALLENGE:
+               if (!using_prev_challenge)
+                       eap_mschapv2_copy_challenge(data, reqData);
+               return eap_mschapv2_challenge(sm, data, ret, ms, len, id);
+       case MSCHAPV2_OP_SUCCESS:
+               return eap_mschapv2_success(sm, data, ret, ms, len, id);
+       case MSCHAPV2_OP_FAILURE:
+               return eap_mschapv2_failure(sm, data, ret, ms, len, id);
+       default:
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Unknown op %d - ignored",
+                          ms->op_code);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+}
+
+
+static Boolean eap_mschapv2_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_mschapv2_data *data = priv;
+       return data->success && data->master_key_valid;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_mschapv2_data *data = priv;
+       u8 *key;
+       int key_len;
+
+       if (!data->master_key_valid || !data->success)
+               return NULL;
+
+       key_len = 2 * MSCHAPV2_KEY_LEN;
+
+       key = os_malloc(key_len);
+       if (key == NULL)
+               return NULL;
+
+       /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
+        *      peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
+       get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
+       get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+                               MSCHAPV2_KEY_LEN, 0, 0);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
+                       key, key_len);
+
+       *len = key_len;
+       return key;
+}
+
+
+/**
+ * eap_peer_mschapv2_register - Register EAP-MSCHAPv2 peer method
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register EAP-MSCHAPv2 peer method into the EAP
+ * method list.
+ */
+int eap_peer_mschapv2_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+                                   "MSCHAPV2");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_mschapv2_init;
+       eap->deinit = eap_mschapv2_deinit;
+       eap->process = eap_mschapv2_process;
+       eap->isKeyAvailable = eap_mschapv2_isKeyAvailable;
+       eap->getKey = eap_mschapv2_getKey;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_otp.c b/src/eap_peer/eap_otp.c
new file mode 100644 (file)
index 0000000..556c22f
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * EAP peer method: EAP-OTP (RFC 3748)
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+static void * eap_otp_init(struct eap_sm *sm)
+{
+       /* No need for private data. However, must return non-NULL to indicate
+        * success. */
+       return (void *) 1;
+}
+
+
+static void eap_otp_deinit(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static struct wpabuf * eap_otp_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct wpabuf *resp;
+       const u8 *pos, *password;
+       size_t password_len, len;
+       int otp;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_OTP, reqData, &len);
+       if (pos == NULL) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-OTP: Request message",
+                         pos, len);
+
+       password = eap_get_config_otp(sm, &password_len);
+       if (password)
+               otp = 1;
+       else {
+               password = eap_get_config_password(sm, &password_len);
+               otp = 0;
+       }
+
+       if (password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-OTP: Password not configured");
+               eap_sm_request_otp(sm, (const char *) pos, len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       ret->ignore = FALSE;
+
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_COND_SUCC;
+       ret->allowNotifications = FALSE;
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_OTP, password_len,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+       wpabuf_put_data(resp, password, password_len);
+       wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-OTP: Response",
+                             password, password_len);
+
+       if (otp) {
+               wpa_printf(MSG_DEBUG, "EAP-OTP: Forgetting used password");
+               eap_clear_config_otp(sm);
+       }
+
+       return resp;
+}
+
+
+int eap_peer_otp_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_OTP, "OTP");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_otp_init;
+       eap->deinit = eap_otp_deinit;
+       eap->process = eap_otp_process;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_pax.c b/src/eap_peer/eap_pax.c
new file mode 100644 (file)
index 0000000..2e04831
--- /dev/null
@@ -0,0 +1,530 @@
+/*
+ * EAP peer method: EAP-PAX (RFC 4746)
+ * Copyright (c) 2005-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 "eap_common/eap_pax_common.h"
+#include "eap_i.h"
+
+/*
+ * Note: only PAX_STD subprotocol is currently supported
+ *
+ * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
+ * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
+ * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
+ * RSAES-OAEP).
+ */
+
+struct eap_pax_data {
+       enum { PAX_INIT, PAX_STD_2_SENT, PAX_DONE } state;
+       u8 mac_id, dh_group_id, public_key_id;
+       union {
+               u8 e[2 * EAP_PAX_RAND_LEN];
+               struct {
+                       u8 x[EAP_PAX_RAND_LEN]; /* server rand */
+                       u8 y[EAP_PAX_RAND_LEN]; /* client rand */
+               } r;
+       } rand;
+       char *cid;
+       size_t cid_len;
+       u8 ak[EAP_PAX_AK_LEN];
+       u8 mk[EAP_PAX_MK_LEN];
+       u8 ck[EAP_PAX_CK_LEN];
+       u8 ick[EAP_PAX_ICK_LEN];
+};
+
+
+static void eap_pax_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_pax_init(struct eap_sm *sm)
+{
+       struct eap_pax_data *data;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password(sm, &password_len);
+       if (!identity || !password) {
+               wpa_printf(MSG_INFO, "EAP-PAX: CID (nai) or key (password) "
+                          "not configured");
+               return NULL;
+       }
+
+       if (password_len != EAP_PAX_AK_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Invalid PSK length");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = PAX_INIT;
+
+       data->cid = os_malloc(identity_len);
+       if (data->cid == NULL) {
+               eap_pax_deinit(sm, data);
+               return NULL;
+       }
+       os_memcpy(data->cid, identity, identity_len);
+       data->cid_len = identity_len;
+
+       os_memcpy(data->ak, password, EAP_PAX_AK_LEN);
+
+       return data;
+}
+
+
+static void eap_pax_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_pax_data *data = priv;
+       os_free(data->cid);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_pax_alloc_resp(const struct eap_pax_hdr *req,
+                                         u8 id, u8 op_code, size_t plen)
+{
+       struct wpabuf *resp;
+       struct eap_pax_hdr *pax;
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+                            sizeof(*pax) + plen, EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       pax = wpabuf_put(resp, sizeof(*pax));
+       pax->op_code = op_code;
+       pax->flags = 0;
+       pax->mac_id = req->mac_id;
+       pax->dh_group_id = req->dh_group_id;
+       pax->public_key_id = req->public_key_id;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
+                                            struct eap_method_ret *ret, u8 id,
+                                            const struct eap_pax_hdr *req,
+                                            size_t req_plen)
+{
+       struct wpabuf *resp;
+       const u8 *pos;
+       u8 *rpos;
+       size_t left, plen;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (received)");
+
+       if (data->state != PAX_INIT) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 received in "
+                          "unexpected state (%d) - ignored", data->state);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (req->flags & EAP_PAX_FLAGS_CE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with CE flag set - "
+                          "ignored");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       left = req_plen - sizeof(*req);
+
+       if (left < 2 + EAP_PAX_RAND_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with too short "
+                          "payload");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos = (const u8 *) (req + 1);
+       if (WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-1 with incorrect A "
+                          "length %d (expected %d)",
+                          WPA_GET_BE16(pos), EAP_PAX_RAND_LEN);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos += 2;
+       left -= 2;
+       os_memcpy(data->rand.r.x, pos, EAP_PAX_RAND_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: X (server rand)",
+                   data->rand.r.x, EAP_PAX_RAND_LEN);
+       pos += EAP_PAX_RAND_LEN;
+       left -= EAP_PAX_RAND_LEN;
+
+       if (left > 0) {
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+                           pos, left);
+       }
+
+       if (os_get_random(data->rand.r.y, EAP_PAX_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
+                   data->rand.r.y, EAP_PAX_RAND_LEN);
+
+       if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
+                                          data->mk, data->ck, data->ick) < 0)
+       {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-2 (sending)");
+
+       plen = 2 + EAP_PAX_RAND_LEN + 2 + data->cid_len + 2 + EAP_PAX_MAC_LEN +
+               EAP_PAX_ICV_LEN;
+       resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_STD_2, plen);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_be16(resp, EAP_PAX_RAND_LEN);
+       wpabuf_put_data(resp, data->rand.r.y, EAP_PAX_RAND_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: B = Y (client rand)",
+                   data->rand.r.y, EAP_PAX_RAND_LEN);
+
+       wpabuf_put_be16(resp, data->cid_len);
+       wpabuf_put_data(resp, data->cid, data->cid_len);
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
+                         (u8 *) data->cid, data->cid_len);
+
+       wpabuf_put_be16(resp, EAP_PAX_MAC_LEN);
+       rpos = wpabuf_put(resp, EAP_PAX_MAC_LEN);
+       eap_pax_mac(req->mac_id, data->ck, EAP_PAX_CK_LEN,
+                   data->rand.r.x, EAP_PAX_RAND_LEN,
+                   data->rand.r.y, EAP_PAX_RAND_LEN,
+                   (u8 *) data->cid, data->cid_len, rpos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
+                   rpos, EAP_PAX_MAC_LEN);
+
+       /* Optional ADE could be added here, if needed */
+
+       rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
+       eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
+                   wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
+                   NULL, 0, NULL, 0, rpos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
+
+       data->state = PAX_STD_2_SENT;
+       data->mac_id = req->mac_id;
+       data->dh_group_id = req->dh_group_id;
+       data->public_key_id = req->public_key_id;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
+                                            struct eap_method_ret *ret, u8 id,
+                                            const struct eap_pax_hdr *req,
+                                            size_t req_plen)
+{
+       struct wpabuf *resp;
+       u8 *rpos, mac[EAP_PAX_MAC_LEN];
+       const u8 *pos;
+       size_t left;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (received)");
+
+       if (data->state != PAX_STD_2_SENT) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 received in "
+                          "unexpected state (%d) - ignored", data->state);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (req->flags & EAP_PAX_FLAGS_CE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with CE flag set - "
+                          "ignored");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       left = req_plen - sizeof(*req);
+
+       if (left < 2 + EAP_PAX_MAC_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with too short "
+                          "payload");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos = (const u8 *) (req + 1);
+       if (WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: PAX_STD-3 with incorrect "
+                          "MAC_CK length %d (expected %d)",
+                          WPA_GET_BE16(pos), EAP_PAX_MAC_LEN);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       pos += 2;
+       left -= 2;
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
+                   pos, EAP_PAX_MAC_LEN);
+       eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+                   data->rand.r.y, EAP_PAX_RAND_LEN,
+                   (u8 *) data->cid, data->cid_len, NULL, 0, mac);
+       if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
+                          "received");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
+                           mac, EAP_PAX_MAC_LEN);
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+
+       pos += EAP_PAX_MAC_LEN;
+       left -= EAP_PAX_MAC_LEN;
+
+       if (left > 0) {
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+                           pos, left);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: PAX-ACK (sending)");
+
+       resp = eap_pax_alloc_resp(req, id, EAP_PAX_OP_ACK, EAP_PAX_ICV_LEN);
+       if (resp == NULL)
+               return NULL;
+
+       /* Optional ADE could be added here, if needed */
+
+       rpos = wpabuf_put(resp, EAP_PAX_ICV_LEN);
+       eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+                   wpabuf_head(resp), wpabuf_len(resp) - EAP_PAX_ICV_LEN,
+                   NULL, 0, NULL, 0, rpos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", rpos, EAP_PAX_ICV_LEN);
+
+       data->state = PAX_DONE;
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       ret->allowNotifications = FALSE;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_pax_data *data = priv;
+       const struct eap_pax_hdr *req;
+       struct wpabuf *resp;
+       u8 icvbuf[EAP_PAX_ICV_LEN], id;
+       const u8 *icv, *pos;
+       size_t len;
+       u16 flen, mlen;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, reqData, &len);
+       if (pos == NULL || len < EAP_PAX_ICV_LEN) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       id = eap_get_id(reqData);
+       req = (const struct eap_pax_hdr *) pos;
+       flen = len - EAP_PAX_ICV_LEN;
+       mlen = wpabuf_len(reqData) - EAP_PAX_ICV_LEN;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
+                  "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
+                  "public_key_id 0x%x",
+                  req->op_code, req->flags, req->mac_id, req->dh_group_id,
+                  req->public_key_id);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
+                   pos, len - EAP_PAX_ICV_LEN);
+
+       if (data->state != PAX_INIT && data->mac_id != req->mac_id) {
+               wpa_printf(MSG_INFO, "EAP-PAX: MAC ID changed during "
+                          "authentication (was 0x%d, is 0x%d)",
+                          data->mac_id, req->mac_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state != PAX_INIT && data->dh_group_id != req->dh_group_id) {
+               wpa_printf(MSG_INFO, "EAP-PAX: DH Group ID changed during "
+                          "authentication (was 0x%d, is 0x%d)",
+                          data->dh_group_id, req->dh_group_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state != PAX_INIT &&
+           data->public_key_id != req->public_key_id) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Public Key ID changed during "
+                          "authentication (was 0x%d, is 0x%d)",
+                          data->public_key_id, req->public_key_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       /* TODO: add support EAP_PAX_HMAC_SHA256_128 */
+       if (req->mac_id != EAP_PAX_MAC_HMAC_SHA1_128) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Unsupported MAC ID 0x%x",
+                          req->mac_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (req->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Unsupported DH Group ID 0x%x",
+                          req->dh_group_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (req->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Unsupported Public Key ID 0x%x",
+                          req->public_key_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (req->flags & EAP_PAX_FLAGS_MF) {
+               /* TODO: add support for reassembling fragments */
+               wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported - "
+                          "ignored packet");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       icv = pos + len - EAP_PAX_ICV_LEN;
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
+       if (req->op_code == EAP_PAX_OP_STD_1) {
+               eap_pax_mac(req->mac_id, (u8 *) "", 0,
+                           wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
+                           icvbuf);
+       } else {
+               eap_pax_mac(req->mac_id, data->ick, EAP_PAX_ICK_LEN,
+                           wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
+                           icvbuf);
+       }
+       if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
+                          "message");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
+                           icvbuf, EAP_PAX_ICV_LEN);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       switch (req->op_code) {
+       case EAP_PAX_OP_STD_1:
+               resp = eap_pax_process_std_1(data, ret, id, req, flen);
+               break;
+       case EAP_PAX_OP_STD_3:
+               resp = eap_pax_process_std_3(data, ret, id, req, flen);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-PAX: ignoring message with unknown "
+                          "op_code %d", req->op_code);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (ret->methodState == METHOD_DONE) {
+               ret->allowNotifications = FALSE;
+       }
+
+       return resp;
+}
+
+
+static Boolean eap_pax_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_pax_data *data = priv;
+       return data->state == PAX_DONE;
+}
+
+
+static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pax_data *data = priv;
+       u8 *key;
+
+       if (data->state != PAX_DONE)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_MSK_LEN;
+       eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+                   "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
+                   EAP_MSK_LEN, key);
+
+       return key;
+}
+
+
+static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pax_data *data = priv;
+       u8 *key;
+
+       if (data->state != PAX_DONE)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+                   "Extended Master Session Key",
+                   data->rand.e, 2 * EAP_PAX_RAND_LEN,
+                   EAP_EMSK_LEN, key);
+
+       return key;
+}
+
+
+int eap_peer_pax_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_pax_init;
+       eap->deinit = eap_pax_deinit;
+       eap->process = eap_pax_process;
+       eap->isKeyAvailable = eap_pax_isKeyAvailable;
+       eap->getKey = eap_pax_getKey;
+       eap->get_emsk = eap_pax_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
new file mode 100644 (file)
index 0000000..2b72084
--- /dev/null
@@ -0,0 +1,1288 @@
+/*
+ * EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * 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 "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+#include "tncc.h"
+
+
+/* Maximum supported PEAP version
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+       struct eap_ssl_data ssl;
+
+       int peap_version, force_peap_version, force_new_label;
+
+       const struct eap_method *phase2_method;
+       void *phase2_priv;
+       int phase2_success;
+       int phase2_eap_success;
+       int phase2_eap_started;
+
+       struct eap_method_type phase2_type;
+       struct eap_method_type *phase2_types;
+       size_t num_phase2_types;
+
+       int peap_outer_success; /* 0 = PEAP terminated on Phase 2 inner
+                                * EAP-Success
+                                * 1 = reply with tunneled EAP-Success to inner
+                                * EAP-Success and expect AS to send outer
+                                * (unencrypted) EAP-Success after this
+                                * 2 = reply with PEAP/TLS ACK to inner
+                                * EAP-Success and expect AS to send outer
+                                * (unencrypted) EAP-Success after this */
+       int resuming; /* starting a resumed session */
+       int reauth; /* reauthentication */
+       u8 *key_data;
+
+       struct wpabuf *pending_phase2_req;
+       enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
+       int crypto_binding_used;
+       u8 binding_nonce[32];
+       u8 ipmk[40];
+       u8 cmk[20];
+       int soh; /* Whether IF-TNCCS-SOH (Statement of Health; Microsoft NAP)
+                 * is enabled. */
+};
+
+
+static int eap_peap_parse_phase1(struct eap_peap_data *data,
+                                const char *phase1)
+{
+       const char *pos;
+
+       pos = os_strstr(phase1, "peapver=");
+       if (pos) {
+               data->force_peap_version = atoi(pos + 8);
+               data->peap_version = data->force_peap_version;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Forced PEAP version %d",
+                          data->force_peap_version);
+       }
+
+       if (os_strstr(phase1, "peaplabel=1")) {
+               data->force_new_label = 1;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Force new label for key "
+                          "derivation");
+       }
+
+       if (os_strstr(phase1, "peap_outer_success=0")) {
+               data->peap_outer_success = 0;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: terminate authentication on "
+                          "tunneled EAP-Success");
+       } else if (os_strstr(phase1, "peap_outer_success=1")) {
+               data->peap_outer_success = 1;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: send tunneled EAP-Success "
+                          "after receiving tunneled EAP-Success");
+       } else if (os_strstr(phase1, "peap_outer_success=2")) {
+               data->peap_outer_success = 2;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: send PEAP/TLS ACK after "
+                          "receiving tunneled EAP-Success");
+       }
+
+       if (os_strstr(phase1, "crypto_binding=0")) {
+               data->crypto_binding = NO_BINDING;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Do not use cryptobinding");
+       } else if (os_strstr(phase1, "crypto_binding=1")) {
+               data->crypto_binding = OPTIONAL_BINDING;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Optional cryptobinding");
+       } else if (os_strstr(phase1, "crypto_binding=2")) {
+               data->crypto_binding = REQUIRE_BINDING;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Require cryptobinding");
+       }
+
+#ifdef EAP_TNC
+       if (os_strstr(phase1, "tnc=soh2")) {
+               data->soh = 2;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+       } else if (os_strstr(phase1, "tnc=soh1")) {
+               data->soh = 1;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 1 enabled");
+       } else if (os_strstr(phase1, "tnc=soh")) {
+               data->soh = 2;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: SoH version 2 enabled");
+       }
+#endif /* EAP_TNC */
+
+       return 0;
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+       struct eap_peap_data *data;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       sm->peap_done = FALSE;
+       data->peap_version = EAP_PEAP_VERSION;
+       data->force_peap_version = -1;
+       data->peap_outer_success = 2;
+       data->crypto_binding = OPTIONAL_BINDING;
+
+       if (config && config->phase1 &&
+           eap_peap_parse_phase1(data, config->phase1) < 0) {
+               eap_peap_deinit(sm, data);
+               return NULL;
+       }
+
+       if (eap_peer_select_phase2_methods(config, "auth=",
+                                          &data->phase2_types,
+                                          &data->num_phase2_types) < 0) {
+               eap_peap_deinit(sm, data);
+               return NULL;
+       }
+
+       data->phase2_type.vendor = EAP_VENDOR_IETF;
+       data->phase2_type.method = EAP_TYPE_NONE;
+
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+               eap_peap_deinit(sm, data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_peap_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       if (data == NULL)
+               return;
+       if (data->phase2_priv && data->phase2_method)
+               data->phase2_method->deinit(sm, data->phase2_priv);
+       os_free(data->phase2_types);
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);
+       os_free(data->key_data);
+       wpabuf_free(data->pending_phase2_req);
+       os_free(data);
+}
+
+
+/**
+ * eap_tlv_build_nak - Build EAP-TLV NAK message
+ * @id: EAP identifier for the header
+ * @nak_type: TLV type (EAP_TLV_*)
+ * Returns: Buffer to the allocated EAP-TLV NAK message or %NULL on failure
+ *
+ * This funtion builds an EAP-TLV NAK message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_nak(int id, u16 nak_type)
+{
+       struct wpabuf *msg;
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, 10,
+                           EAP_CODE_RESPONSE, id);
+       if (msg == NULL)
+               return NULL;
+
+       wpabuf_put_u8(msg, 0x80); /* Mandatory */
+       wpabuf_put_u8(msg, EAP_TLV_NAK_TLV);
+       wpabuf_put_be16(msg, 6); /* Length */
+       wpabuf_put_be32(msg, 0); /* Vendor-Id */
+       wpabuf_put_be16(msg, nak_type); /* NAK-Type */
+
+       return msg;
+}
+
+
+static int eap_peap_get_isk(struct eap_sm *sm, struct eap_peap_data *data,
+                           u8 *isk, size_t isk_len)
+{
+       u8 *key;
+       size_t key_len;
+
+       os_memset(isk, 0, isk_len);
+       if (data->phase2_method == NULL || data->phase2_priv == NULL ||
+           data->phase2_method->isKeyAvailable == NULL ||
+           data->phase2_method->getKey == NULL)
+               return 0;
+
+       if (!data->phase2_method->isKeyAvailable(sm, data->phase2_priv) ||
+           (key = data->phase2_method->getKey(sm, data->phase2_priv,
+                                              &key_len)) == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not get key material "
+                          "from Phase 2");
+               return -1;
+       }
+
+       if (key_len > isk_len)
+               key_len = isk_len;
+       os_memcpy(isk, key, key_len);
+       os_free(key);
+
+       return 0;
+}
+
+
+static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
+{
+       u8 *tk;
+       u8 isk[32], imck[60];
+
+       /*
+        * Tunnel key (TK) is the first 60 octets of the key generated by
+        * phase 1 of PEAP (based on TLS).
+        */
+       tk = data->key_data;
+       if (tk == NULL)
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
+
+       if (data->reauth &&
+           tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+               /* Fast-connect: IPMK|CMK = TK */
+               os_memcpy(data->ipmk, tk, 40);
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK from TK",
+                               data->ipmk, 40);
+               os_memcpy(data->cmk, tk + 40, 20);
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK from TK",
+                               data->cmk, 20);
+               return 0;
+       }
+
+       if (eap_peap_get_isk(sm, data, isk, sizeof(isk)) < 0)
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
+
+       /*
+        * IPMK Seed = "Inner Methods Compound Keys" | ISK
+        * TempKey = First 40 octets of TK
+        * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
+        * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
+        * in the end of the label just before ISK; is that just a typo?)
+        */
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
+       peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
+                    isk, sizeof(isk), imck, sizeof(imck));
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+                       imck, sizeof(imck));
+
+       os_memcpy(data->ipmk, imck, 40);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+       os_memcpy(data->cmk, imck + 40, 20);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+       return 0;
+}
+
+
+static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
+                                    struct eap_peap_data *data,
+                                    struct wpabuf *buf)
+{
+       u8 *mac;
+       u8 eap_type = EAP_TYPE_PEAP;
+       const u8 *addr[2];
+       size_t len[2];
+       u16 tlv_type;
+
+       /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+       addr[0] = wpabuf_put(buf, 0);
+       len[0] = 60;
+       addr[1] = &eap_type;
+       len[1] = 1;
+
+       tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
+       if (data->peap_version >= 2)
+               tlv_type |= EAP_TLV_TYPE_MANDATORY;
+       wpabuf_put_be16(buf, tlv_type);
+       wpabuf_put_be16(buf, 56);
+
+       wpabuf_put_u8(buf, 0); /* Reserved */
+       wpabuf_put_u8(buf, data->peap_version); /* Version */
+       wpabuf_put_u8(buf, data->peap_version); /* RecvVersion */
+       wpabuf_put_u8(buf, 1); /* SubType: 0 = Request, 1 = Response */
+       wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
+       mac = wpabuf_put(buf, 20); /* Compound_MAC */
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK", data->cmk, 20);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
+                   addr[0], len[0]);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
+                   addr[1], len[1]);
+       hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC", mac, SHA1_MAC_LEN);
+       data->crypto_binding_used = 1;
+
+       return 0;
+}
+
+
+/**
+ * eap_tlv_build_result - Build EAP-TLV Result message
+ * @id: EAP identifier for the header
+ * @status: Status (EAP_TLV_RESULT_SUCCESS or EAP_TLV_RESULT_FAILURE)
+ * Returns: Buffer to the allocated EAP-TLV Result message or %NULL on failure
+ *
+ * This funtion builds an EAP-TLV Result message. The caller is responsible for
+ * freeing the returned buffer.
+ */
+static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
+                                           struct eap_peap_data *data,
+                                           int crypto_tlv_used,
+                                           int id, u16 status)
+{
+       struct wpabuf *msg;
+       size_t len;
+
+       if (data->crypto_binding == NO_BINDING)
+               crypto_tlv_used = 0;
+
+       len = 6;
+       if (crypto_tlv_used)
+               len += 60; /* Cryptobinding TLV */
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len,
+                           EAP_CODE_RESPONSE, id);
+       if (msg == NULL)
+               return NULL;
+
+       wpabuf_put_u8(msg, 0x80); /* Mandatory */
+       wpabuf_put_u8(msg, EAP_TLV_RESULT_TLV);
+       wpabuf_put_be16(msg, 2); /* Length */
+       wpabuf_put_be16(msg, status); /* Status */
+
+       if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
+                                         struct eap_peap_data *data,
+                                         const u8 *crypto_tlv,
+                                         size_t crypto_tlv_len)
+{
+       u8 buf[61], mac[SHA1_MAC_LEN];
+       const u8 *pos;
+
+       if (eap_peap_derive_cmk(sm, data) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Could not derive CMK");
+               return -1;
+       }
+
+       if (crypto_tlv_len != 4 + 56) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
+                          "length %d", (int) crypto_tlv_len);
+               return -1;
+       }
+
+       pos = crypto_tlv;
+       pos += 4; /* TLV header */
+       if (pos[1] != data->peap_version) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
+                          "mismatch (was %d; expected %d)",
+                          pos[1], data->peap_version);
+               return -1;
+       }
+
+       if (pos[3] != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
+                          "SubType %d", pos[3]);
+               return -1;
+       }
+       pos += 4;
+       os_memcpy(data->binding_nonce, pos, 32);
+       pos += 32; /* Nonce */
+
+       /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+       os_memcpy(buf, crypto_tlv, 60);
+       os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
+       buf[60] = EAP_TYPE_PEAP;
+       wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Compound_MAC data",
+                   buf, sizeof(buf));
+       hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
+
+       if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
+                          "cryptobinding TLV");
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
+                           pos, SHA1_MAC_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Expected MAC",
+                           mac, SHA1_MAC_LEN);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
+
+       return 0;
+}
+
+
+/**
+ * eap_tlv_process - Process a received EAP-TLV message and generate a response
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @ret: Return values from EAP request validation and processing
+ * @req: EAP-TLV request to be processed. The caller must have validated that
+ * the buffer is large enough to contain full request (hdr->length bytes) and
+ * that the EAP type is EAP_TYPE_TLV.
+ * @resp: Buffer to return a pointer to the allocated response message. This
+ * field should be initialized to %NULL before the call. The value will be
+ * updated if a response message is generated. The caller is responsible for
+ * freeing the allocated message.
+ * @force_failure: Force negotiation to fail
+ * Returns: 0 on success, -1 on failure
+ */
+static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
+                          struct eap_method_ret *ret,
+                          const struct wpabuf *req, struct wpabuf **resp,
+                          int force_failure)
+{
+       size_t left, tlv_len;
+       const u8 *pos;
+       const u8 *result_tlv = NULL, *crypto_tlv = NULL;
+       size_t result_tlv_len = 0, crypto_tlv_len = 0;
+       int tlv_type, mandatory;
+
+       /* Parse TLVs */
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, req, &left);
+       if (pos == NULL)
+               return -1;
+       wpa_hexdump(MSG_DEBUG, "EAP-TLV: Received TLVs", pos, left);
+       while (left >= 4) {
+               mandatory = !!(pos[0] & 0x80);
+               tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+               pos += 2;
+               tlv_len = WPA_GET_BE16(pos);
+               pos += 2;
+               left -= 4;
+               if (tlv_len > left) {
+                       wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun "
+                                  "(tlv_len=%lu left=%lu)",
+                                  (unsigned long) tlv_len,
+                                  (unsigned long) left);
+                       return -1;
+               }
+               switch (tlv_type) {
+               case EAP_TLV_RESULT_TLV:
+                       result_tlv = pos;
+                       result_tlv_len = tlv_len;
+                       break;
+               case EAP_TLV_CRYPTO_BINDING_TLV:
+                       crypto_tlv = pos;
+                       crypto_tlv_len = tlv_len;
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG, "EAP-TLV: Unsupported TLV Type "
+                                  "%d%s", tlv_type,
+                                  mandatory ? " (mandatory)" : "");
+                       if (mandatory) {
+                               /* NAK TLV and ignore all TLVs in this packet.
+                                */
+                               *resp = eap_tlv_build_nak(eap_get_id(req),
+                                                         tlv_type);
+                               return *resp == NULL ? -1 : 0;
+                       }
+                       /* Ignore this TLV, but process other TLVs */
+                       break;
+               }
+
+               pos += tlv_len;
+               left -= tlv_len;
+       }
+       if (left) {
+               wpa_printf(MSG_DEBUG, "EAP-TLV: Last TLV too short in "
+                          "Request (left=%lu)", (unsigned long) left);
+               return -1;
+       }
+
+       /* Process supported TLVs */
+       if (crypto_tlv && data->crypto_binding != NO_BINDING) {
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
+                           crypto_tlv, crypto_tlv_len);
+               if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
+                                                  crypto_tlv_len + 4) < 0) {
+                       if (result_tlv == NULL)
+                               return -1;
+                       force_failure = 1;
+                       crypto_tlv = NULL; /* do not include Cryptobinding TLV
+                                           * in response, if the received
+                                           * cryptobinding was invalid. */
+               }
+       } else if (!crypto_tlv && data->crypto_binding == REQUIRE_BINDING) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
+               return -1;
+       }
+
+       if (result_tlv) {
+               int status, resp_status;
+               wpa_hexdump(MSG_DEBUG, "EAP-TLV: Result TLV",
+                           result_tlv, result_tlv_len);
+               if (result_tlv_len < 2) {
+                       wpa_printf(MSG_INFO, "EAP-TLV: Too short Result TLV "
+                                  "(len=%lu)",
+                                  (unsigned long) result_tlv_len);
+                       return -1;
+               }
+               status = WPA_GET_BE16(result_tlv);
+               if (status == EAP_TLV_RESULT_SUCCESS) {
+                       wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Success "
+                                  "- EAP-TLV/Phase2 Completed");
+                       if (force_failure) {
+                               wpa_printf(MSG_INFO, "EAP-TLV: Earlier failure"
+                                          " - force failed Phase 2");
+                               resp_status = EAP_TLV_RESULT_FAILURE;
+                               ret->decision = DECISION_FAIL;
+                       } else {
+                               resp_status = EAP_TLV_RESULT_SUCCESS;
+                               ret->decision = DECISION_UNCOND_SUCC;
+                       }
+               } else if (status == EAP_TLV_RESULT_FAILURE) {
+                       wpa_printf(MSG_INFO, "EAP-TLV: TLV Result - Failure");
+                       resp_status = EAP_TLV_RESULT_FAILURE;
+                       ret->decision = DECISION_FAIL;
+               } else {
+                       wpa_printf(MSG_INFO, "EAP-TLV: Unknown TLV Result "
+                                  "Status %d", status);
+                       resp_status = EAP_TLV_RESULT_FAILURE;
+                       ret->decision = DECISION_FAIL;
+               }
+               ret->methodState = METHOD_DONE;
+
+               *resp = eap_tlv_build_result(sm, data, crypto_tlv != NULL,
+                                            eap_get_id(req), resp_status);
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
+{
+       struct wpabuf *e;
+       struct eap_tlv_hdr *tlv;
+
+       if (buf == NULL)
+               return NULL;
+
+       /* Encapsulate EAP packet in EAP-Payload TLV */
+       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
+       e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+       if (e == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
+                          "for TLV encapsulation");
+               wpabuf_free(buf);
+               return NULL;
+       }
+       tlv = wpabuf_put(e, sizeof(*tlv));
+       tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                    EAP_TLV_EAP_PAYLOAD_TLV);
+       tlv->length = host_to_be16(wpabuf_len(buf));
+       wpabuf_put_buf(e, buf);
+       wpabuf_free(buf);
+       return e;
+}
+
+
+static int eap_peap_phase2_request(struct eap_sm *sm,
+                                  struct eap_peap_data *data,
+                                  struct eap_method_ret *ret,
+                                  struct wpabuf *req,
+                                  struct wpabuf **resp)
+{
+       struct eap_hdr *hdr = wpabuf_mhead(req);
+       size_t len = be_to_host16(hdr->length);
+       u8 *pos;
+       struct eap_method_ret iret;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (len <= sizeof(struct eap_hdr)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: too short "
+                          "Phase 2 request (len=%lu)", (unsigned long) len);
+               return -1;
+       }
+       pos = (u8 *) (hdr + 1);
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Request: type=%d", *pos);
+       switch (*pos) {
+       case EAP_TYPE_IDENTITY:
+               *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+               break;
+       case EAP_TYPE_TLV:
+               os_memset(&iret, 0, sizeof(iret));
+               if (eap_tlv_process(sm, data, &iret, req, resp,
+                                   data->phase2_eap_started &&
+                                   !data->phase2_eap_success)) {
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       return -1;
+               }
+               if (iret.methodState == METHOD_DONE ||
+                   iret.methodState == METHOD_MAY_CONT) {
+                       ret->methodState = iret.methodState;
+                       ret->decision = iret.decision;
+                       data->phase2_success = 1;
+               }
+               break;
+       case EAP_TYPE_EXPANDED:
+#ifdef EAP_TNC
+               if (data->soh) {
+                       const u8 *epos;
+                       size_t eleft;
+
+                       epos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21,
+                                               req, &eleft);
+                       if (epos) {
+                               struct wpabuf *buf;
+                               wpa_printf(MSG_DEBUG,
+                                          "EAP-PEAP: SoH EAP Extensions");
+                               buf = tncc_process_soh_request(data->soh,
+                                                              epos, eleft);
+                               if (buf) {
+                                       *resp = eap_msg_alloc(
+                                               EAP_VENDOR_MICROSOFT, 0x21,
+                                               wpabuf_len(buf),
+                                               EAP_CODE_RESPONSE,
+                                               hdr->identifier);
+                                       if (*resp == NULL) {
+                                               ret->methodState = METHOD_DONE;
+                                               ret->decision = DECISION_FAIL;
+                                               return -1;
+                                       }
+                                       wpabuf_put_buf(*resp, buf);
+                                       wpabuf_free(buf);
+                                       break;
+                               }
+                       }
+               }
+#endif /* EAP_TNC */
+               /* fall through */
+       default:
+               if (data->phase2_type.vendor == EAP_VENDOR_IETF &&
+                   data->phase2_type.method == EAP_TYPE_NONE) {
+                       size_t i;
+                       for (i = 0; i < data->num_phase2_types; i++) {
+                               if (data->phase2_types[i].vendor !=
+                                   EAP_VENDOR_IETF ||
+                                   data->phase2_types[i].method != *pos)
+                                       continue;
+
+                               data->phase2_type.vendor =
+                                       data->phase2_types[i].vendor;
+                               data->phase2_type.method =
+                                       data->phase2_types[i].method;
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Selected "
+                                          "Phase 2 EAP vendor %d method %d",
+                                          data->phase2_type.vendor,
+                                          data->phase2_type.method);
+                               break;
+                       }
+               }
+               if (*pos != data->phase2_type.method ||
+                   *pos == EAP_TYPE_NONE) {
+                       if (eap_peer_tls_phase2_nak(data->phase2_types,
+                                                   data->num_phase2_types,
+                                                   hdr, resp))
+                               return -1;
+                       return 0;
+               }
+
+               if (data->phase2_priv == NULL) {
+                       data->phase2_method = eap_peer_get_eap_method(
+                               data->phase2_type.vendor,
+                               data->phase2_type.method);
+                       if (data->phase2_method) {
+                               sm->init_phase2 = 1;
+                               data->phase2_priv =
+                                       data->phase2_method->init(sm);
+                               sm->init_phase2 = 0;
+                       }
+               }
+               if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-PEAP: failed to initialize "
+                                  "Phase 2 EAP method %d", *pos);
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       return -1;
+               }
+               data->phase2_eap_started = 1;
+               os_memset(&iret, 0, sizeof(iret));
+               *resp = data->phase2_method->process(sm, data->phase2_priv,
+                                                    &iret, req);
+               if ((iret.methodState == METHOD_DONE ||
+                    iret.methodState == METHOD_MAY_CONT) &&
+                   (iret.decision == DECISION_UNCOND_SUCC ||
+                    iret.decision == DECISION_COND_SUCC)) {
+                       data->phase2_eap_success = 1;
+                       data->phase2_success = 1;
+               }
+               break;
+       }
+
+       if (*resp == NULL &&
+           (config->pending_req_identity || config->pending_req_password ||
+            config->pending_req_otp || config->pending_req_new_password)) {
+               wpabuf_free(data->pending_phase2_req);
+               data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
+       }
+
+       return 0;
+}
+
+
+static int eap_peap_decrypt(struct eap_sm *sm, struct eap_peap_data *data,
+                           struct eap_method_ret *ret,
+                           const struct eap_hdr *req,
+                           const struct wpabuf *in_data,
+                           struct wpabuf **out_data)
+{
+       struct wpabuf *in_decrypted = NULL;
+       int res, skip_change = 0;
+       struct eap_hdr *hdr, *rhdr;
+       struct wpabuf *resp = NULL;
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+                  " Phase 2", (unsigned long) wpabuf_len(in_data));
+
+       if (data->pending_phase2_req) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 request - "
+                          "skip decryption and use old data");
+               /* Clear TLS reassembly state. */
+               eap_peer_tls_reset_input(&data->ssl);
+               in_decrypted = data->pending_phase2_req;
+               data->pending_phase2_req = NULL;
+               skip_change = 1;
+               goto continue_req;
+       }
+
+       if (wpabuf_len(in_data) == 0 && sm->workaround &&
+           data->phase2_success) {
+               /*
+                * Cisco ACS seems to be using TLS ACK to terminate
+                * EAP-PEAPv0/GTC. Try to reply with TLS ACK.
+                */
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Received TLS ACK, but "
+                          "expected data - acknowledge with TLS ACK since "
+                          "Phase 2 has been completed");
+               ret->decision = DECISION_COND_SUCC;
+               ret->methodState = METHOD_DONE;
+               return 1;
+       } else if (wpabuf_len(in_data) == 0) {
+               /* Received TLS ACK - requesting more fragments */
+               return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+                                           data->peap_version,
+                                           req->identifier, NULL, out_data);
+       }
+
+       res = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+       if (res)
+               return res;
+
+continue_req:
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+                       in_decrypted);
+
+       hdr = wpabuf_mhead(in_decrypted);
+       if (wpabuf_len(in_decrypted) == 5 && hdr->code == EAP_CODE_REQUEST &&
+           be_to_host16(hdr->length) == 5 &&
+           eap_get_type(in_decrypted) == EAP_TYPE_IDENTITY) {
+               /* At least FreeRADIUS seems to send full EAP header with
+                * EAP Request Identity */
+               skip_change = 1;
+       }
+       if (wpabuf_len(in_decrypted) >= 5 && hdr->code == EAP_CODE_REQUEST &&
+           eap_get_type(in_decrypted) == EAP_TYPE_TLV) {
+               skip_change = 1;
+       }
+
+       if (data->peap_version == 0 && !skip_change) {
+               struct eap_hdr *nhdr;
+               struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
+                                                  wpabuf_len(in_decrypted));
+               if (nmsg == NULL) {
+                       wpabuf_free(in_decrypted);
+                       return 0;
+               }
+               nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
+               wpabuf_put_buf(nmsg, in_decrypted);
+               nhdr->code = req->code;
+               nhdr->identifier = req->identifier;
+               nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+                                           wpabuf_len(in_decrypted));
+
+               wpabuf_free(in_decrypted);
+               in_decrypted = nmsg;
+       }
+
+       if (data->peap_version >= 2) {
+               struct eap_tlv_hdr *tlv;
+               struct wpabuf *nmsg;
+
+               if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+                                  "EAP TLV");
+                       wpabuf_free(in_decrypted);
+                       return 0;
+               }
+               tlv = wpabuf_mhead(in_decrypted);
+               if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
+                   EAP_TLV_EAP_PAYLOAD_TLV) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+                       wpabuf_free(in_decrypted);
+                       return 0;
+               }
+               if (sizeof(*tlv) + be_to_host16(tlv->length) >
+                   wpabuf_len(in_decrypted)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+                                  "length");
+                       wpabuf_free(in_decrypted);
+                       return 0;
+               }
+               hdr = (struct eap_hdr *) (tlv + 1);
+               if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+                                  "EAP packet in EAP TLV");
+                       wpabuf_free(in_decrypted);
+                       return 0;
+               }
+
+               nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+               if (nmsg == NULL) {
+                       wpabuf_free(in_decrypted);
+                       return 0;
+               }
+
+               wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+               wpabuf_free(in_decrypted);
+               in_decrypted = nmsg;
+       }
+
+       hdr = wpabuf_mhead(in_decrypted);
+       if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+                          "EAP frame (len=%lu)",
+                          (unsigned long) wpabuf_len(in_decrypted));
+               wpabuf_free(in_decrypted);
+               return 0;
+       }
+       len = be_to_host16(hdr->length);
+       if (len > wpabuf_len(in_decrypted)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+                          "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+                          (unsigned long) wpabuf_len(in_decrypted),
+                          (unsigned long) len);
+               wpabuf_free(in_decrypted);
+               return 0;
+       }
+       if (len < wpabuf_len(in_decrypted)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Odd.. Phase 2 EAP header has "
+                          "shorter length than full decrypted data "
+                          "(%lu < %lu)",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_len(in_decrypted));
+       }
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+                  "identifier=%d length=%lu", hdr->code, hdr->identifier,
+                  (unsigned long) len);
+       switch (hdr->code) {
+       case EAP_CODE_REQUEST:
+               if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
+                                           &resp)) {
+                       wpabuf_free(in_decrypted);
+                       wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
+                                  "processing failed");
+                       return 0;
+               }
+               break;
+       case EAP_CODE_SUCCESS:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+               if (data->peap_version == 1) {
+                       /* EAP-Success within TLS tunnel is used to indicate
+                        * shutdown of the TLS channel. The authentication has
+                        * been completed. */
+                       if (data->phase2_eap_started &&
+                           !data->phase2_eap_success) {
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 "
+                                          "Success used to indicate success, "
+                                          "but Phase 2 EAP was not yet "
+                                          "completed successfully");
+                               ret->methodState = METHOD_DONE;
+                               ret->decision = DECISION_FAIL;
+                               wpabuf_free(in_decrypted);
+                               return 0;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
+                                  "EAP-Success within TLS tunnel - "
+                                  "authentication completed");
+                       ret->decision = DECISION_UNCOND_SUCC;
+                       ret->methodState = METHOD_DONE;
+                       data->phase2_success = 1;
+                       if (data->peap_outer_success == 2) {
+                               wpabuf_free(in_decrypted);
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
+                                          "to finish authentication");
+                               return 1;
+                       } else if (data->peap_outer_success == 1) {
+                               /* Reply with EAP-Success within the TLS
+                                * channel to complete the authentication. */
+                               resp = wpabuf_alloc(sizeof(struct eap_hdr));
+                               if (resp) {
+                                       rhdr = wpabuf_put(resp, sizeof(*rhdr));
+                                       rhdr->code = EAP_CODE_SUCCESS;
+                                       rhdr->identifier = hdr->identifier;
+                                       rhdr->length =
+                                               host_to_be16(sizeof(*rhdr));
+                               }
+                       } else {
+                               /* No EAP-Success expected for Phase 1 (outer,
+                                * unencrypted auth), so force EAP state
+                                * machine to SUCCESS state. */
+                               sm->peap_done = TRUE;
+                       }
+               } else {
+                       /* FIX: ? */
+               }
+               break;
+       case EAP_CODE_FAILURE:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+               ret->decision = DECISION_FAIL;
+               ret->methodState = METHOD_MAY_CONT;
+               ret->allowNotifications = FALSE;
+               /* Reply with EAP-Failure within the TLS channel to complete
+                * failure reporting. */
+               resp = wpabuf_alloc(sizeof(struct eap_hdr));
+               if (resp) {
+                       rhdr = wpabuf_put(resp, sizeof(*rhdr));
+                       rhdr->code = EAP_CODE_FAILURE;
+                       rhdr->identifier = hdr->identifier;
+                       rhdr->length = host_to_be16(sizeof(*rhdr));
+               }
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+                          "Phase 2 EAP header", hdr->code);
+               break;
+       }
+
+       wpabuf_free(in_decrypted);
+
+       if (resp) {
+               int skip_change2 = 0;
+               struct wpabuf *rmsg, buf;
+
+               wpa_hexdump_buf_key(MSG_DEBUG,
+                                   "EAP-PEAP: Encrypting Phase 2 data", resp);
+               /* PEAP version changes */
+               if (data->peap_version >= 2) {
+                       resp = eap_peapv2_tlv_eap_payload(resp);
+                       if (resp == NULL)
+                               return -1;
+               }
+               if (wpabuf_len(resp) >= 5 &&
+                   wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
+                   eap_get_type(resp) == EAP_TYPE_TLV)
+                       skip_change2 = 1;
+               rmsg = resp;
+               if (data->peap_version == 0 && !skip_change2) {
+                       wpabuf_set(&buf, wpabuf_head_u8(resp) +
+                                  sizeof(struct eap_hdr),
+                                  wpabuf_len(resp) - sizeof(struct eap_hdr));
+                       rmsg = &buf;
+               }
+
+               if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_PEAP,
+                                        data->peap_version, req->identifier,
+                                        rmsg, out_data)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
+                                  "a Phase 2 frame");
+               }
+               wpabuf_free(resp);
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
+                                       struct eap_method_ret *ret,
+                                       const struct wpabuf *reqData)
+{
+       const struct eap_hdr *req;
+       size_t left;
+       int res;
+       u8 flags, id;
+       struct wpabuf *resp;
+       const u8 *pos;
+       struct eap_peap_data *data = priv;
+
+       pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_PEAP, ret,
+                                       reqData, &left, &flags);
+       if (pos == NULL)
+               return NULL;
+       req = wpabuf_head(reqData);
+       id = req->identifier;
+
+       if (flags & EAP_TLS_FLAGS_START) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own "
+                          "ver=%d)", flags & EAP_TLS_VERSION_MASK,
+                       data->peap_version);
+               if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version)
+                       data->peap_version = flags & EAP_TLS_VERSION_MASK;
+               if (data->force_peap_version >= 0 &&
+                   data->force_peap_version != data->peap_version) {
+                       wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select "
+                                  "forced PEAP version %d",
+                                  data->force_peap_version);
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_FAIL;
+                       ret->allowNotifications = FALSE;
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Using PEAP version %d",
+                          data->peap_version);
+               left = 0; /* make sure that this frame is empty, even though it
+                          * should always be, anyway */
+       }
+
+       resp = NULL;
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+           !data->resuming) {
+               struct wpabuf msg;
+               wpabuf_set(&msg, pos, left);
+               res = eap_peap_decrypt(sm, data, ret, req, &msg, &resp);
+       } else {
+               res = eap_peer_tls_process_helper(sm, &data->ssl,
+                                                 EAP_TYPE_PEAP,
+                                                 data->peap_version, id, pos,
+                                                 left, &resp);
+
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+                       char *label;
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-PEAP: TLS done, proceed to Phase 2");
+                       os_free(data->key_data);
+                       /* draft-josefsson-ppext-eap-tls-eap-05.txt
+                        * specifies that PEAPv1 would use "client PEAP
+                        * encryption" as the label. However, most existing
+                        * PEAPv1 implementations seem to be using the old
+                        * label, "client EAP encryption", instead. Use the old
+                        * label by default, but allow it to be configured with
+                        * phase1 parameter peaplabel=1. */
+                       if (data->peap_version > 1 || data->force_new_label)
+                               label = "client PEAP encryption";
+                       else
+                               label = "client EAP encryption";
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: using label '%s' in "
+                                  "key derivation", label);
+                       data->key_data =
+                               eap_peer_tls_derive_key(sm, &data->ssl, label,
+                                                       EAP_TLS_KEY_LEN);
+                       if (data->key_data) {
+                               wpa_hexdump_key(MSG_DEBUG, 
+                                               "EAP-PEAP: Derived key",
+                                               data->key_data,
+                                               EAP_TLS_KEY_LEN);
+                       } else {
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
+                                          "derive key");
+                       }
+
+                       if (sm->workaround && data->resuming) {
+                               /*
+                                * At least few RADIUS servers (Aegis v1.1.6;
+                                * but not v1.1.4; and Cisco ACS) seem to be
+                                * terminating PEAPv1 (Aegis) or PEAPv0 (Cisco
+                                * ACS) session resumption with outer
+                                * EAP-Success. This does not seem to follow
+                                * draft-josefsson-pppext-eap-tls-eap-05.txt
+                                * section 4.2, so only allow this if EAP
+                                * workarounds are enabled.
+                                */
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Workaround - "
+                                          "allow outer EAP-Success to "
+                                          "terminate PEAP resumption");
+                               ret->decision = DECISION_COND_SUCC;
+                               data->phase2_success = 1;
+                       }
+
+                       data->resuming = 0;
+               }
+
+               if (res == 2) {
+                       struct wpabuf msg;
+                       /*
+                        * Application data included in the handshake message.
+                        */
+                       wpabuf_free(data->pending_phase2_req);
+                       data->pending_phase2_req = resp;
+                       resp = NULL;
+                       wpabuf_set(&msg, pos, left);
+                       res = eap_peap_decrypt(sm, data, ret, req, &msg,
+                                              &resp);
+               }
+       }
+
+       if (ret->methodState == METHOD_DONE) {
+               ret->allowNotifications = FALSE;
+       }
+
+       if (res == 1) {
+               wpabuf_free(resp);
+               return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
+                                             data->peap_version);
+       }
+
+       return resp;
+}
+
+
+static Boolean eap_peap_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+               data->phase2_success;
+}
+
+
+static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       wpabuf_free(data->pending_phase2_req);
+       data->pending_phase2_req = NULL;
+       data->crypto_binding_used = 0;
+}
+
+
+static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       os_free(data->key_data);
+       data->key_data = NULL;
+       if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+               os_free(data);
+               return NULL;
+       }
+       if (data->phase2_priv && data->phase2_method &&
+           data->phase2_method->init_for_reauth)
+               data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+       data->phase2_success = 0;
+       data->phase2_eap_success = 0;
+       data->phase2_eap_started = 0;
+       data->resuming = 1;
+       data->reauth = 1;
+       sm->peap_done = FALSE;
+       return priv;
+}
+
+
+static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
+                              size_t buflen, int verbose)
+{
+       struct eap_peap_data *data = priv;
+       int len, ret;
+
+       len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+       if (data->phase2_method) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "EAP-PEAPv%d Phase2 method=%s\n",
+                                 data->peap_version,
+                                 data->phase2_method->name);
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+       }
+       return len;
+}
+
+
+static Boolean eap_peap_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_peap_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL || !data->phase2_success)
+               return NULL;
+
+       key = os_malloc(EAP_TLS_KEY_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_TLS_KEY_LEN;
+
+       if (data->crypto_binding_used) {
+               u8 csk[128];
+               /*
+                * Note: It looks like Microsoft implementation requires null
+                * termination for this label while the one used for deriving
+                * IPMK|CMK did not use null termination.
+                */
+               peap_prfplus(data->peap_version, data->ipmk, 40,
+                            "Session Key Generating Function",
+                            (u8 *) "\00", 1, csk, sizeof(csk));
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+               os_memcpy(key, csk, EAP_TLS_KEY_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+                           key, EAP_TLS_KEY_LEN);
+       } else
+               os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+       return key;
+}
+
+
+int eap_peer_peap_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_peap_init;
+       eap->deinit = eap_peap_deinit;
+       eap->process = eap_peap_process;
+       eap->isKeyAvailable = eap_peap_isKeyAvailable;
+       eap->getKey = eap_peap_getKey;
+       eap->get_status = eap_peap_get_status;
+       eap->has_reauth_data = eap_peap_has_reauth_data;
+       eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
+       eap->init_for_reauth = eap_peap_init_for_reauth;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_psk.c b/src/eap_peer/eap_psk.c
new file mode 100644 (file)
index 0000000..ccf871e
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * EAP peer method: EAP-PSK (RFC 4764)
+ * 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.
+ *
+ * Note: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_common/eap_psk_common.h"
+#include "eap_i.h"
+
+
+struct eap_psk_data {
+       enum { PSK_INIT, PSK_MAC_SENT, PSK_DONE } state;
+       u8 rand_p[EAP_PSK_RAND_LEN];
+       u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+       u8 *id_s, *id_p;
+       size_t id_s_len, id_p_len;
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+       struct eap_psk_data *data;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (!password || password_len != 16) {
+               wpa_printf(MSG_INFO, "EAP-PSK: 16-octet pre-shared key not "
+                          "configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       if (eap_psk_key_setup(password, data->ak, data->kdk)) {
+               os_free(data);
+               return NULL;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+       data->state = PSK_INIT;
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity) {
+               data->id_p = os_malloc(identity_len);
+               if (data->id_p)
+                       os_memcpy(data->id_p, identity, identity_len);
+               data->id_p_len = identity_len;
+       }
+       if (data->id_p == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PSK: could not get own identity");
+               os_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_psk_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_psk_data *data = priv;
+       os_free(data->id_s);
+       os_free(data->id_p);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_psk_process_1(struct eap_psk_data *data,
+                                        struct eap_method_ret *ret,
+                                        const struct wpabuf *reqData)
+{
+       const struct eap_psk_hdr_1 *hdr1;
+       struct eap_psk_hdr_2 *hdr2;
+       struct wpabuf *resp;
+       u8 *buf, *pos;
+       size_t buflen, len;
+       const u8 *cpos;
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: in INIT state");
+
+       cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+       hdr1 = (const struct eap_psk_hdr_1 *) cpos;
+       if (cpos == NULL || len < sizeof(*hdr1)) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Invalid first message "
+                          "length (%lu; expected %lu or more)",
+                          (unsigned long) len,
+                          (unsigned long) sizeof(*hdr1));
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr1->flags);
+       if (EAP_PSK_FLAGS_GET_T(hdr1->flags) != 0) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 0)",
+                          EAP_PSK_FLAGS_GET_T(hdr1->flags));
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr1->rand_s,
+                   EAP_PSK_RAND_LEN);
+       os_free(data->id_s);
+       data->id_s_len = len - sizeof(*hdr1);
+       data->id_s = os_malloc(data->id_s_len);
+       if (data->id_s == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory for "
+                          "ID_S (len=%lu)", (unsigned long) data->id_s_len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       os_memcpy(data->id_s, (u8 *) (hdr1 + 1), data->id_s_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_S",
+                         data->id_s, data->id_s_len);
+
+       if (os_get_random(data->rand_p, EAP_PSK_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+                            sizeof(*hdr2) + data->id_p_len, EAP_CODE_RESPONSE,
+                            eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+       hdr2 = wpabuf_put(resp, sizeof(*hdr2));
+       hdr2->flags = EAP_PSK_FLAGS_SET_T(1); /* T=1 */
+       os_memcpy(hdr2->rand_s, hdr1->rand_s, EAP_PSK_RAND_LEN);
+       os_memcpy(hdr2->rand_p, data->rand_p, EAP_PSK_RAND_LEN);
+       wpabuf_put_data(resp, data->id_p, data->id_p_len);
+       /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+       buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               wpabuf_free(resp);
+               return NULL;
+       }
+       os_memcpy(buf, data->id_p, data->id_p_len);
+       pos = buf + data->id_p_len;
+       os_memcpy(pos, data->id_s, data->id_s_len);
+       pos += data->id_s_len;
+       os_memcpy(pos, hdr1->rand_s, EAP_PSK_RAND_LEN);
+       pos += EAP_PSK_RAND_LEN;
+       os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+       if (omac1_aes_128(data->ak, buf, buflen, hdr2->mac_p)) {
+               os_free(buf);
+               wpabuf_free(resp);
+               return NULL;
+       }
+       os_free(buf);
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_P", hdr2->rand_p,
+                   EAP_PSK_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", hdr2->mac_p, EAP_PSK_MAC_LEN);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: ID_P",
+                         data->id_p, data->id_p_len);
+
+       data->state = PSK_MAC_SENT;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
+                                        struct eap_method_ret *ret,
+                                        const struct wpabuf *reqData)
+{
+       const struct eap_psk_hdr_3 *hdr3;
+       struct eap_psk_hdr_4 *hdr4;
+       struct wpabuf *resp;
+       u8 *buf, *rpchannel, nonce[16], *decrypted;
+       const u8 *pchannel, *tag, *msg;
+       u8 mac[EAP_PSK_MAC_LEN];
+       size_t buflen, left, data_len, len, plen;
+       int failed = 0;
+       const u8 *pos;
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: in MAC_SENT state");
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+                              reqData, &len);
+       hdr3 = (const struct eap_psk_hdr_3 *) pos;
+       if (pos == NULL || len < sizeof(*hdr3)) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Invalid third message "
+                          "length (%lu; expected %lu or more)",
+                          (unsigned long) len,
+                          (unsigned long) sizeof(*hdr3));
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       left = len - sizeof(*hdr3);
+       pchannel = (const u8 *) (hdr3 + 1);
+       wpa_printf(MSG_DEBUG, "EAP-PSK: Flags=0x%x", hdr3->flags);
+       if (EAP_PSK_FLAGS_GET_T(hdr3->flags) != 2) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Unexpected T=%d (expected 2)",
+                          EAP_PSK_FLAGS_GET_T(hdr3->flags));
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: RAND_S", hdr3->rand_s,
+                   EAP_PSK_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_S", hdr3->mac_s, EAP_PSK_MAC_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL", pchannel, left);
+
+       if (left < 4 + 16 + 1) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+                          "third message (len=%lu, expected 21)",
+                          (unsigned long) left);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+       buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return NULL;
+       os_memcpy(buf, data->id_s, data->id_s_len);
+       os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+       if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+               os_free(buf);
+               return NULL;
+       }
+       os_free(buf);
+       if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+               wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
+                          "message");
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-PSK: MAC_S verified successfully");
+
+       if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek,
+                               data->msk, data->emsk)) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+       os_memset(nonce, 0, 12);
+       os_memcpy(nonce + 12, pchannel, 4);
+       pchannel += 4;
+       left -= 4;
+
+       tag = pchannel;
+       pchannel += 16;
+       left -= 16;
+
+       msg = pchannel;
+
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - nonce",
+                   nonce, sizeof(nonce));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - hdr",
+                   wpabuf_head(reqData), 5);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: PCHANNEL - cipher msg", msg, left);
+
+       decrypted = os_malloc(left);
+       if (decrypted == NULL) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return NULL;
+       }
+       os_memcpy(decrypted, msg, left);
+
+       if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+                               wpabuf_head(reqData),
+                               sizeof(struct eap_hdr) + 1 +
+                               sizeof(*hdr3) - EAP_PSK_MAC_LEN, decrypted,
+                               left, tag)) {
+               wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+               os_free(decrypted);
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+                   decrypted, left);
+
+       /* Verify R flag */
+       switch (decrypted[0] >> 6) {
+       case EAP_PSK_R_FLAG_CONT:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+               failed = 1;
+               break;
+       case EAP_PSK_R_FLAG_DONE_SUCCESS:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+               break;
+       case EAP_PSK_R_FLAG_DONE_FAILURE:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+               wpa_printf(MSG_INFO, "EAP-PSK: Authentication server rejected "
+                          "authentication");
+               failed = 1;
+               break;
+       }
+
+       data_len = 1;
+       if ((decrypted[0] & EAP_PSK_E_FLAG) && left > 1)
+               data_len++;
+       plen = sizeof(*hdr4) + 4 + 16 + data_len;
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK, plen,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL) {
+               os_free(decrypted);
+               return NULL;
+       }
+       hdr4 = wpabuf_put(resp, sizeof(*hdr4));
+       hdr4->flags = EAP_PSK_FLAGS_SET_T(3); /* T=3 */
+       os_memcpy(hdr4->rand_s, hdr3->rand_s, EAP_PSK_RAND_LEN);
+       rpchannel = wpabuf_put(resp, 4 + 16 + data_len);
+
+       /* nonce++ */
+       inc_byte_array(nonce, sizeof(nonce));
+       os_memcpy(rpchannel, nonce + 12, 4);
+
+       if (decrypted[0] & EAP_PSK_E_FLAG) {
+               wpa_printf(MSG_DEBUG, "EAP-PSK: Unsupported E (Ext) flag");
+               failed = 1;
+               rpchannel[4 + 16] = (EAP_PSK_R_FLAG_DONE_FAILURE << 6) |
+                       EAP_PSK_E_FLAG;
+               if (left > 1) {
+                       /* Add empty EXT_Payload with same EXT_Type */
+                       rpchannel[4 + 16 + 1] = decrypted[1];
+               }
+       } else if (failed)
+               rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_FAILURE << 6;
+       else
+               rpchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (plaintext)",
+                   rpchannel + 4 + 16, data_len);
+       if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+                               wpabuf_head(resp),
+                               sizeof(struct eap_hdr) + 1 + sizeof(*hdr4),
+                               rpchannel + 4 + 16, data_len, rpchannel + 4)) {
+               os_free(decrypted);
+               wpabuf_free(resp);
+               return NULL;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: reply message (PCHANNEL)",
+                   rpchannel, 4 + 16 + data_len);
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: Completed %ssuccessfully",
+                  failed ? "un" : "");
+       data->state = PSK_DONE;
+       ret->methodState = METHOD_DONE;
+       ret->decision = failed ? DECISION_FAIL : DECISION_UNCOND_SUCC;
+
+       os_free(decrypted);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_psk_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_psk_data *data = priv;
+       const u8 *pos;
+       struct wpabuf *resp = NULL;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, reqData, &len);
+       if (pos == NULL) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       switch (data->state) {
+       case PSK_INIT:
+               resp = eap_psk_process_1(data, ret, reqData);
+               break;
+       case PSK_MAC_SENT:
+               resp = eap_psk_process_3(data, ret, reqData);
+               break;
+       case PSK_DONE:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: in DONE state - ignore "
+                          "unexpected message");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (ret->methodState == METHOD_DONE) {
+               ret->allowNotifications = FALSE;
+       }
+
+       return resp;
+}
+
+
+static Boolean eap_psk_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_psk_data *data = priv;
+       return data->state == PSK_DONE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_psk_data *data = priv;
+       u8 *key;
+
+       if (data->state != PSK_DONE)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_MSK_LEN;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+
+       return key;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_psk_data *data = priv;
+       u8 *key;
+
+       if (data->state != PSK_DONE)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+       return key;
+}
+
+
+int eap_peer_psk_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_psk_init;
+       eap->deinit = eap_psk_deinit;
+       eap->process = eap_psk_process;
+       eap->isKeyAvailable = eap_psk_isKeyAvailable;
+       eap->getKey = eap_psk_getKey;
+       eap->get_emsk = eap_psk_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
new file mode 100644 (file)
index 0000000..bb06bb2
--- /dev/null
@@ -0,0 +1,499 @@
+/*
+ * EAP peer method: EAP-SAKE (RFC 4763)
+ * 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 "eap_peer/eap_i.h"
+#include "eap_common/eap_sake_common.h"
+
+struct eap_sake_data {
+       enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
+       u8 root_secret_a[EAP_SAKE_ROOT_SECRET_LEN];
+       u8 root_secret_b[EAP_SAKE_ROOT_SECRET_LEN];
+       u8 rand_s[EAP_SAKE_RAND_LEN];
+       u8 rand_p[EAP_SAKE_RAND_LEN];
+       struct {
+               u8 auth[EAP_SAKE_TEK_AUTH_LEN];
+               u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
+       } tek;
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 session_id;
+       int session_id_set;
+       u8 *peerid;
+       size_t peerid_len;
+       u8 *serverid;
+       size_t serverid_len;
+};
+
+
+static const char * eap_sake_state_txt(int state)
+{
+       switch (state) {
+       case IDENTITY:
+               return "IDENTITY";
+       case CHALLENGE:
+               return "CHALLENGE";
+       case CONFIRM:
+               return "CONFIRM";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_sake_state(struct eap_sake_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
+                  eap_sake_state_txt(data->state),
+                  eap_sake_state_txt(state));
+       data->state = state;
+}
+
+
+static void eap_sake_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_sake_init(struct eap_sm *sm)
+{
+       struct eap_sake_data *data;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (!password || password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: No key of correct length "
+                          "configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = IDENTITY;
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity) {
+               data->peerid = os_malloc(identity_len);
+               if (data->peerid == NULL) {
+                       eap_sake_deinit(sm, data);
+                       return NULL;
+               }
+               os_memcpy(data->peerid, identity, identity_len);
+               data->peerid_len = identity_len;
+       }
+
+       os_memcpy(data->root_secret_a, password, EAP_SAKE_ROOT_SECRET_LEN);
+       os_memcpy(data->root_secret_b,
+                 password + EAP_SAKE_ROOT_SECRET_LEN,
+                 EAP_SAKE_ROOT_SECRET_LEN);
+
+       return data;
+}
+
+
+static void eap_sake_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_sake_data *data = priv;
+       os_free(data->serverid);
+       os_free(data->peerid);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
+                                         int id, size_t length, u8 subtype)
+{
+       struct eap_sake_hdr *sake;
+       struct wpabuf *msg;
+       size_t plen;
+
+       plen = length + sizeof(struct eap_sake_hdr);
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
+                           EAP_CODE_RESPONSE, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
+                          "request");
+               return NULL;
+       }
+
+       sake = wpabuf_put(msg, sizeof(*sake));
+       sake->version = EAP_SAKE_VERSION;
+       sake->session_id = data->session_id;
+       sake->subtype = subtype;
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_sake_process_identity(struct eap_sm *sm,
+                                                struct eap_sake_data *data,
+                                                struct eap_method_ret *ret,
+                                                const struct wpabuf *reqData,
+                                                const u8 *payload,
+                                                size_t payload_len)
+{
+       struct eap_sake_parse_attr attr;
+       struct wpabuf *resp;
+
+       if (data->state != IDENTITY) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Identity");
+
+       if (eap_sake_parse_attributes(payload, payload_len, &attr))
+               return NULL;
+
+       if (!attr.perm_id_req && !attr.any_id_req) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: No AT_PERM_ID_REQ or "
+                          "AT_ANY_ID_REQ in Request/Identity");
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Identity");
+
+       resp = eap_sake_build_msg(data, eap_get_id(reqData),
+                                 2 + data->peerid_len,
+                                 EAP_SAKE_SUBTYPE_IDENTITY);
+       if (resp == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
+       eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
+                         data->peerid, data->peerid_len);
+
+       eap_sake_state(data, CHALLENGE);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
+                                                 struct eap_sake_data *data,
+                                                 struct eap_method_ret *ret,
+                                                 const struct wpabuf *reqData,
+                                                 const u8 *payload,
+                                                 size_t payload_len)
+{
+       struct eap_sake_parse_attr attr;
+       struct wpabuf *resp;
+       u8 *rpos;
+       size_t rlen;
+
+       if (data->state != IDENTITY && data->state != CHALLENGE) {
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge received "
+                          "in unexpected state (%d)", data->state);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       if (data->state == IDENTITY)
+               eap_sake_state(data, CHALLENGE);
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Challenge");
+
+       if (eap_sake_parse_attributes(payload, payload_len, &attr))
+               return NULL;
+
+       if (!attr.rand_s) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Request/Challenge did not "
+                          "include AT_RAND_S");
+               return NULL;
+       }
+
+       os_memcpy(data->rand_s, attr.rand_s, EAP_SAKE_RAND_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
+                   data->rand_s, EAP_SAKE_RAND_LEN);
+
+       if (os_get_random(data->rand_p, EAP_SAKE_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_P (peer rand)",
+                   data->rand_p, EAP_SAKE_RAND_LEN);
+
+       os_free(data->serverid);
+       data->serverid = NULL;
+       data->serverid_len = 0;
+       if (attr.serverid) {
+               wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-SAKE: SERVERID",
+                                 attr.serverid, attr.serverid_len);
+               data->serverid = os_malloc(attr.serverid_len);
+               if (data->serverid == NULL)
+                       return NULL;
+               os_memcpy(data->serverid, attr.serverid, attr.serverid_len);
+               data->serverid_len = attr.serverid_len;
+       }
+
+       eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
+                            data->rand_s, data->rand_p,
+                            (u8 *) &data->tek, data->msk, data->emsk);
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
+
+       rlen = 2 + EAP_SAKE_RAND_LEN + 2 + EAP_SAKE_MIC_LEN;
+       if (data->peerid)
+               rlen += 2 + data->peerid_len;
+       resp = eap_sake_build_msg(data, eap_get_id(reqData), rlen,
+                                 EAP_SAKE_SUBTYPE_CHALLENGE);
+       if (resp == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_P");
+       eap_sake_add_attr(resp, EAP_SAKE_AT_RAND_P,
+                         data->rand_p, EAP_SAKE_RAND_LEN);
+
+       if (data->peerid) {
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PEERID");
+               eap_sake_add_attr(resp, EAP_SAKE_AT_PEERID,
+                                 data->peerid, data->peerid_len);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
+       wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
+       wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
+       rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
+       if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+                                data->serverid, data->serverid_len,
+                                data->peerid, data->peerid_len, 1,
+                                wpabuf_head(resp), wpabuf_len(resp), rpos,
+                                rpos)) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       eap_sake_state(data, CONFIRM);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
+                                               struct eap_sake_data *data,
+                                               struct eap_method_ret *ret,
+                                               const struct wpabuf *reqData,
+                                               const u8 *payload,
+                                               size_t payload_len)
+{
+       struct eap_sake_parse_attr attr;
+       u8 mic_s[EAP_SAKE_MIC_LEN];
+       struct wpabuf *resp;
+       u8 *rpos;
+
+       if (data->state != CONFIRM) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Request/Confirm");
+
+       if (eap_sake_parse_attributes(payload, payload_len, &attr))
+               return NULL;
+
+       if (!attr.mic_s) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Request/Confirm did not "
+                          "include AT_MIC_S");
+               return NULL;
+       }
+
+       eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+                            data->serverid, data->serverid_len,
+                            data->peerid, data->peerid_len, 0,
+                            wpabuf_head(reqData), wpabuf_len(reqData),
+                            attr.mic_s, mic_s);
+       if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
+               eap_sake_state(data, FAILURE);
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               ret->allowNotifications = FALSE;
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending "
+                          "Response/Auth-Reject");
+               return eap_sake_build_msg(data, eap_get_id(reqData), 0,
+                                         EAP_SAKE_SUBTYPE_AUTH_REJECT);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Confirm");
+
+       resp = eap_sake_build_msg(data, eap_get_id(reqData),
+                                 2 + EAP_SAKE_MIC_LEN,
+                                 EAP_SAKE_SUBTYPE_CONFIRM);
+       if (resp == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_P");
+       wpabuf_put_u8(resp, EAP_SAKE_AT_MIC_P);
+       wpabuf_put_u8(resp, 2 + EAP_SAKE_MIC_LEN);
+       rpos = wpabuf_put(resp, EAP_SAKE_MIC_LEN);
+       if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+                                data->serverid, data->serverid_len,
+                                data->peerid, data->peerid_len, 1,
+                                wpabuf_head(resp), wpabuf_len(resp), rpos,
+                                rpos)) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       eap_sake_state(data, SUCCESS);
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       ret->allowNotifications = FALSE;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_sake_process(struct eap_sm *sm, void *priv,
+                                       struct eap_method_ret *ret,
+                                       const struct wpabuf *reqData)
+{
+       struct eap_sake_data *data = priv;
+       const struct eap_sake_hdr *req;
+       struct wpabuf *resp;
+       const u8 *pos, *end;
+       size_t len;
+       u8 subtype, session_id;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, reqData, &len);
+       if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       req = (const struct eap_sake_hdr *) pos;
+       end = pos + len;
+       subtype = req->subtype;
+       session_id = req->session_id;
+       pos = (const u8 *) (req + 1);
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype %d "
+                  "session_id %d", subtype, session_id);
+       wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
+                   pos, end - pos);
+
+       if (data->session_id_set && data->session_id != session_id) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
+                          session_id, data->session_id);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       data->session_id = session_id;
+       data->session_id_set = 1;
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       switch (subtype) {
+       case EAP_SAKE_SUBTYPE_IDENTITY:
+               resp = eap_sake_process_identity(sm, data, ret, reqData,
+                                                pos, end - pos);
+               break;
+       case EAP_SAKE_SUBTYPE_CHALLENGE:
+               resp = eap_sake_process_challenge(sm, data, ret, reqData,
+                                                 pos, end - pos);
+               break;
+       case EAP_SAKE_SUBTYPE_CONFIRM:
+               resp = eap_sake_process_confirm(sm, data, ret, reqData,
+                                               pos, end - pos);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring message with "
+                          "unknown subtype %d", subtype);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (ret->methodState == METHOD_DONE)
+               ret->allowNotifications = FALSE;
+
+       return resp;
+}
+
+
+static Boolean eap_sake_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_sake_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sake_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sake_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+int eap_peer_sake_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_sake_init;
+       eap->deinit = eap_sake_deinit;
+       eap->process = eap_sake_process;
+       eap->isKeyAvailable = eap_sake_isKeyAvailable;
+       eap->getKey = eap_sake_getKey;
+       eap->get_emsk = eap_sake_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_sim.c b/src/eap_peer/eap_sim.c
new file mode 100644 (file)
index 0000000..3d8afb2
--- /dev/null
@@ -0,0 +1,1100 @@
+/*
+ * EAP peer method: EAP-SIM (RFC 4186)
+ * 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 "pcsc_funcs.h"
+#include "crypto/milenage.h"
+#include "eap_peer/eap_i.h"
+#include "eap_config.h"
+#include "eap_common/eap_sim_common.h"
+
+
+struct eap_sim_data {
+       u8 *ver_list;
+       size_t ver_list_len;
+       int selected_version;
+       size_t min_num_chal, num_chal;
+
+       u8 kc[3][EAP_SIM_KC_LEN];
+       u8 sres[3][EAP_SIM_SRES_LEN];
+       u8 nonce_mt[EAP_SIM_NONCE_MT_LEN], nonce_s[EAP_SIM_NONCE_S_LEN];
+       u8 mk[EAP_SIM_MK_LEN];
+       u8 k_aut[EAP_SIM_K_AUT_LEN];
+       u8 k_encr[EAP_SIM_K_ENCR_LEN];
+       u8 msk[EAP_SIM_KEYING_DATA_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 rand[3][GSM_RAND_LEN];
+
+       int num_id_req, num_notification;
+       u8 *pseudonym;
+       size_t pseudonym_len;
+       u8 *reauth_id;
+       size_t reauth_id_len;
+       int reauth;
+       unsigned int counter, counter_too_small;
+       u8 *last_eap_identity;
+       size_t last_eap_identity_len;
+       enum {
+               CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+       } state;
+       int result_ind, use_result_ind;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_sim_state_txt(int state)
+{
+       switch (state) {
+       case CONTINUE:
+               return "CONTINUE";
+       case RESULT_SUCCESS:
+               return "RESULT_SUCCESS";
+       case RESULT_FAILURE:
+               return "RESULT_FAILURE";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
+                  eap_sim_state_txt(data->state),
+                  eap_sim_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+       struct eap_sim_data *data;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+                          "for NONCE_MT");
+               os_free(data);
+               return NULL;
+       }
+
+       data->min_num_chal = 2;
+       if (config && config->phase1) {
+               char *pos = os_strstr(config->phase1, "sim_min_num_chal=");
+               if (pos) {
+                       data->min_num_chal = atoi(pos + 17);
+                       if (data->min_num_chal < 2 || data->min_num_chal > 3) {
+                               wpa_printf(MSG_WARNING, "EAP-SIM: Invalid "
+                                          "sim_min_num_chal configuration "
+                                          "(%lu, expected 2 or 3)",
+                                          (unsigned long) data->min_num_chal);
+                               os_free(data);
+                               return NULL;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: Set minimum number of "
+                                  "challenges to %lu",
+                                  (unsigned long) data->min_num_chal);
+               }
+
+               data->result_ind = os_strstr(config->phase1, "result_ind=1") !=
+                       NULL;
+       }
+
+       eap_sim_state(data, CONTINUE);
+
+       return data;
+}
+
+
+static void eap_sim_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       if (data) {
+               os_free(data->ver_list);
+               os_free(data->pseudonym);
+               os_free(data->reauth_id);
+               os_free(data->last_eap_identity);
+               os_free(data);
+       }
+}
+
+
+static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
+{
+       struct eap_peer_config *conf;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication algorithm");
+
+       conf = eap_get_config(sm);
+       if (conf == NULL)
+               return -1;
+       if (conf->pcsc) {
+               if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
+                                  data->sres[0], data->kc[0]) ||
+                   scard_gsm_auth(sm->scard_ctx, data->rand[1],
+                                  data->sres[1], data->kc[1]) ||
+                   (data->num_chal > 2 &&
+                    scard_gsm_auth(sm->scard_ctx, data->rand[2],
+                                   data->sres[2], data->kc[2]))) {
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: GSM SIM "
+                                  "authentication could not be completed");
+                       return -1;
+               }
+               return 0;
+       }
+
+#ifdef CONFIG_SIM_SIMULATOR
+       if (conf->password) {
+               u8 opc[16], k[16];
+               const char *pos;
+               size_t i;
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Use internal GSM-Milenage "
+                          "implementation for authentication");
+               if (conf->password_len < 65) {
+                       wpa_printf(MSG_DEBUG, "EAP-SIM: invalid GSM-Milenage "
+                                  "password");
+                       return -1;
+               }
+               pos = (const char *) conf->password;
+               if (hexstr2bin(pos, k, 16))
+                       return -1;
+               pos += 32;
+               if (*pos != ':')
+                       return -1;
+               pos++;
+
+               if (hexstr2bin(pos, opc, 16))
+                       return -1;
+
+               for (i = 0; i < data->num_chal; i++) {
+                       if (gsm_milenage(opc, k, data->rand[i],
+                                        data->sres[i], data->kc[i])) {
+                               wpa_printf(MSG_DEBUG, "EAP-SIM: "
+                                          "GSM-Milenage authentication "
+                                          "could not be completed");
+                               return -1;
+                       }
+                       wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
+                                   data->rand[i], GSM_RAND_LEN);
+                       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
+                                       data->sres[i], EAP_SIM_SRES_LEN);
+                       wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
+                                       data->kc[i], EAP_SIM_KC_LEN);
+               }
+               return 0;
+       }
+#endif /* CONFIG_SIM_SIMULATOR */
+
+#ifdef CONFIG_SIM_HARDCODED
+       /* These hardcoded Kc and SRES values are used for testing. RAND to
+        * KC/SREC mapping is very bogus as far as real authentication is
+        * concerned, but it is quite useful for cases where the AS is rotating
+        * the order of pre-configured values. */
+       {
+               size_t i;
+
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Use hardcoded Kc and SRES "
+                          "values for testing");
+
+               for (i = 0; i < data->num_chal; i++) {
+                       if (data->rand[i][0] == 0xaa) {
+                               os_memcpy(data->kc[i],
+                                         "\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7",
+                                         EAP_SIM_KC_LEN);
+                               os_memcpy(data->sres[i], "\xd1\xd2\xd3\xd4",
+                                         EAP_SIM_SRES_LEN);
+                       } else if (data->rand[i][0] == 0xbb) {
+                               os_memcpy(data->kc[i],
+                                         "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7",
+                                         EAP_SIM_KC_LEN);
+                               os_memcpy(data->sres[i], "\xe1\xe2\xe3\xe4",
+                                         EAP_SIM_SRES_LEN);
+                       } else {
+                               os_memcpy(data->kc[i],
+                                         "\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7",
+                                         EAP_SIM_KC_LEN);
+                               os_memcpy(data->sres[i], "\xf1\xf2\xf3\xf4",
+                                         EAP_SIM_SRES_LEN);
+                       }
+               }
+       }
+
+       return 0;
+
+#else /* CONFIG_SIM_HARDCODED */
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: No GSM authentication algorithm "
+                  "enabled");
+       return -1;
+
+#endif /* CONFIG_SIM_HARDCODED */
+}
+
+
+static int eap_sim_supported_ver(int version)
+{
+       return version == EAP_SIM_VERSION;
+}
+
+
+#define CLEAR_PSEUDONYM        0x01
+#define CLEAR_REAUTH_ID        0x02
+#define CLEAR_EAP_ID   0x04
+
+static void eap_sim_clear_identities(struct eap_sim_data *data, int id)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SIM: forgetting old%s%s%s",
+                  id & CLEAR_PSEUDONYM ? " pseudonym" : "",
+                  id & CLEAR_REAUTH_ID ? " reauth_id" : "",
+                  id & CLEAR_EAP_ID ? " eap_id" : "");
+       if (id & CLEAR_PSEUDONYM) {
+               os_free(data->pseudonym);
+               data->pseudonym = NULL;
+               data->pseudonym_len = 0;
+       }
+       if (id & CLEAR_REAUTH_ID) {
+               os_free(data->reauth_id);
+               data->reauth_id = NULL;
+               data->reauth_id_len = 0;
+       }
+       if (id & CLEAR_EAP_ID) {
+               os_free(data->last_eap_identity);
+               data->last_eap_identity = NULL;
+               data->last_eap_identity_len = 0;
+       }
+}
+
+
+static int eap_sim_learn_ids(struct eap_sim_data *data,
+                            struct eap_sim_attrs *attr)
+{
+       if (attr->next_pseudonym) {
+               os_free(data->pseudonym);
+               data->pseudonym = os_malloc(attr->next_pseudonym_len);
+               if (data->pseudonym == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+                                  "next pseudonym");
+                       return -1;
+               }
+               os_memcpy(data->pseudonym, attr->next_pseudonym,
+                         attr->next_pseudonym_len);
+               data->pseudonym_len = attr->next_pseudonym_len;
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP-SIM: (encr) AT_NEXT_PSEUDONYM",
+                                 data->pseudonym,
+                                 data->pseudonym_len);
+       }
+
+       if (attr->next_reauth_id) {
+               os_free(data->reauth_id);
+               data->reauth_id = os_malloc(attr->next_reauth_id_len);
+               if (data->reauth_id == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: (encr) No memory for "
+                                  "next reauth_id");
+                       return -1;
+               }
+               os_memcpy(data->reauth_id, attr->next_reauth_id,
+                         attr->next_reauth_id_len);
+               data->reauth_id_len = attr->next_reauth_id_len;
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP-SIM: (encr) AT_NEXT_REAUTH_ID",
+                                 data->reauth_id,
+                                 data->reauth_id_len);
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
+                                           int err)
+{
+       struct eap_sim_msg *msg;
+
+       eap_sim_state(data, FAILURE);
+       data->num_id_req = 0;
+       data->num_notification = 0;
+
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_CLIENT_ERROR);
+       eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
+                                             struct eap_sim_data *data, u8 id,
+                                             enum eap_sim_id_req id_req)
+{
+       const u8 *identity = NULL;
+       size_t identity_len = 0;
+       struct eap_sim_msg *msg;
+
+       data->reauth = 0;
+       if (id_req == ANY_ID && data->reauth_id) {
+               identity = data->reauth_id;
+               identity_len = data->reauth_id_len;
+               data->reauth = 1;
+       } else if ((id_req == ANY_ID || id_req == FULLAUTH_ID) &&
+                  data->pseudonym) {
+               identity = data->pseudonym;
+               identity_len = data->pseudonym_len;
+               eap_sim_clear_identities(data, CLEAR_REAUTH_ID);
+       } else if (id_req != NO_ID_REQ) {
+               identity = eap_get_config_identity(sm, &identity_len);
+               if (identity) {
+                       eap_sim_clear_identities(data, CLEAR_PSEUDONYM |
+                                                CLEAR_REAUTH_ID);
+               }
+       }
+       if (id_req != NO_ID_REQ)
+               eap_sim_clear_identities(data, CLEAR_EAP_ID);
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-SIM Start (id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+                              EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START);
+       if (!data->reauth) {
+               wpa_hexdump(MSG_DEBUG, "   AT_NONCE_MT",
+                           data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+               eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_MT, 0,
+                               data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+               wpa_printf(MSG_DEBUG, "   AT_SELECTED_VERSION %d",
+                          data->selected_version);
+               eap_sim_msg_add(msg, EAP_SIM_AT_SELECTED_VERSION,
+                               data->selected_version, NULL, 0);
+       }
+
+       if (identity) {
+               wpa_hexdump_ascii(MSG_DEBUG, "   AT_IDENTITY",
+                                 identity, identity_len);
+               eap_sim_msg_add(msg, EAP_SIM_AT_IDENTITY, identity_len,
+                               identity, identity_len);
+       }
+
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
+                                                 u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-SIM Challenge (id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_CHALLENGE);
+       if (data->use_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres,
+                                 data->num_chal * EAP_SIM_SRES_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
+                                              u8 id, int counter_too_small)
+{
+       struct eap_sim_msg *msg;
+       unsigned int counter;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-SIM Reauthentication (id=%d)",
+                  id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_REAUTHENTICATION);
+       wpa_printf(MSG_DEBUG, "   AT_IV");
+       wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+       eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+       if (counter_too_small) {
+               wpa_printf(MSG_DEBUG, "   *AT_COUNTER_TOO_SMALL");
+               eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER_TOO_SMALL, 0, NULL, 0);
+               counter = data->counter_too_small;
+       } else
+               counter = data->counter;
+
+       wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", counter);
+       eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+
+       if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+                          "AT_ENCR_DATA");
+               eap_sim_msg_free(msg);
+               return NULL;
+       }
+       if (data->use_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, data->nonce_s,
+                                 EAP_SIM_NONCE_S_LEN);
+}
+
+
+static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data,
+                                                    u8 id, u16 notification)
+{
+       struct eap_sim_msg *msg;
+       u8 *k_aut = (notification & 0x4000) == 0 ? data->k_aut : NULL;
+
+       wpa_printf(MSG_DEBUG, "Generating EAP-SIM Notification (id=%d)", id);
+       msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id,
+                              EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION);
+       if (k_aut && data->reauth) {
+               wpa_printf(MSG_DEBUG, "   AT_IV");
+               wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+               eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+                                          EAP_SIM_AT_ENCR_DATA);
+               wpa_printf(MSG_DEBUG, "   *AT_COUNTER %d", data->counter);
+               eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+                               NULL, 0);
+               if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+                                            EAP_SIM_AT_PADDING)) {
+                       wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+                                  "AT_ENCR_DATA");
+                       eap_sim_msg_free(msg);
+                       return NULL;
+               }
+       }
+       if (k_aut) {
+               wpa_printf(MSG_DEBUG, "   AT_MAC");
+               eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       }
+       return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+}
+
+
+static struct wpabuf * eap_sim_process_start(struct eap_sm *sm,
+                                            struct eap_sim_data *data, u8 id,
+                                            struct eap_sim_attrs *attr)
+{
+       int selected_version = -1, id_error;
+       size_t i;
+       u8 *pos;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Start");
+       if (attr->version_list == NULL) {
+               wpa_printf(MSG_INFO, "EAP-SIM: No AT_VERSION_LIST in "
+                          "SIM/Start");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNSUPPORTED_VERSION);
+       }
+
+       os_free(data->ver_list);
+       data->ver_list = os_malloc(attr->version_list_len);
+       if (data->ver_list == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to allocate "
+                          "memory for version list");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+       os_memcpy(data->ver_list, attr->version_list, attr->version_list_len);
+       data->ver_list_len = attr->version_list_len;
+       pos = data->ver_list;
+       for (i = 0; i < data->ver_list_len / 2; i++) {
+               int ver = pos[0] * 256 + pos[1];
+               pos += 2;
+               if (eap_sim_supported_ver(ver)) {
+                       selected_version = ver;
+                       break;
+               }
+       }
+       if (selected_version < 0) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Could not find a supported "
+                          "version");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNSUPPORTED_VERSION);
+       }
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Selected Version %d",
+                  selected_version);
+       data->selected_version = selected_version;
+
+       id_error = 0;
+       switch (attr->id_req) {
+       case NO_ID_REQ:
+               break;
+       case ANY_ID:
+               if (data->num_id_req > 0)
+                       id_error++;
+               data->num_id_req++;
+               break;
+       case FULLAUTH_ID:
+               if (data->num_id_req > 1)
+                       id_error++;
+               data->num_id_req++;
+               break;
+       case PERMANENT_ID:
+               if (data->num_id_req > 2)
+                       id_error++;
+               data->num_id_req++;
+               break;
+       }
+       if (id_error) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Too many ID requests "
+                          "used within one authentication");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       return eap_sim_response_start(sm, data, id, attr->id_req);
+}
+
+
+static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
+                                                struct eap_sim_data *data,
+                                                u8 id,
+                                                const struct wpabuf *reqData,
+                                                struct eap_sim_attrs *attr)
+{
+       const u8 *identity;
+       size_t identity_len;
+       struct eap_sim_attrs eattr;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
+       data->reauth = 0;
+       if (!attr->mac || !attr->rand) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+                          "did not include%s%s",
+                          !attr->mac ? " AT_MAC" : "",
+                          !attr->rand ? " AT_RAND" : "");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: %lu challenges",
+                  (unsigned long) attr->num_chal);
+       if (attr->num_chal < data->min_num_chal) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Insufficient number of "
+                          "challenges (%lu)", (unsigned long) attr->num_chal);
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_INSUFFICIENT_NUM_OF_CHAL);
+       }
+       if (attr->num_chal > 3) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Too many challenges "
+                          "(%lu)", (unsigned long) attr->num_chal);
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       /* Verify that RANDs are different */
+       if (os_memcmp(attr->rand, attr->rand + GSM_RAND_LEN,
+                  GSM_RAND_LEN) == 0 ||
+           (attr->num_chal > 2 &&
+            (os_memcmp(attr->rand, attr->rand + 2 * GSM_RAND_LEN,
+                       GSM_RAND_LEN) == 0 ||
+             os_memcmp(attr->rand + GSM_RAND_LEN,
+                       attr->rand + 2 * GSM_RAND_LEN,
+                       GSM_RAND_LEN) == 0))) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Same RAND used multiple times");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_RAND_NOT_FRESH);
+       }
+
+       os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
+       data->num_chal = attr->num_chal;
+               
+       if (eap_sim_gsm_auth(sm, data)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+       if (data->last_eap_identity) {
+               identity = data->last_eap_identity;
+               identity_len = data->last_eap_identity_len;
+       } else if (data->pseudonym) {
+               identity = data->pseudonym;
+               identity_len = data->pseudonym_len;
+       } else
+               identity = eap_get_config_identity(sm, &identity_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Selected identity for MK "
+                         "derivation", identity, identity_len);
+       eap_sim_derive_mk(identity, identity_len, data->nonce_mt,
+                         data->selected_version, data->ver_list,
+                         data->ver_list_len, data->num_chal,
+                         (const u8 *) data->kc, data->mk);
+       eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+                           data->emsk);
+       if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, data->nonce_mt,
+                              EAP_SIM_NONCE_MT_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+                          "used invalid AT_MAC");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       /* Old reauthentication and pseudonym identities must not be used
+        * anymore. In other words, if no new identities are received, full
+        * authentication will be used on next reauthentication. */
+       eap_sim_clear_identities(data, CLEAR_PSEUDONYM | CLEAR_REAUTH_ID |
+                                CLEAR_EAP_ID);
+
+       if (attr->encr_data) {
+               u8 *decrypted;
+               decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                              attr->encr_data_len, attr->iv,
+                                              &eattr, 0);
+               if (decrypted == NULL) {
+                       return eap_sim_client_error(
+                               data, id, EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+               }
+               eap_sim_learn_ids(data, &eattr);
+               os_free(decrypted);
+       }
+
+       if (data->result_ind && attr->result_ind)
+               data->use_result_ind = 1;
+
+       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+               eap_sim_state(data, data->use_result_ind ?
+                             RESULT_SUCCESS : SUCCESS);
+       }
+
+       data->num_id_req = 0;
+       data->num_notification = 0;
+       /* RFC 4186 specifies that counter is initialized to one after
+        * fullauth, but initializing it to zero makes it easier to implement
+        * reauth verification. */
+       data->counter = 0;
+       return eap_sim_response_challenge(data, id);
+}
+
+
+static int eap_sim_process_notification_reauth(struct eap_sim_data *data,
+                                              struct eap_sim_attrs *attr)
+{
+       struct eap_sim_attrs eattr;
+       u8 *decrypted;
+
+       if (attr->encr_data == NULL || attr->iv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Notification message after "
+                          "reauth did not include encrypted data");
+               return -1;
+       }
+
+       decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                      attr->encr_data_len, attr->iv, &eattr,
+                                      0);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+                          "data from notification message");
+               return -1;
+       }
+
+       if (eattr.counter < 0 || (size_t) eattr.counter != data->counter) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Counter in notification "
+                          "message does not match with counter in reauth "
+                          "message");
+               os_free(decrypted);
+               return -1;
+       }
+
+       os_free(decrypted);
+       return 0;
+}
+
+
+static int eap_sim_process_notification_auth(struct eap_sim_data *data,
+                                            const struct wpabuf *reqData,
+                                            struct eap_sim_attrs *attr)
+{
+       if (attr->mac == NULL) {
+               wpa_printf(MSG_INFO, "EAP-SIM: no AT_MAC in after_auth "
+                          "Notification message");
+               return -1;
+       }
+
+       if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+       {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Notification message "
+                          "used invalid AT_MAC");
+               return -1;
+       }
+
+       if (data->reauth &&
+           eap_sim_process_notification_reauth(data, attr)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Invalid notification "
+                          "message after reauth");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_sim_process_notification(
+       struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+       const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Notification");
+       if (data->num_notification > 0) {
+               wpa_printf(MSG_INFO, "EAP-SIM: too many notification "
+                          "rounds (only one allowed)");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+       data->num_notification++;
+       if (attr->notification == -1) {
+               wpa_printf(MSG_INFO, "EAP-SIM: no AT_NOTIFICATION in "
+                          "Notification message");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if ((attr->notification & 0x4000) == 0 &&
+           eap_sim_process_notification_auth(data, reqData, attr)) {
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       eap_sim_report_notification(sm->msg_ctx, attr->notification, 0);
+       if (attr->notification >= 0 && attr->notification < 32768) {
+               eap_sim_state(data, FAILURE);
+       } else if (attr->notification == EAP_SIM_SUCCESS &&
+                  data->state == RESULT_SUCCESS)
+               eap_sim_state(data, SUCCESS);
+       return eap_sim_response_notification(data, id, attr->notification);
+}
+
+
+static struct wpabuf * eap_sim_process_reauthentication(
+       struct eap_sm *sm, struct eap_sim_data *data, u8 id,
+       const struct wpabuf *reqData, struct eap_sim_attrs *attr)
+{
+       struct eap_sim_attrs eattr;
+       u8 *decrypted;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Reauthentication");
+
+       if (data->reauth_id == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Server is trying "
+                          "reauthentication, but no reauth_id available");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       data->reauth = 1;
+       if (eap_sim_verify_mac(data->k_aut, reqData, attr->mac, (u8 *) "", 0))
+       {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+                          "did not have valid AT_MAC");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (attr->encr_data == NULL || attr->iv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+                          "message did not include encrypted data");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                      attr->encr_data_len, attr->iv, &eattr,
+                                      0);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+                          "data from reauthentication message");
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (eattr.nonce_s == NULL || eattr.counter < 0) {
+               wpa_printf(MSG_INFO, "EAP-SIM: (encr) No%s%s in reauth packet",
+                          !eattr.nonce_s ? " AT_NONCE_S" : "",
+                          eattr.counter < 0 ? " AT_COUNTER" : "");
+               os_free(decrypted);
+               return eap_sim_client_error(data, id,
+                                           EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+       }
+
+       if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+               wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter "
+                          "(%d <= %d)", eattr.counter, data->counter);
+               data->counter_too_small = eattr.counter;
+               /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
+                * reauth_id must not be used to start a new reauthentication.
+                * However, since it was used in the last EAP-Response-Identity
+                * packet, it has to saved for the following fullauth to be
+                * used in MK derivation. */
+               os_free(data->last_eap_identity);
+               data->last_eap_identity = data->reauth_id;
+               data->last_eap_identity_len = data->reauth_id_len;
+               data->reauth_id = NULL;
+               data->reauth_id_len = 0;
+               os_free(decrypted);
+               return eap_sim_response_reauth(data, id, 1);
+       }
+       data->counter = eattr.counter;
+
+       os_memcpy(data->nonce_s, eattr.nonce_s, EAP_SIM_NONCE_S_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM: (encr) AT_NONCE_S",
+                   data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+       eap_sim_derive_keys_reauth(data->counter,
+                                  data->reauth_id, data->reauth_id_len,
+                                  data->nonce_s, data->mk, data->msk,
+                                  data->emsk);
+       eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+       eap_sim_learn_ids(data, &eattr);
+
+       if (data->result_ind && attr->result_ind)
+               data->use_result_ind = 1;
+
+       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+               eap_sim_state(data, data->use_result_ind ?
+                             RESULT_SUCCESS : SUCCESS);
+       }
+
+       data->num_id_req = 0;
+       data->num_notification = 0;
+       if (data->counter > EAP_SIM_MAX_FAST_REAUTHS) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Maximum number of "
+                          "fast reauths performed - force fullauth");
+               eap_sim_clear_identities(data, CLEAR_REAUTH_ID | CLEAR_EAP_ID);
+       }
+       os_free(decrypted);
+       return eap_sim_response_reauth(data, id, 0);
+}
+
+
+static struct wpabuf * eap_sim_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_sim_data *data = priv;
+       const struct eap_hdr *req;
+       u8 subtype, id;
+       struct wpabuf *res;
+       const u8 *pos;
+       struct eap_sim_attrs attr;
+       size_t len;
+
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-SIM: EAP data", reqData);
+       if (eap_get_config_identity(sm, &len) == NULL) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Identity not configured");
+               eap_sm_request_identity(sm);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, reqData, &len);
+       if (pos == NULL || len < 1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       req = wpabuf_head(reqData);
+       id = req->identifier;
+       len = be_to_host16(req->length);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       subtype = *pos++;
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Subtype=%d", subtype);
+       pos += 2; /* Reserved */
+
+       if (eap_sim_parse_attr(pos, wpabuf_head_u8(reqData) + len, &attr, 0,
+                              0)) {
+               res = eap_sim_client_error(data, id,
+                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+               goto done;
+       }
+
+       switch (subtype) {
+       case EAP_SIM_SUBTYPE_START:
+               res = eap_sim_process_start(sm, data, id, &attr);
+               break;
+       case EAP_SIM_SUBTYPE_CHALLENGE:
+               res = eap_sim_process_challenge(sm, data, id, reqData, &attr);
+               break;
+       case EAP_SIM_SUBTYPE_NOTIFICATION:
+               res = eap_sim_process_notification(sm, data, id, reqData,
+                                                  &attr);
+               break;
+       case EAP_SIM_SUBTYPE_REAUTHENTICATION:
+               res = eap_sim_process_reauthentication(sm, data, id, reqData,
+                                                      &attr);
+               break;
+       case EAP_SIM_SUBTYPE_CLIENT_ERROR:
+               wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Client-Error");
+               res = eap_sim_client_error(data, id,
+                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown subtype=%d", subtype);
+               res = eap_sim_client_error(data, id,
+                                          EAP_SIM_UNABLE_TO_PROCESS_PACKET);
+               break;
+       }
+
+done:
+       if (data->state == FAILURE) {
+               ret->decision = DECISION_FAIL;
+               ret->methodState = METHOD_DONE;
+       } else if (data->state == SUCCESS) {
+               ret->decision = data->use_result_ind ?
+                       DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
+               ret->methodState = data->use_result_ind ?
+                       METHOD_DONE : METHOD_MAY_CONT;
+       } else if (data->state == RESULT_FAILURE)
+               ret->methodState = METHOD_CONT;
+       else if (data->state == RESULT_SUCCESS)
+               ret->methodState = METHOD_CONT;
+
+       if (ret->methodState == METHOD_DONE) {
+               ret->allowNotifications = FALSE;
+       }
+
+       return res;
+}
+
+
+static Boolean eap_sim_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       return data->pseudonym || data->reauth_id;
+}
+
+
+static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       eap_sim_clear_identities(data, CLEAR_EAP_ID);
+       data->use_result_ind = 0;
+}
+
+
+static void * eap_sim_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       if (os_get_random(data->nonce_mt, EAP_SIM_NONCE_MT_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to get random data "
+                          "for NONCE_MT");
+               os_free(data);
+               return NULL;
+       }
+       data->num_id_req = 0;
+       data->num_notification = 0;
+       eap_sim_state(data, CONTINUE);
+       return priv;
+}
+
+
+static const u8 * eap_sim_get_identity(struct eap_sm *sm, void *priv,
+                                      size_t *len)
+{
+       struct eap_sim_data *data = priv;
+
+       if (data->reauth_id) {
+               *len = data->reauth_id_len;
+               return data->reauth_id;
+       }
+
+       if (data->pseudonym) {
+               *len = data->pseudonym_len;
+               return data->pseudonym;
+       }
+
+       return NULL;
+}
+
+
+static Boolean eap_sim_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sim_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_SIM_KEYING_DATA_LEN;
+       os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+
+       return key;
+}
+
+
+static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sim_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+
+       return key;
+}
+
+
+int eap_peer_sim_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_sim_init;
+       eap->deinit = eap_sim_deinit;
+       eap->process = eap_sim_process;
+       eap->isKeyAvailable = eap_sim_isKeyAvailable;
+       eap->getKey = eap_sim_getKey;
+       eap->has_reauth_data = eap_sim_has_reauth_data;
+       eap->deinit_for_reauth = eap_sim_deinit_for_reauth;
+       eap->init_for_reauth = eap_sim_init_for_reauth;
+       eap->get_identity = eap_sim_get_identity;
+       eap->get_emsk = eap_sim_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
new file mode 100644 (file)
index 0000000..20b2212
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * EAP peer method: EAP-TLS (RFC 2716)
+ * 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 "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+       struct eap_ssl_data ssl;
+       u8 *key_data;
+};
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+       struct eap_tls_data *data;
+       struct eap_peer_config *config = eap_get_config(sm);
+       if (config == NULL ||
+           ((sm->init_phase2 ? config->private_key2 : config->private_key)
+            == NULL &&
+            (sm->init_phase2 ? config->engine2 : config->engine) == 0)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Private key not configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_deinit(sm, data);
+               if (config->engine) {
+                       wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting Smartcard "
+                                  "PIN");
+                       eap_sm_request_pin(sm);
+                       sm->ignore = TRUE;
+               } else if (config->private_key && !config->private_key_passwd)
+               {
+                       wpa_printf(MSG_DEBUG, "EAP-TLS: Requesting private "
+                                  "key passphrase");
+                       eap_sm_request_passphrase(sm);
+                       sm->ignore = TRUE;
+               }
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_tls_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       if (data == NULL)
+               return;
+       eap_peer_tls_ssl_deinit(sm, &data->ssl);
+       os_free(data->key_data);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_failure(struct eap_sm *sm,
+                                      struct eap_tls_data *data,
+                                      struct eap_method_ret *ret, int res,
+                                      struct wpabuf *resp, u8 id)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TLS: TLS processing failed");
+
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_FAIL;
+
+       if (res == -1) {
+               struct eap_peer_config *config = eap_get_config(sm);
+               if (config) {
+                       /*
+                        * The TLS handshake failed. So better forget the old
+                        * PIN. It may be wrong, we cannot be sure but trying
+                        * the wrong one again might block it on the card--so
+                        * better ask the user again.
+                        */
+                       os_free(config->pin);
+                       config->pin = NULL;
+               }
+       }
+
+       if (resp) {
+               /*
+                * This is likely an alert message, so send it instead of just
+                * ACKing the error.
+                */
+               return resp;
+       }
+
+       return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+}
+
+
+static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
+                           struct eap_method_ret *ret)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+
+       os_free(data->key_data);
+       data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+                                                "client EAP encryption",
+                                                EAP_TLS_KEY_LEN +
+                                                EAP_EMSK_LEN);
+       if (data->key_data) {
+               wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived key",
+                               data->key_data, EAP_TLS_KEY_LEN);
+               wpa_hexdump_key(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+                               data->key_data + EAP_TLS_KEY_LEN,
+                               EAP_EMSK_LEN);
+       } else {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to derive key");
+       }
+}
+
+
+static struct wpabuf * eap_tls_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       size_t left;
+       int res;
+       struct wpabuf *resp;
+       u8 flags, id;
+       const u8 *pos;
+       struct eap_tls_data *data = priv;
+
+       pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TLS, ret,
+                                       reqData, &left, &flags);
+       if (pos == NULL)
+               return NULL;
+       id = eap_get_id(reqData);
+
+       if (flags & EAP_TLS_FLAGS_START) {
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Start");
+               left = 0; /* make sure that this frame is empty, even though it
+                          * should always be, anyway */
+       }
+
+       resp = NULL;
+       res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TLS, 0, id,
+                                         pos, left, &resp);
+
+       if (res < 0) {
+               return eap_tls_failure(sm, data, ret, res, resp, id);
+       }
+
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+               eap_tls_success(sm, data, ret);
+
+       if (res == 1) {
+               wpabuf_free(resp);
+               return eap_peer_tls_build_ack(id, EAP_TYPE_TLS, 0);
+       }
+
+       return resp;
+}
+
+
+static Boolean eap_tls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       return tls_connection_established(sm->ssl_ctx, data->ssl.conn);
+}
+
+
+static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+}
+
+
+static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       os_free(data->key_data);
+       data->key_data = NULL;
+       if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+               os_free(data);
+               return NULL;
+       }
+       return priv;
+}
+
+
+static int eap_tls_get_status(struct eap_sm *sm, void *priv, char *buf,
+                             size_t buflen, int verbose)
+{
+       struct eap_tls_data *data = priv;
+       return eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+}
+
+
+static Boolean eap_tls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       return data->key_data != NULL;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL)
+               return NULL;
+
+       key = os_malloc(EAP_TLS_KEY_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_TLS_KEY_LEN;
+       os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+       return key;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+       return key;
+}
+
+
+int eap_peer_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_tls_init;
+       eap->deinit = eap_tls_deinit;
+       eap->process = eap_tls_process;
+       eap->isKeyAvailable = eap_tls_isKeyAvailable;
+       eap->getKey = eap_tls_getKey;
+       eap->get_status = eap_tls_get_status;
+       eap->has_reauth_data = eap_tls_has_reauth_data;
+       eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+       eap->init_for_reauth = eap_tls_init_for_reauth;
+       eap->get_emsk = eap_tls_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
new file mode 100644 (file)
index 0000000..7bd50f6
--- /dev/null
@@ -0,0 +1,1020 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * 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 "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+static int eap_tls_check_blob(struct eap_sm *sm, const char **name,
+                             const u8 **data, size_t *data_len)
+{
+       const struct wpa_config_blob *blob;
+
+       if (*name == NULL || os_strncmp(*name, "blob://", 7) != 0)
+               return 0;
+
+       blob = eap_get_config_blob(sm, *name + 7);
+       if (blob == NULL) {
+               wpa_printf(MSG_ERROR, "%s: Named configuration blob '%s' not "
+                          "found", __func__, *name + 7);
+               return -1;
+       }
+
+       *name = NULL;
+       *data = blob->data;
+       *data_len = blob->len;
+
+       return 0;
+}
+
+
+static void eap_tls_params_flags(struct tls_connection_params *params,
+                                const char *txt)
+{
+       if (txt == NULL)
+               return;
+       if (os_strstr(txt, "tls_allow_md5=1"))
+               params->flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
+       if (os_strstr(txt, "tls_disable_time_checks=1"))
+               params->flags |= TLS_CONN_DISABLE_TIME_CHECKS;
+}
+
+
+static void eap_tls_params_from_conf1(struct tls_connection_params *params,
+                                     struct eap_peer_config *config)
+{
+       params->ca_cert = (char *) config->ca_cert;
+       params->ca_path = (char *) config->ca_path;
+       params->client_cert = (char *) config->client_cert;
+       params->private_key = (char *) config->private_key;
+       params->private_key_passwd = (char *) config->private_key_passwd;
+       params->dh_file = (char *) config->dh_file;
+       params->subject_match = (char *) config->subject_match;
+       params->altsubject_match = (char *) config->altsubject_match;
+       params->engine = config->engine;
+       params->engine_id = config->engine_id;
+       params->pin = config->pin;
+       params->key_id = config->key_id;
+       params->cert_id = config->cert_id;
+       params->ca_cert_id = config->ca_cert_id;
+       eap_tls_params_flags(params, config->phase1);
+}
+
+
+static void eap_tls_params_from_conf2(struct tls_connection_params *params,
+                                     struct eap_peer_config *config)
+{
+       params->ca_cert = (char *) config->ca_cert2;
+       params->ca_path = (char *) config->ca_path2;
+       params->client_cert = (char *) config->client_cert2;
+       params->private_key = (char *) config->private_key2;
+       params->private_key_passwd = (char *) config->private_key2_passwd;
+       params->dh_file = (char *) config->dh_file2;
+       params->subject_match = (char *) config->subject_match2;
+       params->altsubject_match = (char *) config->altsubject_match2;
+       params->engine = config->engine2;
+       params->engine_id = config->engine2_id;
+       params->pin = config->pin2;
+       params->key_id = config->key2_id;
+       params->cert_id = config->cert2_id;
+       params->ca_cert_id = config->ca_cert2_id;
+       eap_tls_params_flags(params, config->phase2);
+}
+
+
+static int eap_tls_params_from_conf(struct eap_sm *sm,
+                                   struct eap_ssl_data *data,
+                                   struct tls_connection_params *params,
+                                   struct eap_peer_config *config, int phase2)
+{
+       os_memset(params, 0, sizeof(*params));
+       if (phase2) {
+               wpa_printf(MSG_DEBUG, "TLS: using phase2 config options");
+               eap_tls_params_from_conf2(params, config);
+       } else {
+               wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
+               eap_tls_params_from_conf1(params, config);
+       }
+       params->tls_ia = data->tls_ia;
+
+       /*
+        * Use blob data, if available. Otherwise, leave reference to external
+        * file as-is.
+        */
+       if (eap_tls_check_blob(sm, &params->ca_cert, &params->ca_cert_blob,
+                              &params->ca_cert_blob_len) ||
+           eap_tls_check_blob(sm, &params->client_cert,
+                              &params->client_cert_blob,
+                              &params->client_cert_blob_len) ||
+           eap_tls_check_blob(sm, &params->private_key,
+                              &params->private_key_blob,
+                              &params->private_key_blob_len) ||
+           eap_tls_check_blob(sm, &params->dh_file, &params->dh_blob,
+                              &params->dh_blob_len)) {
+               wpa_printf(MSG_INFO, "SSL: Failed to get configuration blobs");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_tls_init_connection(struct eap_sm *sm,
+                                  struct eap_ssl_data *data,
+                                  struct eap_peer_config *config,
+                                  struct tls_connection_params *params)
+{
+       int res;
+
+       data->conn = tls_connection_init(sm->ssl_ctx);
+       if (data->conn == NULL) {
+               wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+                          "connection");
+               return -1;
+       }
+
+       res = tls_connection_set_params(sm->ssl_ctx, data->conn, params);
+       if (res == TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED) {
+               /*
+                * At this point with the pkcs11 engine the PIN might be wrong.
+                * We reset the PIN in the configuration to be sure to not use
+                * it again and the calling function must request a new one.
+                */
+               os_free(config->pin);
+               config->pin = NULL;
+       } else if (res == TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED) {
+               wpa_printf(MSG_INFO, "TLS: Failed to load private key");
+               /*
+                * We do not know exactly but maybe the PIN was wrong,
+                * so ask for a new one.
+                */
+               os_free(config->pin);
+               config->pin = NULL;
+               eap_sm_request_pin(sm);
+               sm->ignore = TRUE;
+               tls_connection_deinit(sm->ssl_ctx, data->conn);
+               data->conn = NULL;
+               return -1;
+       } else if (res) {
+               wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection "
+                          "parameters");
+               tls_connection_deinit(sm->ssl_ctx, data->conn);
+               data->conn = NULL;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_init - Initialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @config: Pointer to the network configuration
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to initialize shared TLS functionality for EAP-TLS,
+ * EAP-PEAP, EAP-TTLS, and EAP-FAST.
+ */
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+                         struct eap_peer_config *config)
+{
+       struct tls_connection_params params;
+
+       if (config == NULL)
+               return -1;
+
+       data->eap = sm;
+       data->phase2 = sm->init_phase2;
+       if (eap_tls_params_from_conf(sm, data, &params, config, data->phase2) <
+           0)
+               return -1;
+
+       if (eap_tls_init_connection(sm, data, config, &params) < 0)
+               return -1;
+
+       data->tls_out_limit = config->fragment_size;
+       if (data->phase2) {
+               /* Limit the fragment size in the inner TLS authentication
+                * since the outer authentication with EAP-PEAP does not yet
+                * support fragmentation */
+               if (data->tls_out_limit > 100)
+                       data->tls_out_limit -= 100;
+       }
+
+       if (config->phase1 &&
+           os_strstr(config->phase1, "include_tls_length=1")) {
+               wpa_printf(MSG_DEBUG, "TLS: Include TLS Message Length in "
+                          "unfragmented packets");
+               data->include_tls_length = 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_ssl_deinit - Deinitialize shared TLS functionality
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ *
+ * This function deinitializes shared TLS functionality that was initialized
+ * with eap_peer_tls_ssl_init().
+ */
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+       tls_connection_deinit(sm->ssl_ctx, data->conn);
+       eap_peer_tls_reset_input(data);
+       eap_peer_tls_reset_output(data);
+}
+
+
+/**
+ * eap_peer_tls_derive_key - Derive a key based on TLS session data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @len: Length of the key material to generate (usually 64 for MSK)
+ * Returns: Pointer to allocated key on success or %NULL on failure
+ *
+ * This function uses TLS-PRF to generate pseudo-random data based on the TLS
+ * session data (client/server random and master key). Each key type may use a
+ * different label to bind the key usage into the generated material.
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+                            const char *label, size_t len)
+{
+       struct tls_keys keys;
+       u8 *rnd = NULL, *out;
+
+       out = os_malloc(len);
+       if (out == NULL)
+               return NULL;
+
+       /* First, try to use TLS library function for PRF, if available. */
+       if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
+           0)
+               return out;
+
+       /*
+        * TLS library did not support key generation, so get the needed TLS
+        * session parameters and use an internal implementation of TLS PRF to
+        * derive the key.
+        */
+       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+               goto fail;
+
+       if (keys.client_random == NULL || keys.server_random == NULL ||
+           keys.master_key == NULL)
+               goto fail;
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       if (rnd == NULL)
+               goto fail;
+       os_memcpy(rnd, keys.client_random, keys.client_random_len);
+       os_memcpy(rnd + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       if (tls_prf(keys.master_key, keys.master_key_len,
+                   label, rnd, keys.client_random_len +
+                   keys.server_random_len, out, len))
+               goto fail;
+
+       os_free(rnd);
+       return out;
+
+fail:
+       os_free(out);
+       os_free(rnd);
+       return NULL;
+}
+
+
+/**
+ * eap_peer_tls_reassemble_fragment - Reassemble a received fragment
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * Returns: 0 on success, 1 if more data is needed for the full message, or
+ * -1 on error
+ */
+static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data,
+                                           const struct wpabuf *in_data)
+{
+       size_t tls_in_len, in_len;
+
+       tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0;
+       in_len = in_data ? wpabuf_len(in_data) : 0;
+
+       if (tls_in_len + in_len == 0) {
+               /* No message data received?! */
+               wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: "
+                          "tls_in_left=%lu tls_in_len=%lu in_len=%lu",
+                          (unsigned long) data->tls_in_left,
+                          (unsigned long) tls_in_len,
+                          (unsigned long) in_len);
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+
+       if (tls_in_len + in_len > 65536) {
+               /*
+                * Limit length to avoid rogue servers from causing large
+                * memory allocations.
+                */
+               wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size over "
+                          "64 kB)");
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+
+       if (in_len > data->tls_in_left) {
+               /* Sender is doing something odd - reject message */
+               wpa_printf(MSG_INFO, "SSL: more data than TLS message length "
+                          "indicated");
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+
+       if (wpabuf_resize(&data->tls_in, in_len) < 0) {
+               wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS "
+                          "data");
+               eap_peer_tls_reset_input(data);
+               return -1;
+       }
+       wpabuf_put_buf(data->tls_in, in_data);
+       data->tls_in_left -= in_len;
+
+       if (data->tls_in_left > 0) {
+               wpa_printf(MSG_DEBUG, "SSL: Need %lu bytes more input "
+                          "data", (unsigned long) data->tls_in_left);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_data_reassemble - Reassemble TLS data
+ * @data: Data for TLS processing
+ * @in_data: Next incoming TLS segment
+ * @need_more_input: Variable for returning whether more input data is needed
+ * to reassemble this TLS packet
+ * Returns: Pointer to output data, %NULL on error or when more data is needed
+ * for the full message (in which case, *need_more_input is also set to 1).
+ *
+ * This function reassembles TLS fragments. Caller must not free the returned
+ * data buffer since an internal pointer to it is maintained.
+ */
+static const struct wpabuf * eap_peer_tls_data_reassemble(
+       struct eap_ssl_data *data, const struct wpabuf *in_data,
+       int *need_more_input)
+{
+       *need_more_input = 0;
+
+       if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) {
+               /* Message has fragments */
+               int res = eap_peer_tls_reassemble_fragment(data, in_data);
+               if (res) {
+                       if (res == 1)
+                               *need_more_input = 1;
+                       return NULL;
+               }
+
+               /* Message is now fully reassembled. */
+       } else {
+               /* No fragments in this message, so just make a copy of it. */
+               data->tls_in_left = 0;
+               data->tls_in = wpabuf_dup(in_data);
+               if (data->tls_in == NULL)
+                       return NULL;
+       }
+
+       return data->tls_in;
+}
+
+
+/**
+ * eap_tls_process_input - Process incoming TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to application data (if available)
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, -1 on failure
+ */
+static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data,
+                                const u8 *in_data, size_t in_len,
+                                struct wpabuf **out_data)
+{
+       const struct wpabuf *msg;
+       int need_more_input;
+       struct wpabuf *appl_data;
+       struct wpabuf buf;
+
+       wpabuf_set(&buf, in_data, in_len);
+       msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input);
+       if (msg == NULL)
+               return need_more_input ? 1 : -1;
+
+       /* Full TLS message reassembled - continue handshake processing */
+       if (data->tls_out) {
+               /* This should not happen.. */
+               wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending "
+                          "tls_out data even though tls_out_len = 0");
+               wpabuf_free(data->tls_out);
+               WPA_ASSERT(data->tls_out == NULL);
+       }
+       appl_data = NULL;
+       data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn,
+                                                msg, &appl_data);
+
+       eap_peer_tls_reset_input(data);
+
+       if (appl_data &&
+           tls_connection_established(sm->ssl_ctx, data->conn) &&
+           !tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+               wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data",
+                                   appl_data);
+               *out_data = appl_data;
+               return 2;
+       }
+
+       wpabuf_free(appl_data);
+
+       return 0;
+}
+
+
+/**
+ * eap_tls_process_output - Process outgoing TLS message
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @ret: Return value to use on success
+ * @out_data: Buffer for returning the allocated output buffer
+ * Returns: ret (0 or 1) on success, -1 on failure
+ */
+static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type,
+                                 int peap_version, u8 id, int ret,
+                                 struct wpabuf **out_data)
+{
+       size_t len;
+       u8 *flags;
+       int more_fragments, length_included;
+
+       if (data->tls_out == NULL)
+               return -1;
+       len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+       wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total "
+                  "%lu bytes)",
+                  (unsigned long) len,
+                  (unsigned long) wpabuf_len(data->tls_out));
+
+       /*
+        * Limit outgoing message to the configured maximum size. Fragment
+        * message if needed.
+        */
+       if (len > data->tls_out_limit) {
+               more_fragments = 1;
+               len = data->tls_out_limit;
+               wpa_printf(MSG_DEBUG, "SSL: sending %lu bytes, more fragments "
+                          "will follow", (unsigned long) len);
+       } else
+               more_fragments = 0;
+
+       length_included = data->tls_out_pos == 0 &&
+               (wpabuf_len(data->tls_out) > data->tls_out_limit ||
+                data->include_tls_length);
+       if (!length_included &&
+           eap_type == EAP_TYPE_PEAP && peap_version == 0 &&
+           !tls_connection_established(data->eap->ssl_ctx, data->conn)) {
+               /*
+                * Windows Server 2008 NPS really wants to have the TLS Message
+                * length included in phase 0 even for unfragmented frames or
+                * it will get very confused with Compound MAC calculation and
+                * Outer TLVs.
+                */
+               length_included = 1;
+       }
+
+       *out_data = eap_msg_alloc(EAP_VENDOR_IETF, eap_type,
+                                 1 + length_included * 4 + len,
+                                 EAP_CODE_RESPONSE, id);
+       if (*out_data == NULL)
+               return -1;
+
+       flags = wpabuf_put(*out_data, 1);
+       *flags = peap_version;
+       if (more_fragments)
+               *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+       if (length_included) {
+               *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+               wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out));
+       }
+
+       wpabuf_put_data(*out_data,
+                       wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+                       len);
+       data->tls_out_pos += len;
+
+       if (!more_fragments)
+               eap_peer_tls_reset_output(data);
+
+       return ret;
+}
+
+
+/**
+ * eap_peer_tls_process_helper - Process TLS handshake message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Message received from the server
+ * @in_len: Length of in_data
+ * @out_data: Buffer for returning a pointer to the response message
+ * Returns: 0 on success, 1 if more input data is needed, 2 if application data
+ * is available, or -1 on failure
+ *
+ * This function can be used to process TLS handshake messages. It reassembles
+ * the received fragments and uses a TLS library to process the messages. The
+ * response data from the TLS library is fragmented to suitable output messages
+ * that the caller can send out.
+ *
+ * out_data is used to return the response message if the return value of this
+ * function is 0, 2, or -1. In case of failure, the message is likely a TLS
+ * alarm message. The caller is responsible for freeing the allocated buffer if
+ * *out_data is not %NULL.
+ *
+ * This function is called for each received TLS message during the TLS
+ * handshake after eap_peer_tls_process_init() call and possible processing of
+ * TLS Flags field. Once the handshake has been completed, i.e., when
+ * tls_connection_established() returns 1, EAP method specific decrypting of
+ * the tunneled data is used.
+ */
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+                               EapType eap_type, int peap_version,
+                               u8 id, const u8 *in_data, size_t in_len,
+                               struct wpabuf **out_data)
+{
+       int ret = 0;
+
+       *out_data = NULL;
+
+       if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) {
+               wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output "
+                          "fragments are waiting to be sent out");
+               return -1;
+       }
+
+       if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+               /*
+                * No more data to send out - expect to receive more data from
+                * the AS.
+                */
+               int res = eap_tls_process_input(sm, data, in_data, in_len,
+                                               out_data);
+               if (res) {
+                       /*
+                        * Input processing failed (res = -1) or more data is
+                        * needed (res = 1).
+                        */
+                       return res;
+               }
+
+               /*
+                * The incoming message has been reassembled and processed. The
+                * response was allocated into data->tls_out buffer.
+                */
+       }
+
+       if (data->tls_out == NULL) {
+               /*
+                * No outgoing fragments remaining from the previous message
+                * and no new message generated. This indicates an error in TLS
+                * processing.
+                */
+               eap_peer_tls_reset_output(data);
+               return -1;
+       }
+
+       if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+               /* TLS processing has failed - return error */
+               wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+                          "report error");
+               ret = -1;
+               /* TODO: clean pin if engine used? */
+       }
+
+       if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) {
+               /*
+                * TLS negotiation should now be complete since all other cases
+                * needing more data should have been caught above based on
+                * the TLS Message Length field.
+                */
+               wpa_printf(MSG_DEBUG, "SSL: No data to be sent out");
+               wpabuf_free(data->tls_out);
+               data->tls_out = NULL;
+               return 1;
+       }
+
+       /* Send the pending message (in fragments, if needed). */
+       return eap_tls_process_output(data, eap_type, peap_version, id, ret,
+                                     out_data);
+}
+
+
+/**
+ * eap_peer_tls_build_ack - Build a TLS ACK frame
+ * @id: EAP identifier for the response
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * Returns: Pointer to the allocated ACK frame or %NULL on failure
+ */
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+                                      int peap_version)
+{
+       struct wpabuf *resp;
+
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_RESPONSE,
+                            id);
+       if (resp == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "SSL: Building ACK (type=%d id=%d ver=%d)",
+                  (int) eap_type, id, peap_version);
+       wpabuf_put_u8(resp, peap_version); /* Flags */
+       return resp;
+}
+
+
+/**
+ * eap_peer_tls_reauth_init - Re-initialize shared TLS for session resumption
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+       eap_peer_tls_reset_input(data);
+       eap_peer_tls_reset_output(data);
+       return tls_connection_shutdown(sm->ssl_ctx, data->conn);
+}
+
+
+/**
+ * eap_peer_tls_status - Get TLS status
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ */
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+                       char *buf, size_t buflen, int verbose)
+{
+       char name[128];
+       int len = 0, ret;
+
+       if (tls_get_cipher(sm->ssl_ctx, data->conn, name, sizeof(name)) == 0) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "EAP TLS cipher=%s\n", name);
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+       }
+
+       return len;
+}
+
+
+/**
+ * eap_peer_tls_process_init - Initial validation/processing of EAP requests
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @ret: Return values from EAP request validation and processing
+ * @reqData: EAP request to be processed (eapReqData)
+ * @len: Buffer for returning length of the remaining payload
+ * @flags: Buffer for returning TLS flags
+ * Returns: Pointer to payload after TLS flags and length or %NULL on failure
+ *
+ * This function validates the EAP header and processes the optional TLS
+ * Message Length field. If this is the first fragment of a TLS message, the
+ * TLS reassembly code is initialized to receive the indicated number of bytes.
+ *
+ * EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-FAST methods are expected to use this
+ * function as the first step in processing received messages. They will need
+ * to process the flags (apart from Message Length Included) that are returned
+ * through the flags pointer and the message payload that will be returned (and
+ * the length is returned through the len pointer). Return values (ret) are set
+ * for continuation of EAP method processing. The caller is responsible for
+ * setting these to indicate completion (either success or failure) based on
+ * the authentication result.
+ */
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+                                    struct eap_ssl_data *data,
+                                    EapType eap_type,
+                                    struct eap_method_ret *ret,
+                                    const struct wpabuf *reqData,
+                                    size_t *len, u8 *flags)
+{
+       const u8 *pos;
+       size_t left;
+       unsigned int tls_msg_len;
+
+       if (tls_get_errors(sm->ssl_ctx)) {
+               wpa_printf(MSG_INFO, "SSL: TLS errors detected");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData, &left);
+       if (pos == NULL) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+       if (left == 0) {
+               wpa_printf(MSG_DEBUG, "SSL: Invalid TLS message: no Flags "
+                          "octet included");
+               if (!sm->workaround) {
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+
+               wpa_printf(MSG_DEBUG, "SSL: Workaround - assume no Flags "
+                          "indicates ACK frame");
+               *flags = 0;
+       } else {
+               *flags = *pos++;
+               left--;
+       }
+       wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - "
+                  "Flags 0x%02x", (unsigned long) wpabuf_len(reqData),
+                  *flags);
+       if (*flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+               if (left < 4) {
+                       wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+                                  "length");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               tls_msg_len = WPA_GET_BE32(pos);
+               wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+                          tls_msg_len);
+               if (data->tls_in_left == 0) {
+                       data->tls_in_total = tls_msg_len;
+                       data->tls_in_left = tls_msg_len;
+                       wpabuf_free(data->tls_in);
+                       data->tls_in = NULL;
+               }
+               pos += 4;
+               left -= 4;
+       }
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       *len = left;
+       return pos;
+}
+
+
+/**
+ * eap_peer_tls_reset_input - Reset input buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for input buffers and resets input
+ * state.
+ */
+void eap_peer_tls_reset_input(struct eap_ssl_data *data)
+{
+       data->tls_in_left = data->tls_in_total = 0;
+       wpabuf_free(data->tls_in);
+       data->tls_in = NULL;
+}
+
+
+/**
+ * eap_peer_tls_reset_output - Reset output buffers
+ * @data: Data for TLS processing
+ *
+ * This function frees any allocated memory for output buffers and resets
+ * output state.
+ */
+void eap_peer_tls_reset_output(struct eap_ssl_data *data)
+{
+       data->tls_out_pos = 0;
+       wpabuf_free(data->tls_out);
+       data->tls_out = NULL;
+}
+
+
+/**
+ * eap_peer_tls_decrypt - Decrypt received phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @in_data: Message received from the server
+ * @in_decrypted: Buffer for returning a pointer to the decrypted message
+ * Returns: 0 on success, 1 if more input data is needed, or -1 on failure
+ */
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **in_decrypted)
+{
+       const struct wpabuf *msg;
+       int need_more_input;
+
+       msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input);
+       if (msg == NULL)
+               return need_more_input ? 1 : -1;
+
+       *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg);
+       eap_peer_tls_reset_input(data);
+       if (*in_decrypted == NULL) {
+               wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data");
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_encrypt - Encrypt phase 2 TLS message
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP type (EAP_TYPE_TLS, EAP_TYPE_PEAP, ...)
+ * @peap_version: Version number for EAP-PEAP/TTLS
+ * @id: EAP identifier for the response
+ * @in_data: Plaintext phase 2 data to encrypt or %NULL to continue fragments
+ * @out_data: Buffer for returning a pointer to the encrypted response message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+                        EapType eap_type, int peap_version, u8 id,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **out_data)
+{
+       if (in_data) {
+               eap_peer_tls_reset_output(data);
+               data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn,
+                                                      in_data);
+               if (data->tls_out == NULL) {
+                       wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 "
+                                  "data (in_len=%lu)",
+                                  (unsigned long) wpabuf_len(in_data));
+                       eap_peer_tls_reset_output(data);
+                       return -1;
+               }
+       }
+
+       return eap_tls_process_output(data, eap_type, peap_version, id, 0,
+                                     out_data);
+}
+
+
+/**
+ * eap_peer_select_phase2_methods - Select phase 2 EAP method
+ * @config: Pointer to the network configuration
+ * @prefix: 'phase2' configuration prefix, e.g., "auth="
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to parse EAP method list and select allowed methods
+ * for Phase2 authentication.
+ */
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+                                  const char *prefix,
+                                  struct eap_method_type **types,
+                                  size_t *num_types)
+{
+       char *start, *pos, *buf;
+       struct eap_method_type *methods = NULL, *_methods;
+       u8 method;
+       size_t num_methods = 0, prefix_len;
+
+       if (config == NULL || config->phase2 == NULL)
+               goto get_defaults;
+
+       start = buf = os_strdup(config->phase2);
+       if (buf == NULL)
+               return -1;
+
+       prefix_len = os_strlen(prefix);
+
+       while (start && *start != '\0') {
+               int vendor;
+               pos = os_strstr(start, prefix);
+               if (pos == NULL)
+                       break;
+               if (start != pos && *(pos - 1) != ' ') {
+                       start = pos + prefix_len;
+                       continue;
+               }
+
+               start = pos + prefix_len;
+               pos = os_strchr(start, ' ');
+               if (pos)
+                       *pos++ = '\0';
+               method = eap_get_phase2_type(start, &vendor);
+               if (vendor == EAP_VENDOR_IETF && method == EAP_TYPE_NONE) {
+                       wpa_printf(MSG_ERROR, "TLS: Unsupported Phase2 EAP "
+                                  "method '%s'", start);
+               } else {
+                       num_methods++;
+                       _methods = os_realloc(methods,
+                                             num_methods * sizeof(*methods));
+                       if (_methods == NULL) {
+                               os_free(methods);
+                               os_free(buf);
+                               return -1;
+                       }
+                       methods = _methods;
+                       methods[num_methods - 1].vendor = vendor;
+                       methods[num_methods - 1].method = method;
+               }
+
+               start = pos;
+       }
+
+       os_free(buf);
+
+get_defaults:
+       if (methods == NULL)
+               methods = eap_get_phase2_types(config, &num_methods);
+
+       if (methods == NULL) {
+               wpa_printf(MSG_ERROR, "TLS: No Phase2 EAP methods available");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "TLS: Phase2 EAP types",
+                   (u8 *) methods,
+                   num_methods * sizeof(struct eap_method_type));
+
+       *types = methods;
+       *num_types = num_methods;
+
+       return 0;
+}
+
+
+/**
+ * eap_peer_tls_phase2_nak - Generate EAP-Nak for Phase 2
+ * @types: Buffer for returning allocated list of allowed EAP methods
+ * @num_types: Buffer for returning number of allocated EAP methods
+ * @hdr: EAP-Request header (and the following EAP type octet)
+ * @resp: Buffer for returning the EAP-Nak message
+ * Returns: 0 on success, -1 on failure
+ */
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+                           struct eap_hdr *hdr, struct wpabuf **resp)
+{
+       u8 *pos = (u8 *) (hdr + 1);
+       size_t i;
+
+       /* TODO: add support for expanded Nak */
+       wpa_printf(MSG_DEBUG, "TLS: Phase 2 Request: Nak type=%d", *pos);
+       wpa_hexdump(MSG_DEBUG, "TLS: Allowed Phase2 EAP types",
+                   (u8 *) types, num_types * sizeof(struct eap_method_type));
+       *resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_NAK, num_types,
+                             EAP_CODE_RESPONSE, hdr->identifier);
+       if (*resp == NULL)
+               return -1;
+
+       for (i = 0; i < num_types; i++) {
+               if (types[i].vendor == EAP_VENDOR_IETF &&
+                   types[i].method < 256)
+                       wpabuf_put_u8(*resp, types[i].method);
+       }
+
+       eap_update_len(*resp);
+
+       return 0;
+}
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
new file mode 100644 (file)
index 0000000..e9e0998
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
+ * 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.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+       /**
+        * conn - TLS connection context data from tls_connection_init()
+        */
+       struct tls_connection *conn;
+
+       /**
+        * tls_out - TLS message to be sent out in fragments
+        */
+       struct wpabuf *tls_out;
+
+       /**
+        * tls_out_pos - The current position in the outgoing TLS message
+        */
+       size_t tls_out_pos;
+
+       /**
+        * tls_out_limit - Maximum fragment size for outgoing TLS messages
+        */
+       size_t tls_out_limit;
+
+       /**
+        * tls_in - Received TLS message buffer for re-assembly
+        */
+       struct wpabuf *tls_in;
+
+       /**
+        * tls_in_left - Number of remaining bytes in the incoming TLS message
+        */
+       size_t tls_in_left;
+
+       /**
+        * tls_in_total - Total number of bytes in the incoming TLS message
+        */
+       size_t tls_in_total;
+
+       /**
+        * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+        */
+       int phase2;
+
+       /**
+        * include_tls_length - Whether the TLS length field is included even
+        * if the TLS data is not fragmented
+        */
+       int include_tls_length;
+
+       /**
+        * tls_ia - Whether TLS/IA is enabled for this TLS connection
+        */
+       int tls_ia;
+
+       /**
+        * eap - EAP state machine allocated with eap_peer_sm_init()
+        */
+       struct eap_sm *eap;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+
+int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+                         struct eap_peer_config *config);
+void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+                            const char *label, size_t len);
+int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data,
+                               EapType eap_type, int peap_version,
+                               u8 id, const u8 *in_data, size_t in_len,
+                               struct wpabuf **out_data);
+struct wpabuf * eap_peer_tls_build_ack(u8 id, EapType eap_type,
+                                      int peap_version);
+int eap_peer_tls_reauth_init(struct eap_sm *sm, struct eap_ssl_data *data);
+int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
+                       char *buf, size_t buflen, int verbose);
+const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
+                                    struct eap_ssl_data *data,
+                                    EapType eap_type,
+                                    struct eap_method_ret *ret,
+                                    const struct wpabuf *reqData,
+                                    size_t *len, u8 *flags);
+void eap_peer_tls_reset_input(struct eap_ssl_data *data);
+void eap_peer_tls_reset_output(struct eap_ssl_data *data);
+int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **in_decrypted);
+int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data,
+                        EapType eap_type, int peap_version, u8 id,
+                        const struct wpabuf *in_data,
+                        struct wpabuf **out_data);
+int eap_peer_select_phase2_methods(struct eap_peer_config *config,
+                                  const char *prefix,
+                                  struct eap_method_type **types,
+                                  size_t *num_types);
+int eap_peer_tls_phase2_nak(struct eap_method_type *types, size_t num_types,
+                           struct eap_hdr *hdr, struct wpabuf **resp);
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/src/eap_peer/eap_tnc.c b/src/eap_peer/eap_tnc.c
new file mode 100644 (file)
index 0000000..6c95f72
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * EAP peer method: EAP-TNC (Trusted Network Connect)
+ * 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"
+
+#include "common.h"
+#include "base64.h"
+#include "eap_i.h"
+#include "tncc.h"
+
+
+struct eap_tnc_data {
+       enum { WAIT_START, PROC_MSG, WAIT_FRAG_ACK, DONE, FAIL } state;
+       struct tncc_data *tncc;
+       struct wpabuf *in_buf;
+       struct wpabuf *out_buf;
+       size_t out_used;
+       size_t fragment_size;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+       struct eap_tnc_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = WAIT_START;
+       data->fragment_size = 1300;
+       data->tncc = tncc_init();
+       if (data->tncc == NULL) {
+               os_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_tnc_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_tnc_data *data = priv;
+
+       wpabuf_free(data->in_buf);
+       wpabuf_free(data->out_buf);
+       tncc_deinit(data->tncc);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
+{
+       struct wpabuf *msg;
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
+                          "for fragment ack");
+               return NULL;
+       }
+       wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
+
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data,
+                                        struct eap_method_ret *ret, u8 id)
+{
+       struct wpabuf *resp;
+       u8 flags;
+       size_t send_len, plen;
+
+       ret->ignore = FALSE;
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Response");
+       ret->allowNotifications = TRUE;
+
+       flags = EAP_TNC_VERSION;
+       send_len = wpabuf_len(data->out_buf) - data->out_used;
+       if (1 + send_len > data->fragment_size) {
+               send_len = data->fragment_size - 1;
+               flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
+               if (data->out_used == 0) {
+                       flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
+                       send_len -= 4;
+               }
+       }
+
+       plen = 1 + send_len;
+       if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+               plen += 4;
+       resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, flags); /* Flags */
+       if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+               wpabuf_put_be32(resp, wpabuf_len(data->out_buf));
+
+       wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+                       send_len);
+       data->out_used += send_len;
+
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+
+       if (data->out_used == wpabuf_len(data->out_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->out_buf);
+               data->out_buf = NULL;
+               data->out_used = 0;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->out_buf) -
+                          data->out_used);
+               data->state = WAIT_FRAG_ACK;
+       }
+
+       return resp;
+}
+
+
+static int eap_tnc_process_cont(struct eap_tnc_data *data,
+                               const u8 *buf, size_t len)
+{
+       /* Process continuation of a pending message */
+       if (len > wpabuf_tailroom(data->in_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
+               data->state = FAIL;
+               return -1;
+       }
+
+       wpabuf_put_data(data->in_buf, buf, len);
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for "
+                  "%lu bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->in_buf));
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_tnc_process_fragment(struct eap_tnc_data *data,
+                                               struct eap_method_ret *ret,
+                                               u8 id, u8 flags,
+                                               u32 message_length,
+                                               const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
+                          "fragmented packet");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->in_buf == NULL) {
+               /* First fragment of the message */
+               data->in_buf = wpabuf_alloc(message_length);
+               if (data->in_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
+                                  "message");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               wpabuf_put_data(data->in_buf, buf, len);
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
+                          "fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->in_buf));
+       }
+
+       return eap_tnc_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_tnc_data *data = priv;
+       struct wpabuf *resp;
+       const u8 *pos, *end;
+       u8 *rpos, *rpos1;
+       size_t len, rlen;
+       size_t imc_len;
+       char *start_buf, *end_buf;
+       size_t start_len, end_len;
+       int tncs_done = 0;
+       u8 flags, id;
+       u32 message_length = 0;
+       struct wpabuf tmpbuf;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, reqData, &len);
+       if (pos == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (pos=%p len=%lu)",
+                          pos, (unsigned long) len);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       id = eap_get_id(reqData);
+
+       end = pos + len;
+
+       if (len == 0)
+               flags = 0; /* fragment ack */
+       else
+               flags = *pos++;
+
+       if (len > 0 && (flags & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
+                          flags & EAP_TNC_VERSION_MASK);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
+               if (end - pos < 4) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               message_length = WPA_GET_BE32(pos);
+               pos += 4;
+
+               if (message_length < (u32) (end - pos)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
+                                  "Length (%d; %ld remaining in this msg)",
+                                  message_length, (long) (end - pos));
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
+                  "Message Length %u", flags, message_length);
+
+       if (data->state == WAIT_FRAG_ACK) {
+               if (len > 1) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in "
+                                  "WAIT_FRAG_ACK state");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
+               data->state = PROC_MSG;
+               return eap_tnc_build_msg(data, ret, id);
+       }
+
+       if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+               
+       if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
+               return eap_tnc_process_fragment(data, ret, id, flags,
+                                               message_length, pos,
+                                               end - pos);
+       }
+
+       if (data->in_buf == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&tmpbuf, pos, end - pos);
+               data->in_buf = &tmpbuf;
+       }
+
+       if (data->state == WAIT_START) {
+               if (!(flags & EAP_TNC_FLAGS_START)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Server did not use "
+                                  "start flag in the first message");
+                       ret->ignore = TRUE;
+                       goto fail;
+               }
+
+               tncc_init_connection(data->tncc);
+
+               data->state = PROC_MSG;
+       } else {
+               enum tncc_process_res res;
+
+               if (flags & EAP_TNC_FLAGS_START) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Server used start "
+                                  "flag again");
+                       ret->ignore = TRUE;
+                       goto fail;
+               }
+
+               res = tncc_process_if_tnccs(data->tncc,
+                                           wpabuf_head(data->in_buf),
+                                           wpabuf_len(data->in_buf));
+               switch (res) {
+               case TNCCS_PROCESS_ERROR:
+                       ret->ignore = TRUE;
+                       goto fail;
+               case TNCCS_PROCESS_OK_NO_RECOMMENDATION:
+               case TNCCS_RECOMMENDATION_ERROR:
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: No "
+                                  "TNCCS-Recommendation received");
+                       break;
+               case TNCCS_RECOMMENDATION_ALLOW:
+                       wpa_msg(sm->msg_ctx, MSG_INFO,
+                               "TNC: Recommendation = allow");
+                       tncs_done = 1;
+                       break;
+               case TNCCS_RECOMMENDATION_NONE:
+                       wpa_msg(sm->msg_ctx, MSG_INFO,
+                               "TNC: Recommendation = none");
+                       tncs_done = 1;
+                       break;
+               case TNCCS_RECOMMENDATION_ISOLATE:
+                       wpa_msg(sm->msg_ctx, MSG_INFO,
+                               "TNC: Recommendation = isolate");
+                       tncs_done = 1;
+                       break;
+               }
+       }
+
+       if (data->in_buf != &tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_UNCOND_SUCC;
+       ret->allowNotifications = TRUE;
+
+       if (data->out_buf) {
+               data->state = PROC_MSG;
+               return eap_tnc_build_msg(data, ret, id);
+       }
+
+       if (tncs_done) {
+               resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1,
+                                    EAP_CODE_RESPONSE, eap_get_id(reqData));
+               if (resp == NULL)
+                       return NULL;
+
+               wpabuf_put_u8(resp, EAP_TNC_VERSION);
+               wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS done - reply with an "
+                          "empty ACK message");
+               return resp;
+       }
+
+       imc_len = tncc_total_send_len(data->tncc);
+
+       start_buf = tncc_if_tnccs_start(data->tncc);
+       if (start_buf == NULL)
+               return NULL;
+       start_len = os_strlen(start_buf);
+       end_buf = tncc_if_tnccs_end();
+       if (end_buf == NULL) {
+               os_free(start_buf);
+               return NULL;
+       }
+       end_len = os_strlen(end_buf);
+
+       rlen = start_len + imc_len + end_len;
+       resp = wpabuf_alloc(rlen);
+       if (resp == NULL) {
+               os_free(start_buf);
+               os_free(end_buf);
+               return NULL;
+       }
+
+       wpabuf_put_data(resp, start_buf, start_len);
+       os_free(start_buf);
+
+       rpos1 = wpabuf_put(resp, 0);
+       rpos = tncc_copy_send_buf(data->tncc, rpos1);
+       wpabuf_put(resp, rpos - rpos1);
+
+       wpabuf_put_data(resp, end_buf, end_len);
+       os_free(end_buf);
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Response",
+                         wpabuf_head(resp), wpabuf_len(resp));
+
+       data->out_buf = resp;
+       data->state = PROC_MSG;
+       return eap_tnc_build_msg(data, ret, id);
+
+fail:
+       if (data->in_buf == &tmpbuf)
+               data->in_buf = NULL;
+       return NULL;
+}
+
+
+int eap_peer_tnc_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_tnc_init;
+       eap->deinit = eap_tnc_deinit;
+       eap->process = eap_tnc_process;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
new file mode 100644 (file)
index 0000000..2573780
--- /dev/null
@@ -0,0 +1,1986 @@
+/*
+ * EAP peer method: EAP-TTLS (RFC 5281)
+ * 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 "crypto/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+#include "mschapv2.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_config.h"
+
+
+/* Maximum supported TTLS version
+ * 0 = RFC 5281
+ * 1 = draft-funk-eap-ttls-v1-00.txt
+ */
+#ifndef EAP_TTLS_VERSION
+#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
+#endif /* EAP_TTLS_VERSION */
+
+
+#define MSCHAPV2_KEY_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+       struct eap_ssl_data ssl;
+       int ssl_initialized;
+
+       int ttls_version, force_ttls_version;
+
+       const struct eap_method *phase2_method;
+       void *phase2_priv;
+       int phase2_success;
+       int phase2_start;
+
+       enum phase2_types {
+               EAP_TTLS_PHASE2_EAP,
+               EAP_TTLS_PHASE2_MSCHAPV2,
+               EAP_TTLS_PHASE2_MSCHAP,
+               EAP_TTLS_PHASE2_PAP,
+               EAP_TTLS_PHASE2_CHAP
+       } phase2_type;
+       struct eap_method_type phase2_eap_type;
+       struct eap_method_type *phase2_eap_types;
+       size_t num_phase2_eap_types;
+
+       u8 auth_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+       int auth_response_valid;
+       u8 master_key[MSCHAPV2_MASTER_KEY_LEN]; /* MSCHAPv2 master key */
+       u8 ident;
+       int resuming; /* starting a resumed session */
+       int reauth; /* reauthentication */
+       u8 *key_data;
+
+       struct wpabuf *pending_phase2_req;
+
+#ifdef EAP_TNC
+       int ready_for_tnc;
+       int tnc_started;
+#endif /* EAP_TNC */
+};
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+       struct eap_ttls_data *data;
+       struct eap_peer_config *config = eap_get_config(sm);
+       char *selected;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->ttls_version = EAP_TTLS_VERSION;
+       data->force_ttls_version = -1;
+       selected = "EAP";
+       data->phase2_type = EAP_TTLS_PHASE2_EAP;
+
+#if EAP_TTLS_VERSION > 0
+       if (config && config->phase1) {
+               const char *pos = os_strstr(config->phase1, "ttlsver=");
+               if (pos) {
+                       data->force_ttls_version = atoi(pos + 8);
+                       data->ttls_version = data->force_ttls_version;
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Forced TTLS version "
+                                  "%d", data->force_ttls_version);
+               }
+       }
+#endif /* EAP_TTLS_VERSION */
+
+       if (config && config->phase2) {
+               if (os_strstr(config->phase2, "autheap=")) {
+                       selected = "EAP";
+                       data->phase2_type = EAP_TTLS_PHASE2_EAP;
+               } else if (os_strstr(config->phase2, "auth=MSCHAPV2")) {
+                       selected = "MSCHAPV2";
+                       data->phase2_type = EAP_TTLS_PHASE2_MSCHAPV2;
+               } else if (os_strstr(config->phase2, "auth=MSCHAP")) {
+                       selected = "MSCHAP";
+                       data->phase2_type = EAP_TTLS_PHASE2_MSCHAP;
+               } else if (os_strstr(config->phase2, "auth=PAP")) {
+                       selected = "PAP";
+                       data->phase2_type = EAP_TTLS_PHASE2_PAP;
+               } else if (os_strstr(config->phase2, "auth=CHAP")) {
+                       selected = "CHAP";
+                       data->phase2_type = EAP_TTLS_PHASE2_CHAP;
+               }
+       }
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 type: %s", selected);
+
+       if (data->phase2_type == EAP_TTLS_PHASE2_EAP) {
+               if (eap_peer_select_phase2_methods(config, "autheap=",
+                                                  &data->phase2_eap_types,
+                                                  &data->num_phase2_eap_types)
+                   < 0) {
+                       eap_ttls_deinit(sm, data);
+                       return NULL;
+               }
+
+               data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+               data->phase2_eap_type.method = EAP_TYPE_NONE;
+       }
+
+#if EAP_TTLS_VERSION > 0
+       if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
+           data->ttls_version > 0) {
+               if (data->force_ttls_version > 0) {
+                       wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
+                                  "TLS library does not support TLS/IA.",
+                                  data->force_ttls_version);
+                       eap_ttls_deinit(sm, data);
+                       return NULL;
+               }
+               data->ttls_version = 0;
+       }
+#endif /* EAP_TTLS_VERSION */
+
+       return data;
+}
+
+
+static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
+                                      struct eap_ttls_data *data)
+{
+       if (data->phase2_priv && data->phase2_method) {
+               data->phase2_method->deinit(sm, data->phase2_priv);
+               data->phase2_method = NULL;
+               data->phase2_priv = NULL;
+       }
+}
+
+
+static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       if (data == NULL)
+               return;
+       eap_ttls_phase2_eap_deinit(sm, data);
+       os_free(data->phase2_eap_types);
+       if (data->ssl_initialized)
+               eap_peer_tls_ssl_deinit(sm, &data->ssl);
+       os_free(data->key_data);
+       wpabuf_free(data->pending_phase2_req);
+       os_free(data);
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+                            int mandatory, size_t len)
+{
+       struct ttls_avp_vendor *avp;
+       u8 flags;
+       size_t hdrlen;
+
+       avp = (struct ttls_avp_vendor *) avphdr;
+       flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+       if (vendor_id) {
+               flags |= AVP_FLAGS_VENDOR;
+               hdrlen = sizeof(*avp);
+               avp->vendor_id = host_to_be32(vendor_id);
+       } else {
+               hdrlen = sizeof(struct ttls_avp);
+       }
+
+       avp->avp_code = host_to_be32(avp_code);
+       avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+
+       return avphdr + hdrlen;
+}
+
+
+static u8 * eap_ttls_avp_add(u8 *start, u8 *avphdr, u32 avp_code,
+                            u32 vendor_id, int mandatory,
+                            const u8 *data, size_t len)
+{
+       u8 *pos;
+       pos = eap_ttls_avp_hdr(avphdr, avp_code, vendor_id, mandatory, len);
+       os_memcpy(pos, data, len);
+       pos += len;
+       AVP_PAD(start, pos);
+       return pos;
+}
+
+
+static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
+                                   int mandatory)
+{
+       struct wpabuf *msg;
+       u8 *avp, *pos;
+
+       msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
+       if (msg == NULL) {
+               wpabuf_free(*resp);
+               *resp = NULL;
+               return -1;
+       }
+
+       avp = wpabuf_mhead(msg);
+       pos = eap_ttls_avp_hdr(avp, avp_code, 0, mandatory, wpabuf_len(*resp));
+       os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
+       pos += wpabuf_len(*resp);
+       AVP_PAD(avp, pos);
+       wpabuf_free(*resp);
+       wpabuf_put(msg, pos - avp);
+       *resp = msg;
+       return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
+                                           struct eap_ttls_data *data,
+                                           const u8 *key, size_t key_len)
+{
+       u8 *buf;
+       size_t buf_len;
+       int ret;
+
+       if (key) {
+               buf_len = 2 + key_len;
+               buf = os_malloc(buf_len);
+               if (buf == NULL)
+                       return -1;
+               WPA_PUT_BE16(buf, key_len);
+               os_memcpy(buf + 2, key, key_len);
+       } else {
+               buf = NULL;
+               buf_len = 0;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
+                       "secret permutation", buf, buf_len);
+       ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
+                                                    data->ssl.conn,
+                                                    buf, buf_len);
+       os_free(buf);
+
+       return ret;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static int eap_ttls_v0_derive_key(struct eap_sm *sm,
+                                 struct eap_ttls_data *data)
+{
+       os_free(data->key_data);
+       data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
+                                                "ttls keying material",
+                                                EAP_TLS_KEY_LEN);
+       if (!data->key_data) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+                       data->key_data, EAP_TLS_KEY_LEN);
+
+       return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static int eap_ttls_v1_derive_key(struct eap_sm *sm,
+                                 struct eap_ttls_data *data)
+{
+       struct tls_keys keys;
+       u8 *rnd;
+
+       os_free(data->key_data);
+       data->key_data = NULL;
+
+       os_memset(&keys, 0, sizeof(keys));
+       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+           keys.client_random == NULL || keys.server_random == NULL ||
+           keys.inner_secret == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+                          "client random, or server random to derive keying "
+                          "material");
+               return -1;
+       }
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       data->key_data = os_malloc(EAP_TLS_KEY_LEN);
+       if (rnd == NULL || data->key_data == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
+               os_free(rnd);
+               os_free(data->key_data);
+               data->key_data = NULL;
+               return -1;
+       }
+       os_memcpy(rnd, keys.client_random, keys.client_random_len);
+       os_memcpy(rnd + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+                   "ttls v1 keying material", rnd, keys.client_random_len +
+                   keys.server_random_len, data->key_data, EAP_TLS_KEY_LEN)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+               os_free(rnd);
+               os_free(data->key_data);
+               data->key_data = NULL;
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
+                   rnd, keys.client_random_len + keys.server_random_len);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
+                       keys.inner_secret, keys.inner_secret_len);
+
+       os_free(rnd);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+                       data->key_data, EAP_TLS_KEY_LEN);
+
+       return 0;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+                                       struct eap_ttls_data *data, size_t len)
+{
+#if EAP_TTLS_VERSION > 0
+       struct tls_keys keys;
+       u8 *challenge, *rnd;
+#endif /* EAP_TTLS_VERSION */
+
+       if (data->ttls_version == 0) {
+               return eap_peer_tls_derive_key(sm, &data->ssl,
+                                              "ttls challenge", len);
+       }
+
+#if EAP_TTLS_VERSION > 0
+
+       os_memset(&keys, 0, sizeof(keys));
+       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+           keys.client_random == NULL || keys.server_random == NULL ||
+           keys.inner_secret == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+                          "client random, or server random to derive "
+                          "implicit challenge");
+               return NULL;
+       }
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       challenge = os_malloc(len);
+       if (rnd == NULL || challenge == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
+                          "challenge derivation");
+               os_free(rnd);
+               os_free(challenge);
+               return NULL;
+       }
+       os_memcpy(rnd, keys.server_random, keys.server_random_len);
+       os_memcpy(rnd + keys.server_random_len, keys.client_random,
+                 keys.client_random_len);
+
+       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+                   "inner application challenge", rnd,
+                   keys.client_random_len + keys.server_random_len,
+                   challenge, len)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
+                          "challenge");
+               os_free(rnd);
+               os_free(challenge);
+               return NULL;
+       }
+
+       os_free(rnd);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
+                       challenge, len);
+
+       return challenge;
+
+#else /* EAP_TTLS_VERSION */
+
+       return NULL;
+
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static void eap_ttlsv1_phase2_eap_finish(struct eap_sm *sm,
+                                        struct eap_ttls_data *data,
+                                        struct eap_method_ret *ret)
+{
+#if EAP_TTLS_VERSION > 0
+       if (data->ttls_version > 0) {
+               const struct eap_method *m = data->phase2_method;
+               void *priv = data->phase2_priv;
+
+               /* TTLSv1 requires TLS/IA FinalPhaseFinished */
+               if (ret->decision == DECISION_UNCOND_SUCC)
+                       ret->decision = DECISION_COND_SUCC;
+               ret->methodState = METHOD_CONT;
+
+               if (ret->decision == DECISION_COND_SUCC &&
+                   m->isKeyAvailable && m->getKey &&
+                   m->isKeyAvailable(sm, priv)) {
+                       u8 *key;
+                       size_t key_len;
+                       key = m->getKey(sm, priv, &key_len);
+                       if (key) {
+                               eap_ttls_ia_permute_inner_secret(
+                                       sm, data, key, key_len);
+                               os_free(key);
+                       }
+               }
+       }
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static void eap_ttls_phase2_select_eap_method(struct eap_ttls_data *data,
+                                             u8 method)
+{
+       size_t i;
+       for (i = 0; i < data->num_phase2_eap_types; i++) {
+               if (data->phase2_eap_types[i].vendor != EAP_VENDOR_IETF ||
+                   data->phase2_eap_types[i].method != method)
+                       continue;
+
+               data->phase2_eap_type.vendor =
+                       data->phase2_eap_types[i].vendor;
+               data->phase2_eap_type.method =
+                       data->phase2_eap_types[i].method;
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+                          "Phase 2 EAP vendor %d method %d",
+                          data->phase2_eap_type.vendor,
+                          data->phase2_eap_type.method);
+               break;
+       }
+}
+
+
+static int eap_ttls_phase2_eap_process(struct eap_sm *sm,
+                                      struct eap_ttls_data *data,
+                                      struct eap_method_ret *ret,
+                                      struct eap_hdr *hdr, size_t len,
+                                      struct wpabuf **resp)
+{
+       struct wpabuf msg;
+       struct eap_method_ret iret;
+
+       os_memset(&iret, 0, sizeof(iret));
+       wpabuf_set(&msg, hdr, len);
+       *resp = data->phase2_method->process(sm, data->phase2_priv, &iret,
+                                            &msg);
+       if ((iret.methodState == METHOD_DONE ||
+            iret.methodState == METHOD_MAY_CONT) &&
+           (iret.decision == DECISION_UNCOND_SUCC ||
+            iret.decision == DECISION_COND_SUCC ||
+            iret.decision == DECISION_FAIL)) {
+               ret->methodState = iret.methodState;
+               ret->decision = iret.decision;
+       }
+       eap_ttlsv1_phase2_eap_finish(sm, data, ret);
+
+       return 0;
+}
+
+
+static int eap_ttls_phase2_request_eap_method(struct eap_sm *sm,
+                                             struct eap_ttls_data *data,
+                                             struct eap_method_ret *ret,
+                                             struct eap_hdr *hdr, size_t len,
+                                             u8 method, struct wpabuf **resp)
+{
+#ifdef EAP_TNC
+       if (data->tnc_started && data->phase2_method &&
+           data->phase2_priv && method == EAP_TYPE_TNC &&
+           data->phase2_eap_type.method == EAP_TYPE_TNC)
+               return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len,
+                                                  resp);
+
+       if (data->ready_for_tnc && !data->tnc_started &&
+           method == EAP_TYPE_TNC) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+                          "EAP method");
+               data->tnc_started = 1;
+       }
+
+       if (data->tnc_started) {
+               if (data->phase2_eap_type.vendor != EAP_VENDOR_IETF ||
+                   data->phase2_eap_type.method == EAP_TYPE_TNC) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected EAP "
+                                  "type %d for TNC", method);
+                       return -1;
+               }
+
+               data->phase2_eap_type.vendor = EAP_VENDOR_IETF;
+               data->phase2_eap_type.method = method;
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Selected "
+                          "Phase 2 EAP vendor %d method %d (TNC)",
+                          data->phase2_eap_type.vendor,
+                          data->phase2_eap_type.method);
+
+               if (data->phase2_type == EAP_TTLS_PHASE2_EAP)
+                       eap_ttls_phase2_eap_deinit(sm, data);
+       }
+#endif /* EAP_TNC */
+
+       if (data->phase2_eap_type.vendor == EAP_VENDOR_IETF &&
+           data->phase2_eap_type.method == EAP_TYPE_NONE)
+               eap_ttls_phase2_select_eap_method(data, method);
+
+       if (method != data->phase2_eap_type.method || method == EAP_TYPE_NONE)
+       {
+               if (eap_peer_tls_phase2_nak(data->phase2_eap_types,
+                                           data->num_phase2_eap_types,
+                                           hdr, resp))
+                       return -1;
+               return 0;
+       }
+
+       if (data->phase2_priv == NULL) {
+               data->phase2_method = eap_peer_get_eap_method(
+                       EAP_VENDOR_IETF, method);
+               if (data->phase2_method) {
+                       sm->init_phase2 = 1;
+                       data->phase2_priv = data->phase2_method->init(sm);
+                       sm->init_phase2 = 0;
+               }
+       }
+       if (data->phase2_priv == NULL || data->phase2_method == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: failed to initialize "
+                          "Phase 2 EAP method %d", method);
+               return -1;
+       }
+
+       return eap_ttls_phase2_eap_process(sm, data, ret, hdr, len, resp);
+}
+
+
+static int eap_ttls_phase2_request_eap(struct eap_sm *sm,
+                                      struct eap_ttls_data *data,
+                                      struct eap_method_ret *ret,
+                                      struct eap_hdr *hdr,
+                                      struct wpabuf **resp)
+{
+       size_t len = be_to_host16(hdr->length);
+       u8 *pos;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       if (len <= sizeof(struct eap_hdr)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: too short "
+                          "Phase 2 request (len=%lu)", (unsigned long) len);
+               return -1;
+       }
+       pos = (u8 *) (hdr + 1);
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP Request: type=%d", *pos);
+       switch (*pos) {
+       case EAP_TYPE_IDENTITY:
+               *resp = eap_sm_buildIdentity(sm, hdr->identifier, 1);
+               break;
+       default:
+               if (eap_ttls_phase2_request_eap_method(sm, data, ret, hdr, len,
+                                                      *pos, resp) < 0)
+                       return -1;
+               break;
+       }
+
+       if (*resp == NULL &&
+           (config->pending_req_identity || config->pending_req_password ||
+            config->pending_req_otp)) {
+               return 0;
+       }
+
+       if (*resp == NULL)
+               return -1;
+
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-TTLS: AVP encapsulate EAP Response",
+                       *resp);
+       return eap_ttls_avp_encapsulate(resp, RADIUS_ATTR_EAP_MESSAGE, 1);
+}
+
+
+static void eap_ttlsv1_permute_inner(struct eap_sm *sm,
+                                    struct eap_ttls_data *data)
+{
+#if EAP_TTLS_VERSION > 0
+       u8 session_key[2 * MSCHAPV2_KEY_LEN];
+
+       if (data->ttls_version == 0)
+               return;
+
+       get_asymetric_start_key(data->master_key, session_key,
+                               MSCHAPV2_KEY_LEN, 0, 0);
+       get_asymetric_start_key(data->master_key,
+                               session_key + MSCHAPV2_KEY_LEN,
+                               MSCHAPV2_KEY_LEN, 1, 0);
+       eap_ttls_ia_permute_inner_secret(sm, data, session_key,
+                                        sizeof(session_key));
+#endif /* EAP_TTLS_VERSION */
+}
+
+
+static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
+                                           struct eap_ttls_data *data,
+                                           struct eap_method_ret *ret,
+                                           struct wpabuf **resp)
+{
+       struct wpabuf *msg;
+       u8 *buf, *pos, *challenge, *peer_challenge;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+       int pwhash;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAPV2 Request");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (identity == NULL || password == NULL)
+               return -1;
+
+       msg = wpabuf_alloc(identity_len + 1000);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "EAP-TTLS/MSCHAPV2: Failed to allocate memory");
+               return -1;
+       }
+       pos = buf = wpabuf_mhead(msg);
+
+       /* User-Name */
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+                              identity, identity_len);
+
+       /* MS-CHAP-Challenge */
+       challenge = eap_ttls_implicit_challenge(
+               sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+       if (challenge == NULL) {
+               wpabuf_free(msg);
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+                          "implicit challenge");
+               return -1;
+       }
+       peer_challenge = challenge + 1 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,
+                              challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+       /* MS-CHAP2-Response */
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_RESPONSE,
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,
+                              EAP_TTLS_MSCHAPV2_RESPONSE_LEN);
+       data->ident = challenge[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN];
+       *pos++ = data->ident;
+       *pos++ = 0; /* Flags */
+       os_memcpy(pos, peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+       pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN;
+       os_memset(pos, 0, 8); /* Reserved, must be zero */
+       pos += 8;
+       if (mschapv2_derive_response(identity, identity_len, password,
+                                    password_len, pwhash, challenge,
+                                    peer_challenge, pos, data->auth_response,
+                                    data->master_key)) {
+               wpabuf_free(msg);
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
+                          "response");
+               return -1;
+       }
+       data->auth_response_valid = 1;
+
+       eap_ttlsv1_permute_inner(sm, data);
+
+       pos += 24;
+       os_free(challenge);
+       AVP_PAD(buf, pos);
+
+       wpabuf_put(msg, pos - buf);
+       *resp = msg;
+
+       if (sm->workaround && data->ttls_version == 0) {
+               /* At least FreeRADIUS seems to be terminating
+                * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
+                * packet. */
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
+                          "allow success without tunneled response");
+               ret->methodState = METHOD_MAY_CONT;
+               ret->decision = DECISION_COND_SUCC;
+       }
+
+       return 0;
+}
+
+
+static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
+                                         struct eap_ttls_data *data,
+                                         struct eap_method_ret *ret,
+                                         struct wpabuf **resp)
+{
+       struct wpabuf *msg;
+       u8 *buf, *pos, *challenge;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+       int pwhash;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 MSCHAP Request");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password2(sm, &password_len, &pwhash);
+       if (identity == NULL || password == NULL)
+               return -1;
+
+       msg = wpabuf_alloc(identity_len + 1000);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "EAP-TTLS/MSCHAP: Failed to allocate memory");
+               return -1;
+       }
+       pos = buf = wpabuf_mhead(msg);
+
+       /* User-Name */
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+                              identity, identity_len);
+
+       /* MS-CHAP-Challenge */
+       challenge = eap_ttls_implicit_challenge(
+               sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+       if (challenge == NULL) {
+               wpabuf_free(msg);
+               wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
+                          "implicit challenge");
+               return -1;
+       }
+
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_MS_CHAP_CHALLENGE,
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,
+                              challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+
+       /* MS-CHAP-Response */
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_RESPONSE,
+                              RADIUS_VENDOR_ID_MICROSOFT, 1,
+                              EAP_TTLS_MSCHAP_RESPONSE_LEN);
+       data->ident = challenge[EAP_TTLS_MSCHAP_CHALLENGE_LEN];
+       *pos++ = data->ident;
+       *pos++ = 1; /* Flags: Use NT style passwords */
+       os_memset(pos, 0, 24); /* LM-Response */
+       pos += 24;
+       if (pwhash) {
+               challenge_response(challenge, password, pos); /* NT-Response */
+               wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password hash",
+                               password, 16);
+       } else {
+               nt_challenge_response(challenge, password, password_len,
+                                     pos); /* NT-Response */
+               wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: MSCHAP password",
+                                     password, password_len);
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP implicit challenge",
+                   challenge, EAP_TTLS_MSCHAP_CHALLENGE_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: MSCHAP response", pos, 24);
+       pos += 24;
+       os_free(challenge);
+       AVP_PAD(buf, pos);
+
+       wpabuf_put(msg, pos - buf);
+       *resp = msg;
+
+       if (data->ttls_version > 0) {
+               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+                * so do not allow connection to be terminated yet. */
+               ret->methodState = METHOD_CONT;
+               ret->decision = DECISION_COND_SUCC;
+       } else {
+               /* EAP-TTLS/MSCHAP does not provide tunneled success
+                * notification, so assume that Phase2 succeeds. */
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_COND_SUCC;
+       }
+
+       return 0;
+}
+
+
+static int eap_ttls_phase2_request_pap(struct eap_sm *sm,
+                                      struct eap_ttls_data *data,
+                                      struct eap_method_ret *ret,
+                                      struct wpabuf **resp)
+{
+       struct wpabuf *msg;
+       u8 *buf, *pos;
+       size_t pad;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 PAP Request");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password(sm, &password_len);
+       if (identity == NULL || password == NULL)
+               return -1;
+
+       msg = wpabuf_alloc(identity_len + password_len + 100);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "EAP-TTLS/PAP: Failed to allocate memory");
+               return -1;
+       }
+       pos = buf = wpabuf_mhead(msg);
+
+       /* User-Name */
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+                              identity, identity_len);
+
+       /* User-Password; in RADIUS, this is encrypted, but EAP-TTLS encrypts
+        * the data, so no separate encryption is used in the AVP itself.
+        * However, the password is padded to obfuscate its length. */
+       pad = password_len == 0 ? 16 : (16 - (password_len & 15)) & 15;
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_USER_PASSWORD, 0, 1,
+                              password_len + pad);
+       os_memcpy(pos, password, password_len);
+       pos += password_len;
+       os_memset(pos, 0, pad);
+       pos += pad;
+       AVP_PAD(buf, pos);
+
+       wpabuf_put(msg, pos - buf);
+       *resp = msg;
+
+       if (data->ttls_version > 0) {
+               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+                * so do not allow connection to be terminated yet. */
+               ret->methodState = METHOD_CONT;
+               ret->decision = DECISION_COND_SUCC;
+       } else {
+               /* EAP-TTLS/PAP does not provide tunneled success notification,
+                * so assume that Phase2 succeeds. */
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_COND_SUCC;
+       }
+
+       return 0;
+}
+
+
+static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
+                                       struct eap_ttls_data *data,
+                                       struct eap_method_ret *ret,
+                                       struct wpabuf **resp)
+{
+       struct wpabuf *msg;
+       u8 *buf, *pos, *challenge;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase 2 CHAP Request");
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       password = eap_get_config_password(sm, &password_len);
+       if (identity == NULL || password == NULL)
+               return -1;
+
+       msg = wpabuf_alloc(identity_len + 1000);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "EAP-TTLS/CHAP: Failed to allocate memory");
+               return -1;
+       }
+       pos = buf = wpabuf_mhead(msg);
+
+       /* User-Name */
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_USER_NAME, 0, 1,
+                              identity, identity_len);
+
+       /* CHAP-Challenge */
+       challenge = eap_ttls_implicit_challenge(
+               sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+       if (challenge == NULL) {
+               wpabuf_free(msg);
+               wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
+                          "implicit challenge");
+               return -1;
+       }
+
+       pos = eap_ttls_avp_add(buf, pos, RADIUS_ATTR_CHAP_CHALLENGE, 0, 1,
+                              challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+
+       /* CHAP-Password */
+       pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_CHAP_PASSWORD, 0, 1,
+                              1 + EAP_TTLS_CHAP_PASSWORD_LEN);
+       data->ident = challenge[EAP_TTLS_CHAP_CHALLENGE_LEN];
+       *pos++ = data->ident;
+
+       /* MD5(Ident + Password + Challenge) */
+       chap_md5(data->ident, password, password_len, challenge,
+                EAP_TTLS_CHAP_CHALLENGE_LEN, pos);
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: CHAP username",
+                         identity, identity_len);
+       wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: CHAP password",
+                             password, password_len);
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP implicit challenge",
+                   challenge, EAP_TTLS_CHAP_CHALLENGE_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: CHAP password",
+                   pos, EAP_TTLS_CHAP_PASSWORD_LEN);
+       pos += EAP_TTLS_CHAP_PASSWORD_LEN;
+       os_free(challenge);
+       AVP_PAD(buf, pos);
+
+       wpabuf_put(msg, pos - buf);
+       *resp = msg;
+
+       if (data->ttls_version > 0) {
+               /* EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report success,
+                * so do not allow connection to be terminated yet. */
+               ret->methodState = METHOD_CONT;
+               ret->decision = DECISION_COND_SUCC;
+       } else {
+               /* EAP-TTLS/CHAP does not provide tunneled success
+                * notification, so assume that Phase2 succeeds. */
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_COND_SUCC;
+       }
+
+       return 0;
+}
+
+
+static int eap_ttls_phase2_request(struct eap_sm *sm,
+                                  struct eap_ttls_data *data,
+                                  struct eap_method_ret *ret,
+                                  struct eap_hdr *hdr,
+                                  struct wpabuf **resp)
+{
+       int res = 0;
+       size_t len;
+       enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+       if (data->tnc_started) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Processing TNC");
+               phase2_type = EAP_TTLS_PHASE2_EAP;
+       }
+#endif /* EAP_TNC */
+
+       if (phase2_type == EAP_TTLS_PHASE2_MSCHAPV2 ||
+           phase2_type == EAP_TTLS_PHASE2_MSCHAP ||
+           phase2_type == EAP_TTLS_PHASE2_PAP ||
+           phase2_type == EAP_TTLS_PHASE2_CHAP) {
+               if (eap_get_config_identity(sm, &len) == NULL) {
+                       wpa_printf(MSG_INFO,
+                                  "EAP-TTLS: Identity not configured");
+                       eap_sm_request_identity(sm);
+                       if (eap_get_config_password(sm, &len) == NULL)
+                               eap_sm_request_password(sm);
+                       return 0;
+               }
+
+               if (eap_get_config_password(sm, &len) == NULL) {
+                       wpa_printf(MSG_INFO,
+                                  "EAP-TTLS: Password not configured");
+                       eap_sm_request_password(sm);
+                       return 0;
+               }
+       }
+
+       switch (phase2_type) {
+       case EAP_TTLS_PHASE2_EAP:
+               res = eap_ttls_phase2_request_eap(sm, data, ret, hdr, resp);
+               break;
+       case EAP_TTLS_PHASE2_MSCHAPV2:
+               res = eap_ttls_phase2_request_mschapv2(sm, data, ret, resp);
+               break;
+       case EAP_TTLS_PHASE2_MSCHAP:
+               res = eap_ttls_phase2_request_mschap(sm, data, ret, resp);
+               break;
+       case EAP_TTLS_PHASE2_PAP:
+               res = eap_ttls_phase2_request_pap(sm, data, ret, resp);
+               break;
+       case EAP_TTLS_PHASE2_CHAP:
+               res = eap_ttls_phase2_request_chap(sm, data, ret, resp);
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Phase 2 - Unknown");
+               res = -1;
+               break;
+       }
+
+       if (res < 0) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+       }
+
+       return res;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static struct wpabuf * eap_ttls_build_phase_finished(
+       struct eap_sm *sm, struct eap_ttls_data *data, int id, int final)
+{
+       struct wpabuf *req, *buf;
+
+       buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx,
+                                                   data->ssl.conn,
+                                                   final);
+       if (buf == NULL)
+               return NULL;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS,
+                           1 + wpabuf_len(buf),
+                           EAP_CODE_RESPONSE, id);
+       if (req == NULL) {
+               wpabuf_free(buf);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, data->ttls_version);
+       wpabuf_put_buf(req, buf);
+       wpabuf_free(buf);
+       eap_update_len(req);
+
+       return req;
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+struct ttls_parse_avp {
+       u8 *mschapv2;
+       u8 *eapdata;
+       size_t eap_len;
+       int mschapv2_error;
+};
+
+
+static int eap_ttls_parse_attr_eap(const u8 *dpos, size_t dlen,
+                                  struct ttls_parse_avp *parse)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+       if (parse->eapdata == NULL) {
+               parse->eapdata = os_malloc(dlen);
+               if (parse->eapdata == NULL) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+                                  "memory for Phase 2 EAP data");
+                       return -1;
+               }
+               os_memcpy(parse->eapdata, dpos, dlen);
+               parse->eap_len = dlen;
+       } else {
+               u8 *neweap = os_realloc(parse->eapdata, parse->eap_len + dlen);
+               if (neweap == NULL) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to allocate "
+                                  "memory for Phase 2 EAP data");
+                       return -1;
+               }
+               os_memcpy(neweap + parse->eap_len, dpos, dlen);
+               parse->eapdata = neweap;
+               parse->eap_len += dlen;
+       }
+
+       return 0;
+}
+
+
+static int eap_ttls_parse_avp(u8 *pos, size_t left,
+                             struct ttls_parse_avp *parse)
+{
+       struct ttls_avp *avp;
+       u32 avp_code, avp_length, vendor_id = 0;
+       u8 avp_flags, *dpos;
+       size_t dlen;
+
+       avp = (struct ttls_avp *) pos;
+       avp_code = be_to_host32(avp->avp_code);
+       avp_length = be_to_host32(avp->avp_length);
+       avp_flags = (avp_length >> 24) & 0xff;
+       avp_length &= 0xffffff;
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+                  "length=%d", (int) avp_code, avp_flags,
+                  (int) avp_length);
+
+       if (avp_length > left) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+                          "(len=%d, left=%lu) - dropped",
+                          (int) avp_length, (unsigned long) left);
+               return -1;
+       }
+
+       if (avp_length < sizeof(*avp)) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length %d",
+                          avp_length);
+               return -1;
+       }
+
+       dpos = (u8 *) (avp + 1);
+       dlen = avp_length - sizeof(*avp);
+       if (avp_flags & AVP_FLAGS_VENDOR) {
+               if (dlen < 4) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Vendor AVP "
+                                  "underflow");
+                       return -1;
+               }
+               vendor_id = WPA_GET_BE32(dpos);
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+                          (int) vendor_id);
+               dpos += 4;
+               dlen -= 4;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+       if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+               if (eap_ttls_parse_attr_eap(dpos, dlen, parse) < 0)
+                       return -1;
+       } else if (vendor_id == 0 && avp_code == RADIUS_ATTR_REPLY_MESSAGE) {
+               /* This is an optional message that can be displayed to
+                * the user. */
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: AVP - Reply-Message",
+                                 dpos, dlen);
+       } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+                  avp_code == RADIUS_ATTR_MS_CHAP2_SUCCESS) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP2-Success",
+                                 dpos, dlen);
+               if (dlen != 43) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Unexpected "
+                                  "MS-CHAP2-Success length "
+                                  "(len=%lu, expected 43)",
+                                  (unsigned long) dlen);
+                       return -1;
+               }
+               parse->mschapv2 = dpos;
+       } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+                  avp_code == RADIUS_ATTR_MS_CHAP_ERROR) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: MS-CHAP-Error",
+                                 dpos, dlen);
+               parse->mschapv2_error = 1;
+       } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported mandatory AVP "
+                          "code %d vendor_id %d - dropped",
+                          (int) avp_code, (int) vendor_id);
+               return -1;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported AVP "
+                          "code %d vendor_id %d",
+                          (int) avp_code, (int) vendor_id);
+       }
+
+       return avp_length;
+}
+
+
+static int eap_ttls_parse_avps(struct wpabuf *in_decrypted,
+                              struct ttls_parse_avp *parse)
+{
+       u8 *pos;
+       size_t left, pad;
+       int avp_length;
+
+       pos = wpabuf_mhead(in_decrypted);
+       left = wpabuf_len(in_decrypted);
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 AVPs", pos, left);
+       if (left < sizeof(struct ttls_avp)) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 AVP frame"
+                          " len=%lu expected %lu or more - dropped",
+                          (unsigned long) left,
+                          (unsigned long) sizeof(struct ttls_avp));
+               return -1;
+       }
+
+       /* Parse AVPs */
+       os_memset(parse, 0, sizeof(*parse));
+
+       while (left > 0) {
+               avp_length = eap_ttls_parse_avp(pos, left, parse);
+               if (avp_length < 0)
+                       return -1;
+
+               pad = (4 - (avp_length & 3)) & 3;
+               pos += avp_length + pad;
+               if (left < avp_length + pad)
+                       left = 0;
+               else
+                       left -= avp_length + pad;
+       }
+
+       return 0;
+}
+
+
+static u8 * eap_ttls_fake_identity_request(void)
+{
+       struct eap_hdr *hdr;
+       u8 *buf;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: empty data in beginning of "
+                  "Phase 2 - use fake EAP-Request Identity");
+       buf = os_malloc(sizeof(*hdr) + 1);
+       if (buf == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate "
+                          "memory for fake EAP-Identity Request");
+               return NULL;
+       }
+
+       hdr = (struct eap_hdr *) buf;
+       hdr->code = EAP_CODE_REQUEST;
+       hdr->identifier = 0;
+       hdr->length = host_to_be16(sizeof(*hdr) + 1);
+       buf[sizeof(*hdr)] = EAP_TYPE_IDENTITY;
+
+       return buf;
+}
+
+
+static int eap_ttls_encrypt_response(struct eap_sm *sm,
+                                    struct eap_ttls_data *data,
+                                    struct wpabuf *resp, u8 identifier,
+                                    struct wpabuf **out_data)
+{
+       if (resp == NULL)
+               return 0;
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Encrypting Phase 2 data",
+                           resp);
+       if (eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+                                data->ttls_version, identifier,
+                                resp, out_data)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
+                          "frame");
+               return -1;
+       }
+       wpabuf_free(resp);
+
+       return 0;
+}
+
+
+static int eap_ttls_process_phase2_eap(struct eap_sm *sm,
+                                      struct eap_ttls_data *data,
+                                      struct eap_method_ret *ret,
+                                      struct ttls_parse_avp *parse,
+                                      struct wpabuf **resp)
+{
+       struct eap_hdr *hdr;
+       size_t len;
+
+       if (parse->eapdata == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: No EAP Message in the "
+                          "packet - dropped");
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Phase 2 EAP",
+                   parse->eapdata, parse->eap_len);
+       hdr = (struct eap_hdr *) parse->eapdata;
+
+       if (parse->eap_len < sizeof(*hdr)) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Too short Phase 2 EAP "
+                          "frame (len=%lu, expected %lu or more) - dropped",
+                          (unsigned long) parse->eap_len,
+                          (unsigned long) sizeof(*hdr));
+               return -1;
+       }
+       len = be_to_host16(hdr->length);
+       if (len > parse->eap_len) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Length mismatch in Phase 2 "
+                          "EAP frame (EAP hdr len=%lu, EAP data len in "
+                          "AVP=%lu)",
+                          (unsigned long) len,
+                          (unsigned long) parse->eap_len);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: received Phase 2: code=%d "
+                  "identifier=%d length=%lu",
+                  hdr->code, hdr->identifier, (unsigned long) len);
+       switch (hdr->code) {
+       case EAP_CODE_REQUEST:
+               if (eap_ttls_phase2_request(sm, data, ret, hdr, resp)) {
+                       wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+                                  "processing failed");
+                       return -1;
+               }
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-TTLS: Unexpected code=%d in "
+                          "Phase 2 EAP header", hdr->code);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+                                           struct eap_ttls_data *data,
+                                           struct eap_method_ret *ret,
+                                           struct ttls_parse_avp *parse)
+{
+       if (parse->mschapv2_error) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Received "
+                          "MS-CHAP-Error - failed");
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               /* Reply with empty data to ACK error */
+               return 1;
+       }
+
+       if (parse->mschapv2 == NULL) {
+#ifdef EAP_TNC
+               if (data->phase2_success && parse->eapdata) {
+                       /*
+                        * Allow EAP-TNC to be started after successfully
+                        * completed MSCHAPV2.
+                        */
+                       return 1;
+               }
+#endif /* EAP_TNC */
+               wpa_printf(MSG_WARNING, "EAP-TTLS: no MS-CHAP2-Success AVP "
+                          "received for Phase2 MSCHAPV2");
+               return -1;
+       }
+       if (parse->mschapv2[0] != data->ident) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Ident mismatch for Phase 2 "
+                          "MSCHAPV2 (received Ident 0x%02x, expected 0x%02x)",
+                          parse->mschapv2[0], data->ident);
+               return -1;
+       }
+       if (!data->auth_response_valid ||
+           mschapv2_verify_auth_response(data->auth_response,
+                                         parse->mschapv2 + 1, 42)) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid authenticator "
+                          "response in Phase 2 MSCHAPV2 success request");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 MSCHAPV2 "
+                  "authentication succeeded");
+       if (data->ttls_version > 0) {
+               /*
+                * EAP-TTLSv1 uses TLS/IA FinalPhaseFinished to report
+                * success, so do not allow connection to be terminated
+                * yet.
+                */
+               ret->methodState = METHOD_CONT;
+               ret->decision = DECISION_COND_SUCC;
+       } else {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_UNCOND_SUCC;
+               data->phase2_success = 1;
+       }
+
+       /*
+        * Reply with empty data; authentication server will reply
+        * with EAP-Success after this.
+        */
+       return 1;
+}
+
+
+#ifdef EAP_TNC
+static int eap_ttls_process_tnc_start(struct eap_sm *sm,
+                                     struct eap_ttls_data *data,
+                                     struct eap_method_ret *ret,
+                                     struct ttls_parse_avp *parse,
+                                     struct wpabuf **resp)
+{
+       /* TNC uses inner EAP method after non-EAP TTLS phase 2. */
+       if (parse->eapdata == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+                          "unexpected tunneled data (no EAP)");
+               return -1;
+       }
+
+       if (!data->ready_for_tnc) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received "
+                          "EAP after non-EAP, but not ready for TNC");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start TNC after completed "
+                  "non-EAP method");
+       data->tnc_started = 1;
+
+       if (eap_ttls_process_phase2_eap(sm, data, ret, parse, resp) < 0)
+               return -1;
+
+       return 0;
+}
+#endif /* EAP_TNC */
+
+
+static int eap_ttls_process_decrypted(struct eap_sm *sm,
+                                     struct eap_ttls_data *data,
+                                     struct eap_method_ret *ret,
+                                     u8 identifier,
+                                     struct ttls_parse_avp *parse,
+                                     struct wpabuf *in_decrypted,
+                                     struct wpabuf **out_data)
+{
+       struct wpabuf *resp = NULL;
+       struct eap_peer_config *config = eap_get_config(sm);
+       int res;
+       enum phase2_types phase2_type = data->phase2_type;
+
+#ifdef EAP_TNC
+       if (data->tnc_started)
+               phase2_type = EAP_TTLS_PHASE2_EAP;
+#endif /* EAP_TNC */
+
+       switch (phase2_type) {
+       case EAP_TTLS_PHASE2_EAP:
+               if (eap_ttls_process_phase2_eap(sm, data, ret, parse, &resp) <
+                   0)
+                       return -1;
+               break;
+       case EAP_TTLS_PHASE2_MSCHAPV2:
+               res = eap_ttls_process_phase2_mschapv2(sm, data, ret, parse);
+#ifdef EAP_TNC
+               if (res == 1 && parse->eapdata && data->phase2_success) {
+                       /*
+                        * TNC may be required as the next
+                        * authentication method within the tunnel.
+                        */
+                       ret->methodState = METHOD_MAY_CONT;
+                       data->ready_for_tnc = 1;
+                       if (eap_ttls_process_tnc_start(sm, data, ret, parse,
+                                                      &resp) == 0)
+                               break;
+               }
+#endif /* EAP_TNC */
+               return res;
+       case EAP_TTLS_PHASE2_MSCHAP:
+       case EAP_TTLS_PHASE2_PAP:
+       case EAP_TTLS_PHASE2_CHAP:
+#ifdef EAP_TNC
+               if (eap_ttls_process_tnc_start(sm, data, ret, parse, &resp) <
+                   0)
+                       return -1;
+               break;
+#else /* EAP_TNC */
+               /* EAP-TTLS/{MSCHAP,PAP,CHAP} should not send any TLS tunneled
+                * requests to the supplicant */
+               wpa_printf(MSG_INFO, "EAP-TTLS: Phase 2 received unexpected "
+                          "tunneled data");
+               return -1;
+#endif /* EAP_TNC */
+       }
+
+       if (resp) {
+               if (eap_ttls_encrypt_response(sm, data, resp, identifier,
+                                             out_data) < 0)
+                       return -1;
+       } else if (config->pending_req_identity ||
+                  config->pending_req_password ||
+                  config->pending_req_otp ||
+                  config->pending_req_new_password) {
+               wpabuf_free(data->pending_phase2_req);
+               data->pending_phase2_req = wpabuf_dup(in_decrypted);
+       }
+
+       return 0;
+}
+
+
+#if EAP_TTLS_VERSION > 0
+static void eap_ttls_final_phase_finished(struct eap_sm *sm,
+                                         struct eap_ttls_data *data,
+                                         struct eap_method_ret *ret,
+                                         u8 identifier,
+                                         struct wpabuf **out_data)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished received");
+       wpa_printf(MSG_INFO, "EAP-TTLS: TLS/IA authentication succeeded");
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_UNCOND_SUCC;
+       data->phase2_success = 1;
+       *out_data = eap_ttls_build_phase_finished(sm, data, identifier, 1);
+       eap_ttls_v1_derive_key(sm, data);
+}
+#endif /* EAP_TTLS_VERSION */
+
+
+static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
+                                             struct eap_ttls_data *data,
+                                             struct eap_method_ret *ret,
+                                             u8 identifier,
+                                             struct wpabuf **out_data)
+{
+       int retval = 0;
+       struct eap_hdr *hdr;
+       struct wpabuf *resp;
+
+       hdr = (struct eap_hdr *) eap_ttls_fake_identity_request();
+       if (hdr == NULL) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               return -1;
+       }
+
+       resp = NULL;
+       if (eap_ttls_phase2_request(sm, data, ret, hdr, &resp)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Phase2 Request "
+                          "processing failed");
+               retval = -1;
+       } else {
+               retval = eap_ttls_encrypt_response(sm, data, resp, identifier,
+                                                  out_data);
+       }
+
+       os_free(hdr);
+
+       if (retval < 0) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+       }
+
+       return retval;
+}
+
+
+static int eap_ttls_phase2_start(struct eap_sm *sm, struct eap_ttls_data *data,
+                                struct eap_method_ret *ret, u8 identifier,
+                                struct wpabuf **out_data)
+{
+       data->phase2_start = 0;
+
+       /*
+        * EAP-TTLS does not use Phase2 on fast re-auth; this must be done only
+        * if TLS part was indeed resuming a previous session. Most
+        * Authentication Servers terminate EAP-TTLS before reaching this
+        * point, but some do not. Make wpa_supplicant stop phase 2 here, if
+        * needed.
+        */
+       if (data->reauth &&
+           tls_connection_resumed(sm->ssl_ctx, data->ssl.conn)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Session resumption - "
+                          "skip phase 2");
+               *out_data = eap_peer_tls_build_ack(identifier, EAP_TYPE_TTLS,
+                                                  data->ttls_version);
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_UNCOND_SUCC;
+               data->phase2_success = 1;
+               return 0;
+       }
+
+       return eap_ttls_implicit_identity_request(sm, data, ret, identifier,
+                                                 out_data);
+}
+
+
+static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
+                           struct eap_method_ret *ret, u8 identifier,
+                           const struct wpabuf *in_data,
+                           struct wpabuf **out_data)
+{
+       struct wpabuf *in_decrypted = NULL;
+       int retval = 0;
+       struct ttls_parse_avp parse;
+
+       os_memset(&parse, 0, sizeof(parse));
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+                  " Phase 2",
+                  in_data ? (unsigned long) wpabuf_len(in_data) : 0);
+
+       if (data->pending_phase2_req) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 request - "
+                          "skip decryption and use old data");
+               /* Clear TLS reassembly state. */
+               eap_peer_tls_reset_input(&data->ssl);
+
+               in_decrypted = data->pending_phase2_req;
+               data->pending_phase2_req = NULL;
+               if (wpabuf_len(in_decrypted) == 0) {
+                       wpabuf_free(in_decrypted);
+                       return eap_ttls_implicit_identity_request(
+                               sm, data, ret, identifier, out_data);
+               }
+               goto continue_req;
+       }
+
+       if ((in_data == NULL || wpabuf_len(in_data) == 0) &&
+           data->phase2_start) {
+               return eap_ttls_phase2_start(sm, data, ret, identifier,
+                                            out_data);
+       }
+
+       if (in_data == NULL || wpabuf_len(in_data) == 0) {
+               /* Received TLS ACK - requesting more fragments */
+               return eap_peer_tls_encrypt(sm, &data->ssl, EAP_TYPE_TTLS,
+                                           data->ttls_version,
+                                           identifier, NULL, out_data);
+       }
+
+       retval = eap_peer_tls_decrypt(sm, &data->ssl, in_data, &in_decrypted);
+       if (retval)
+               goto done;
+
+#if EAP_TTLS_VERSION > 0
+       if (data->ttls_version > 0 &&
+           (in_decrypted == NULL || wpabuf_len(in_decrypted) == 0) &&
+           tls_connection_ia_final_phase_finished(sm->ssl_ctx,
+                                                  data->ssl.conn)) {
+               eap_ttls_final_phase_finished(sm, data, ret, identifier,
+                                             out_data);
+               goto done;
+       }
+#endif /* EAP_TTLS_VERSION */
+
+continue_req:
+       data->phase2_start = 0;
+
+       if (eap_ttls_parse_avps(in_decrypted, &parse) < 0) {
+               retval = -1;
+               goto done;
+       }
+
+       retval = eap_ttls_process_decrypted(sm, data, ret, identifier,
+                                           &parse, in_decrypted, out_data);
+
+done:
+       wpabuf_free(in_decrypted);
+       os_free(parse.eapdata);
+
+       if (retval < 0) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+       }
+
+       return retval;
+}
+
+
+static int eap_ttls_process_start(struct eap_sm *sm,
+                                 struct eap_ttls_data *data, u8 flags,
+                                 struct eap_method_ret *ret)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)",
+                  flags & EAP_TLS_VERSION_MASK, data->ttls_version);
+#if EAP_TTLS_VERSION > 0
+       if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version)
+               data->ttls_version = flags & EAP_TLS_VERSION_MASK;
+       if (data->force_ttls_version >= 0 &&
+           data->force_ttls_version != data->ttls_version) {
+               wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select "
+                          "forced TTLS version %d",
+                          data->force_ttls_version);
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               ret->allowNotifications = FALSE;
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Using TTLS version %d",
+                  data->ttls_version);
+
+       if (data->ttls_version > 0)
+               data->ssl.tls_ia = 1;
+#endif /* EAP_TTLS_VERSION */
+       if (!data->ssl_initialized &&
+           eap_peer_tls_ssl_init(sm, &data->ssl, config)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+               return -1;
+       }
+       data->ssl_initialized = 1;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Start");
+
+       return 0;
+}
+
+
+static int eap_ttls_process_handshake(struct eap_sm *sm,
+                                     struct eap_ttls_data *data,
+                                     struct eap_method_ret *ret,
+                                     u8 identifier,
+                                     const u8 *in_data, size_t in_len,
+                                     struct wpabuf **out_data)
+{
+       int res;
+
+       res = eap_peer_tls_process_helper(sm, &data->ssl, EAP_TYPE_TTLS,
+                                         data->ttls_version, identifier,
+                                         in_data, in_len, out_data);
+
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: TLS done, proceed to "
+                          "Phase 2");
+               if (data->resuming) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: fast reauth - may "
+                                  "skip Phase 2");
+                       ret->decision = DECISION_COND_SUCC;
+                       ret->methodState = METHOD_MAY_CONT;
+               }
+               data->phase2_start = 1;
+               if (data->ttls_version == 0)
+                       eap_ttls_v0_derive_key(sm, data);
+
+               if (*out_data == NULL || wpabuf_len(*out_data) == 0) {
+                       if (eap_ttls_decrypt(sm, data, ret, identifier,
+                                            NULL, out_data)) {
+                               wpa_printf(MSG_WARNING, "EAP-TTLS: "
+                                          "failed to process early "
+                                          "start for Phase 2");
+                       }
+                       res = 0;
+               }
+               data->resuming = 0;
+       }
+
+       if (res == 2) {
+               struct wpabuf msg;
+               /*
+                * Application data included in the handshake message.
+                */
+               wpabuf_free(data->pending_phase2_req);
+               data->pending_phase2_req = *out_data;
+               *out_data = NULL;
+               wpabuf_set(&msg, in_data, in_len);
+               res = eap_ttls_decrypt(sm, data, ret, identifier, &msg,
+                                      out_data);
+       }
+
+       return res;
+}
+
+
+static void eap_ttls_check_auth_status(struct eap_sm *sm, 
+                                      struct eap_ttls_data *data,
+                                      struct eap_method_ret *ret)
+{
+       if (data->ttls_version == 0 && ret->methodState == METHOD_DONE) {
+               ret->allowNotifications = FALSE;
+               if (ret->decision == DECISION_UNCOND_SUCC ||
+                   ret->decision == DECISION_COND_SUCC) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+                                  "completed successfully");
+                       data->phase2_success = 1;
+#ifdef EAP_TNC
+                       if (!data->ready_for_tnc && !data->tnc_started) {
+                               /*
+                                * TNC may be required as the next
+                                * authentication method within the tunnel.
+                                */
+                               ret->methodState = METHOD_MAY_CONT;
+                               data->ready_for_tnc = 1;
+                       }
+#endif /* EAP_TNC */
+               }
+       } else if (data->ttls_version == 0 &&
+                  ret->methodState == METHOD_MAY_CONT &&
+                  (ret->decision == DECISION_UNCOND_SUCC ||
+                   ret->decision == DECISION_COND_SUCC)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Authentication "
+                                  "completed successfully (MAY_CONT)");
+                       data->phase2_success = 1;
+       }
+}
+
+
+static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
+                                       struct eap_method_ret *ret,
+                                       const struct wpabuf *reqData)
+{
+       size_t left;
+       int res;
+       u8 flags, id;
+       struct wpabuf *resp;
+       const u8 *pos;
+       struct eap_ttls_data *data = priv;
+
+       pos = eap_peer_tls_process_init(sm, &data->ssl, EAP_TYPE_TTLS, ret,
+                                       reqData, &left, &flags);
+       if (pos == NULL)
+               return NULL;
+       id = eap_get_id(reqData);
+
+       if (flags & EAP_TLS_FLAGS_START) {
+               if (eap_ttls_process_start(sm, data, flags, ret) < 0)
+                       return NULL;
+
+               /* RFC 5281, Ch. 9.2:
+                * "This packet MAY contain additional information in the form
+                * of AVPs, which may provide useful hints to the client"
+                * For now, ignore any potential extra data.
+                */
+               left = 0;
+       } else if (!data->ssl_initialized) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: First message did not "
+                          "include Start flag");
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
+               ret->allowNotifications = FALSE;
+               return NULL;
+       }
+
+       resp = NULL;
+       if (tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+           !data->resuming) {
+               struct wpabuf msg;
+               wpabuf_set(&msg, pos, left);
+               res = eap_ttls_decrypt(sm, data, ret, id, &msg, &resp);
+       } else {
+               res = eap_ttls_process_handshake(sm, data, ret, id,
+                                                pos, left, &resp);
+       }
+
+       eap_ttls_check_auth_status(sm, data, ret);
+
+       /* FIX: what about res == -1? Could just move all error processing into
+        * the other functions and get rid of this res==1 case here. */
+       if (res == 1) {
+               wpabuf_free(resp);
+               return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
+                                             data->ttls_version);
+       }
+       return resp;
+}
+
+
+static Boolean eap_ttls_has_reauth_data(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       return tls_connection_established(sm->ssl_ctx, data->ssl.conn) &&
+               data->phase2_success;
+}
+
+
+static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       wpabuf_free(data->pending_phase2_req);
+       data->pending_phase2_req = NULL;
+#ifdef EAP_TNC
+       data->ready_for_tnc = 0;
+       data->tnc_started = 0;
+#endif /* EAP_TNC */
+}
+
+
+static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       os_free(data->key_data);
+       data->key_data = NULL;
+       if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
+               os_free(data);
+               return NULL;
+       }
+       if (data->phase2_priv && data->phase2_method &&
+           data->phase2_method->init_for_reauth)
+               data->phase2_method->init_for_reauth(sm, data->phase2_priv);
+       data->phase2_start = 0;
+       data->phase2_success = 0;
+       data->resuming = 1;
+       data->reauth = 1;
+       return priv;
+}
+
+
+static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
+                              size_t buflen, int verbose)
+{
+       struct eap_ttls_data *data = priv;
+       int len, ret;
+
+       len = eap_peer_tls_status(sm, &data->ssl, buf, buflen, verbose);
+       ret = os_snprintf(buf + len, buflen - len,
+                         "EAP-TTLSv%d Phase2 method=",
+                         data->ttls_version);
+       if (ret < 0 || (size_t) ret >= buflen - len)
+               return len;
+       len += ret;
+       switch (data->phase2_type) {
+       case EAP_TTLS_PHASE2_EAP:
+               ret = os_snprintf(buf + len, buflen - len, "EAP-%s\n",
+                                 data->phase2_method ?
+                                 data->phase2_method->name : "?");
+               break;
+       case EAP_TTLS_PHASE2_MSCHAPV2:
+               ret = os_snprintf(buf + len, buflen - len, "MSCHAPV2\n");
+               break;
+       case EAP_TTLS_PHASE2_MSCHAP:
+               ret = os_snprintf(buf + len, buflen - len, "MSCHAP\n");
+               break;
+       case EAP_TTLS_PHASE2_PAP:
+               ret = os_snprintf(buf + len, buflen - len, "PAP\n");
+               break;
+       case EAP_TTLS_PHASE2_CHAP:
+               ret = os_snprintf(buf + len, buflen - len, "CHAP\n");
+               break;
+       default:
+               ret = 0;
+               break;
+       }
+       if (ret < 0 || (size_t) ret >= buflen - len)
+               return len;
+       len += ret;
+
+       return len;
+}
+
+
+static Boolean eap_ttls_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       return data->key_data != NULL && data->phase2_success;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL || !data->phase2_success)
+               return NULL;
+
+       key = os_malloc(EAP_TLS_KEY_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_TLS_KEY_LEN;
+       os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
+
+       return key;
+}
+
+
+int eap_peer_ttls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_ttls_init;
+       eap->deinit = eap_ttls_deinit;
+       eap->process = eap_ttls_process;
+       eap->isKeyAvailable = eap_ttls_isKeyAvailable;
+       eap->getKey = eap_ttls_getKey;
+       eap->get_status = eap_ttls_get_status;
+       eap->has_reauth_data = eap_ttls_has_reauth_data;
+       eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
+       eap->init_for_reauth = eap_ttls_init_for_reauth;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_vendor_test.c b/src/eap_peer/eap_vendor_test.c
new file mode 100644 (file)
index 0000000..3e114c1
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * EAP peer method: Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-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.
+ *
+ * This file implements a vendor specific test method using EAP expanded types.
+ * This is only for test use and must not be used for authentication since no
+ * security is provided.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#ifdef TEST_PENDING_REQUEST
+#include "eloop.h"
+#endif /* TEST_PENDING_REQUEST */
+
+
+#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+/* #define TEST_PENDING_REQUEST */
+
+struct eap_vendor_test_data {
+       enum { INIT, CONFIRM, SUCCESS } state;
+       int first_try;
+};
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+       struct eap_vendor_test_data *data;
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = INIT;
+       data->first_try = 1;
+       return data;
+}
+
+
+static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_vendor_test_data *data = priv;
+       os_free(data);
+}
+
+
+#ifdef TEST_PENDING_REQUEST
+static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
+{
+       struct eap_sm *sm = eloop_ctx;
+       wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Ready to re-process pending "
+                  "request");
+       eap_notify_pending(sm);
+}
+#endif /* TEST_PENDING_REQUEST */
+
+
+static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
+                                              struct eap_method_ret *ret,
+                                              const struct wpabuf *reqData)
+{
+       struct eap_vendor_test_data *data = priv;
+       struct wpabuf *resp;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, reqData, &len);
+       if (pos == NULL || len < 1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state == INIT && *pos != 1) {
+               wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+                          "%d in INIT state", *pos);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state == CONFIRM && *pos != 3) {
+               wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+                          "%d in CONFIRM state", *pos);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state == SUCCESS) {
+               wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Unexpected message "
+                          "in SUCCESS state");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state == CONFIRM) {
+#ifdef TEST_PENDING_REQUEST
+               if (data->first_try) {
+                       data->first_try = 0;
+                       wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
+                                  "pending request");
+                       ret->ignore = TRUE;
+                       eloop_register_timeout(1, 0, eap_vendor_ready, sm,
+                                              NULL);
+                       return NULL;
+               }
+#endif /* TEST_PENDING_REQUEST */
+       }
+
+       ret->ignore = FALSE;
+
+       wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Generating Response");
+       ret->allowNotifications = TRUE;
+
+       resp = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+                            EAP_CODE_RESPONSE, eap_get_id(reqData));
+       if (resp == NULL)
+               return NULL;
+
+       if (data->state == INIT) {
+               wpabuf_put_u8(resp, 2);
+               data->state = CONFIRM;
+               ret->methodState = METHOD_CONT;
+               ret->decision = DECISION_FAIL;
+       } else {
+               wpabuf_put_u8(resp, 4);
+               data->state = SUCCESS;
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_UNCOND_SUCC;
+       }
+
+       return resp;
+}
+
+
+static Boolean eap_vendor_test_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_vendor_test_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_vendor_test_data *data = priv;
+       u8 *key;
+       const int key_len = 64;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(key_len);
+       if (key == NULL)
+               return NULL;
+
+       os_memset(key, 0x11, key_len / 2);
+       os_memset(key + key_len / 2, 0x22, key_len / 2);
+       *len = key_len;
+
+       return key;
+}
+
+
+int eap_peer_vendor_test_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+                                   "VENDOR-TEST");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_vendor_test_init;
+       eap->deinit = eap_vendor_test_deinit;
+       eap->process = eap_vendor_test_process;
+       eap->isKeyAvailable = eap_vendor_test_isKeyAvailable;
+       eap->getKey = eap_vendor_test_getKey;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
new file mode 100644 (file)
index 0000000..8317f72
--- /dev/null
@@ -0,0 +1,543 @@
+/*
+ * EAP-WSC peer for Wi-Fi Protected Setup
+ * Copyright (c) 2007-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 "common.h"
+#include "uuid.h"
+#include "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+
+struct eap_wsc_data {
+       enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+       int registrar;
+       struct wpabuf *in_buf;
+       struct wpabuf *out_buf;
+       enum wsc_op_code in_op_code, out_op_code;
+       size_t out_used;
+       size_t fragment_size;
+       struct wps_data *wps;
+       struct wps_context *wps_ctx;
+};
+
+
+static const char * eap_wsc_state_txt(int state)
+{
+       switch (state) {
+       case WAIT_START:
+               return "WAIT_START";
+       case MESG:
+               return "MESG";
+       case FRAG_ACK:
+               return "FRAG_ACK";
+       case WAIT_FRAG_ACK:
+               return "WAIT_FRAG_ACK";
+       case DONE:
+               return "DONE";
+       case FAIL:
+               return "FAIL";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+                  eap_wsc_state_txt(data->state),
+                  eap_wsc_state_txt(state));
+       data->state = state;
+}
+
+
+static int eap_wsc_new_ap_settings(struct wps_credential *cred,
+                                  const char *params)
+{
+       const char *pos, *end;
+       size_t len;
+
+       os_memset(cred, 0, sizeof(*cred));
+
+       pos = os_strstr(params, "new_ssid=");
+       if (pos == NULL)
+               return 0;
+       pos += 9;
+       end = os_strchr(pos, ' ');
+       if (end == NULL)
+               len = os_strlen(pos);
+       else
+               len = end - pos;
+       if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
+           hexstr2bin(pos, cred->ssid, len / 2))
+               return -1;
+       cred->ssid_len = len / 2;
+
+       pos = os_strstr(params, "new_auth=");
+       if (pos == NULL)
+               return -1;
+       if (os_strncmp(pos + 9, "OPEN", 4) == 0)
+               cred->auth_type = WPS_AUTH_OPEN;
+       else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
+               cred->auth_type = WPS_AUTH_WPAPSK;
+       else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
+               cred->auth_type = WPS_AUTH_WPA2PSK;
+       else
+               return -1;
+
+       pos = os_strstr(params, "new_encr=");
+       if (pos == NULL)
+               return -1;
+       if (os_strncmp(pos + 9, "NONE", 4) == 0)
+               cred->encr_type = WPS_ENCR_NONE;
+       else if (os_strncmp(pos + 9, "WEP", 3) == 0)
+               cred->encr_type = WPS_ENCR_WEP;
+       else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
+               cred->encr_type = WPS_ENCR_TKIP;
+       else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
+               cred->encr_type = WPS_ENCR_AES;
+       else
+               return -1;
+
+       pos = os_strstr(params, "new_key=");
+       if (pos == NULL)
+               return 0;
+       pos += 8;
+       end = os_strchr(pos, ' ');
+       if (end == NULL)
+               len = os_strlen(pos);
+       else
+               len = end - pos;
+       if ((len & 1) || len > 2 * sizeof(cred->key) ||
+           hexstr2bin(pos, cred->key, len / 2))
+               return -1;
+       cred->key_len = len / 2;
+
+       return 1;
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+       struct eap_wsc_data *data;
+       const u8 *identity;
+       size_t identity_len;
+       int registrar;
+       struct wps_config cfg;
+       const char *pos;
+       const char *phase1;
+       struct wps_context *wps;
+       struct wps_credential new_ap_settings;
+       int res;
+
+       wps = sm->wps;
+       if (wps == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
+               return NULL;
+       }
+
+       identity = eap_get_config_identity(sm, &identity_len);
+
+       if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
+           os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
+               registrar = 1; /* Supplicant is Registrar */
+       else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
+           os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
+               registrar = 0; /* Supplicant is Enrollee */
+       else {
+               wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+                                 identity, identity_len);
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = registrar ? MESG : WAIT_START;
+       data->registrar = registrar;
+       data->wps_ctx = wps;
+
+       os_memset(&cfg, 0, sizeof(cfg));
+       cfg.wps = wps;
+       cfg.registrar = registrar;
+
+       phase1 = eap_get_config_phase1(sm);
+       if (phase1 == NULL) {
+               wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
+                          "set");
+               os_free(data);
+               return NULL;
+       }
+
+       pos = os_strstr(phase1, "pin=");
+       if (pos) {
+               pos += 4;
+               cfg.pin = (const u8 *) pos;
+               while (*pos != '\0' && *pos != ' ')
+                       pos++;
+               cfg.pin_len = pos - (const char *) cfg.pin;
+       } else {
+               pos = os_strstr(phase1, "pbc=1");
+               if (pos)
+                       cfg.pbc = 1;
+       }
+
+       if (cfg.pin == NULL && !cfg.pbc) {
+               wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
+                          "configuration data");
+               os_free(data);
+               return NULL;
+       }
+
+       res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
+       if (res < 0) {
+               os_free(data);
+               return NULL;
+       }
+       if (res == 1) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
+                          "WPS");
+               cfg.new_ap_settings = &new_ap_settings;
+       }
+
+       data->wps = wps_init(&cfg);
+       if (data->wps == NULL) {
+               os_free(data);
+               return NULL;
+       }
+       data->fragment_size = WSC_FRAGMENT_SIZE;
+
+       if (registrar && cfg.pin) {
+               wps_registrar_add_pin(data->wps_ctx->registrar, NULL,
+                                     cfg.pin, cfg.pin_len, 0);
+       }
+
+       /* Use reduced client timeout for WPS to avoid long wait */
+       if (sm->ClientTimeout > 30)
+               sm->ClientTimeout = 30;
+
+       return data;
+}
+
+
+static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_wsc_data *data = priv;
+       wpabuf_free(data->in_buf);
+       wpabuf_free(data->out_buf);
+       wps_deinit(data->wps);
+       os_free(data->wps_ctx->network_key);
+       data->wps_ctx->network_key = NULL;
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
+                                        struct eap_method_ret *ret, u8 id)
+{
+       struct wpabuf *resp;
+       u8 flags;
+       size_t send_len, plen;
+
+       ret->ignore = FALSE;
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
+       ret->allowNotifications = TRUE;
+
+       flags = 0;
+       send_len = wpabuf_len(data->out_buf) - data->out_used;
+       if (2 + send_len > data->fragment_size) {
+               send_len = data->fragment_size - 2;
+               flags |= WSC_FLAGS_MF;
+               if (data->out_used == 0) {
+                       flags |= WSC_FLAGS_LF;
+                       send_len -= 2;
+               }
+       }
+       plen = 2 + send_len;
+       if (flags & WSC_FLAGS_LF)
+               plen += 2;
+       resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
+                            EAP_CODE_RESPONSE, id);
+       if (resp == NULL)
+               return NULL;
+
+       wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
+       wpabuf_put_u8(resp, flags); /* Flags */
+       if (flags & WSC_FLAGS_LF)
+               wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
+
+       wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
+                       send_len);
+       data->out_used += send_len;
+
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+
+       if (data->out_used == wpabuf_len(data->out_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->out_buf);
+               data->out_buf = NULL;
+               data->out_used = 0;
+               if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
+                   data->out_op_code == WSC_NACK ||
+                   data->out_op_code == WSC_Done) {
+                       eap_wsc_state(data, FAIL);
+                       ret->methodState = METHOD_DONE;
+               } else
+                       eap_wsc_state(data, MESG);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->out_buf) -
+                          data->out_used);
+               eap_wsc_state(data, WAIT_FRAG_ACK);
+       }
+
+       return resp;
+}
+
+
+static int eap_wsc_process_cont(struct eap_wsc_data *data,
+                               const u8 *buf, size_t len, u8 op_code)
+{
+       /* Process continuation of a pending message */
+       if (op_code != data->in_op_code) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
+                          "fragment (expected %d)",
+                          op_code, data->in_op_code);
+               return -1;
+       }
+
+       if (len > wpabuf_tailroom(data->in_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
+               eap_wsc_state(data, FAIL);
+               return -1;
+       }
+
+       wpabuf_put_data(data->in_buf, buf, len);
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
+                  "for %lu bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->in_buf));
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
+                                               struct eap_method_ret *ret,
+                                               u8 id, u8 flags, u8 op_code,
+                                               u16 message_length,
+                                               const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
+                          "fragmented packet");
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->in_buf == NULL) {
+               /* First fragment of the message */
+               data->in_buf = wpabuf_alloc(message_length);
+               if (data->in_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
+                                  "message");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               data->in_op_code = op_code;
+               wpabuf_put_data(data->in_buf, buf, len);
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
+                          "fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->in_buf));
+       }
+
+       return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
+}
+
+
+static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_wsc_data *data = priv;
+       const u8 *start, *pos, *end;
+       size_t len;
+       u8 op_code, flags, id;
+       u16 message_length = 0;
+       enum wps_process_res res;
+       struct wpabuf tmpbuf;
+       struct wpabuf *r;
+
+       pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
+                              &len);
+       if (pos == NULL || len < 2) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       id = eap_get_id(reqData);
+
+       start = pos;
+       end = start + len;
+
+       op_code = *pos++;
+       flags = *pos++;
+       if (flags & WSC_FLAGS_LF) {
+               if (end - pos < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               message_length = WPA_GET_BE16(pos);
+               pos += 2;
+
+               if (message_length < end - pos) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
+                                  "Length");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
+                  "Flags 0x%x Message Length %d",
+                  op_code, flags, message_length);
+
+       if (data->state == WAIT_FRAG_ACK) {
+               if (op_code != WSC_FRAG_ACK) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+                                  "in WAIT_FRAG_ACK state", op_code);
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
+               eap_wsc_state(data, MESG);
+               return eap_wsc_build_msg(data, ret, id);
+       }
+
+       if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
+           op_code != WSC_Done && op_code != WSC_Start) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+                          op_code);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->state == WAIT_START) {
+               if (op_code != WSC_Start) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+                                  "in WAIT_START state", op_code);
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
+               eap_wsc_state(data, MESG);
+               /* Start message has empty payload, skip processing */
+               goto send_msg;
+       } else if (op_code == WSC_Start) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+                          op_code);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (data->in_buf &&
+           eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (flags & WSC_FLAGS_MF) {
+               return eap_wsc_process_fragment(data, ret, id, flags, op_code,
+                                               message_length, pos,
+                                               end - pos);
+       }
+
+       if (data->in_buf == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&tmpbuf, pos, end - pos);
+               data->in_buf = &tmpbuf;
+       }
+
+       res = wps_process_msg(data->wps, op_code, data->in_buf);
+       switch (res) {
+       case WPS_DONE:
+               wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
+                          "successfully - wait for EAP failure");
+               eap_wsc_state(data, FAIL);
+               break;
+       case WPS_CONTINUE:
+               eap_wsc_state(data, MESG);
+               break;
+       case WPS_FAILURE:
+       case WPS_PENDING:
+               wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
+               eap_wsc_state(data, FAIL);
+               break;
+       }
+
+       if (data->in_buf != &tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+
+send_msg:
+       if (data->out_buf == NULL) {
+               data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
+               if (data->out_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
+                                  "message from WPS");
+                       return NULL;
+               }
+               data->out_used = 0;
+       }
+
+       eap_wsc_state(data, MESG);
+       r = eap_wsc_build_msg(data, ret, id);
+       if (data->state == FAIL && ret->methodState == METHOD_DONE) {
+               /* Use reduced client timeout for WPS to avoid long wait */
+               if (sm->ClientTimeout > 2)
+                       sm->ClientTimeout = 2;
+       }
+       return r;
+}
+
+
+int eap_peer_wsc_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+                                   "WSC");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_wsc_init;
+       eap->deinit = eap_wsc_deinit;
+       eap->process = eap_wsc_process;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_peer/ikev2.c b/src/eap_peer/ikev2.c
new file mode 100644 (file)
index 0000000..309a331
--- /dev/null
@@ -0,0 +1,1303 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * 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"
+
+#include "common.h"
+#include "crypto/dh_groups.h"
+#include "ikev2.h"
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data)
+{
+       ikev2_free_keys(&data->keys);
+       wpabuf_free(data->i_dh_public);
+       wpabuf_free(data->r_dh_private);
+       os_free(data->IDi);
+       os_free(data->IDr);
+       os_free(data->shared_secret);
+       wpabuf_free(data->i_sign_msg);
+       wpabuf_free(data->r_sign_msg);
+       os_free(data->key_pad);
+}
+
+
+static int ikev2_derive_keys(struct ikev2_responder_data *data)
+{
+       u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN];
+       size_t buf_len, pad_len;
+       struct wpabuf *shared;
+       const struct ikev2_integ_alg *integ;
+       const struct ikev2_prf_alg *prf;
+       const struct ikev2_encr_alg *encr;
+       int ret;
+       const u8 *addr[2];
+       size_t len[2];
+
+       /* RFC 4306, Sect. 2.14 */
+
+       integ = ikev2_get_integ(data->proposal.integ);
+       prf = ikev2_get_prf(data->proposal.prf);
+       encr = ikev2_get_encr(data->proposal.encr);
+       if (integ == NULL || prf == NULL || encr == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal");
+               return -1;
+       }
+
+       shared = dh_derive_shared(data->i_dh_public, data->r_dh_private,
+                                 data->dh);
+       if (shared == NULL)
+               return -1;
+
+       /* Construct Ni | Nr | SPIi | SPIr */
+
+       buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN;
+       buf = os_malloc(buf_len);
+       if (buf == NULL) {
+               wpabuf_free(shared);
+               return -1;
+       }
+
+       pos = buf;
+       os_memcpy(pos, data->i_nonce, data->i_nonce_len);
+       pos += data->i_nonce_len;
+       os_memcpy(pos, data->r_nonce, data->r_nonce_len);
+       pos += data->r_nonce_len;
+       os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
+       pos += IKEV2_SPI_LEN;
+       os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
+#ifdef CCNS_PL
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       {
+               int i;
+               u8 *tmp = pos - IKEV2_SPI_LEN;
+               /* Incorrect byte re-ordering on little endian hosts.. */
+               for (i = 0; i < IKEV2_SPI_LEN; i++)
+                       *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i];
+               for (i = 0; i < IKEV2_SPI_LEN; i++)
+                       *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i];
+       }
+#endif
+#endif /* CCNS_PL */
+
+       /* SKEYSEED = prf(Ni | Nr, g^ir) */
+       /* Use zero-padding per RFC 4306, Sect. 2.14 */
+       pad_len = data->dh->prime_len - wpabuf_len(shared);
+#ifdef CCNS_PL
+       /* Shared secret is not zero-padded correctly */
+       pad_len = 0;
+#endif /* CCNS_PL */
+       pad = os_zalloc(pad_len ? pad_len : 1);
+       if (pad == NULL) {
+               wpabuf_free(shared);
+               os_free(buf);
+               return -1;
+       }
+
+       addr[0] = pad;
+       len[0] = pad_len;
+       addr[1] = wpabuf_head(shared);
+       len[1] = wpabuf_len(shared);
+       if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len,
+                          2, addr, len, skeyseed) < 0) {
+               wpabuf_free(shared);
+               os_free(buf);
+               os_free(pad);
+               return -1;
+       }
+       os_free(pad);
+       wpabuf_free(shared);
+
+       /* DH parameters are not needed anymore, so free them */
+       wpabuf_free(data->i_dh_public);
+       data->i_dh_public = NULL;
+       wpabuf_free(data->r_dh_private);
+       data->r_dh_private = NULL;
+
+       wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED",
+                       skeyseed, prf->hash_len);
+
+       ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len,
+                                  &data->keys);
+       os_free(buf);
+       return ret;
+}
+
+
+static int ikev2_parse_transform(struct ikev2_proposal_data *prop,
+                                const u8 *pos, const u8 *end)
+{
+       int transform_len;
+       const struct ikev2_transform *t;
+       u16 transform_id;
+       const u8 *tend;
+
+       if (end - pos < (int) sizeof(*t)) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short transform");
+               return -1;
+       }
+
+       t = (const struct ikev2_transform *) pos;
+       transform_len = WPA_GET_BE16(t->transform_length);
+       if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
+                          transform_len);
+               return -1;
+       }
+       tend = pos + transform_len;
+
+       transform_id = WPA_GET_BE16(t->transform_id);
+
+       wpa_printf(MSG_DEBUG, "IKEV2:   Transform:");
+       wpa_printf(MSG_DEBUG, "IKEV2:     Type: %d  Transform Length: %d  "
+                  "Transform Type: %d  Transform ID: %d",
+                  t->type, transform_len, t->transform_type, transform_id);
+
+       if (t->type != 0 && t->type != 3) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type");
+               return -1;
+       }
+
+       pos = (const u8 *) (t + 1);
+       if (pos < tend) {
+               wpa_hexdump(MSG_DEBUG, "IKEV2:     Transform Attributes",
+                           pos, tend - pos);
+       }
+
+       switch (t->transform_type) {
+       case IKEV2_TRANSFORM_ENCR:
+               if (ikev2_get_encr(transform_id)) {
+                       if (transform_id == ENCR_AES_CBC) {
+                               if (tend - pos != 4) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: No "
+                                                  "Transform Attr for AES");
+                                       break;
+                               }
+#ifdef CCNS_PL
+                               if (WPA_GET_BE16(pos) != 0x001d /* ?? */) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+                                                  "Key Size attribute for "
+                                                  "AES");
+                                       break;
+                               }
+#else /* CCNS_PL */
+                               if (WPA_GET_BE16(pos) != 0x800e) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+                                                  "Key Size attribute for "
+                                                  "AES");
+                                       break;
+                               }
+#endif /* CCNS_PL */
+                               if (WPA_GET_BE16(pos + 2) != 128) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: "
+                                                  "Unsupported AES key size "
+                                                  "%d bits",
+                                                  WPA_GET_BE16(pos + 2));
+                                       break;
+                               }
+                       }
+                       prop->encr = transform_id;
+               }
+               break;
+       case IKEV2_TRANSFORM_PRF:
+               if (ikev2_get_prf(transform_id))
+                       prop->prf = transform_id;
+               break;
+       case IKEV2_TRANSFORM_INTEG:
+               if (ikev2_get_integ(transform_id))
+                       prop->integ = transform_id;
+               break;
+       case IKEV2_TRANSFORM_DH:
+               if (dh_groups_get(transform_id))
+                       prop->dh = transform_id;
+               break;
+       }
+
+       return transform_len;
+}
+
+
+static int ikev2_parse_proposal(struct ikev2_proposal_data *prop,
+                               const u8 *pos, const u8 *end)
+{
+       const u8 *pend, *ppos;
+       int proposal_len, i;
+       const struct ikev2_proposal *p;
+
+       if (end - pos < (int) sizeof(*p)) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short proposal");
+               return -1;
+       }
+
+       /* FIX: AND processing if multiple proposals use the same # */
+
+       p = (const struct ikev2_proposal *) pos;
+       proposal_len = WPA_GET_BE16(p->proposal_length);
+       if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
+                          proposal_len);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d",
+                  p->proposal_num);
+       wpa_printf(MSG_DEBUG, "IKEV2:   Type: %d  Proposal Length: %d "
+                  " Protocol ID: %d",
+                  p->type, proposal_len, p->protocol_id);
+       wpa_printf(MSG_DEBUG, "IKEV2:   SPI Size: %d  Transforms: %d",
+                  p->spi_size, p->num_transforms);
+
+       if (p->type != 0 && p->type != 2) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type");
+               return -1;
+       }
+
+       if (p->protocol_id != IKEV2_PROTOCOL_IKE) {
+               wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID "
+                          "(only IKE allowed for EAP-IKEv2)");
+               return -1;
+       }
+
+       if (p->proposal_num != prop->proposal_num) {
+               if (p->proposal_num == prop->proposal_num + 1)
+                       prop->proposal_num = p->proposal_num;
+               else {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #");
+                       return -1;
+               }
+       }
+
+       ppos = (const u8 *) (p + 1);
+       pend = pos + proposal_len;
+       if (ppos + p->spi_size > pend) {
+               wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
+                          "in proposal");
+               return -1;
+       }
+       if (p->spi_size) {
+               wpa_hexdump(MSG_DEBUG, "IKEV2:    SPI",
+                           ppos, p->spi_size);
+               ppos += p->spi_size;
+       }
+
+       /*
+        * For initial IKE_SA negotiation, SPI Size MUST be zero; for
+        * subsequent negotiations, it must be 8 for IKE. We only support
+        * initial case for now.
+        */
+       if (p->spi_size != 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size");
+               return -1;
+       }
+
+       if (p->num_transforms == 0) {
+               wpa_printf(MSG_INFO, "IKEV2: At least one transform required");
+               return -1;
+       }
+
+       for (i = 0; i < (int) p->num_transforms; i++) {
+               int tlen = ikev2_parse_transform(prop, ppos, pend);
+               if (tlen < 0)
+                       return -1;
+               ppos += tlen;
+       }
+
+       if (ppos != pend) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected data after "
+                          "transforms");
+               return -1;
+       }
+
+       return proposal_len;
+}
+
+
+static int ikev2_process_sai1(struct ikev2_responder_data *data,
+                             const u8 *sai1, size_t sai1_len)
+{
+       struct ikev2_proposal_data prop;
+       const u8 *pos, *end;
+       int found = 0;
+
+       /* Security Association Payloads: <Proposals> */
+
+       if (sai1 == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: SAi1 not received");
+               return -1;
+       }
+
+       os_memset(&prop, 0, sizeof(prop));
+       prop.proposal_num = 1;
+
+       pos = sai1;
+       end = sai1 + sai1_len;
+
+       while (pos < end) {
+               int plen;
+
+               prop.integ = -1;
+               prop.prf = -1;
+               prop.encr = -1;
+               prop.dh = -1;
+               plen = ikev2_parse_proposal(&prop, pos, end);
+               if (plen < 0)
+                       return -1;
+
+               if (!found && prop.integ != -1 && prop.prf != -1 &&
+                   prop.encr != -1 && prop.dh != -1) {
+                       os_memcpy(&data->proposal, &prop, sizeof(prop));
+                       data->dh = dh_groups_get(prop.dh);
+                       found = 1;
+               }
+
+               pos += plen;
+       }
+
+       if (pos != end) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposals");
+               return -1;
+       }
+
+       if (!found) {
+               wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+                  "INTEG:%d D-H:%d", data->proposal.proposal_num,
+                  data->proposal.encr, data->proposal.prf,
+                  data->proposal.integ, data->proposal.dh);
+
+       return 0;
+}
+
+
+static int ikev2_process_kei(struct ikev2_responder_data *data,
+                            const u8 *kei, size_t kei_len)
+{
+       u16 group;
+
+       /*
+        * Key Exchange Payload:
+        * DH Group # (16 bits)
+        * RESERVED (16 bits)
+        * Key Exchange Data (Diffie-Hellman public value)
+        */
+
+       if (kei == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: KEi not received");
+               return -1;
+       }
+
+       if (kei_len < 4 + 96) {
+               wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+               return -1;
+       }
+
+       group = WPA_GET_BE16(kei);
+       wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u", group);
+
+       if (group != data->proposal.dh) {
+               wpa_printf(MSG_DEBUG, "IKEV2: KEi DH Group #%u does not match "
+                          "with the selected proposal (%u)",
+                          group, data->proposal.dh);
+               /* Reject message with Notify payload of type
+                * INVALID_KE_PAYLOAD (RFC 4306, Sect. 3.4) */
+               data->error_type = INVALID_KE_PAYLOAD;
+               data->state = NOTIFY;
+               return -1;
+       }
+
+       if (data->dh == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+               return -1;
+       }
+
+       /* RFC 4306, Section 3.4:
+        * The length of DH public value MUST be equal to the lenght of the
+        * prime modulus.
+        */
+       if (kei_len - 4 != data->dh->prime_len) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+                          "%ld (expected %ld)",
+                          (long) (kei_len - 4), (long) data->dh->prime_len);
+               return -1;
+       }
+
+       wpabuf_free(data->i_dh_public);
+       data->i_dh_public = wpabuf_alloc(kei_len - 4);
+       if (data->i_dh_public == NULL)
+               return -1;
+       wpabuf_put_data(data->i_dh_public, kei + 4, kei_len - 4);
+
+       wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEi Diffie-Hellman Public Value",
+                       data->i_dh_public);
+       
+       return 0;
+}
+
+
+static int ikev2_process_ni(struct ikev2_responder_data *data,
+                           const u8 *ni, size_t ni_len)
+{
+       if (ni == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Ni not received");
+               return -1;
+       }
+
+       if (ni_len < IKEV2_NONCE_MIN_LEN || ni_len > IKEV2_NONCE_MAX_LEN) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid Ni length %ld",
+                          (long) ni_len);
+               return -1;
+       }
+
+#ifdef CCNS_PL
+       /* Zeros are removed incorrectly from the beginning of the nonces */
+       while (ni_len > 1 && *ni == 0) {
+               ni_len--;
+               ni++;
+       }
+#endif /* CCNS_PL */
+
+       data->i_nonce_len = ni_len;
+       os_memcpy(data->i_nonce, ni, ni_len);
+       wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni",
+                   data->i_nonce, data->i_nonce_len);
+
+       return 0;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_responder_data *data,
+                                const struct ikev2_hdr *hdr,
+                                struct ikev2_payloads *pl)
+{
+       if (ikev2_process_sai1(data, pl->sa, pl->sa_len) < 0 ||
+           ikev2_process_kei(data, pl->ke, pl->ke_len) < 0 ||
+           ikev2_process_ni(data, pl->nonce, pl->nonce_len) < 0)
+               return -1;
+
+       os_memcpy(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN);
+
+       return 0;
+}
+
+
+static int ikev2_process_idi(struct ikev2_responder_data *data,
+                            const u8 *idi, size_t idi_len)
+{
+       u8 id_type;
+
+       if (idi == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No IDi received");
+               return -1;
+       }
+
+       if (idi_len < 4) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short IDi payload");
+               return -1;
+       }
+
+       id_type = idi[0];
+       idi += 4;
+       idi_len -= 4;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: IDi ID Type %d", id_type);
+       wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDi", idi, idi_len);
+       os_free(data->IDi);
+       data->IDi = os_malloc(idi_len);
+       if (data->IDi == NULL)
+               return -1;
+       os_memcpy(data->IDi, idi, idi_len);
+       data->IDi_len = idi_len;
+       data->IDi_type = id_type;
+
+       return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_responder_data *data,
+                             const u8 *cert, size_t cert_len)
+{
+       u8 cert_encoding;
+
+       if (cert == NULL) {
+               if (data->peer_auth == PEER_AUTH_CERT) {
+                       wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+                       return -1;
+               }
+               return 0;
+       }
+
+       if (cert_len < 1) {
+               wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+               return -1;
+       }
+
+       cert_encoding = cert[0];
+       cert++;
+       cert_len--;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+       wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+       /* TODO: validate certificate */
+
+       return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_responder_data *data,
+                                  u8 method, const u8 *auth, size_t auth_len)
+{
+       if (method != AUTH_RSA_SIGN) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+                          "method %d", method);
+               return -1;
+       }
+
+       /* TODO: validate AUTH */
+       return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_responder_data *data,
+                                    u8 method, const u8 *auth,
+                                    size_t auth_len)
+{
+       u8 auth_data[IKEV2_MAX_HASH_LEN];
+       const struct ikev2_prf_alg *prf;
+
+       if (method != AUTH_SHARED_KEY_MIC) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+                          "method %d", method);
+               return -1;
+       }
+
+       /* msg | Nr | prf(SK_pi,IDi') */
+       if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+                                  data->IDi, data->IDi_len, data->IDi_type,
+                                  &data->keys, 1, data->shared_secret,
+                                  data->shared_secret_len,
+                                  data->r_nonce, data->r_nonce_len,
+                                  data->key_pad, data->key_pad_len,
+                                  auth_data) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+               return -1;
+       }
+
+       wpabuf_free(data->i_sign_msg);
+       data->i_sign_msg = NULL;
+
+       prf = ikev2_get_prf(data->proposal.prf);
+       if (prf == NULL)
+               return -1;
+
+       if (auth_len != prf->hash_len ||
+           os_memcmp(auth, auth_data, auth_len) != 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
+               wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
+                           auth, auth_len);
+               wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+                           auth_data, prf->hash_len);
+               data->error_type = AUTHENTICATION_FAILED;
+               data->state = NOTIFY;
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Server authenticated successfully "
+                  "using shared keys");
+
+       return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_responder_data *data,
+                             const u8 *auth, size_t auth_len)
+{
+       u8 auth_method;
+
+       if (auth == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+               return -1;
+       }
+
+       if (auth_len < 4) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+                          "Payload");
+               return -1;
+       }
+
+       auth_method = auth[0];
+       auth += 4;
+       auth_len -= 4;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+       wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+       switch (data->peer_auth) {
+       case PEER_AUTH_CERT:
+               return ikev2_process_auth_cert(data, auth_method, auth,
+                                              auth_len);
+       case PEER_AUTH_SECRET:
+               return ikev2_process_auth_secret(data, auth_method, auth,
+                                                auth_len);
+       }
+
+       return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_responder_data *data,
+                                          u8 next_payload,
+                                          u8 *payload, size_t payload_len)
+{
+       struct ikev2_payloads pl;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+       if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+                                payload_len) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+                          "payloads");
+               return -1;
+       }
+
+       if (ikev2_process_idi(data, pl.idi, pl.idi_len) < 0 ||
+           ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+           ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+               return -1;
+
+       return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_responder_data *data,
+                                const struct ikev2_hdr *hdr,
+                                struct ikev2_payloads *pl)
+{
+       u8 *decrypted;
+       size_t decrypted_len;
+       int ret;
+
+       decrypted = ikev2_decrypt_payload(data->proposal.encr,
+                                         data->proposal.integ,
+                                         &data->keys, 1, hdr, pl->encrypted,
+                                         pl->encrypted_len, &decrypted_len);
+       if (decrypted == NULL)
+               return -1;
+
+       ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+                                             decrypted, decrypted_len);
+       os_free(decrypted);
+
+       return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_responder_data *data,
+                                  u8 exchange_type, u32 message_id)
+{
+       switch (data->state) {
+       case SA_INIT:
+               /* Expect to receive IKE_SA_INIT: HDR, SAi1, KEi, Ni */
+               if (exchange_type != IKE_SA_INIT) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+                                  "%u in SA_INIT state", exchange_type);
+                       return -1;
+               }
+               if (message_id != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+                                  "in SA_INIT state", message_id);
+                       return -1;
+               }
+               break;
+       case SA_AUTH:
+               /* Expect to receive IKE_SA_AUTH:
+                * HDR, SK {IDi, [CERT,] [CERTREQ,] [IDr,]
+                *      AUTH, SAi2, TSi, TSr}
+                */
+               if (exchange_type != IKE_SA_AUTH) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+                                  "%u in SA_AUTH state", exchange_type);
+                       return -1;
+               }
+               if (message_id != 1) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+                                  "in SA_AUTH state", message_id);
+                       return -1;
+               }
+               break;
+       case CHILD_SA:
+               if (exchange_type != CREATE_CHILD_SA) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+                                  "%u in CHILD_SA state", exchange_type);
+                       return -1;
+               }
+               if (message_id != 2) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+                                  "in CHILD_SA state", message_id);
+                       return -1;
+               }
+               break;
+       case NOTIFY:
+       case IKEV2_DONE:
+       case IKEV2_FAILED:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int ikev2_responder_process(struct ikev2_responder_data *data,
+                           const struct wpabuf *buf)
+{
+       const struct ikev2_hdr *hdr;
+       u32 length, message_id;
+       const u8 *pos, *end;
+       struct ikev2_payloads pl;
+
+       wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+                  (unsigned long) wpabuf_len(buf));
+
+       if (wpabuf_len(buf) < sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+               return -1;
+       }
+
+       data->error_type = 0;
+       hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+       end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+       message_id = WPA_GET_BE32(hdr->message_id);
+       length = WPA_GET_BE32(hdr->length);
+
+       wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
+                   hdr->i_spi, IKEV2_SPI_LEN);
+       wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Responder's SPI",
+                   hdr->r_spi, IKEV2_SPI_LEN);
+       wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Version: 0x%x  "
+                  "Exchange Type: %u",
+                  hdr->next_payload, hdr->version, hdr->exchange_type);
+       wpa_printf(MSG_DEBUG, "IKEV2:   Message ID: %u  Length: %u",
+                  message_id, length);
+
+       if (hdr->version != IKEV2_VERSION) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+                          "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+               return -1;
+       }
+
+       if (length != wpabuf_len(buf)) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+                          "RX: %lu)", (unsigned long) length,
+                          (unsigned long) wpabuf_len(buf));
+               return -1;
+       }
+
+       if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+               return -1;
+
+       if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+           IKEV2_HDR_INITIATOR) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+                          hdr->flags);
+               return -1;
+       }
+
+       if (data->state != SA_INIT) {
+               if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+                                  "Initiator's SPI");
+                       return -1;
+               }
+               if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+                                  "Responder's SPI");
+                       return -1;
+               }
+       }
+
+       pos = (const u8 *) (hdr + 1);
+       if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+               return -1;
+
+       if (data->state == SA_INIT) {
+               data->last_msg = LAST_MSG_SA_INIT;
+               if (ikev2_process_sa_init(data, hdr, &pl) < 0) {
+                       if (data->state == NOTIFY)
+                               return 0;
+                       return -1;
+               }
+               wpabuf_free(data->i_sign_msg);
+               data->i_sign_msg = wpabuf_dup(buf);
+       }
+
+       if (data->state == SA_AUTH) {
+               data->last_msg = LAST_MSG_SA_AUTH;
+               if (ikev2_process_sa_auth(data, hdr, &pl) < 0) {
+                       if (data->state == NOTIFY)
+                               return 0;
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_responder_data *data,
+                           struct wpabuf *msg, u8 exchange_type,
+                           u8 next_payload, u32 message_id)
+{
+       struct ikev2_hdr *hdr;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+       /* HDR - RFC 4306, Sect. 3.1 */
+       hdr = wpabuf_put(msg, sizeof(*hdr));
+       os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+       os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+       hdr->next_payload = next_payload;
+       hdr->version = IKEV2_VERSION;
+       hdr->exchange_type = exchange_type;
+       hdr->flags = IKEV2_HDR_RESPONSE;
+       WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sar1(struct ikev2_responder_data *data,
+                           struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       struct ikev2_proposal *p;
+       struct ikev2_transform *t;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding SAr1 payload");
+
+       /* SAr1 - RFC 4306, Sect. 2.7 and 3.3 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+
+       p = wpabuf_put(msg, sizeof(*p));
+#ifdef CCNS_PL
+       /* Seems to require that the Proposal # is 1 even though RFC 4306
+        * Sect 3.3.1 has following requirement "When a proposal is accepted,
+        * all of the proposal numbers in the SA payload MUST be the same and
+        * MUST match the number on the proposal sent that was accepted.".
+        */
+       p->proposal_num = 1;
+#else /* CCNS_PL */
+       p->proposal_num = data->proposal.proposal_num;
+#endif /* CCNS_PL */
+       p->protocol_id = IKEV2_PROTOCOL_IKE;
+       p->num_transforms = 4;
+
+       t = wpabuf_put(msg, sizeof(*t));
+       t->type = 3;
+       t->transform_type = IKEV2_TRANSFORM_ENCR;
+       WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+       if (data->proposal.encr == ENCR_AES_CBC) {
+               /* Transform Attribute: Key Len = 128 bits */
+#ifdef CCNS_PL
+               wpabuf_put_be16(msg, 0x001d); /* ?? */
+#else /* CCNS_PL */
+               wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+#endif /* CCNS_PL */
+               wpabuf_put_be16(msg, 128); /* 128-bit key */
+       }
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+       WPA_PUT_BE16(t->transform_length, plen);
+
+       t = wpabuf_put(msg, sizeof(*t));
+       t->type = 3;
+       WPA_PUT_BE16(t->transform_length, sizeof(*t));
+       t->transform_type = IKEV2_TRANSFORM_PRF;
+       WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+       t = wpabuf_put(msg, sizeof(*t));
+       t->type = 3;
+       WPA_PUT_BE16(t->transform_length, sizeof(*t));
+       t->transform_type = IKEV2_TRANSFORM_INTEG;
+       WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+       t = wpabuf_put(msg, sizeof(*t));
+       WPA_PUT_BE16(t->transform_length, sizeof(*t));
+       t->transform_type = IKEV2_TRANSFORM_DH;
+       WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+       WPA_PUT_BE16(p->proposal_length, plen);
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+
+       return 0;
+}
+
+
+static int ikev2_build_ker(struct ikev2_responder_data *data,
+                          struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       struct wpabuf *pv;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding KEr payload");
+
+       pv = dh_init(data->dh, &data->r_dh_private);
+       if (pv == NULL) {
+               wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+               return -1;
+       }
+
+       /* KEr - RFC 4306, Sect. 3.4 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+
+       wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+       wpabuf_put(msg, 2); /* RESERVED */
+       /*
+        * RFC 4306, Sect. 3.4: possible zero padding for public value to
+        * match the length of the prime.
+        */
+       wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+       wpabuf_put_buf(msg, pv);
+       wpabuf_free(pv);
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_nr(struct ikev2_responder_data *data,
+                         struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding Nr payload");
+
+       /* Nr - RFC 4306, Sect. 3.9 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+       wpabuf_put_data(msg, data->r_nonce, data->r_nonce_len);
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_idr(struct ikev2_responder_data *data,
+                          struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding IDr payload");
+
+       if (data->IDr == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No IDr available");
+               return -1;
+       }
+
+       /* IDr - RFC 4306, Sect. 3.5 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+       wpabuf_put_u8(msg, ID_KEY_ID);
+       wpabuf_put(msg, 3); /* RESERVED */
+       wpabuf_put_data(msg, data->IDr, data->IDr_len);
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_responder_data *data,
+                           struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       const struct ikev2_prf_alg *prf;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+       prf = ikev2_get_prf(data->proposal.prf);
+       if (prf == NULL)
+               return -1;
+
+       /* Authentication - RFC 4306, Sect. 3.8 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+       wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+       wpabuf_put(msg, 3); /* RESERVED */
+
+       /* msg | Ni | prf(SK_pr,IDr') */
+       if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+                                  data->IDr, data->IDr_len, ID_KEY_ID,
+                                  &data->keys, 0, data->shared_secret,
+                                  data->shared_secret_len,
+                                  data->i_nonce, data->i_nonce_len,
+                                  data->key_pad, data->key_pad_len,
+                                  wpabuf_put(msg, prf->hash_len)) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+               return -1;
+       }
+       wpabuf_free(data->r_sign_msg);
+       data->r_sign_msg = NULL;
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_notification(struct ikev2_responder_data *data,
+                                   struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding Notification payload");
+
+       if (data->error_type == 0) {
+               wpa_printf(MSG_INFO, "IKEV2: No Notify Message Type "
+                          "available");
+               return -1;
+       }
+
+       /* Notify - RFC 4306, Sect. 3.10 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+#ifdef CCNS_PL
+       wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */
+#else /* CCNS_PL */
+       wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */
+#endif /* CCNS_PL */
+       wpabuf_put_u8(msg, 0); /* SPI Size */
+       wpabuf_put_be16(msg, data->error_type);
+
+       switch (data->error_type) {
+       case INVALID_KE_PAYLOAD:
+               if (data->proposal.dh == -1) {
+                       wpa_printf(MSG_INFO, "IKEV2: No DH Group selected for "
+                                  "INVALID_KE_PAYLOAD notifications");
+                       return -1;
+               }
+               wpabuf_put_be16(msg, data->proposal.dh);
+               wpa_printf(MSG_DEBUG, "IKEV2: INVALID_KE_PAYLOAD - request "
+                          "DH Group #%d", data->proposal.dh);
+               break;
+       case AUTHENTICATION_FAILED:
+               /* no associated data */
+               break;
+       default:
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported Notify Message Type "
+                          "%d", data->error_type);
+               return -1;
+       }
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
+{
+       struct wpabuf *msg;
+
+       /* build IKE_SA_INIT: HDR, SAr1, KEr, Nr, [CERTREQ], [SK{IDr}] */
+
+       if (os_get_random(data->r_spi, IKEV2_SPI_LEN))
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Responder's SPI",
+                   data->r_spi, IKEV2_SPI_LEN);
+
+       data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
+       if (os_get_random(data->r_nonce, data->r_nonce_len))
+               return NULL;
+#ifdef CCNS_PL
+       /* Zeros are removed incorrectly from the beginning of the nonces in
+        * key derivation; as a workaround, make sure Nr does not start with
+        * zero.. */
+       if (data->r_nonce[0] == 0)
+               data->r_nonce[0] = 1;
+#endif /* CCNS_PL */
+       wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len);
+
+       msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500);
+       if (msg == NULL)
+               return NULL;
+
+       ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0);
+       if (ikev2_build_sar1(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) ||
+           ikev2_build_ker(data, msg, IKEV2_PAYLOAD_NONCE) ||
+           ikev2_build_nr(data, msg, data->peer_auth == PEER_AUTH_SECRET ?
+                          IKEV2_PAYLOAD_ENCRYPTED :
+                          IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (ikev2_derive_keys(data)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (data->peer_auth == PEER_AUTH_CERT) {
+               /* TODO: CERTREQ with SHA-1 hashes of Subject Public Key Info
+                * for trust agents */
+       }
+
+       if (data->peer_auth == PEER_AUTH_SECRET) {
+               struct wpabuf *plain = wpabuf_alloc(data->IDr_len + 1000);
+               if (plain == NULL) {
+                       wpabuf_free(msg);
+                       return NULL;
+               }
+               if (ikev2_build_idr(data, plain,
+                                   IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+                   ikev2_build_encrypted(data->proposal.encr,
+                                         data->proposal.integ,
+                                         &data->keys, 0, msg, plain,
+                                         IKEV2_PAYLOAD_IDr)) {
+                       wpabuf_free(plain);
+                       wpabuf_free(msg);
+                       return NULL;
+               }
+               wpabuf_free(plain);
+       }
+
+       ikev2_update_hdr(msg);
+
+       wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg);
+
+       data->state = SA_AUTH;
+
+       wpabuf_free(data->r_sign_msg);
+       data->r_sign_msg = wpabuf_dup(msg);
+
+       return msg;
+}
+
+
+static struct wpabuf * ikev2_build_sa_auth(struct ikev2_responder_data *data)
+{
+       struct wpabuf *msg, *plain;
+
+       /* build IKE_SA_AUTH: HDR, SK {IDr, [CERT,] AUTH} */
+
+       msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000);
+       if (msg == NULL)
+               return NULL;
+       ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1);
+
+       plain = wpabuf_alloc(data->IDr_len + 1000);
+       if (plain == NULL) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (ikev2_build_idr(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) ||
+           ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+           ikev2_build_encrypted(data->proposal.encr, data->proposal.integ,
+                                 &data->keys, 0, msg, plain,
+                                 IKEV2_PAYLOAD_IDr)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg);
+
+       data->state = IKEV2_DONE;
+
+       return msg;
+}
+
+
+static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data)
+{
+       struct wpabuf *msg;
+
+       msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000);
+       if (msg == NULL)
+               return NULL;
+       if (data->last_msg == LAST_MSG_SA_AUTH) {
+               /* HDR, SK{N} */
+               struct wpabuf *plain = wpabuf_alloc(100);
+               if (plain == NULL) {
+                       wpabuf_free(msg);
+                       return NULL;
+               }
+               ikev2_build_hdr(data, msg, IKE_SA_AUTH,
+                               IKEV2_PAYLOAD_ENCRYPTED, 1);
+               if (ikev2_build_notification(data, plain,
+                                            IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+                   ikev2_build_encrypted(data->proposal.encr,
+                                         data->proposal.integ,
+                                         &data->keys, 0, msg, plain,
+                                         IKEV2_PAYLOAD_NOTIFICATION)) {
+                       wpabuf_free(plain);
+                       wpabuf_free(msg);
+                       return NULL;
+               }
+               data->state = IKEV2_FAILED;
+       } else {
+               /* HDR, N */
+               ikev2_build_hdr(data, msg, IKE_SA_INIT,
+                               IKEV2_PAYLOAD_NOTIFICATION, 0);
+               if (ikev2_build_notification(data, msg,
+                                            IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+                       wpabuf_free(msg);
+                       return NULL;
+               }
+               data->state = SA_INIT;
+       }
+
+       ikev2_update_hdr(msg);
+
+       wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (Notification)",
+                       msg);
+
+       return msg;
+}
+
+
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data)
+{
+       switch (data->state) {
+       case SA_INIT:
+               return ikev2_build_sa_init(data);
+       case SA_AUTH:
+               return ikev2_build_sa_auth(data);
+       case CHILD_SA:
+               return NULL;
+       case NOTIFY:
+               return ikev2_build_notify(data);
+       case IKEV2_DONE:
+       case IKEV2_FAILED:
+               return NULL;
+       }
+       return NULL;
+}
diff --git a/src/eap_peer/ikev2.h b/src/eap_peer/ikev2.h
new file mode 100644 (file)
index 0000000..9ca0ca5
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * IKEv2 responder (RFC 4306) for EAP-IKEV2
+ * 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.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+       u8 proposal_num;
+       int integ;
+       int prf;
+       int encr;
+       int dh;
+};
+
+
+struct ikev2_responder_data {
+       enum { SA_INIT, SA_AUTH, CHILD_SA, NOTIFY, IKEV2_DONE, IKEV2_FAILED }
+               state;
+       u8 i_spi[IKEV2_SPI_LEN];
+       u8 r_spi[IKEV2_SPI_LEN];
+       u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+       size_t i_nonce_len;
+       u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+       size_t r_nonce_len;
+       struct wpabuf *i_dh_public;
+       struct wpabuf *r_dh_private;
+       struct ikev2_proposal_data proposal;
+       const struct dh_group *dh;
+       struct ikev2_keys keys;
+       u8 *IDi;
+       size_t IDi_len;
+       u8 IDi_type;
+       u8 *IDr;
+       size_t IDr_len;
+       struct wpabuf *r_sign_msg;
+       struct wpabuf *i_sign_msg;
+       u8 *shared_secret;
+       size_t shared_secret_len;
+       enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+       u8 *key_pad;
+       size_t key_pad_len;
+       u16 error_type;
+       enum { LAST_MSG_SA_INIT, LAST_MSG_SA_AUTH } last_msg;
+};
+
+
+void ikev2_responder_deinit(struct ikev2_responder_data *data);
+int ikev2_responder_process(struct ikev2_responder_data *data,
+                           const struct wpabuf *buf);
+struct wpabuf * ikev2_responder_build(struct ikev2_responder_data *data);
+
+#endif /* IKEV2_H */
diff --git a/src/eap_peer/mschapv2.c b/src/eap_peer/mschapv2.c
new file mode 100644 (file)
index 0000000..b8fb075
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * 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 "crypto/ms_funcs.h"
+#include "mschapv2.h"
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len)
+{
+       size_t i;
+
+       /*
+        * MSCHAPv2 does not include optional domain name in the
+        * challenge-response calculation, so remove domain prefix
+        * (if present).
+        */
+
+       for (i = 0; i < *len; i++) {
+               if (username[i] == '\\') {
+                       *len -= i + 1;
+                       return username + i + 1;
+               }
+       }
+
+       return username;
+}
+
+
+int mschapv2_derive_response(const u8 *identity, size_t identity_len,
+                            const u8 *password, size_t password_len,
+                            int pwhash,
+                            const u8 *auth_challenge,
+                            const u8 *peer_challenge,
+                            u8 *nt_response, u8 *auth_response,
+                            u8 *master_key)
+{
+       const u8 *username;
+       size_t username_len;
+       u8 password_hash[16], password_hash_hash[16];
+
+       wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Identity",
+                         identity, identity_len);
+       username_len = identity_len;
+       username = mschapv2_remove_domain(identity, &username_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: Username",
+                         username, username_len);
+
+       wpa_hexdump(MSG_DEBUG, "MSCHAPV2: auth_challenge",
+                   auth_challenge, MSCHAPV2_CHAL_LEN);
+       wpa_hexdump(MSG_DEBUG, "MSCHAPV2: peer_challenge",
+                   peer_challenge, MSCHAPV2_CHAL_LEN);
+       wpa_hexdump_ascii(MSG_DEBUG, "MSCHAPV2: username",
+                         username, username_len);
+       /* Authenticator response is not really needed yet, but calculate it
+        * here so that challenges need not be saved. */
+       if (pwhash) {
+               wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: password hash",
+                               password, password_len);
+               generate_nt_response_pwhash(auth_challenge, peer_challenge,
+                                           username, username_len,
+                                           password, nt_response);
+               generate_authenticator_response_pwhash(
+                       password, peer_challenge, auth_challenge,
+                       username, username_len, nt_response, auth_response);
+       } else {
+               wpa_hexdump_ascii_key(MSG_DEBUG, "MSCHAPV2: password",
+                                     password, password_len);
+               generate_nt_response(auth_challenge, peer_challenge,
+                                    username, username_len,
+                                    password, password_len, nt_response);
+               generate_authenticator_response(password, password_len,
+                                               peer_challenge, auth_challenge,
+                                               username, username_len,
+                                               nt_response, auth_response);
+       }
+       wpa_hexdump(MSG_DEBUG, "MSCHAPV2: NT Response",
+                   nt_response, MSCHAPV2_NT_RESPONSE_LEN);
+       wpa_hexdump(MSG_DEBUG, "MSCHAPV2: Auth Response",
+                   auth_response, MSCHAPV2_AUTH_RESPONSE_LEN);
+
+       /* Generate master_key here since we have the needed data available. */
+       if (pwhash) {
+               if (hash_nt_password_hash(password, password_hash_hash))
+                       return -1;
+       } else {
+               if (nt_password_hash(password, password_len, password_hash) ||
+                   hash_nt_password_hash(password_hash, password_hash_hash))
+                       return -1;
+       }
+       get_master_key(password_hash_hash, nt_response, master_key);
+       wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key",
+                       master_key, MSCHAPV2_MASTER_KEY_LEN);
+
+       return 0;
+}
+
+
+int mschapv2_verify_auth_response(const u8 *auth_response,
+                                 const u8 *buf, size_t buf_len)
+{
+       u8 recv_response[MSCHAPV2_AUTH_RESPONSE_LEN];
+       if (buf_len < 2 + 2 * MSCHAPV2_AUTH_RESPONSE_LEN ||
+           buf[0] != 'S' || buf[1] != '=' ||
+           hexstr2bin((char *) (buf + 2), recv_response,
+                      MSCHAPV2_AUTH_RESPONSE_LEN) ||
+           os_memcmp(auth_response, recv_response,
+                     MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+               return -1;
+       return 0;
+}
diff --git a/src/eap_peer/mschapv2.h b/src/eap_peer/mschapv2.h
new file mode 100644 (file)
index 0000000..90dad31
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * MSCHAPV2 (RFC 2759)
+ * 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 MSCHAPV2_H
+#define MSCHAPV2_H
+
+#define MSCHAPV2_CHAL_LEN 16
+#define MSCHAPV2_NT_RESPONSE_LEN 24
+#define MSCHAPV2_AUTH_RESPONSE_LEN 20
+#define MSCHAPV2_MASTER_KEY_LEN 16
+
+const u8 * mschapv2_remove_domain(const u8 *username, size_t *len);
+int mschapv2_derive_response(const u8 *username, size_t username_len,
+                            const u8 *password, size_t password_len,
+                            int pwhash,
+                            const u8 *auth_challenge,
+                            const u8 *peer_challenge,
+                            u8 *nt_response, u8 *auth_response,
+                            u8 *master_key);
+int mschapv2_verify_auth_response(const u8 *auth_response,
+                                 const u8 *buf, size_t buf_len);
+
+#endif /* MSCHAPV2_H */
diff --git a/src/eap_peer/tncc.c b/src/eap_peer/tncc.c
new file mode 100644 (file)
index 0000000..eaaa168
--- /dev/null
@@ -0,0 +1,1369 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * 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"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <dlfcn.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include "common.h"
+#include "base64.h"
+#include "tncc.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_defs.h"
+
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+#define TNC_CONFIG_FILE "/etc/tnc_config"
+#define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
+#define IF_TNCCS_START \
+"<?xml version=\"1.0\"?>\n" \
+"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMC */
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMCID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_MessageSubtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+
+typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
+       TNC_IMCID imcID,
+       char *functionName,
+       void **pOutfunctionPointer);
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_IFIMC_VERSION_1 1
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION               0x00000001
+#define TNC_TNCCS_ERROR                                0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE            0x00000003
+#define TNC_TNCCS_REASONSTRINGS                        0x00000004
+
+
+/* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
+enum {
+       SSOH_MS_MACHINE_INVENTORY = 1,
+       SSOH_MS_QUARANTINE_STATE = 2,
+       SSOH_MS_PACKET_INFO = 3,
+       SSOH_MS_SYSTEMGENERATED_IDS = 4,
+       SSOH_MS_MACHINENAME = 5,
+       SSOH_MS_CORRELATIONID = 6,
+       SSOH_MS_INSTALLED_SHVS = 7,
+       SSOH_MS_MACHINE_INVENTORY_EX = 8
+};
+
+struct tnc_if_imc {
+       struct tnc_if_imc *next;
+       char *name;
+       char *path;
+       void *dlhandle; /* from dlopen() */
+       TNC_IMCID imcID;
+       TNC_ConnectionID connectionID;
+       TNC_MessageTypeList supported_types;
+       size_t num_supported_types;
+       u8 *imc_send;
+       size_t imc_send_len;
+
+       /* Functions implemented by IMCs (with TNC_IMC_ prefix) */
+       TNC_Result (*Initialize)(
+               TNC_IMCID imcID,
+               TNC_Version minVersion,
+               TNC_Version maxVersion,
+               TNC_Version *pOutActualVersion);
+       TNC_Result (*NotifyConnectionChange)(
+               TNC_IMCID imcID,
+               TNC_ConnectionID connectionID,
+               TNC_ConnectionState newState);
+       TNC_Result (*BeginHandshake)(
+               TNC_IMCID imcID,
+               TNC_ConnectionID connectionID);
+       TNC_Result (*ReceiveMessage)(
+               TNC_IMCID imcID,
+               TNC_ConnectionID connectionID,
+               TNC_BufferReference messageBuffer,
+               TNC_UInt32 messageLength,
+               TNC_MessageType messageType);
+       TNC_Result (*BatchEnding)(
+               TNC_IMCID imcID,
+               TNC_ConnectionID connectionID);
+       TNC_Result (*Terminate)(TNC_IMCID imcID);
+       TNC_Result (*ProvideBindFunction)(
+               TNC_IMCID imcID,
+               TNC_TNCC_BindFunctionPointer bindFunction);
+};
+
+struct tncc_data {
+       struct tnc_if_imc *imc;
+       unsigned int last_batchid;
+};
+
+#define TNC_MAX_IMC_ID 10
+static struct tnc_if_imc *tnc_imc[TNC_MAX_IMC_ID] = { NULL };
+
+
+/* TNCC functions that IMCs can call */
+
+TNC_Result TNC_TNCC_ReportMessageTypes(
+       TNC_IMCID imcID,
+       TNC_MessageTypeList supportedTypes,
+       TNC_UInt32 typeCount)
+{
+       TNC_UInt32 i;
+       struct tnc_if_imc *imc;
+
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_ReportMessageTypes(imcID=%lu "
+                  "typeCount=%lu)",
+                  (unsigned long) imcID, (unsigned long) typeCount);
+
+       for (i = 0; i < typeCount; i++) {
+               wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+                          i, supportedTypes[i]);
+       }
+
+       if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       imc = tnc_imc[imcID];
+       os_free(imc->supported_types);
+       imc->supported_types =
+               os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+       if (imc->supported_types == NULL)
+               return TNC_RESULT_FATAL;
+       os_memcpy(imc->supported_types, supportedTypes,
+                 typeCount * sizeof(TNC_MessageTypeList));
+       imc->num_supported_types = typeCount;
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_SendMessage(
+       TNC_IMCID imcID,
+       TNC_ConnectionID connectionID,
+       TNC_BufferReference message,
+       TNC_UInt32 messageLength,
+       TNC_MessageType messageType)
+{
+       struct tnc_if_imc *imc;
+       unsigned char *b64;
+       size_t b64len;
+
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage(imcID=%lu "
+                  "connectionID=%lu messageType=%lu)",
+                  imcID, connectionID, messageType);
+       wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCC_SendMessage",
+                         message, messageLength);
+
+       if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       b64 = base64_encode(message, messageLength, &b64len);
+       if (b64 == NULL)
+               return TNC_RESULT_FATAL;
+
+       imc = tnc_imc[imcID];
+       os_free(imc->imc_send);
+       imc->imc_send_len = 0;
+       imc->imc_send = os_zalloc(b64len + 100);
+       if (imc->imc_send == NULL) {
+               os_free(b64);
+               return TNC_RESULT_OTHER;
+       }
+
+       imc->imc_send_len =
+               os_snprintf((char *) imc->imc_send, b64len + 100,
+                           "<IMC-IMV-Message><Type>%08X</Type>"
+                           "<Base64>%s</Base64></IMC-IMV-Message>",
+                           (unsigned int) messageType, b64);
+
+       os_free(b64);
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_RequestHandshakeRetry(
+       TNC_IMCID imcID,
+       TNC_ConnectionID connectionID,
+       TNC_RetryReason reason)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_RequestHandshakeRetry");
+
+       if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       /*
+        * TODO: trigger a call to eapol_sm_request_reauth(). This would
+        * require that the IMC continues to be loaded in memory afer
+        * authentication..
+        */
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_LogMessage(TNC_IMCID imcID, TNC_UInt32 severity,
+                              const char *message)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_9048_LogMessage(imcID=%lu "
+                  "severity==%lu message='%s')",
+                  imcID, severity, message);
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_9048_UserMessage(TNC_IMCID imcID, TNC_ConnectionID connectionID,
+                               const char *message)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_9048_UserMessage(imcID=%lu "
+                  "connectionID==%lu message='%s')",
+                  imcID, connectionID, message);
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCC_BindFunction(
+       TNC_IMCID imcID,
+       char *functionName,
+       void **pOutfunctionPointer)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCC_BindFunction(imcID=%lu, "
+                  "functionName='%s')", (unsigned long) imcID, functionName);
+
+       if (imcID >= TNC_MAX_IMC_ID || tnc_imc[imcID] == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       if (pOutfunctionPointer == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       if (os_strcmp(functionName, "TNC_TNCC_ReportMessageTypes") == 0)
+               *pOutfunctionPointer = TNC_TNCC_ReportMessageTypes;
+       else if (os_strcmp(functionName, "TNC_TNCC_SendMessage") == 0)
+               *pOutfunctionPointer = TNC_TNCC_SendMessage;
+       else if (os_strcmp(functionName, "TNC_TNCC_RequestHandshakeRetry") ==
+                0)
+               *pOutfunctionPointer = TNC_TNCC_RequestHandshakeRetry;
+       else if (os_strcmp(functionName, "TNC_9048_LogMessage") == 0)
+               *pOutfunctionPointer = TNC_9048_LogMessage;
+       else if (os_strcmp(functionName, "TNC_9048_UserMessage") == 0)
+               *pOutfunctionPointer = TNC_9048_UserMessage;
+       else
+               *pOutfunctionPointer = NULL;
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncc_get_sym(void *handle, char *func)
+{
+       void *fptr;
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef _WIN32_WCE
+       fptr = GetProcAddressA(handle, func);
+#else /* _WIN32_WCE */
+       fptr = GetProcAddress(handle, func);
+#endif /* _WIN32_WCE */
+#else /* CONFIG_NATIVE_WINDOWS */
+       fptr = dlsym(handle, func);
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       return fptr;
+}
+
+
+static int tncc_imc_resolve_funcs(struct tnc_if_imc *imc)
+{
+       void *handle = imc->dlhandle;
+
+       /* Mandatory IMC functions */
+       imc->Initialize = tncc_get_sym(handle, "TNC_IMC_Initialize");
+       if (imc->Initialize == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+                          "TNC_IMC_Initialize");
+               return -1;
+       }
+
+       imc->BeginHandshake = tncc_get_sym(handle, "TNC_IMC_BeginHandshake");
+       if (imc->BeginHandshake == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+                          "TNC_IMC_BeginHandshake");
+               return -1;
+       }
+
+       imc->ProvideBindFunction =
+               tncc_get_sym(handle, "TNC_IMC_ProvideBindFunction");
+       if (imc->ProvideBindFunction == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: IMC does not export "
+                          "TNC_IMC_ProvideBindFunction");
+               return -1;
+       }
+
+       /* Optional IMC functions */
+       imc->NotifyConnectionChange =
+               tncc_get_sym(handle, "TNC_IMC_NotifyConnectionChange");
+       imc->ReceiveMessage = tncc_get_sym(handle, "TNC_IMC_ReceiveMessage");
+       imc->BatchEnding = tncc_get_sym(handle, "TNC_IMC_BatchEnding");
+       imc->Terminate = tncc_get_sym(handle, "TNC_IMC_Terminate");
+
+       return 0;
+}
+
+
+static int tncc_imc_initialize(struct tnc_if_imc *imc)
+{
+       TNC_Result res;
+       TNC_Version imc_ver;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Initialize for IMC '%s'",
+                  imc->name);
+       res = imc->Initialize(imc->imcID, TNC_IFIMC_VERSION_1,
+                             TNC_IFIMC_VERSION_1, &imc_ver);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Initialize: res=%lu imc_ver=%lu",
+                  (unsigned long) res, (unsigned long) imc_ver);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_terminate(struct tnc_if_imc *imc)
+{
+       TNC_Result res;
+
+       if (imc->Terminate == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_Terminate for IMC '%s'",
+                  imc->name);
+       res = imc->Terminate(imc->imcID);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_Terminate: %lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_provide_bind_function(struct tnc_if_imc *imc)
+{
+       TNC_Result res;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_ProvideBindFunction for "
+                  "IMC '%s'", imc->name);
+       res = imc->ProvideBindFunction(imc->imcID, TNC_TNCC_BindFunction);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_ProvideBindFunction: res=%lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_notify_connection_change(struct tnc_if_imc *imc,
+                                            TNC_ConnectionState state)
+{
+       TNC_Result res;
+
+       if (imc->NotifyConnectionChange == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_NotifyConnectionChange(%d)"
+                  " for IMC '%s'", (int) state, imc->name);
+       res = imc->NotifyConnectionChange(imc->imcID, imc->connectionID,
+                                         state);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_imc_begin_handshake(struct tnc_if_imc *imc)
+{
+       TNC_Result res;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMC_BeginHandshake for IMC "
+                  "'%s'", imc->name);
+       res = imc->BeginHandshake(imc->imcID, imc->connectionID);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_BeginHandshake: %lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncc_load_imc(struct tnc_if_imc *imc)
+{
+       if (imc->path == NULL) {
+               wpa_printf(MSG_DEBUG, "TNC: No IMC configured");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TNC: Opening IMC: %s (%s)",
+                  imc->name, imc->path);
+#ifdef CONFIG_NATIVE_WINDOWS
+#ifdef UNICODE
+       {
+               TCHAR *lib = wpa_strdup_tchar(imc->path);
+               if (lib == NULL)
+                       return -1;
+               imc->dlhandle = LoadLibrary(lib);
+               os_free(lib);
+       }
+#else /* UNICODE */
+       imc->dlhandle = LoadLibrary(imc->path);
+#endif /* UNICODE */
+       if (imc->dlhandle == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %d",
+                          imc->name, imc->path, (int) GetLastError());
+               return -1;
+       }
+#else /* CONFIG_NATIVE_WINDOWS */
+       imc->dlhandle = dlopen(imc->path, RTLD_LAZY);
+       if (imc->dlhandle == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to open IMC '%s' (%s): %s",
+                          imc->name, imc->path, dlerror());
+               return -1;
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       if (tncc_imc_resolve_funcs(imc) < 0) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMC functions");
+               return -1;
+       }
+
+       if (tncc_imc_initialize(imc) < 0 ||
+           tncc_imc_provide_bind_function(imc) < 0) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMC");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void tncc_unload_imc(struct tnc_if_imc *imc)
+{
+       tncc_imc_terminate(imc);
+       tnc_imc[imc->imcID] = NULL;
+
+       if (imc->dlhandle) {
+#ifdef CONFIG_NATIVE_WINDOWS
+               FreeLibrary(imc->dlhandle);
+#else /* CONFIG_NATIVE_WINDOWS */
+               dlclose(imc->dlhandle);
+#endif /* CONFIG_NATIVE_WINDOWS */
+       }
+       os_free(imc->name);
+       os_free(imc->path);
+       os_free(imc->supported_types);
+       os_free(imc->imc_send);
+}
+
+
+static int tncc_supported_type(struct tnc_if_imc *imc, unsigned int type)
+{
+       size_t i;
+       unsigned int vendor, subtype;
+
+       if (imc == NULL || imc->supported_types == NULL)
+               return 0;
+
+       vendor = type >> 8;
+       subtype = type & 0xff;
+
+       for (i = 0; i < imc->num_supported_types; i++) {
+               unsigned int svendor, ssubtype;
+               svendor = imc->supported_types[i] >> 8;
+               ssubtype = imc->supported_types[i] & 0xff;
+               if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
+                   (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static void tncc_send_to_imcs(struct tncc_data *tncc, unsigned int type,
+                             const u8 *msg, size_t len)
+{
+       struct tnc_if_imc *imc;
+       TNC_Result res;
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMC(s)", msg, len);
+
+       for (imc = tncc->imc; imc; imc = imc->next) {
+               if (imc->ReceiveMessage == NULL ||
+                   !tncc_supported_type(imc, type))
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMC '%s'",
+                          imc->name);
+               res = imc->ReceiveMessage(imc->imcID, imc->connectionID,
+                                         (TNC_BufferReference) msg, len,
+                                         type);
+               wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
+                          (unsigned long) res);
+       }
+}
+
+
+void tncc_init_connection(struct tncc_data *tncc)
+{
+       struct tnc_if_imc *imc;
+
+       for (imc = tncc->imc; imc; imc = imc->next) {
+               tncc_imc_notify_connection_change(
+                       imc, TNC_CONNECTION_STATE_CREATE);
+               tncc_imc_notify_connection_change(
+                       imc, TNC_CONNECTION_STATE_HANDSHAKE);
+
+               os_free(imc->imc_send);
+               imc->imc_send = NULL;
+               imc->imc_send_len = 0;
+
+               tncc_imc_begin_handshake(imc);
+       }
+}
+
+
+size_t tncc_total_send_len(struct tncc_data *tncc)
+{
+       struct tnc_if_imc *imc;
+
+       size_t len = 0;
+       for (imc = tncc->imc; imc; imc = imc->next)
+               len += imc->imc_send_len;
+       return len;
+}
+
+
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos)
+{
+       struct tnc_if_imc *imc;
+
+       for (imc = tncc->imc; imc; imc = imc->next) {
+               if (imc->imc_send == NULL)
+                       continue;
+
+               os_memcpy(pos, imc->imc_send, imc->imc_send_len);
+               pos += imc->imc_send_len;
+               os_free(imc->imc_send);
+               imc->imc_send = NULL;
+               imc->imc_send_len = 0;
+       }
+
+       return pos;
+}
+
+
+char * tncc_if_tnccs_start(struct tncc_data *tncc)
+{
+       char *buf = os_malloc(1000);
+       if (buf == NULL)
+               return NULL;
+       tncc->last_batchid++;
+       os_snprintf(buf, 1000, IF_TNCCS_START, tncc->last_batchid);
+       return buf;
+}
+
+
+char * tncc_if_tnccs_end(void)
+{
+       char *buf = os_malloc(100);
+       if (buf == NULL)
+               return NULL;
+       os_snprintf(buf, 100, IF_TNCCS_END);
+       return buf;
+}
+
+
+static void tncc_notify_recommendation(struct tncc_data *tncc,
+                                      enum tncc_process_res res)
+{
+       TNC_ConnectionState state;
+       struct tnc_if_imc *imc;
+
+       switch (res) {
+       case TNCCS_RECOMMENDATION_ALLOW:
+               state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+               break;
+       case TNCCS_RECOMMENDATION_NONE:
+               state = TNC_CONNECTION_STATE_ACCESS_NONE;
+               break;
+       case TNCCS_RECOMMENDATION_ISOLATE:
+               state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+               break;
+       default:
+               state = TNC_CONNECTION_STATE_ACCESS_NONE;
+               break;
+       }
+
+       for (imc = tncc->imc; imc; imc = imc->next)
+               tncc_imc_notify_connection_change(imc, state);
+}
+
+
+static int tncc_get_type(char *start, unsigned int *type)
+{
+       char *pos = os_strstr(start, "<Type>");
+       if (pos == NULL)
+               return -1;
+       pos += 6;
+       *type = strtoul(pos, NULL, 16);
+       return 0;
+}
+
+
+static unsigned char * tncc_get_base64(char *start, size_t *decoded_len)
+{
+       char *pos, *pos2;
+       unsigned char *decoded;
+
+       pos = os_strstr(start, "<Base64>");
+       if (pos == NULL)
+               return NULL;
+
+       pos += 8;
+       pos2 = os_strstr(pos, "</Base64>");
+       if (pos2 == NULL)
+               return NULL;
+       *pos2 = '\0';
+
+       decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
+                               decoded_len);
+       *pos2 = '<';
+       if (decoded == NULL) {
+               wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
+       }
+
+       return decoded;
+}
+
+
+static enum tncc_process_res tncc_get_recommendation(char *start)
+{
+       char *pos, *pos2, saved;
+       int recom;
+
+       pos = os_strstr(start, "<TNCCS-Recommendation ");
+       if (pos == NULL)
+               return TNCCS_RECOMMENDATION_ERROR;
+
+       pos += 21;
+       pos = os_strstr(pos, " type=");
+       if (pos == NULL)
+               return TNCCS_RECOMMENDATION_ERROR;
+       pos += 6;
+
+       if (*pos == '"')
+               pos++;
+
+       pos2 = pos;
+       while (*pos2 != '\0' && *pos2 != '"' && *pos2 != '>')
+               pos2++;
+
+       if (*pos2 == '\0')
+               return TNCCS_RECOMMENDATION_ERROR;
+
+       saved = *pos2;
+       *pos2 = '\0';
+       wpa_printf(MSG_DEBUG, "TNC: TNCCS-Recommendation: '%s'", pos);
+
+       recom = TNCCS_RECOMMENDATION_ERROR;
+       if (os_strcmp(pos, "allow") == 0)
+               recom = TNCCS_RECOMMENDATION_ALLOW;
+       else if (os_strcmp(pos, "none") == 0)
+               recom = TNCCS_RECOMMENDATION_NONE;
+       else if (os_strcmp(pos, "isolate") == 0)
+               recom = TNCCS_RECOMMENDATION_ISOLATE;
+
+       *pos2 = saved;
+
+       return recom;
+}
+
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+                                           const u8 *msg, size_t len)
+{
+       char *buf, *start, *end, *pos, *pos2, *payload;
+       unsigned int batch_id;
+       unsigned char *decoded;
+       size_t decoded_len;
+       enum tncc_process_res res = TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+       int recommendation_msg = 0;
+
+       buf = os_malloc(len + 1);
+       if (buf == NULL)
+               return TNCCS_PROCESS_ERROR;
+
+       os_memcpy(buf, msg, len);
+       buf[len] = '\0';
+       start = os_strstr(buf, "<TNCCS-Batch ");
+       end = os_strstr(buf, "</TNCCS-Batch>");
+       if (start == NULL || end == NULL || start > end) {
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+
+       start += 13;
+       while (*start == ' ')
+               start++;
+       *end = '\0';
+
+       pos = os_strstr(start, "BatchId=");
+       if (pos == NULL) {
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+
+       pos += 8;
+       if (*pos == '"')
+               pos++;
+       batch_id = atoi(pos);
+       wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
+                  batch_id);
+       if (batch_id != tncc->last_batchid + 1) {
+               wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
+                          "%u (expected %u)",
+                          batch_id, tncc->last_batchid + 1);
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+       tncc->last_batchid = batch_id;
+
+       while (*pos != '\0' && *pos != '>')
+               pos++;
+       if (*pos == '\0') {
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+       pos++;
+       payload = start;
+
+       /*
+        * <IMC-IMV-Message>
+        * <Type>01234567</Type>
+        * <Base64>foo==</Base64>
+        * </IMC-IMV-Message>
+        */
+
+       while (*start) {
+               char *endpos;
+               unsigned int type;
+
+               pos = os_strstr(start, "<IMC-IMV-Message>");
+               if (pos == NULL)
+                       break;
+               start = pos + 17;
+               end = os_strstr(start, "</IMC-IMV-Message>");
+               if (end == NULL)
+                       break;
+               *end = '\0';
+               endpos = end;
+               end += 18;
+
+               if (tncc_get_type(start, &type) < 0) {
+                       *endpos = '<';
+                       start = end;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
+
+               decoded = tncc_get_base64(start, &decoded_len);
+               if (decoded == NULL) {
+                       *endpos = '<';
+                       start = end;
+                       continue;
+               }
+
+               tncc_send_to_imcs(tncc, type, decoded, decoded_len);
+
+               os_free(decoded);
+
+               start = end;
+       }
+
+       /*
+        * <TNCC-TNCS-Message>
+        * <Type>01234567</Type>
+        * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
+        * <Base64>foo==</Base64>
+        * </TNCC-TNCS-Message>
+        */
+
+       start = payload;
+       while (*start) {
+               unsigned int type;
+               char *xml, *xmlend, *endpos;
+
+               pos = os_strstr(start, "<TNCC-TNCS-Message>");
+               if (pos == NULL)
+                       break;
+               start = pos + 19;
+               end = os_strstr(start, "</TNCC-TNCS-Message>");
+               if (end == NULL)
+                       break;
+               *end = '\0';
+               endpos = end;
+               end += 20;
+
+               if (tncc_get_type(start, &type) < 0) {
+                       *endpos = '<';
+                       start = end;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
+                          type);
+
+               /* Base64 OR XML */
+               decoded = NULL;
+               xml = NULL;
+               xmlend = NULL;
+               pos = os_strstr(start, "<XML>");
+               if (pos) {
+                       pos += 5;
+                       pos2 = os_strstr(pos, "</XML>");
+                       if (pos2 == NULL) {
+                               *endpos = '<';
+                               start = end;
+                               continue;
+                       }
+                       xmlend = pos2;
+                       xml = pos;
+               } else {
+                       decoded = tncc_get_base64(start, &decoded_len);
+                       if (decoded == NULL) {
+                               *endpos = '<';
+                               start = end;
+                               continue;
+                       }
+               }
+
+               if (decoded) {
+                       wpa_hexdump_ascii(MSG_MSGDUMP,
+                                         "TNC: TNCC-TNCS-Message Base64",
+                                         decoded, decoded_len);
+                       os_free(decoded);
+               }
+
+               if (xml) {
+                       wpa_hexdump_ascii(MSG_MSGDUMP,
+                                         "TNC: TNCC-TNCS-Message XML",
+                                         (unsigned char *) xml,
+                                         xmlend - xml);
+               }
+
+               if (type == TNC_TNCCS_RECOMMENDATION && xml) {
+                       /*
+                        * <TNCCS-Recommendation type="allow">
+                        * </TNCCS-Recommendation>
+                        */
+                       *xmlend = '\0';
+                       res = tncc_get_recommendation(xml);
+                       *xmlend = '<';
+                       recommendation_msg = 1;
+               }
+
+               start = end;
+       }
+
+       os_free(buf);
+
+       if (recommendation_msg)
+               tncc_notify_recommendation(tncc, res);
+
+       return res;
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+static int tncc_read_config_reg(struct tncc_data *tncc, HKEY hive)
+{
+       HKEY hk, hk2;
+       LONG ret;
+       DWORD i;
+       struct tnc_if_imc *imc, *last;
+       int j;
+
+       last = tncc->imc;
+       while (last && last->next)
+               last = last->next;
+
+       ret = RegOpenKeyEx(hive, TNC_WINREG_PATH, 0, KEY_ENUMERATE_SUB_KEYS,
+                          &hk);
+       if (ret != ERROR_SUCCESS)
+               return 0;
+
+       for (i = 0; ; i++) {
+               TCHAR name[255], *val;
+               DWORD namelen, buflen;
+
+               namelen = 255;
+               ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
+                                  NULL);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "TNC: RegEnumKeyEx failed: 0x%x",
+                                  (unsigned int) ret);
+                       break;
+               }
+
+               if (namelen >= 255)
+                       namelen = 255 - 1;
+               name[namelen] = '\0';
+
+               wpa_printf(MSG_DEBUG, "TNC: IMC '" TSTR "'", name);
+
+               ret = RegOpenKeyEx(hk, name, 0, KEY_QUERY_VALUE, &hk2);
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "Could not open IMC key '" TSTR
+                                  "'", name);
+                       continue;
+               }
+
+               ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL, NULL,
+                                     &buflen);
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "TNC: Could not read Path from "
+                                  "IMC key '" TSTR "'", name);
+                       RegCloseKey(hk2);
+                       continue;
+               }
+
+               val = os_malloc(buflen);
+               if (val == NULL) {
+                       RegCloseKey(hk2);
+                       continue;
+               }
+
+               ret = RegQueryValueEx(hk2, TEXT("Path"), NULL, NULL,
+                                     (LPBYTE) val, &buflen);
+               if (ret != ERROR_SUCCESS) {
+                       os_free(val);
+                       RegCloseKey(hk2);
+                       continue;
+               }
+
+               RegCloseKey(hk2);
+
+               wpa_unicode2ascii_inplace(val);
+               wpa_printf(MSG_DEBUG, "TNC: IMC Path '%s'", (char *) val);
+
+               for (j = 0; j < TNC_MAX_IMC_ID; j++) {
+                       if (tnc_imc[j] == NULL)
+                               break;
+               }
+               if (j >= TNC_MAX_IMC_ID) {
+                       wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
+                       os_free(val);
+                       continue;
+               }
+
+               imc = os_zalloc(sizeof(*imc));
+               if (imc == NULL) {
+                       os_free(val);
+                       break;
+               }
+
+               imc->imcID = j;
+
+               wpa_unicode2ascii_inplace(name);
+               imc->name = os_strdup((char *) name);
+               imc->path = os_strdup((char *) val);
+
+               os_free(val);
+
+               if (last == NULL)
+                       tncc->imc = imc;
+               else
+                       last->next = imc;
+               last = imc;
+
+               tnc_imc[imc->imcID] = imc;
+       }
+
+       RegCloseKey(hk);
+
+       return 0;
+}
+
+
+static int tncc_read_config(struct tncc_data *tncc)
+{
+       if (tncc_read_config_reg(tncc, HKEY_LOCAL_MACHINE) < 0 ||
+           tncc_read_config_reg(tncc, HKEY_CURRENT_USER) < 0)
+               return -1;
+       return 0;
+}
+
+#else /* CONFIG_NATIVE_WINDOWS */
+
+static struct tnc_if_imc * tncc_parse_imc(char *start, char *end, int *error)
+{
+       struct tnc_if_imc *imc;
+       char *pos, *pos2;
+       int i;
+
+       for (i = 0; i < TNC_MAX_IMC_ID; i++) {
+               if (tnc_imc[i] == NULL)
+                       break;
+       }
+       if (i >= TNC_MAX_IMC_ID) {
+               wpa_printf(MSG_DEBUG, "TNC: Too many IMCs");
+               return NULL;
+       }
+
+       imc = os_zalloc(sizeof(*imc));
+       if (imc == NULL) {
+               *error = 1;
+               return NULL;
+       }
+
+       imc->imcID = i;
+
+       pos = start;
+       wpa_printf(MSG_DEBUG, "TNC: Configured IMC: %s", pos);
+       if (pos + 1 >= end || *pos != '"') {
+               wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+                          "(no starting quotation mark)", start);
+               os_free(imc);
+               return NULL;
+       }
+
+       pos++;
+       pos2 = pos;
+       while (pos2 < end && *pos2 != '"')
+               pos2++;
+       if (pos2 >= end) {
+               wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+                          "(no ending quotation mark)", start);
+               os_free(imc);
+               return NULL;
+       }
+       *pos2 = '\0';
+       wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
+       imc->name = os_strdup(pos);
+
+       pos = pos2 + 1;
+       if (pos >= end || *pos != ' ') {
+               wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMC line '%s' "
+                          "(no space after name)", start);
+               os_free(imc->name);
+               os_free(imc);
+               return NULL;
+       }
+
+       pos++;
+       wpa_printf(MSG_DEBUG, "TNC: IMC file: '%s'", pos);
+       imc->path = os_strdup(pos);
+       tnc_imc[imc->imcID] = imc;
+
+       return imc;
+}
+
+
+static int tncc_read_config(struct tncc_data *tncc)
+{
+       char *config, *end, *pos, *line_end;
+       size_t config_len;
+       struct tnc_if_imc *imc, *last;
+
+       last = NULL;
+
+       config = os_readfile(TNC_CONFIG_FILE, &config_len);
+       if (config == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
+                          "file '%s'", TNC_CONFIG_FILE);
+               return -1;
+       }
+
+       end = config + config_len;
+       for (pos = config; pos < end; pos = line_end + 1) {
+               line_end = pos;
+               while (*line_end != '\n' && *line_end != '\r' &&
+                      line_end < end)
+                       line_end++;
+               *line_end = '\0';
+
+               if (os_strncmp(pos, "IMC ", 4) == 0) {
+                       int error = 0;
+
+                       imc = tncc_parse_imc(pos + 4, line_end, &error);
+                       if (error)
+                               return -1;
+                       if (imc) {
+                               if (last == NULL)
+                                       tncc->imc = imc;
+                               else
+                                       last->next = imc;
+                               last = imc;
+                       }
+               }
+       }
+
+       os_free(config);
+
+       return 0;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct tncc_data * tncc_init(void)
+{
+       struct tncc_data *tncc;
+       struct tnc_if_imc *imc;
+
+       tncc = os_zalloc(sizeof(*tncc));
+       if (tncc == NULL)
+               return NULL;
+
+       /* TODO:
+        * move loading and Initialize() to a location that is not
+        *    re-initialized for every EAP-TNC session (?)
+        */
+
+       if (tncc_read_config(tncc) < 0) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
+               goto failed;
+       }
+
+       for (imc = tncc->imc; imc; imc = imc->next) {
+               if (tncc_load_imc(imc)) {
+                       wpa_printf(MSG_ERROR, "TNC: Failed to load IMC '%s'",
+                                  imc->name);
+                       goto failed;
+               }
+       }
+
+       return tncc;
+
+failed:
+       tncc_deinit(tncc);
+       return NULL;
+}
+
+
+void tncc_deinit(struct tncc_data *tncc)
+{
+       struct tnc_if_imc *imc, *prev;
+
+       imc = tncc->imc;
+       while (imc) {
+               tncc_unload_imc(imc);
+
+               prev = imc;
+               imc = imc->next;
+               os_free(prev);
+       }
+
+       os_free(tncc);
+}
+
+
+static struct wpabuf * tncc_build_soh(int ver)
+{
+       struct wpabuf *buf;
+       u8 *tlv_len, *tlv_len2, *outer_len, *inner_len, *ssoh_len, *end;
+       u8 correlation_id[24];
+       /* TODO: get correct name */
+       char *machinename = "wpa_supplicant@w1.fi";
+
+       if (os_get_random(correlation_id, sizeof(correlation_id)))
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "TNC: SoH Correlation ID",
+                   correlation_id, sizeof(correlation_id));
+
+       buf = wpabuf_alloc(200);
+       if (buf == NULL)
+               return NULL;
+
+       /* Vendor-Specific TLV (Microsoft) - SoH */
+       wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
+       tlv_len = wpabuf_put(buf, 2); /* Length */
+       wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
+       wpabuf_put_be16(buf, 0x01); /* TLV Type - SoH TLV */
+       tlv_len2 = wpabuf_put(buf, 2); /* Length */
+
+       /* SoH Header */
+       wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* Outer Type */
+       outer_len = wpabuf_put(buf, 2);
+       wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+       wpabuf_put_be16(buf, ver); /* Inner Type */
+       inner_len = wpabuf_put(buf, 2);
+
+       if (ver == 2) {
+               /* SoH Mode Sub-Header */
+               /* Outer Type */
+               wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
+               wpabuf_put_be16(buf, 4 + 24 + 1 + 1); /* Length */
+               wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+               /* Value: */
+               wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
+               wpabuf_put_u8(buf, 0x01); /* Intent Flag - Request */
+               wpabuf_put_u8(buf, 0x00); /* Content-Type Flag */
+       }
+
+       /* SSoH TLV */
+       /* System-Health-Id */
+       wpabuf_put_be16(buf, 0x0002); /* Type */
+       wpabuf_put_be16(buf, 4); /* Length */
+       wpabuf_put_be32(buf, 79616);
+       /* Vendor-Specific Attribute */
+       wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV);
+       ssoh_len = wpabuf_put(buf, 2);
+       wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* IANA SMI Code */
+
+       /* MS-Packet-Info */
+       wpabuf_put_u8(buf, SSOH_MS_PACKET_INFO);
+       /* Note: IF-TNCCS-SOH v1.0 r8 claims this field to be:
+        * Reserved(4 bits) r(1 bit) Vers(3 bits), but Windows XP
+        * SP3 seems to be sending 0x11 for SSoH, i.e., r(request/response) bit
+        * would not be in the specified location.
+        * [MS-SOH] 4.0.2: Reserved(3 bits) r(1 bit) Vers(4 bits)
+        */
+       wpabuf_put_u8(buf, 0x11); /* r=request, vers=1 */
+
+       /* MS-Machine-Inventory */
+       /* TODO: get correct values; 0 = not applicable for OS */
+       wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY);
+       wpabuf_put_be32(buf, 0); /* osVersionMajor */
+       wpabuf_put_be32(buf, 0); /* osVersionMinor */
+       wpabuf_put_be32(buf, 0); /* osVersionBuild */
+       wpabuf_put_be16(buf, 0); /* spVersionMajor */
+       wpabuf_put_be16(buf, 0); /* spVersionMinor */
+       wpabuf_put_be16(buf, 0); /* procArch */
+
+       /* MS-MachineName */
+       wpabuf_put_u8(buf, SSOH_MS_MACHINENAME);
+       wpabuf_put_be16(buf, os_strlen(machinename) + 1);
+       wpabuf_put_data(buf, machinename, os_strlen(machinename) + 1);
+
+       /* MS-CorrelationId */
+       wpabuf_put_u8(buf, SSOH_MS_CORRELATIONID);
+       wpabuf_put_data(buf, correlation_id, sizeof(correlation_id));
+
+       /* MS-Quarantine-State */
+       wpabuf_put_u8(buf, SSOH_MS_QUARANTINE_STATE);
+       wpabuf_put_be16(buf, 1); /* Flags: ExtState=0, f=0, qState=1 */
+       wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (hi) */
+       wpabuf_put_be32(buf, 0xffffffff); /* ProbTime (lo) */
+       wpabuf_put_be16(buf, 1); /* urlLenInBytes */
+       wpabuf_put_u8(buf, 0); /* null termination for the url */
+
+       /* MS-Machine-Inventory-Ex */
+       wpabuf_put_u8(buf, SSOH_MS_MACHINE_INVENTORY_EX);
+       wpabuf_put_be32(buf, 0); /* Reserved
+                                 * (note: Windows XP SP3 uses 0xdecafbad) */
+       wpabuf_put_u8(buf, 1); /* ProductType: Client */
+
+       /* Update SSoH Length */
+       end = wpabuf_put(buf, 0);
+       WPA_PUT_BE16(ssoh_len, end - ssoh_len - 2);
+
+       /* TODO: SoHReportEntry TLV (zero or more) */
+
+       /* Update length fields */
+       end = wpabuf_put(buf, 0);
+       WPA_PUT_BE16(tlv_len, end - tlv_len - 2);
+       WPA_PUT_BE16(tlv_len2, end - tlv_len2 - 2);
+       WPA_PUT_BE16(outer_len, end - outer_len - 2);
+       WPA_PUT_BE16(inner_len, end - inner_len - 2);
+
+       return buf;
+}
+
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len)
+{
+       const u8 *pos;
+
+       wpa_hexdump(MSG_DEBUG, "TNC: SoH Request", data, len);
+
+       if (len < 12)
+               return NULL;
+
+       /* SoH Request */
+       pos = data;
+
+       /* TLV Type */
+       if (WPA_GET_BE16(pos) != EAP_TLV_VENDOR_SPECIFIC_TLV)
+               return NULL;
+       pos += 2;
+
+       /* Length */
+       if (WPA_GET_BE16(pos) < 8)
+               return NULL;
+       pos += 2;
+
+       /* Vendor_Id */
+       if (WPA_GET_BE32(pos) != EAP_VENDOR_MICROSOFT)
+               return NULL;
+       pos += 4;
+
+       /* TLV Type */
+       if (WPA_GET_BE16(pos) != 0x02 /* SoH request TLV */)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "TNC: SoH Request TLV received");
+
+       return tncc_build_soh(2);
+}
diff --git a/src/eap_peer/tncc.h b/src/eap_peer/tncc.h
new file mode 100644 (file)
index 0000000..4d42a05
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * EAP-TNC - TNCC (IF-IMC and IF-TNCCS)
+ * 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.
+ */
+
+#ifndef TNCC_H
+#define TNCC_H
+
+struct tncc_data;
+
+struct tncc_data * tncc_init(void);
+void tncc_deinit(struct tncc_data *tncc);
+void tncc_init_connection(struct tncc_data *tncc);
+size_t tncc_total_send_len(struct tncc_data *tncc);
+u8 * tncc_copy_send_buf(struct tncc_data *tncc, u8 *pos);
+char * tncc_if_tnccs_start(struct tncc_data *tncc);
+char * tncc_if_tnccs_end(void);
+
+enum tncc_process_res {
+       TNCCS_PROCESS_ERROR = -1,
+       TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+       TNCCS_RECOMMENDATION_ERROR,
+       TNCCS_RECOMMENDATION_ALLOW,
+       TNCCS_RECOMMENDATION_NONE,
+       TNCCS_RECOMMENDATION_ISOLATE
+};
+
+enum tncc_process_res tncc_process_if_tnccs(struct tncc_data *tncc,
+                                           const u8 *msg, size_t len);
+
+struct wpabuf * tncc_process_soh_request(int ver, const u8 *data, size_t len);
+
+#endif /* TNCC_H */
diff --git a/src/eap_server/Makefile b/src/eap_server/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
new file mode 100644 (file)
index 0000000..92400a5
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * hostapd / EAP Full Authenticator state machine (RFC 4137)
+ * 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 EAP_H
+#define EAP_H
+
+#include "common/defs.h"
+#include "eap_common/eap_defs.h"
+#include "eap_server/eap_methods.h"
+#include "wpabuf.h"
+
+struct eap_sm;
+
+#define EAP_MAX_METHODS 8
+
+#define EAP_TTLS_AUTH_PAP 1
+#define EAP_TTLS_AUTH_CHAP 2
+#define EAP_TTLS_AUTH_MSCHAP 4
+#define EAP_TTLS_AUTH_MSCHAPV2 8
+
+struct eap_user {
+       struct {
+               int vendor;
+               u32 method;
+       } methods[EAP_MAX_METHODS];
+       u8 *password;
+       size_t password_len;
+       int password_hash; /* whether password is hashed with
+                           * nt_password_hash() */
+       int phase2;
+       int force_version;
+       int ttls_auth; /* bitfield of
+                       * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
+};
+
+struct eap_eapol_interface {
+       /* Lower layer to full authenticator variables */
+       Boolean eapResp; /* shared with EAPOL Backend Authentication */
+       struct wpabuf *eapRespData;
+       Boolean portEnabled;
+       int retransWhile;
+       Boolean eapRestart; /* shared with EAPOL Authenticator PAE */
+       int eapSRTT;
+       int eapRTTVAR;
+
+       /* Full authenticator to lower layer variables */
+       Boolean eapReq; /* shared with EAPOL Backend Authentication */
+       Boolean eapNoReq; /* shared with EAPOL Backend Authentication */
+       Boolean eapSuccess;
+       Boolean eapFail;
+       Boolean eapTimeout;
+       struct wpabuf *eapReqData;
+       u8 *eapKeyData;
+       size_t eapKeyDataLen;
+       Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
+
+       /* AAA interface to full authenticator variables */
+       Boolean aaaEapReq;
+       Boolean aaaEapNoReq;
+       Boolean aaaSuccess;
+       Boolean aaaFail;
+       struct wpabuf *aaaEapReqData;
+       u8 *aaaEapKeyData;
+       size_t aaaEapKeyDataLen;
+       Boolean aaaEapKeyAvailable;
+       int aaaMethodTimeout;
+
+       /* Full authenticator to AAA interface variables */
+       Boolean aaaEapResp;
+       struct wpabuf *aaaEapRespData;
+       /* aaaIdentity -> eap_get_identity() */
+       Boolean aaaTimeout;
+};
+
+struct eapol_callbacks {
+       int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+                           int phase2, struct eap_user *user);
+       const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
+};
+
+struct eap_config {
+       void *ssl_ctx;
+       void *msg_ctx;
+       void *eap_sim_db_priv;
+       Boolean backend_auth;
+       int eap_server;
+       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;
+       const struct wpabuf *assoc_wps_ie;
+       const u8 *peer_addr;
+};
+
+
+struct eap_sm * eap_server_sm_init(void *eapol_ctx,
+                                  struct eapol_callbacks *eapol_cb,
+                                  struct eap_config *eap_conf);
+void eap_server_sm_deinit(struct eap_sm *sm);
+int eap_server_sm_step(struct eap_sm *sm);
+void eap_sm_notify_cached(struct eap_sm *sm);
+void eap_sm_pending_cb(struct eap_sm *sm);
+int eap_sm_method_pending(struct eap_sm *sm);
+const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
+struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
+
+#endif /* EAP_H */
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
new file mode 100644 (file)
index 0000000..4269a8c
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * hostapd / EAP Authenticator state machine internal structures (RFC 4137)
+ * 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 EAP_I_H
+#define EAP_I_H
+
+#include "wpabuf.h"
+#include "eap_server/eap.h"
+#include "eap_common/eap_common.h"
+
+/* RFC 4137 - EAP Standalone Authenticator */
+
+/**
+ * struct eap_method - EAP method interface
+ * This structure defines the EAP method interface. Each method will need to
+ * register its own EAP type, EAP name, and set of function pointers for method
+ * specific operations. This interface is based on section 5.4 of RFC 4137.
+ */
+struct eap_method {
+       int vendor;
+       EapType method;
+       const char *name;
+
+       void * (*init)(struct eap_sm *sm);
+       void * (*initPickUp)(struct eap_sm *sm);
+       void (*reset)(struct eap_sm *sm, void *priv);
+
+       struct wpabuf * (*buildReq)(struct eap_sm *sm, void *priv, u8 id);
+       int (*getTimeout)(struct eap_sm *sm, void *priv);
+       Boolean (*check)(struct eap_sm *sm, void *priv,
+                        struct wpabuf *respData);
+       void (*process)(struct eap_sm *sm, void *priv,
+                       struct wpabuf *respData);
+       Boolean (*isDone)(struct eap_sm *sm, void *priv);
+       u8 * (*getKey)(struct eap_sm *sm, void *priv, size_t *len);
+       /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt,
+        * but it is useful in implementing Policy.getDecision() */
+       Boolean (*isSuccess)(struct eap_sm *sm, void *priv);
+
+       /**
+        * free - Free EAP method data
+        * @method: Pointer to the method data registered with
+        * eap_server_method_register().
+        *
+        * This function will be called when the EAP method is being
+        * unregistered. If the EAP method allocated resources during
+        * registration (e.g., allocated struct eap_method), they should be
+        * freed in this function. No other method functions will be called
+        * after this call. If this function is not defined (i.e., function
+        * pointer is %NULL), a default handler is used to release the method
+        * data with free(method). This is suitable for most cases.
+        */
+       void (*free)(struct eap_method *method);
+
+#define EAP_SERVER_METHOD_INTERFACE_VERSION 1
+       /**
+        * version - Version of the EAP server method interface
+        *
+        * The EAP server method implementation should set this variable to
+        * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the
+        * EAP method is using supported API version when using dynamically
+        * loadable EAP methods.
+        */
+       int version;
+
+       /**
+        * next - Pointer to the next EAP method
+        *
+        * This variable is used internally in the EAP method registration code
+        * to create a linked list of registered EAP methods.
+        */
+       struct eap_method *next;
+
+       /**
+        * get_emsk - Get EAP method specific keying extended material (EMSK)
+        * @sm: Pointer to EAP state machine allocated with eap_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @len: Pointer to a variable to store EMSK length
+        * Returns: EMSK or %NULL if not available
+        *
+        * This function can be used to get the extended keying material from
+        * the EAP method. The key may already be stored in the method-specific
+        * private data or this function may derive the key.
+        */
+       u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+};
+
+/**
+ * struct eap_sm - EAP server state machine data
+ */
+struct eap_sm {
+       enum {
+               EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED,
+               EAP_INTEGRITY_CHECK, EAP_METHOD_RESPONSE, EAP_METHOD_REQUEST,
+               EAP_PROPOSE_METHOD, EAP_SELECT_ACTION, EAP_SEND_REQUEST,
+               EAP_DISCARD, EAP_NAK, EAP_RETRANSMIT, EAP_SUCCESS, EAP_FAILURE,
+               EAP_TIMEOUT_FAILURE, EAP_PICK_UP_METHOD,
+               EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
+               EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
+               EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
+               EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
+       } EAP_state;
+
+       /* Constants */
+       int MaxRetrans;
+
+       struct eap_eapol_interface eap_if;
+
+       /* Full authenticator state machine local variables */
+
+       /* Long-term (maintained betwen packets) */
+       EapType currentMethod;
+       int currentId;
+       enum {
+               METHOD_PROPOSED, METHOD_CONTINUE, METHOD_END
+       } methodState;
+       int retransCount;
+       struct wpabuf *lastReqData;
+       int methodTimeout;
+
+       /* Short-term (not maintained between packets) */
+       Boolean rxResp;
+       int respId;
+       EapType respMethod;
+       int respVendor;
+       u32 respVendorMethod;
+       Boolean ignore;
+       enum {
+               DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
+               DECISION_PASSTHROUGH
+       } decision;
+
+       /* Miscellaneous variables */
+       const struct eap_method *m; /* selected EAP method */
+       /* not defined in RFC 4137 */
+       Boolean changed;
+       void *eapol_ctx, *msg_ctx;
+       struct eapol_callbacks *eapol_cb;
+       void *eap_method_priv;
+       u8 *identity;
+       size_t identity_len;
+       /* Whether Phase 2 method should validate identity match */
+       int require_identity_match;
+       int lastId; /* Identifier used in the last EAP-Packet */
+       struct eap_user *user;
+       int user_eap_method_index;
+       int init_phase2;
+       void *ssl_ctx;
+       void *eap_sim_db_priv;
+       Boolean backend_auth;
+       Boolean update_user;
+       int eap_server;
+
+       int num_rounds;
+       enum {
+               METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT
+       } method_pending;
+
+       u8 *auth_challenge;
+       u8 *peer_challenge;
+
+       u8 *pac_opaque_encr_key;
+       u8 *eap_fast_a_id;
+       size_t eap_fast_a_id_len;
+       char *eap_fast_a_id_info;
+       enum {
+               NO_PROV, ANON_PROV, AUTH_PROV, BOTH_PROV
+       } eap_fast_prov;
+       int pac_key_lifetime;
+       int pac_key_refresh_time;
+       int eap_sim_aka_result_ind;
+       int tnc;
+       struct wps_context *wps;
+       struct wpabuf *assoc_wps_ie;
+
+       Boolean start_reauth;
+
+       u8 peer_addr[ETH_ALEN];
+};
+
+int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+                int phase2);
+void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
+
+#endif /* EAP_I_H */
diff --git a/src/eap_server/eap_methods.h b/src/eap_server/eap_methods.h
new file mode 100644 (file)
index 0000000..5d4d92c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * EAP server method registration
+ * 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.
+ */
+
+#ifndef EAP_SERVER_METHODS_H
+#define EAP_SERVER_METHODS_H
+
+#include "eap_common/eap_defs.h"
+
+const struct eap_method * eap_server_get_eap_method(int vendor,
+                                                   EapType method);
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+                                           EapType method, const char *name);
+void eap_server_method_free(struct eap_method *method);
+int eap_server_method_register(struct eap_method *method);
+
+EapType eap_server_get_type(const char *name, int *vendor);
+void eap_server_unregister_methods(void);
+const char * eap_server_get_name(int vendor, EapType type);
+
+/* EAP server method registration calls for statically linked in methods */
+int eap_server_identity_register(void);
+int eap_server_md5_register(void);
+int eap_server_tls_register(void);
+int eap_server_mschapv2_register(void);
+int eap_server_peap_register(void);
+int eap_server_tlv_register(void);
+int eap_server_gtc_register(void);
+int eap_server_ttls_register(void);
+int eap_server_sim_register(void);
+int eap_server_aka_register(void);
+int eap_server_aka_prime_register(void);
+int eap_server_pax_register(void);
+int eap_server_psk_register(void);
+int eap_server_sake_register(void);
+int eap_server_gpsk_register(void);
+int eap_server_vendor_test_register(void);
+int eap_server_fast_register(void);
+int eap_server_wsc_register(void);
+int eap_server_ikev2_register(void);
+int eap_server_tnc_register(void);
+
+#endif /* EAP_SERVER_METHODS_H */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
new file mode 100644 (file)
index 0000000..fdc26f9
--- /dev/null
@@ -0,0 +1,1364 @@
+/*
+ * hostapd / EAP Full Authenticator state machine (RFC 4137)
+ * 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.
+ *
+ * This state machine is based on the full authenticator state machine defined
+ * in RFC 4137. However, to support backend authentication in RADIUS
+ * authentication server functionality, parts of backend authenticator (also
+ * from RFC 4137) are mixed in. This functionality is enabled by setting
+ * backend_auth configuration variable to TRUE.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+#include "state_machine.h"
+#include "common/wpa_ctrl.h"
+
+#define STATE_MACHINE_DATA struct eap_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAP"
+
+#define EAP_MAX_AUTH_ROUNDS 50
+
+static void eap_user_free(struct eap_user *user);
+
+
+/* EAP state machines are described in RFC 4137 */
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+                                  int eapSRTT, int eapRTTVAR,
+                                  int methodTimeout);
+static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp);
+static int eap_sm_getId(const struct wpabuf *data);
+static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id);
+static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id);
+static int eap_sm_nextId(struct eap_sm *sm, int id);
+static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
+                                size_t len);
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor);
+static int eap_sm_Policy_getDecision(struct eap_sm *sm);
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
+
+
+static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
+{
+       if (src == NULL)
+               return -1;
+
+       wpabuf_free(*dst);
+       *dst = wpabuf_dup(src);
+       return *dst ? 0 : -1;
+}
+
+
+static int eap_copy_data(u8 **dst, size_t *dst_len,
+                        const u8 *src, size_t src_len)
+{
+       if (src == NULL)
+               return -1;
+
+       os_free(*dst);
+       *dst = os_malloc(src_len);
+       if (*dst) {
+               os_memcpy(*dst, src, src_len);
+               *dst_len = src_len;
+               return 0;
+       } else {
+               *dst_len = 0;
+               return -1;
+       }
+}
+
+#define EAP_COPY(dst, src) \
+       eap_copy_data((dst), (dst ## Len), (src), (src ## Len))
+
+
+/**
+ * eap_user_get - Fetch user information from the database
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @identity: Identity (User-Name) of the user
+ * @identity_len: Length of identity in bytes
+ * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user
+ * Returns: 0 on success, or -1 on failure
+ *
+ * This function is used to fetch user information for EAP. The user will be
+ * selected based on the specified identity. sm->user and
+ * sm->user_eap_method_index are updated for the new user when a matching user
+ * is found. sm->user can be used to get user information (e.g., password).
+ */
+int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
+                int phase2)
+{
+       struct eap_user *user;
+
+       if (sm == NULL || sm->eapol_cb == NULL ||
+           sm->eapol_cb->get_eap_user == NULL)
+               return -1;
+
+       eap_user_free(sm->user);
+       sm->user = NULL;
+
+       user = os_zalloc(sizeof(*user));
+       if (user == NULL)
+           return -1;
+
+       if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity,
+                                      identity_len, phase2, user) != 0) {
+               eap_user_free(user);
+               return -1;
+       }
+
+       sm->user = user;
+       sm->user_eap_method_index = 0;
+
+       return 0;
+}
+
+
+SM_STATE(EAP, DISABLED)
+{
+       SM_ENTRY(EAP, DISABLED);
+       sm->num_rounds = 0;
+}
+
+
+SM_STATE(EAP, INITIALIZE)
+{
+       SM_ENTRY(EAP, INITIALIZE);
+
+       sm->currentId = -1;
+       sm->eap_if.eapSuccess = FALSE;
+       sm->eap_if.eapFail = FALSE;
+       sm->eap_if.eapTimeout = FALSE;
+       os_free(sm->eap_if.eapKeyData);
+       sm->eap_if.eapKeyData = NULL;
+       sm->eap_if.eapKeyDataLen = 0;
+       sm->eap_if.eapKeyAvailable = FALSE;
+       sm->eap_if.eapRestart = FALSE;
+
+       /*
+        * This is not defined in RFC 4137, but method state needs to be
+        * reseted here so that it does not remain in success state when
+        * re-authentication starts.
+        */
+       if (sm->m && sm->eap_method_priv) {
+               sm->m->reset(sm, sm->eap_method_priv);
+               sm->eap_method_priv = NULL;
+       }
+       sm->m = NULL;
+       sm->user_eap_method_index = 0;
+
+       if (sm->backend_auth) {
+               sm->currentMethod = EAP_TYPE_NONE;
+               /* parse rxResp, respId, respMethod */
+               eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+               if (sm->rxResp) {
+                       sm->currentId = sm->respId;
+               }
+       }
+       sm->num_rounds = 0;
+       sm->method_pending = METHOD_PENDING_NONE;
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED
+               MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, PICK_UP_METHOD)
+{
+       SM_ENTRY(EAP, PICK_UP_METHOD);
+
+       if (eap_sm_Policy_doPickUp(sm, sm->respMethod)) {
+               sm->currentMethod = sm->respMethod;
+               if (sm->m && sm->eap_method_priv) {
+                       sm->m->reset(sm, sm->eap_method_priv);
+                       sm->eap_method_priv = NULL;
+               }
+               sm->m = eap_server_get_eap_method(EAP_VENDOR_IETF,
+                                                 sm->currentMethod);
+               if (sm->m && sm->m->initPickUp) {
+                       sm->eap_method_priv = sm->m->initPickUp(sm);
+                       if (sm->eap_method_priv == NULL) {
+                               wpa_printf(MSG_DEBUG, "EAP: Failed to "
+                                          "initialize EAP method %d",
+                                          sm->currentMethod);
+                               sm->m = NULL;
+                               sm->currentMethod = EAP_TYPE_NONE;
+                       }
+               } else {
+                       sm->m = NULL;
+                       sm->currentMethod = EAP_TYPE_NONE;
+               }
+       }
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+               "method=%u", sm->currentMethod);
+}
+
+
+SM_STATE(EAP, IDLE)
+{
+       SM_ENTRY(EAP, IDLE);
+
+       sm->eap_if.retransWhile = eap_sm_calculateTimeout(
+               sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
+               sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT)
+{
+       SM_ENTRY(EAP, RETRANSMIT);
+
+       sm->retransCount++;
+       if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
+               if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
+                       sm->eap_if.eapReq = TRUE;
+       }
+}
+
+
+SM_STATE(EAP, RECEIVED)
+{
+       SM_ENTRY(EAP, RECEIVED);
+
+       /* parse rxResp, respId, respMethod */
+       eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+       sm->num_rounds++;
+}
+
+
+SM_STATE(EAP, DISCARD)
+{
+       SM_ENTRY(EAP, DISCARD);
+       sm->eap_if.eapResp = FALSE;
+       sm->eap_if.eapNoReq = TRUE;
+}
+
+
+SM_STATE(EAP, SEND_REQUEST)
+{
+       SM_ENTRY(EAP, SEND_REQUEST);
+
+       sm->retransCount = 0;
+       if (sm->eap_if.eapReqData) {
+               if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
+               {
+                       sm->eap_if.eapResp = FALSE;
+                       sm->eap_if.eapReq = TRUE;
+               } else {
+                       sm->eap_if.eapResp = FALSE;
+                       sm->eap_if.eapReq = FALSE;
+               }
+       } else {
+               wpa_printf(MSG_INFO, "EAP: SEND_REQUEST - no eapReqData");
+               sm->eap_if.eapResp = FALSE;
+               sm->eap_if.eapReq = FALSE;
+               sm->eap_if.eapNoReq = TRUE;
+       }
+}
+
+
+SM_STATE(EAP, INTEGRITY_CHECK)
+{
+       SM_ENTRY(EAP, INTEGRITY_CHECK);
+
+       if (sm->m->check) {
+               sm->ignore = sm->m->check(sm, sm->eap_method_priv,
+                                         sm->eap_if.eapRespData);
+       }
+}
+
+
+SM_STATE(EAP, METHOD_REQUEST)
+{
+       SM_ENTRY(EAP, METHOD_REQUEST);
+
+       if (sm->m == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP: method not initialized");
+               return;
+       }
+
+       sm->currentId = eap_sm_nextId(sm, sm->currentId);
+       wpa_printf(MSG_DEBUG, "EAP: building EAP-Request: Identifier %d",
+                  sm->currentId);
+       sm->lastId = sm->currentId;
+       wpabuf_free(sm->eap_if.eapReqData);
+       sm->eap_if.eapReqData = sm->m->buildReq(sm, sm->eap_method_priv,
+                                               sm->currentId);
+       if (sm->m->getTimeout)
+               sm->methodTimeout = sm->m->getTimeout(sm, sm->eap_method_priv);
+       else
+               sm->methodTimeout = 0;
+}
+
+
+SM_STATE(EAP, METHOD_RESPONSE)
+{
+       SM_ENTRY(EAP, METHOD_RESPONSE);
+
+       sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
+       if (sm->m->isDone(sm, sm->eap_method_priv)) {
+               eap_sm_Policy_update(sm, NULL, 0);
+               os_free(sm->eap_if.eapKeyData);
+               if (sm->m->getKey) {
+                       sm->eap_if.eapKeyData = sm->m->getKey(
+                               sm, sm->eap_method_priv,
+                               &sm->eap_if.eapKeyDataLen);
+               } else {
+                       sm->eap_if.eapKeyData = NULL;
+                       sm->eap_if.eapKeyDataLen = 0;
+               }
+               sm->methodState = METHOD_END;
+       } else {
+               sm->methodState = METHOD_CONTINUE;
+       }
+}
+
+
+SM_STATE(EAP, PROPOSE_METHOD)
+{
+       int vendor;
+       EapType type;
+
+       SM_ENTRY(EAP, PROPOSE_METHOD);
+
+       type = eap_sm_Policy_getNextMethod(sm, &vendor);
+       if (vendor == EAP_VENDOR_IETF)
+               sm->currentMethod = type;
+       else
+               sm->currentMethod = EAP_TYPE_EXPANDED;
+       if (sm->m && sm->eap_method_priv) {
+               sm->m->reset(sm, sm->eap_method_priv);
+               sm->eap_method_priv = NULL;
+       }
+       sm->m = eap_server_get_eap_method(vendor, type);
+       if (sm->m) {
+               sm->eap_method_priv = sm->m->init(sm);
+               if (sm->eap_method_priv == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP: Failed to initialize EAP "
+                                  "method %d", sm->currentMethod);
+                       sm->m = NULL;
+                       sm->currentMethod = EAP_TYPE_NONE;
+               }
+       }
+       if (sm->currentMethod == EAP_TYPE_IDENTITY ||
+           sm->currentMethod == EAP_TYPE_NOTIFICATION)
+               sm->methodState = METHOD_CONTINUE;
+       else
+               sm->methodState = METHOD_PROPOSED;
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
+               "vendor=%u method=%u", vendor, sm->currentMethod);
+}
+
+
+SM_STATE(EAP, NAK)
+{
+       const struct eap_hdr *nak;
+       size_t len = 0;
+       const u8 *pos;
+       const u8 *nak_list = NULL;
+
+       SM_ENTRY(EAP, NAK);
+
+       if (sm->eap_method_priv) {
+               sm->m->reset(sm, sm->eap_method_priv);
+               sm->eap_method_priv = NULL;
+       }
+       sm->m = NULL;
+
+       nak = wpabuf_head(sm->eap_if.eapRespData);
+       if (nak && wpabuf_len(sm->eap_if.eapRespData) > sizeof(*nak)) {
+               len = be_to_host16(nak->length);
+               if (len > wpabuf_len(sm->eap_if.eapRespData))
+                       len = wpabuf_len(sm->eap_if.eapRespData);
+               pos = (const u8 *) (nak + 1);
+               len -= sizeof(*nak);
+               if (*pos == EAP_TYPE_NAK) {
+                       pos++;
+                       len--;
+                       nak_list = pos;
+               }
+       }
+       eap_sm_Policy_update(sm, nak_list, len);
+}
+
+
+SM_STATE(EAP, SELECT_ACTION)
+{
+       SM_ENTRY(EAP, SELECT_ACTION);
+
+       sm->decision = eap_sm_Policy_getDecision(sm);
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE)
+{
+       SM_ENTRY(EAP, TIMEOUT_FAILURE);
+
+       sm->eap_if.eapTimeout = TRUE;
+}
+
+
+SM_STATE(EAP, FAILURE)
+{
+       SM_ENTRY(EAP, FAILURE);
+
+       wpabuf_free(sm->eap_if.eapReqData);
+       sm->eap_if.eapReqData = eap_sm_buildFailure(sm, sm->currentId);
+       wpabuf_free(sm->lastReqData);
+       sm->lastReqData = NULL;
+       sm->eap_if.eapFail = TRUE;
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+               MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, SUCCESS)
+{
+       SM_ENTRY(EAP, SUCCESS);
+
+       wpabuf_free(sm->eap_if.eapReqData);
+       sm->eap_if.eapReqData = eap_sm_buildSuccess(sm, sm->currentId);
+       wpabuf_free(sm->lastReqData);
+       sm->lastReqData = NULL;
+       if (sm->eap_if.eapKeyData)
+               sm->eap_if.eapKeyAvailable = TRUE;
+       sm->eap_if.eapSuccess = TRUE;
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+               MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
+{
+       SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
+
+       wpabuf_free(sm->eap_if.aaaEapRespData);
+       sm->eap_if.aaaEapRespData = NULL;
+}
+
+
+SM_STATE(EAP, IDLE2)
+{
+       SM_ENTRY(EAP, IDLE2);
+
+       sm->eap_if.retransWhile = eap_sm_calculateTimeout(
+               sm, sm->retransCount, sm->eap_if.eapSRTT, sm->eap_if.eapRTTVAR,
+               sm->methodTimeout);
+}
+
+
+SM_STATE(EAP, RETRANSMIT2)
+{
+       SM_ENTRY(EAP, RETRANSMIT2);
+
+       sm->retransCount++;
+       if (sm->retransCount <= sm->MaxRetrans && sm->lastReqData) {
+               if (eap_copy_buf(&sm->eap_if.eapReqData, sm->lastReqData) == 0)
+                       sm->eap_if.eapReq = TRUE;
+       }
+}
+
+
+SM_STATE(EAP, RECEIVED2)
+{
+       SM_ENTRY(EAP, RECEIVED2);
+
+       /* parse rxResp, respId, respMethod */
+       eap_sm_parseEapResp(sm, sm->eap_if.eapRespData);
+}
+
+
+SM_STATE(EAP, DISCARD2)
+{
+       SM_ENTRY(EAP, DISCARD2);
+       sm->eap_if.eapResp = FALSE;
+       sm->eap_if.eapNoReq = TRUE;
+}
+
+
+SM_STATE(EAP, SEND_REQUEST2)
+{
+       SM_ENTRY(EAP, SEND_REQUEST2);
+
+       sm->retransCount = 0;
+       if (sm->eap_if.eapReqData) {
+               if (eap_copy_buf(&sm->lastReqData, sm->eap_if.eapReqData) == 0)
+               {
+                       sm->eap_if.eapResp = FALSE;
+                       sm->eap_if.eapReq = TRUE;
+               } else {
+                       sm->eap_if.eapResp = FALSE;
+                       sm->eap_if.eapReq = FALSE;
+               }
+       } else {
+               wpa_printf(MSG_INFO, "EAP: SEND_REQUEST2 - no eapReqData");
+               sm->eap_if.eapResp = FALSE;
+               sm->eap_if.eapReq = FALSE;
+               sm->eap_if.eapNoReq = TRUE;
+       }
+}
+
+
+SM_STATE(EAP, AAA_REQUEST)
+{
+       SM_ENTRY(EAP, AAA_REQUEST);
+
+       if (sm->eap_if.eapRespData == NULL) {
+               wpa_printf(MSG_INFO, "EAP: AAA_REQUEST - no eapRespData");
+               return;
+       }
+
+       /*
+        * if (respMethod == IDENTITY)
+        *      aaaIdentity = eapRespData
+        * This is already taken care of by the EAP-Identity method which
+        * stores the identity into sm->identity.
+        */
+
+       eap_copy_buf(&sm->eap_if.aaaEapRespData, sm->eap_if.eapRespData);
+}
+
+
+SM_STATE(EAP, AAA_RESPONSE)
+{
+       SM_ENTRY(EAP, AAA_RESPONSE);
+
+       eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+       sm->currentId = eap_sm_getId(sm->eap_if.eapReqData);
+       sm->methodTimeout = sm->eap_if.aaaMethodTimeout;
+}
+
+
+SM_STATE(EAP, AAA_IDLE)
+{
+       SM_ENTRY(EAP, AAA_IDLE);
+
+       sm->eap_if.aaaFail = FALSE;
+       sm->eap_if.aaaSuccess = FALSE;
+       sm->eap_if.aaaEapReq = FALSE;
+       sm->eap_if.aaaEapNoReq = FALSE;
+       sm->eap_if.aaaEapResp = TRUE;
+}
+
+
+SM_STATE(EAP, TIMEOUT_FAILURE2)
+{
+       SM_ENTRY(EAP, TIMEOUT_FAILURE2);
+
+       sm->eap_if.eapTimeout = TRUE;
+}
+
+
+SM_STATE(EAP, FAILURE2)
+{
+       SM_ENTRY(EAP, FAILURE2);
+
+       eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+       sm->eap_if.eapFail = TRUE;
+}
+
+
+SM_STATE(EAP, SUCCESS2)
+{
+       SM_ENTRY(EAP, SUCCESS2);
+
+       eap_copy_buf(&sm->eap_if.eapReqData, sm->eap_if.aaaEapReqData);
+
+       sm->eap_if.eapKeyAvailable = sm->eap_if.aaaEapKeyAvailable;
+       if (sm->eap_if.aaaEapKeyAvailable) {
+               EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
+       } else {
+               os_free(sm->eap_if.eapKeyData);
+               sm->eap_if.eapKeyData = NULL;
+               sm->eap_if.eapKeyDataLen = 0;
+       }
+
+       sm->eap_if.eapSuccess = TRUE;
+
+       /*
+        * Start reauthentication with identity request even though we know the
+        * previously used identity. This is needed to get reauthentication
+        * started properly.
+        */
+       sm->start_reauth = TRUE;
+}
+
+
+SM_STEP(EAP)
+{
+       if (sm->eap_if.eapRestart && sm->eap_if.portEnabled)
+               SM_ENTER_GLOBAL(EAP, INITIALIZE);
+       else if (!sm->eap_if.portEnabled)
+               SM_ENTER_GLOBAL(EAP, DISABLED);
+       else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) {
+               if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) {
+                       wpa_printf(MSG_DEBUG, "EAP: more than %d "
+                                  "authentication rounds - abort",
+                                  EAP_MAX_AUTH_ROUNDS);
+                       sm->num_rounds++;
+                       SM_ENTER_GLOBAL(EAP, FAILURE);
+               }
+       } else switch (sm->EAP_state) {
+       case EAP_INITIALIZE:
+               if (sm->backend_auth) {
+                       if (!sm->rxResp)
+                               SM_ENTER(EAP, SELECT_ACTION);
+                       else if (sm->rxResp &&
+                                (sm->respMethod == EAP_TYPE_NAK ||
+                                 (sm->respMethod == EAP_TYPE_EXPANDED &&
+                                  sm->respVendor == EAP_VENDOR_IETF &&
+                                  sm->respVendorMethod == EAP_TYPE_NAK)))
+                               SM_ENTER(EAP, NAK);
+                       else
+                               SM_ENTER(EAP, PICK_UP_METHOD);
+               } else {
+                       SM_ENTER(EAP, SELECT_ACTION);
+               }
+               break;
+       case EAP_PICK_UP_METHOD:
+               if (sm->currentMethod == EAP_TYPE_NONE) {
+                       SM_ENTER(EAP, SELECT_ACTION);
+               } else {
+                       SM_ENTER(EAP, METHOD_RESPONSE);
+               }
+               break;
+       case EAP_DISABLED:
+               if (sm->eap_if.portEnabled)
+                       SM_ENTER(EAP, INITIALIZE);
+               break;
+       case EAP_IDLE:
+               if (sm->eap_if.retransWhile == 0)
+                       SM_ENTER(EAP, RETRANSMIT);
+               else if (sm->eap_if.eapResp)
+                       SM_ENTER(EAP, RECEIVED);
+               break;
+       case EAP_RETRANSMIT:
+               if (sm->retransCount > sm->MaxRetrans)
+                       SM_ENTER(EAP, TIMEOUT_FAILURE);
+               else
+                       SM_ENTER(EAP, IDLE);
+               break;
+       case EAP_RECEIVED:
+               if (sm->rxResp && (sm->respId == sm->currentId) &&
+                   (sm->respMethod == EAP_TYPE_NAK ||
+                    (sm->respMethod == EAP_TYPE_EXPANDED &&
+                     sm->respVendor == EAP_VENDOR_IETF &&
+                     sm->respVendorMethod == EAP_TYPE_NAK))
+                   && (sm->methodState == METHOD_PROPOSED))
+                       SM_ENTER(EAP, NAK);
+               else if (sm->rxResp && (sm->respId == sm->currentId) &&
+                        ((sm->respMethod == sm->currentMethod) ||
+                         (sm->respMethod == EAP_TYPE_EXPANDED &&
+                          sm->respVendor == EAP_VENDOR_IETF &&
+                          sm->respVendorMethod == sm->currentMethod)))
+                       SM_ENTER(EAP, INTEGRITY_CHECK);
+               else {
+                       wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
+                                  "rxResp=%d respId=%d currentId=%d "
+                                  "respMethod=%d currentMethod=%d",
+                                  sm->rxResp, sm->respId, sm->currentId,
+                                  sm->respMethod, sm->currentMethod);
+                       SM_ENTER(EAP, DISCARD);
+               }
+               break;
+       case EAP_DISCARD:
+               SM_ENTER(EAP, IDLE);
+               break;
+       case EAP_SEND_REQUEST:
+               SM_ENTER(EAP, IDLE);
+               break;
+       case EAP_INTEGRITY_CHECK:
+               if (sm->ignore)
+                       SM_ENTER(EAP, DISCARD);
+               else
+                       SM_ENTER(EAP, METHOD_RESPONSE);
+               break;
+       case EAP_METHOD_REQUEST:
+               SM_ENTER(EAP, SEND_REQUEST);
+               break;
+       case EAP_METHOD_RESPONSE:
+               /*
+                * Note: Mechanism to allow EAP methods to wait while going
+                * through pending processing is an extension to RFC 4137
+                * which only defines the transits to SELECT_ACTION and
+                * METHOD_REQUEST from this METHOD_RESPONSE state.
+                */
+               if (sm->methodState == METHOD_END)
+                       SM_ENTER(EAP, SELECT_ACTION);
+               else if (sm->method_pending == METHOD_PENDING_WAIT) {
+                       wpa_printf(MSG_DEBUG, "EAP: Method has pending "
+                                  "processing - wait before proceeding to "
+                                  "METHOD_REQUEST state");
+               } else if (sm->method_pending == METHOD_PENDING_CONT) {
+                       wpa_printf(MSG_DEBUG, "EAP: Method has completed "
+                                  "pending processing - reprocess pending "
+                                  "EAP message");
+                       sm->method_pending = METHOD_PENDING_NONE;
+                       SM_ENTER(EAP, METHOD_RESPONSE);
+               } else
+                       SM_ENTER(EAP, METHOD_REQUEST);
+               break;
+       case EAP_PROPOSE_METHOD:
+               /*
+                * Note: Mechanism to allow EAP methods to wait while going
+                * through pending processing is an extension to RFC 4137
+                * which only defines the transit to METHOD_REQUEST from this
+                * PROPOSE_METHOD state.
+                */
+               if (sm->method_pending == METHOD_PENDING_WAIT) {
+                       wpa_printf(MSG_DEBUG, "EAP: Method has pending "
+                                  "processing - wait before proceeding to "
+                                  "METHOD_REQUEST state");
+                       if (sm->user_eap_method_index > 0)
+                               sm->user_eap_method_index--;
+               } else if (sm->method_pending == METHOD_PENDING_CONT) {
+                       wpa_printf(MSG_DEBUG, "EAP: Method has completed "
+                                  "pending processing - reprocess pending "
+                                  "EAP message");
+                       sm->method_pending = METHOD_PENDING_NONE;
+                       SM_ENTER(EAP, PROPOSE_METHOD);
+               } else
+                       SM_ENTER(EAP, METHOD_REQUEST);
+               break;
+       case EAP_NAK:
+               SM_ENTER(EAP, SELECT_ACTION);
+               break;
+       case EAP_SELECT_ACTION:
+               if (sm->decision == DECISION_FAILURE)
+                       SM_ENTER(EAP, FAILURE);
+               else if (sm->decision == DECISION_SUCCESS)
+                       SM_ENTER(EAP, SUCCESS);
+               else if (sm->decision == DECISION_PASSTHROUGH)
+                       SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
+               else
+                       SM_ENTER(EAP, PROPOSE_METHOD);
+               break;
+       case EAP_TIMEOUT_FAILURE:
+               break;
+       case EAP_FAILURE:
+               break;
+       case EAP_SUCCESS:
+               break;
+
+       case EAP_INITIALIZE_PASSTHROUGH:
+               if (sm->currentId == -1)
+                       SM_ENTER(EAP, AAA_IDLE);
+               else
+                       SM_ENTER(EAP, AAA_REQUEST);
+               break;
+       case EAP_IDLE2:
+               if (sm->eap_if.eapResp)
+                       SM_ENTER(EAP, RECEIVED2);
+               else if (sm->eap_if.retransWhile == 0)
+                       SM_ENTER(EAP, RETRANSMIT2);
+               break;
+       case EAP_RETRANSMIT2:
+               if (sm->retransCount > sm->MaxRetrans)
+                       SM_ENTER(EAP, TIMEOUT_FAILURE2);
+               else
+                       SM_ENTER(EAP, IDLE2);
+               break;
+       case EAP_RECEIVED2:
+               if (sm->rxResp && (sm->respId == sm->currentId))
+                       SM_ENTER(EAP, AAA_REQUEST);
+               else
+                       SM_ENTER(EAP, DISCARD2);
+               break;
+       case EAP_DISCARD2:
+               SM_ENTER(EAP, IDLE2);
+               break;
+       case EAP_SEND_REQUEST2:
+               SM_ENTER(EAP, IDLE2);
+               break;
+       case EAP_AAA_REQUEST:
+               SM_ENTER(EAP, AAA_IDLE);
+               break;
+       case EAP_AAA_RESPONSE:
+               SM_ENTER(EAP, SEND_REQUEST2);
+               break;
+       case EAP_AAA_IDLE:
+               if (sm->eap_if.aaaFail)
+                       SM_ENTER(EAP, FAILURE2);
+               else if (sm->eap_if.aaaSuccess)
+                       SM_ENTER(EAP, SUCCESS2);
+               else if (sm->eap_if.aaaEapReq)
+                       SM_ENTER(EAP, AAA_RESPONSE);
+               else if (sm->eap_if.aaaTimeout)
+                       SM_ENTER(EAP, TIMEOUT_FAILURE2);
+               break;
+       case EAP_TIMEOUT_FAILURE2:
+               break;
+       case EAP_FAILURE2:
+               break;
+       case EAP_SUCCESS2:
+               break;
+       }
+}
+
+
+static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
+                                  int eapSRTT, int eapRTTVAR,
+                                  int methodTimeout)
+{
+       int rto, i;
+
+       if (methodTimeout) {
+               /*
+                * EAP method (either internal or through AAA server, provided
+                * timeout hint. Use that as-is as a timeout for retransmitting
+                * the EAP request if no response is received.
+                */
+               wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
+                          "(from EAP method hint)", methodTimeout);
+               return methodTimeout;
+       }
+
+       /*
+        * RFC 3748 recommends algorithms described in RFC 2988 for estimation
+        * of the retransmission timeout. This should be implemented once
+        * round-trip time measurements are available. For nowm a simple
+        * backoff mechanism is used instead if there are no EAP method
+        * specific hints.
+        *
+        * SRTT = smoothed round-trip time
+        * RTTVAR = round-trip time variation
+        * RTO = retransmission timeout
+        */
+
+       /*
+        * RFC 2988, 2.1: before RTT measurement, set RTO to 3 seconds for
+        * initial retransmission and then double the RTO to provide back off
+        * per 5.5. Limit the maximum RTO to 20 seconds per RFC 3748, 4.3
+        * modified RTOmax.
+        */
+       rto = 3;
+       for (i = 0; i < retransCount; i++) {
+               rto *= 2;
+               if (rto >= 20) {
+                       rto = 20;
+                       break;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP: retransmit timeout %d seconds "
+                  "(from dynamic back off; retransCount=%d)",
+                  rto, retransCount);
+
+       return rto;
+}
+
+
+static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
+{
+       const struct eap_hdr *hdr;
+       size_t plen;
+
+       /* parse rxResp, respId, respMethod */
+       sm->rxResp = FALSE;
+       sm->respId = -1;
+       sm->respMethod = EAP_TYPE_NONE;
+       sm->respVendor = EAP_VENDOR_IETF;
+       sm->respVendorMethod = EAP_TYPE_NONE;
+
+       if (resp == NULL || wpabuf_len(resp) < sizeof(*hdr)) {
+               wpa_printf(MSG_DEBUG, "EAP: parseEapResp: invalid resp=%p "
+                          "len=%lu", resp,
+                          resp ? (unsigned long) wpabuf_len(resp) : 0);
+               return;
+       }
+
+       hdr = wpabuf_head(resp);
+       plen = be_to_host16(hdr->length);
+       if (plen > wpabuf_len(resp)) {
+               wpa_printf(MSG_DEBUG, "EAP: Ignored truncated EAP-Packet "
+                          "(len=%lu plen=%lu)",
+                          (unsigned long) wpabuf_len(resp),
+                          (unsigned long) plen);
+               return;
+       }
+
+       sm->respId = hdr->identifier;
+
+       if (hdr->code == EAP_CODE_RESPONSE)
+               sm->rxResp = TRUE;
+
+       if (plen > sizeof(*hdr)) {
+               u8 *pos = (u8 *) (hdr + 1);
+               sm->respMethod = *pos++;
+               if (sm->respMethod == EAP_TYPE_EXPANDED) {
+                       if (plen < sizeof(*hdr) + 8) {
+                               wpa_printf(MSG_DEBUG, "EAP: Ignored truncated "
+                                          "expanded EAP-Packet (plen=%lu)",
+                                          (unsigned long) plen);
+                               return;
+                       }
+                       sm->respVendor = WPA_GET_BE24(pos);
+                       pos += 3;
+                       sm->respVendorMethod = WPA_GET_BE32(pos);
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
+                  "respMethod=%u respVendor=%u respVendorMethod=%u",
+                  sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
+                  sm->respVendorMethod);
+}
+
+
+static int eap_sm_getId(const struct wpabuf *data)
+{
+       const struct eap_hdr *hdr;
+
+       if (data == NULL || wpabuf_len(data) < sizeof(*hdr))
+               return -1;
+
+       hdr = wpabuf_head(data);
+       wpa_printf(MSG_DEBUG, "EAP: getId: id=%d", hdr->identifier);
+       return hdr->identifier;
+}
+
+
+static struct wpabuf * eap_sm_buildSuccess(struct eap_sm *sm, u8 id)
+{
+       struct wpabuf *msg;
+       struct eap_hdr *resp;
+       wpa_printf(MSG_DEBUG, "EAP: Building EAP-Success (id=%d)", id);
+
+       msg = wpabuf_alloc(sizeof(*resp));
+       if (msg == NULL)
+               return NULL;
+       resp = wpabuf_put(msg, sizeof(*resp));
+       resp->code = EAP_CODE_SUCCESS;
+       resp->identifier = id;
+       resp->length = host_to_be16(sizeof(*resp));
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_sm_buildFailure(struct eap_sm *sm, u8 id)
+{
+       struct wpabuf *msg;
+       struct eap_hdr *resp;
+       wpa_printf(MSG_DEBUG, "EAP: Building EAP-Failure (id=%d)", id);
+
+       msg = wpabuf_alloc(sizeof(*resp));
+       if (msg == NULL)
+               return NULL;
+       resp = wpabuf_put(msg, sizeof(*resp));
+       resp->code = EAP_CODE_FAILURE;
+       resp->identifier = id;
+       resp->length = host_to_be16(sizeof(*resp));
+
+       return msg;
+}
+
+
+static int eap_sm_nextId(struct eap_sm *sm, int id)
+{
+       if (id < 0) {
+               /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a
+                * random number */
+               id = rand() & 0xff;
+               if (id != sm->lastId)
+                       return id;
+       }
+       return (id + 1) & 0xff;
+}
+
+
+/**
+ * eap_sm_process_nak - Process EAP-Response/Nak
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @nak_list: Nak list (allowed methods) from the supplicant
+ * @len: Length of nak_list in bytes
+ *
+ * This function is called when EAP-Response/Nak is received from the
+ * supplicant. This can happen for both phase 1 and phase 2 authentications.
+ */
+void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len)
+{
+       int i;
+       size_t j;
+
+       if (sm->user == NULL)
+               return;
+
+       wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method "
+                  "index %d)", sm->user_eap_method_index);
+
+       wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods",
+                   (u8 *) sm->user->methods,
+                   EAP_MAX_METHODS * sizeof(sm->user->methods[0]));
+       wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer",
+                   nak_list, len);
+
+       i = sm->user_eap_method_index;
+       while (i < EAP_MAX_METHODS &&
+              (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+               sm->user->methods[i].method != EAP_TYPE_NONE)) {
+               if (sm->user->methods[i].vendor != EAP_VENDOR_IETF)
+                       goto not_found;
+               for (j = 0; j < len; j++) {
+                       if (nak_list[j] == sm->user->methods[i].method) {
+                               break;
+                       }
+               }
+
+               if (j < len) {
+                       /* found */
+                       i++;
+                       continue;
+               }
+
+       not_found:
+               /* not found - remove from the list */
+               os_memmove(&sm->user->methods[i], &sm->user->methods[i + 1],
+                          (EAP_MAX_METHODS - i - 1) *
+                          sizeof(sm->user->methods[0]));
+               sm->user->methods[EAP_MAX_METHODS - 1].vendor =
+                       EAP_VENDOR_IETF;
+               sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods",
+                   (u8 *) sm->user->methods, EAP_MAX_METHODS *
+                   sizeof(sm->user->methods[0]));
+}
+
+
+static void eap_sm_Policy_update(struct eap_sm *sm, const u8 *nak_list,
+                                size_t len)
+{
+       if (nak_list == NULL || sm == NULL || sm->user == NULL)
+               return;
+
+       if (sm->user->phase2) {
+               wpa_printf(MSG_DEBUG, "EAP: EAP-Nak received after Phase2 user"
+                          " info was selected - reject");
+               sm->decision = DECISION_FAILURE;
+               return;
+       }
+
+       eap_sm_process_nak(sm, nak_list, len);
+}
+
+
+static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor)
+{
+       EapType next;
+       int idx = sm->user_eap_method_index;
+
+       /* In theory, there should be no problems with starting
+        * re-authentication with something else than EAP-Request/Identity and
+        * this does indeed work with wpa_supplicant. However, at least Funk
+        * Supplicant seemed to ignore re-auth if it skipped
+        * EAP-Request/Identity.
+        * Re-auth sets currentId == -1, so that can be used here to select
+        * whether Identity needs to be requested again. */
+       if (sm->identity == NULL || sm->currentId == -1) {
+               *vendor = EAP_VENDOR_IETF;
+               next = EAP_TYPE_IDENTITY;
+               sm->update_user = TRUE;
+       } else if (sm->user && idx < EAP_MAX_METHODS &&
+                  (sm->user->methods[idx].vendor != EAP_VENDOR_IETF ||
+                   sm->user->methods[idx].method != EAP_TYPE_NONE)) {
+               *vendor = sm->user->methods[idx].vendor;
+               next = sm->user->methods[idx].method;
+               sm->user_eap_method_index++;
+       } else {
+               *vendor = EAP_VENDOR_IETF;
+               next = EAP_TYPE_NONE;
+       }
+       wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d",
+                  *vendor, next);
+       return next;
+}
+
+
+static int eap_sm_Policy_getDecision(struct eap_sm *sm)
+{
+       if (!sm->eap_server && sm->identity && !sm->start_reauth) {
+               wpa_printf(MSG_DEBUG, "EAP: getDecision: -> PASSTHROUGH");
+               return DECISION_PASSTHROUGH;
+       }
+
+       if (sm->m && sm->currentMethod != EAP_TYPE_IDENTITY &&
+           sm->m->isSuccess(sm, sm->eap_method_priv)) {
+               wpa_printf(MSG_DEBUG, "EAP: getDecision: method succeeded -> "
+                          "SUCCESS");
+               sm->update_user = TRUE;
+               return DECISION_SUCCESS;
+       }
+
+       if (sm->m && sm->m->isDone(sm, sm->eap_method_priv) &&
+           !sm->m->isSuccess(sm, sm->eap_method_priv)) {
+               wpa_printf(MSG_DEBUG, "EAP: getDecision: method failed -> "
+                          "FAILURE");
+               sm->update_user = TRUE;
+               return DECISION_FAILURE;
+       }
+
+       if ((sm->user == NULL || sm->update_user) && sm->identity &&
+           !sm->start_reauth) {
+               /*
+                * Allow Identity method to be started once to allow identity
+                * selection hint to be sent from the authentication server,
+                * but prevent a loop of Identity requests by only allowing
+                * this to happen once.
+                */
+               int id_req = 0;
+               if (sm->user && sm->currentMethod == EAP_TYPE_IDENTITY &&
+                   sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
+                   sm->user->methods[0].method == EAP_TYPE_IDENTITY)
+                       id_req = 1;
+               if (eap_user_get(sm, sm->identity, sm->identity_len, 0) != 0) {
+                       wpa_printf(MSG_DEBUG, "EAP: getDecision: user not "
+                                  "found from database -> FAILURE");
+                       return DECISION_FAILURE;
+               }
+               if (id_req && sm->user &&
+                   sm->user->methods[0].vendor == EAP_VENDOR_IETF &&
+                   sm->user->methods[0].method == EAP_TYPE_IDENTITY) {
+                       wpa_printf(MSG_DEBUG, "EAP: getDecision: stop "
+                                  "identity request loop -> FAILURE");
+                       sm->update_user = TRUE;
+                       return DECISION_FAILURE;
+               }
+               sm->update_user = FALSE;
+       }
+       sm->start_reauth = FALSE;
+
+       if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+           (sm->user->methods[sm->user_eap_method_index].vendor !=
+            EAP_VENDOR_IETF ||
+            sm->user->methods[sm->user_eap_method_index].method !=
+            EAP_TYPE_NONE)) {
+               wpa_printf(MSG_DEBUG, "EAP: getDecision: another method "
+                          "available -> CONTINUE");
+               return DECISION_CONTINUE;
+       }
+
+       if (sm->identity == NULL || sm->currentId == -1) {
+               wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
+                          "yet -> CONTINUE");
+               return DECISION_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP: getDecision: no more methods available -> "
+                  "FAILURE");
+       return DECISION_FAILURE;
+}
+
+
+static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method)
+{
+       return method == EAP_TYPE_IDENTITY ? TRUE : FALSE;
+}
+
+
+/**
+ * eap_server_sm_step - Step EAP server state machine
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: 1 if EAP state was changed or 0 if not
+ *
+ * This function advances EAP state machine to a new state to match with the
+ * current variables. This should be called whenever variables used by the EAP
+ * state machine have changed.
+ */
+int eap_server_sm_step(struct eap_sm *sm)
+{
+       int res = 0;
+       do {
+               sm->changed = FALSE;
+               SM_STEP_RUN(EAP);
+               if (sm->changed)
+                       res = 1;
+       } while (sm->changed);
+       return res;
+}
+
+
+static void eap_user_free(struct eap_user *user)
+{
+       if (user == NULL)
+               return;
+       os_free(user->password);
+       user->password = NULL;
+       os_free(user);
+}
+
+
+/**
+ * eap_server_sm_init - Allocate and initialize EAP server state machine
+ * @eapol_ctx: Context data to be used with eapol_cb calls
+ * @eapol_cb: Pointer to EAPOL callback functions
+ * @conf: EAP configuration
+ * Returns: Pointer to the allocated EAP state machine or %NULL on failure
+ *
+ * This function allocates and initializes an EAP state machine.
+ */
+struct eap_sm * eap_server_sm_init(void *eapol_ctx,
+                                  struct eapol_callbacks *eapol_cb,
+                                  struct eap_config *conf)
+{
+       struct eap_sm *sm;
+
+       sm = os_zalloc(sizeof(*sm));
+       if (sm == NULL)
+               return NULL;
+       sm->eapol_ctx = eapol_ctx;
+       sm->eapol_cb = eapol_cb;
+       sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */
+       sm->ssl_ctx = conf->ssl_ctx;
+       sm->msg_ctx = conf->msg_ctx;
+       sm->eap_sim_db_priv = conf->eap_sim_db_priv;
+       sm->backend_auth = conf->backend_auth;
+       sm->eap_server = conf->eap_server;
+       if (conf->pac_opaque_encr_key) {
+               sm->pac_opaque_encr_key = os_malloc(16);
+               if (sm->pac_opaque_encr_key) {
+                       os_memcpy(sm->pac_opaque_encr_key,
+                                 conf->pac_opaque_encr_key, 16);
+               }
+       }
+       if (conf->eap_fast_a_id) {
+               sm->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
+               if (sm->eap_fast_a_id) {
+                       os_memcpy(sm->eap_fast_a_id, conf->eap_fast_a_id,
+                                 conf->eap_fast_a_id_len);
+                       sm->eap_fast_a_id_len = conf->eap_fast_a_id_len;
+               }
+       }
+       if (conf->eap_fast_a_id_info)
+               sm->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
+       sm->eap_fast_prov = conf->eap_fast_prov;
+       sm->pac_key_lifetime = conf->pac_key_lifetime;
+       sm->pac_key_refresh_time = conf->pac_key_refresh_time;
+       sm->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+       sm->tnc = conf->tnc;
+       sm->wps = conf->wps;
+       if (conf->assoc_wps_ie)
+               sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie);
+       if (conf->peer_addr)
+               os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN);
+
+       wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
+
+       return sm;
+}
+
+
+/**
+ * eap_server_sm_deinit - Deinitialize and free an EAP server state machine
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function deinitializes EAP state machine and frees all allocated
+ * resources.
+ */
+void eap_server_sm_deinit(struct eap_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAP: Server state machine removed");
+       if (sm->m && sm->eap_method_priv)
+               sm->m->reset(sm, sm->eap_method_priv);
+       wpabuf_free(sm->eap_if.eapReqData);
+       os_free(sm->eap_if.eapKeyData);
+       wpabuf_free(sm->lastReqData);
+       wpabuf_free(sm->eap_if.eapRespData);
+       os_free(sm->identity);
+       os_free(sm->pac_opaque_encr_key);
+       os_free(sm->eap_fast_a_id);
+       os_free(sm->eap_fast_a_id_info);
+       wpabuf_free(sm->eap_if.aaaEapReqData);
+       wpabuf_free(sm->eap_if.aaaEapRespData);
+       os_free(sm->eap_if.aaaEapKeyData);
+       eap_user_free(sm->user);
+       wpabuf_free(sm->assoc_wps_ie);
+       os_free(sm);
+}
+
+
+/**
+ * eap_sm_notify_cached - Notify EAP state machine of cached PMK
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function is called when PMKSA caching is used to skip EAP
+ * authentication.
+ */
+void eap_sm_notify_cached(struct eap_sm *sm)
+{
+       if (sm == NULL)
+               return;
+
+       sm->EAP_state = EAP_SUCCESS;
+}
+
+
+/**
+ * eap_sm_pending_cb - EAP state machine callback for a pending EAP request
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ *
+ * This function is called when data for a pending EAP-Request is received.
+ */
+void eap_sm_pending_cb(struct eap_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received");
+       if (sm->method_pending == METHOD_PENDING_WAIT)
+               sm->method_pending = METHOD_PENDING_CONT;
+}
+
+
+/**
+ * eap_sm_method_pending - Query whether EAP method is waiting for pending data
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: 1 if method is waiting for pending data or 0 if not
+ */
+int eap_sm_method_pending(struct eap_sm *sm)
+{
+       if (sm == NULL)
+               return 0;
+       return sm->method_pending == METHOD_PENDING_WAIT;
+}
+
+
+/**
+ * eap_get_identity - Get the user identity (from EAP-Response/Identity)
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * @len: Buffer for returning identity length
+ * Returns: Pointer to the user identity or %NULL if not available
+ */
+const u8 * eap_get_identity(struct eap_sm *sm, size_t *len)
+{
+       *len = sm->identity_len;
+       return sm->identity;
+}
+
+
+/**
+ * eap_get_interface - Get pointer to EAP-EAPOL interface data
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: Pointer to the EAP-EAPOL interface data
+ */
+struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm)
+{
+       return &sm->eap_if;
+}
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
new file mode 100644 (file)
index 0000000..4e7db48
--- /dev/null
@@ -0,0 +1,1277 @@
+/*
+ * hostapd / EAP-AKA (RFC 4187) and EAP-AKA' (draft-arkko-eap-aka-kdf)
+ * Copyright (c) 2005-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 "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_i.h"
+#include "eap_server/eap_sim_db.h"
+
+
+struct eap_aka_data {
+       u8 mk[EAP_SIM_MK_LEN];
+       u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+       u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+       u8 k_encr[EAP_SIM_K_ENCR_LEN];
+       u8 k_re[EAP_AKA_PRIME_K_RE_LEN]; /* EAP-AKA' only */
+       u8 msk[EAP_SIM_KEYING_DATA_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 rand[EAP_AKA_RAND_LEN];
+       u8 autn[EAP_AKA_AUTN_LEN];
+       u8 ck[EAP_AKA_CK_LEN];
+       u8 ik[EAP_AKA_IK_LEN];
+       u8 res[EAP_AKA_RES_MAX_LEN];
+       size_t res_len;
+       enum {
+               IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
+       } state;
+       char *next_pseudonym;
+       char *next_reauth_id;
+       u16 counter;
+       struct eap_sim_reauth *reauth;
+       int auts_reported; /* whether the current AUTS has been reported to the
+                           * eap_sim_db */
+       u16 notification;
+       int use_result_ind;
+
+       struct wpabuf *id_msgs;
+       int pending_id;
+       u8 eap_method;
+       u8 *network_name;
+       size_t network_name_len;
+       u16 kdf;
+};
+
+
+static void eap_aka_determine_identity(struct eap_sm *sm,
+                                      struct eap_aka_data *data,
+                                      int before_identity, int after_reauth);
+
+
+static const char * eap_aka_state_txt(int state)
+{
+       switch (state) {
+       case IDENTITY:
+               return "IDENTITY";
+       case CHALLENGE:
+               return "CHALLENGE";
+       case REAUTH:
+               return "REAUTH";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       case NOTIFICATION:
+               return "NOTIFICATION";
+       default:
+               return "Unknown?!";
+       }
+}
+
+
+static void eap_aka_state(struct eap_aka_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s",
+                  eap_aka_state_txt(data->state),
+                  eap_aka_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_aka_init(struct eap_sm *sm)
+{
+       struct eap_aka_data *data;
+
+       if (sm->eap_sim_db_priv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->eap_method = EAP_TYPE_AKA;
+
+       data->state = IDENTITY;
+       eap_aka_determine_identity(sm, data, 1, 0);
+       data->pending_id = -1;
+
+       return data;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+static void * eap_aka_prime_init(struct eap_sm *sm)
+{
+       struct eap_aka_data *data;
+       /* TODO: make ANID configurable; see 3GPP TS 24.302 */
+       char *network_name = "WLAN";
+
+       if (sm->eap_sim_db_priv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->eap_method = EAP_TYPE_AKA_PRIME;
+       data->network_name = os_malloc(os_strlen(network_name));
+       if (data->network_name == NULL) {
+               os_free(data);
+               return NULL;
+       }
+
+       data->network_name_len = os_strlen(network_name);
+       os_memcpy(data->network_name, network_name, data->network_name_len);
+
+       data->state = IDENTITY;
+       eap_aka_determine_identity(sm, data, 1, 0);
+       data->pending_id = -1;
+
+       return data;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+
+static void eap_aka_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       os_free(data->next_pseudonym);
+       os_free(data->next_reauth_id);
+       wpabuf_free(data->id_msgs);
+       os_free(data->network_name);
+       os_free(data);
+}
+
+
+static int eap_aka_add_id_msg(struct eap_aka_data *data,
+                             const struct wpabuf *msg)
+{
+       if (msg == NULL)
+               return -1;
+
+       if (data->id_msgs == NULL) {
+               data->id_msgs = wpabuf_dup(msg);
+               return data->id_msgs == NULL ? -1 : 0;
+       }
+
+       if (wpabuf_resize(&data->id_msgs, wpabuf_len(msg)) < 0)
+               return -1;
+       wpabuf_put_buf(data->id_msgs, msg);
+
+       return 0;
+}
+
+
+static void eap_aka_add_checkcode(struct eap_aka_data *data,
+                                 struct eap_sim_msg *msg)
+{
+       const u8 *addr;
+       size_t len;
+       u8 hash[SHA256_MAC_LEN];
+
+       wpa_printf(MSG_DEBUG, "   AT_CHECKCODE");
+
+       if (data->id_msgs == NULL) {
+               /*
+                * No EAP-AKA/Identity packets were exchanged - send empty
+                * checkcode.
+                */
+               eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, NULL, 0);
+               return;
+       }
+
+       /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+       addr = wpabuf_head(data->id_msgs);
+       len = wpabuf_len(data->id_msgs);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-AKA: AT_CHECKCODE data", addr, len);
+       if (data->eap_method == EAP_TYPE_AKA_PRIME)
+               sha256_vector(1, &addr, &len, hash);
+       else
+               sha1_vector(1, &addr, &len, hash);
+
+       eap_sim_msg_add(msg, EAP_SIM_AT_CHECKCODE, 0, hash,
+                       data->eap_method == EAP_TYPE_AKA_PRIME ?
+                       EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN);
+}
+
+
+static int eap_aka_verify_checkcode(struct eap_aka_data *data,
+                                   const u8 *checkcode, size_t checkcode_len)
+{
+       const u8 *addr;
+       size_t len;
+       u8 hash[SHA256_MAC_LEN];
+       size_t hash_len;
+
+       if (checkcode == NULL)
+               return -1;
+
+       if (data->id_msgs == NULL) {
+               if (checkcode_len != 0) {
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer "
+                                  "indicates that AKA/Identity messages were "
+                                  "used, but they were not");
+                       return -1;
+               }
+               return 0;
+       }
+
+       hash_len = data->eap_method == EAP_TYPE_AKA_PRIME ?
+               EAP_AKA_PRIME_CHECKCODE_LEN : EAP_AKA_CHECKCODE_LEN;
+
+       if (checkcode_len != hash_len) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Checkcode from peer indicates "
+                          "that AKA/Identity message were not used, but they "
+                          "were");
+               return -1;
+       }
+
+       /* Checkcode is SHA1 hash over all EAP-AKA/Identity packets. */
+       addr = wpabuf_head(data->id_msgs);
+       len = wpabuf_len(data->id_msgs);
+       if (data->eap_method == EAP_TYPE_AKA_PRIME)
+               sha256_vector(1, &addr, &len, hash);
+       else
+               sha1_vector(1, &addr, &len, hash);
+
+       if (os_memcmp(hash, checkcode, hash_len) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
+                                             struct eap_aka_data *data, u8 id)
+{
+       struct eap_sim_msg *msg;
+       struct wpabuf *buf;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity");
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_IDENTITY);
+       if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+                                     sm->identity_len)) {
+               wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+               eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+       } else {
+               /*
+                * RFC 4187, Chap. 4.1.4 recommends that identity from EAP is
+                * ignored and the AKA/Identity is used to request the
+                * identity.
+                */
+               wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
+               eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+       }
+       buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
+       if (eap_aka_add_id_msg(data, buf) < 0) {
+               wpabuf_free(buf);
+               return NULL;
+       }
+       data->pending_id = id;
+       return buf;
+}
+
+
+static int eap_aka_build_encr(struct eap_sm *sm, struct eap_aka_data *data,
+                             struct eap_sim_msg *msg, u16 counter,
+                             const u8 *nonce_s)
+{
+       os_free(data->next_pseudonym);
+       data->next_pseudonym =
+               eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1);
+       os_free(data->next_reauth_id);
+       if (data->counter <= EAP_AKA_MAX_FAST_REAUTHS) {
+               data->next_reauth_id =
+                       eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 1);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication "
+                          "count exceeded - force full authentication");
+               data->next_reauth_id = NULL;
+       }
+
+       if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
+           counter == 0 && nonce_s == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "   AT_IV");
+       wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+       eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+       if (counter > 0) {
+               wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
+               eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+       }
+
+       if (nonce_s) {
+               wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
+               eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
+                               EAP_SIM_NONCE_S_LEN);
+       }
+
+       if (data->next_pseudonym) {
+               wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
+                          data->next_pseudonym);
+               eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
+                               os_strlen(data->next_pseudonym),
+                               (u8 *) data->next_pseudonym,
+                               os_strlen(data->next_pseudonym));
+       }
+
+       if (data->next_reauth_id) {
+               wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
+                          data->next_reauth_id);
+               eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
+                               os_strlen(data->next_reauth_id),
+                               (u8 *) data->next_reauth_id,
+                               os_strlen(data->next_reauth_id));
+       }
+
+       if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt "
+                          "AT_ENCR_DATA");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
+                                              struct eap_aka_data *data,
+                                              u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Challenge");
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_CHALLENGE);
+       wpa_printf(MSG_DEBUG, "   AT_RAND");
+       eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, data->rand, EAP_AKA_RAND_LEN);
+       wpa_printf(MSG_DEBUG, "   AT_AUTN");
+       eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN);
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               if (data->kdf) {
+                       /* Add the selected KDF into the beginning */
+                       wpa_printf(MSG_DEBUG, "   AT_KDF");
+                       eap_sim_msg_add(msg, EAP_SIM_AT_KDF, data->kdf,
+                                       NULL, 0);
+               }
+               wpa_printf(MSG_DEBUG, "   AT_KDF");
+               eap_sim_msg_add(msg, EAP_SIM_AT_KDF, EAP_AKA_PRIME_KDF,
+                               NULL, 0);
+               wpa_printf(MSG_DEBUG, "   AT_KDF_INPUT");
+               eap_sim_msg_add(msg, EAP_SIM_AT_KDF_INPUT,
+                               data->network_name_len,
+                               data->network_name, data->network_name_len);
+       }
+
+       if (eap_aka_build_encr(sm, data, msg, 0, NULL)) {
+               eap_sim_msg_free(msg);
+               return NULL;
+       }
+
+       eap_aka_add_checkcode(data, msg);
+
+       if (sm->eap_sim_aka_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+
+#ifdef EAP_SERVER_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA) {
+               u16 flags = 0;
+               int i;
+               int aka_prime_preferred = 0;
+
+               i = 0;
+               while (sm->user && i < EAP_MAX_METHODS &&
+                      (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+                       sm->user->methods[i].method != EAP_TYPE_NONE)) {
+                       if (sm->user->methods[i].vendor == EAP_VENDOR_IETF) {
+                               if (sm->user->methods[i].method ==
+                                   EAP_TYPE_AKA)
+                                       break;
+                               if (sm->user->methods[i].method ==
+                                   EAP_TYPE_AKA_PRIME) {
+                                       aka_prime_preferred = 1;
+                                       break;
+                               }
+                       }
+                       i++;
+               }
+
+               if (aka_prime_preferred)
+                       flags |= EAP_AKA_BIDDING_FLAG_D;
+               eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0);
+       }
+#endif /* EAP_SERVER_AKA_PRIME */
+
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
+                                           struct eap_aka_data *data, u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Re-authentication");
+
+       if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+               return NULL;
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-AKA: NONCE_S",
+                       data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               eap_aka_prime_derive_keys_reauth(data->k_re, data->counter,
+                                                sm->identity,
+                                                sm->identity_len,
+                                                data->nonce_s,
+                                                data->msk, data->emsk);
+       } else {
+               eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+                                   data->msk, data->emsk);
+               eap_sim_derive_keys_reauth(data->counter, sm->identity,
+                                          sm->identity_len, data->nonce_s,
+                                          data->mk, data->msk, data->emsk);
+       }
+
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_REAUTHENTICATION);
+
+       if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
+               eap_sim_msg_free(msg);
+               return NULL;
+       }
+
+       eap_aka_add_checkcode(data, msg);
+
+       if (sm->eap_sim_aka_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm,
+                                                 struct eap_aka_data *data,
+                                                 u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Notification");
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, data->eap_method,
+                              EAP_AKA_SUBTYPE_NOTIFICATION);
+       wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
+       eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
+                       NULL, 0);
+       if (data->use_result_ind) {
+               if (data->reauth) {
+                       wpa_printf(MSG_DEBUG, "   AT_IV");
+                       wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+                       eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+                                                  EAP_SIM_AT_ENCR_DATA);
+                       wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
+                                  data->counter);
+                       eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+                                       NULL, 0);
+
+                       if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+                                                    EAP_SIM_AT_PADDING)) {
+                               wpa_printf(MSG_WARNING, "EAP-AKA: Failed to "
+                                          "encrypt AT_ENCR_DATA");
+                               eap_sim_msg_free(msg);
+                               return NULL;
+                       }
+               }
+
+               wpa_printf(MSG_DEBUG, "   AT_MAC");
+               eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       }
+       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_aka_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_aka_data *data = priv;
+
+       data->auts_reported = 0;
+       switch (data->state) {
+       case IDENTITY:
+               return eap_aka_build_identity(sm, data, id);
+       case CHALLENGE:
+               return eap_aka_build_challenge(sm, data, id);
+       case REAUTH:
+               return eap_aka_build_reauth(sm, data, id);
+       case NOTIFICATION:
+               return eap_aka_build_notification(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
+                          "buildReq", data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_aka_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_aka_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
+                              &len);
+       if (pos == NULL || len < 3) {
+               wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype)
+{
+       if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR ||
+           subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT)
+               return FALSE;
+
+       switch (data->state) {
+       case IDENTITY:
+               if (subtype != EAP_AKA_SUBTYPE_IDENTITY) {
+                       wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       case CHALLENGE:
+               if (subtype != EAP_AKA_SUBTYPE_CHALLENGE &&
+                   subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
+                       wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       case REAUTH:
+               if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) {
+                       wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       case NOTIFICATION:
+               if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) {
+                       wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for "
+                          "processing a response", data->state);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_aka_determine_identity(struct eap_sm *sm,
+                                      struct eap_aka_data *data,
+                                      int before_identity, int after_reauth)
+{
+       const u8 *identity;
+       size_t identity_len;
+       int res;
+
+       identity = NULL;
+       identity_len = 0;
+
+       if (after_reauth && data->reauth) {
+               identity = data->reauth->identity;
+               identity_len = data->reauth->identity_len;
+       } else if (sm->identity && sm->identity_len > 0 &&
+                  sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) {
+               identity = sm->identity;
+               identity_len = sm->identity_len;
+       } else {
+               identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
+                                                   sm->identity,
+                                                   sm->identity_len,
+                                                   &identity_len);
+               if (identity == NULL) {
+                       data->reauth = eap_sim_db_get_reauth_entry(
+                               sm->eap_sim_db_priv, sm->identity,
+                               sm->identity_len);
+                       if (data->reauth &&
+                           data->reauth->aka_prime !=
+                           (data->eap_method == EAP_TYPE_AKA_PRIME)) {
+                               wpa_printf(MSG_DEBUG, "EAP-AKA: Reauth data "
+                                          "was for different AKA version");
+                               data->reauth = NULL;
+                       }
+                       if (data->reauth) {
+                               wpa_printf(MSG_DEBUG, "EAP-AKA: Using fast "
+                                          "re-authentication");
+                               identity = data->reauth->identity;
+                               identity_len = data->reauth->identity_len;
+                               data->counter = data->reauth->counter;
+                               if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+                                       os_memcpy(data->k_encr,
+                                                 data->reauth->k_encr,
+                                                 EAP_SIM_K_ENCR_LEN);
+                                       os_memcpy(data->k_aut,
+                                                 data->reauth->k_aut,
+                                                 EAP_AKA_PRIME_K_AUT_LEN);
+                                       os_memcpy(data->k_re,
+                                                 data->reauth->k_re,
+                                                 EAP_AKA_PRIME_K_RE_LEN);
+                               } else {
+                                       os_memcpy(data->mk, data->reauth->mk,
+                                                 EAP_SIM_MK_LEN);
+                               }
+                       }
+               }
+       }
+
+       if (identity == NULL ||
+           eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+                                     sm->identity_len) < 0) {
+               if (before_identity) {
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name "
+                                  "not known - send AKA-Identity request");
+                       eap_aka_state(data, IDENTITY);
+                       return;
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the "
+                                  "permanent user name is known; try to use "
+                                  "it");
+                       /* eap_sim_db_get_aka_auth() will report failure, if
+                        * this identity is not known. */
+               }
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity",
+                         identity, identity_len);
+
+       if (!after_reauth && data->reauth) {
+               eap_aka_state(data, REAUTH);
+               return;
+       }
+
+       res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity,
+                                     identity_len, data->rand, data->autn,
+                                     data->ik, data->ck, data->res,
+                                     &data->res_len, sm);
+       if (res == EAP_SIM_DB_PENDING) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
+                          "not yet available - pending request");
+               sm->method_pending = METHOD_PENDING_WAIT;
+               return;
+       }
+
+#ifdef EAP_SERVER_AKA_PRIME
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
+                * needed 6-octet SQN ^AK for CK',IK' derivation */
+               eap_aka_prime_derive_ck_ik_prime(data->ck, data->ik,
+                                                data->autn,
+                                                data->network_name,
+                                                data->network_name_len);
+       }
+#endif /* EAP_SERVER_AKA_PRIME */
+
+       data->reauth = NULL;
+       data->counter = 0; /* reset re-auth counter since this is full auth */
+
+       if (res != 0) {
+               wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA "
+                          "authentication data for the peer");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+       if (sm->method_pending == METHOD_PENDING_WAIT) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data "
+                          "available - abort pending wait");
+               sm->method_pending = METHOD_PENDING_NONE;
+       }
+
+       identity_len = sm->identity_len;
+       while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Workaround - drop last null "
+                          "character from identity");
+               identity_len--;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation",
+                         sm->identity, identity_len);
+
+       if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+               eap_aka_prime_derive_keys(identity, identity_len, data->ik,
+                                         data->ck, data->k_encr, data->k_aut,
+                                         data->k_re, data->msk, data->emsk);
+       } else {
+               eap_aka_derive_mk(sm->identity, identity_len, data->ik,
+                                 data->ck, data->mk);
+               eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut,
+                                   data->msk, data->emsk);
+       }
+
+       eap_aka_state(data, CHALLENGE);
+}
+
+
+static void eap_aka_process_identity(struct eap_sm *sm,
+                                    struct eap_aka_data *data,
+                                    struct wpabuf *respData,
+                                    struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity");
+
+       if (attr->mac || attr->iv || attr->encr_data) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute "
+                          "received in EAP-Response/AKA-Identity");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+
+       if (attr->identity) {
+               os_free(sm->identity);
+               sm->identity = os_malloc(attr->identity_len);
+               if (sm->identity) {
+                       os_memcpy(sm->identity, attr->identity,
+                                 attr->identity_len);
+                       sm->identity_len = attr->identity_len;
+               }
+       }
+
+       eap_aka_determine_identity(sm, data, 0, 0);
+       if (eap_get_id(respData) == data->pending_id) {
+               data->pending_id = -1;
+               eap_aka_add_id_msg(data, respData);
+       }
+}
+
+
+static int eap_aka_verify_mac(struct eap_aka_data *data,
+                             const struct wpabuf *req,
+                             const u8 *mac, const u8 *extra,
+                             size_t extra_len)
+{
+       if (data->eap_method == EAP_TYPE_AKA_PRIME)
+               return eap_sim_verify_mac_sha256(data->k_aut, req, mac, extra,
+                                                extra_len);
+       return eap_sim_verify_mac(data->k_aut, req, mac, extra, extra_len);
+}
+
+
+static void eap_aka_process_challenge(struct eap_sm *sm,
+                                     struct eap_aka_data *data,
+                                     struct wpabuf *respData,
+                                     struct eap_sim_attrs *attr)
+{
+       const u8 *identity;
+       size_t identity_len;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge");
+
+#ifdef EAP_SERVER_AKA_PRIME
+#if 0
+       /* KDF negotiation; to be enabled only after more than one KDF is
+        * supported */
+       if (data->eap_method == EAP_TYPE_AKA_PRIME &&
+           attr->kdf_count == 1 && attr->mac == NULL) {
+               if (attr->kdf[0] != EAP_AKA_PRIME_KDF) {
+                       wpa_printf(MSG_WARNING, "EAP-AKA': Peer selected "
+                                  "unknown KDF");
+                       data->notification =
+                               EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+                       eap_aka_state(data, NOTIFICATION);
+                       return;
+               }
+
+               data->kdf = attr->kdf[0];
+
+               /* Allow negotiation to continue with the selected KDF by
+                * sending another Challenge message */
+               wpa_printf(MSG_DEBUG, "EAP-AKA': KDF %d selected", data->kdf);
+               return;
+       }
+#endif
+#endif /* EAP_SERVER_AKA_PRIME */
+
+       if (attr->checkcode &&
+           eap_aka_verify_checkcode(data, attr->checkcode,
+                                    attr->checkcode_len)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Invalid AT_CHECKCODE in the "
+                          "message");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+       if (attr->mac == NULL ||
+           eap_aka_verify_mac(data, respData, attr->mac, NULL, 0)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message "
+                          "did not include valid AT_MAC");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+
+       /*
+        * AT_RES is padded, so verify that there is enough room for RES and
+        * that the RES length in bits matches with the expected RES.
+        */
+       if (attr->res == NULL || attr->res_len < data->res_len ||
+           attr->res_len_bits != data->res_len * 8 ||
+           os_memcmp(attr->res, data->res, data->res_len) != 0) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not "
+                          "include valid AT_RES (attr len=%lu, res len=%lu "
+                          "bits, expected %lu bits)",
+                          (unsigned long) attr->res_len,
+                          (unsigned long) attr->res_len_bits,
+                          (unsigned long) data->res_len * 8);
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the "
+                  "correct AT_MAC");
+       if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+               data->use_result_ind = 1;
+               data->notification = EAP_SIM_SUCCESS;
+               eap_aka_state(data, NOTIFICATION);
+       } else
+               eap_aka_state(data, SUCCESS);
+
+       identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
+                                           sm->identity_len, &identity_len);
+       if (identity == NULL) {
+               identity = sm->identity;
+               identity_len = sm->identity_len;
+       }
+
+       if (data->next_pseudonym) {
+               eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+                                        identity_len,
+                                        data->next_pseudonym);
+               data->next_pseudonym = NULL;
+       }
+       if (data->next_reauth_id) {
+               if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+#ifdef EAP_SERVER_AKA_PRIME
+                       eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+                                                   identity,
+                                                   identity_len,
+                                                   data->next_reauth_id,
+                                                   data->counter + 1,
+                                                   data->k_encr, data->k_aut,
+                                                   data->k_re);
+#endif /* EAP_SERVER_AKA_PRIME */
+               } else {
+                       eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+                                             identity_len,
+                                             data->next_reauth_id,
+                                             data->counter + 1,
+                                             data->mk);
+               }
+               data->next_reauth_id = NULL;
+       }
+}
+
+
+static void eap_aka_process_sync_failure(struct eap_sm *sm,
+                                        struct eap_aka_data *data,
+                                        struct wpabuf *respData,
+                                        struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure");
+
+       if (attr->auts == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure "
+                          "message did not include valid AT_AUTS");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+
+       /* Avoid re-reporting AUTS when processing pending EAP packet by
+        * maintaining a local flag stating whether this AUTS has already been
+        * reported. */
+       if (!data->auts_reported &&
+           eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity,
+                                    sm->identity_len, attr->auts,
+                                    data->rand)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+       data->auts_reported = 1;
+
+       /* Try again after resynchronization */
+       eap_aka_determine_identity(sm, data, 0, 0);
+}
+
+
+static void eap_aka_process_reauth(struct eap_sm *sm,
+                                  struct eap_aka_data *data,
+                                  struct wpabuf *respData,
+                                  struct eap_sim_attrs *attr)
+{
+       struct eap_sim_attrs eattr;
+       u8 *decrypted = NULL;
+       const u8 *identity, *id2;
+       size_t identity_len, id2_len;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Reauthentication");
+
+       if (attr->mac == NULL ||
+           eap_aka_verify_mac(data, respData, attr->mac, data->nonce_s,
+                              EAP_SIM_NONCE_S_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
+                          "did not include valid AT_MAC");
+               goto fail;
+       }
+
+       if (attr->encr_data == NULL || attr->iv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication "
+                          "message did not include encrypted data");
+               goto fail;
+       }
+
+       decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                      attr->encr_data_len, attr->iv, &eattr,
+                                      0);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted "
+                          "data from reauthentication message");
+               goto fail;
+       }
+
+       if (eattr.counter != data->counter) {
+               wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message "
+                          "used incorrect counter %u, expected %u",
+                          eattr.counter, data->counter);
+               goto fail;
+       }
+       os_free(decrypted);
+       decrypted = NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes "
+                  "the correct AT_MAC");
+
+       if (eattr.counter_too_small) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response "
+                          "included AT_COUNTER_TOO_SMALL - starting full "
+                          "authentication");
+               eap_aka_determine_identity(sm, data, 0, 1);
+               return;
+       }
+
+       if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+               data->use_result_ind = 1;
+               data->notification = EAP_SIM_SUCCESS;
+               eap_aka_state(data, NOTIFICATION);
+       } else
+               eap_aka_state(data, SUCCESS);
+
+       if (data->reauth) {
+               identity = data->reauth->identity;
+               identity_len = data->reauth->identity_len;
+       } else {
+               identity = sm->identity;
+               identity_len = sm->identity_len;
+       }
+
+       id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
+                                      identity_len, &id2_len);
+       if (id2) {
+               identity = id2;
+               identity_len = id2_len;
+       }
+
+       if (data->next_pseudonym) {
+               eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+                                        identity_len, data->next_pseudonym);
+               data->next_pseudonym = NULL;
+       }
+       if (data->next_reauth_id) {
+               if (data->eap_method == EAP_TYPE_AKA_PRIME) {
+#ifdef EAP_SERVER_AKA_PRIME
+                       eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv,
+                                                   identity,
+                                                   identity_len,
+                                                   data->next_reauth_id,
+                                                   data->counter + 1,
+                                                   data->k_encr, data->k_aut,
+                                                   data->k_re);
+#endif /* EAP_SERVER_AKA_PRIME */
+               } else {
+                       eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+                                             identity_len,
+                                             data->next_reauth_id,
+                                             data->counter + 1,
+                                             data->mk);
+               }
+               data->next_reauth_id = NULL;
+       } else {
+               eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+               data->reauth = NULL;
+       }
+
+       return;
+
+fail:
+       data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+       eap_aka_state(data, NOTIFICATION);
+       eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+       data->reauth = NULL;
+       os_free(decrypted);
+}
+
+
+static void eap_aka_process_client_error(struct eap_sm *sm,
+                                        struct eap_aka_data *data,
+                                        struct wpabuf *respData,
+                                        struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d",
+                  attr->client_error_code);
+       if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+               eap_aka_state(data, SUCCESS);
+       else
+               eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process_authentication_reject(
+       struct eap_sm *sm, struct eap_aka_data *data,
+       struct wpabuf *respData, struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication");
+       eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process_notification(struct eap_sm *sm,
+                                        struct eap_aka_data *data,
+                                        struct wpabuf *respData,
+                                        struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification");
+       if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+               eap_aka_state(data, SUCCESS);
+       else
+               eap_aka_state(data, FAILURE);
+}
+
+
+static void eap_aka_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_aka_data *data = priv;
+       const u8 *pos, *end;
+       u8 subtype;
+       size_t len;
+       struct eap_sim_attrs attr;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_method, respData,
+                              &len);
+       if (pos == NULL || len < 3)
+               return;
+
+       end = pos + len;
+       subtype = *pos;
+       pos += 3;
+
+       if (eap_aka_subtype_ok(data, subtype)) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected "
+                          "EAP-AKA Subtype in EAP Response");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+
+       if (eap_sim_parse_attr(pos, end, &attr,
+                              data->eap_method == EAP_TYPE_AKA_PRIME ? 2 : 1,
+                              0)) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes");
+               data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH;
+               eap_aka_state(data, NOTIFICATION);
+               return;
+       }
+
+       if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) {
+               eap_aka_process_client_error(sm, data, respData, &attr);
+               return;
+       }
+
+       if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) {
+               eap_aka_process_authentication_reject(sm, data, respData,
+                                                     &attr);
+               return;
+       }
+
+       switch (data->state) {
+       case IDENTITY:
+               eap_aka_process_identity(sm, data, respData, &attr);
+               break;
+       case CHALLENGE:
+               if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) {
+                       eap_aka_process_sync_failure(sm, data, respData,
+                                                    &attr);
+               } else {
+                       eap_aka_process_challenge(sm, data, respData, &attr);
+               }
+               break;
+       case REAUTH:
+               eap_aka_process_reauth(sm, data, respData, &attr);
+               break;
+       case NOTIFICATION:
+               eap_aka_process_notification(sm, data, respData, &attr);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in "
+                          "process", data->state);
+               break;
+       }
+}
+
+
+static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_aka_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+       *len = EAP_SIM_KEYING_DATA_LEN;
+       return key;
+}
+
+
+static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_aka_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+       return key;
+}
+
+
+static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_aka_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_aka_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_aka_init;
+       eap->reset = eap_aka_reset;
+       eap->buildReq = eap_aka_buildReq;
+       eap->check = eap_aka_check;
+       eap->process = eap_aka_process;
+       eap->isDone = eap_aka_isDone;
+       eap->getKey = eap_aka_getKey;
+       eap->isSuccess = eap_aka_isSuccess;
+       eap->get_emsk = eap_aka_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+int eap_server_aka_prime_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_AKA_PRIME,
+                                     "AKA'");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_aka_prime_init;
+       eap->reset = eap_aka_reset;
+       eap->buildReq = eap_aka_buildReq;
+       eap->check = eap_aka_check;
+       eap->process = eap_aka_process;
+       eap->isDone = eap_aka_isDone;
+       eap->getKey = eap_aka_getKey;
+       eap->isSuccess = eap_aka_isSuccess;
+       eap->get_emsk = eap_aka_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+
+       return ret;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
diff --git a/src/eap_server/eap_server_fast.c b/src/eap_server/eap_server_fast.c
new file mode 100644 (file)
index 0000000..39beb33
--- /dev/null
@@ -0,0 +1,1619 @@
+/*
+ * EAP-FAST server (RFC 4851)
+ * 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 "crypto/aes_wrap.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_fast_common.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_fast_reset(struct eap_sm *sm, void *priv);
+
+
+/* Private PAC-Opaque TLV types */
+#define PAC_OPAQUE_TYPE_PAD 0
+#define PAC_OPAQUE_TYPE_KEY 1
+#define PAC_OPAQUE_TYPE_LIFETIME 2
+#define PAC_OPAQUE_TYPE_IDENTITY 3
+
+struct eap_fast_data {
+       struct eap_ssl_data ssl;
+       enum {
+               START, PHASE1, PHASE2_START, PHASE2_ID, PHASE2_METHOD,
+               CRYPTO_BINDING, REQUEST_PAC, SUCCESS, FAILURE
+       } state;
+
+       int fast_version;
+       const struct eap_method *phase2_method;
+       void *phase2_priv;
+       int force_version;
+       int peer_version;
+
+       u8 crypto_binding_nonce[32];
+       int final_result;
+
+       struct eap_fast_key_block_provisioning *key_block_p;
+
+       u8 simck[EAP_FAST_SIMCK_LEN];
+       u8 cmk[EAP_FAST_CMK_LEN];
+       int simck_idx;
+
+       u8 pac_opaque_encr[16];
+       u8 *srv_id;
+       size_t srv_id_len;
+       char *srv_id_info;
+
+       int anon_provisioning;
+       int send_new_pac; /* server triggered re-keying of Tunnel PAC */
+       struct wpabuf *pending_phase2_resp;
+       u8 *identity; /* from PAC-Opaque */
+       size_t identity_len;
+       int eap_seq;
+       int tnc_started;
+
+       int pac_key_lifetime;
+       int pac_key_refresh_time;
+};
+
+
+static int eap_fast_process_phase2_start(struct eap_sm *sm,
+                                        struct eap_fast_data *data);
+
+
+static const char * eap_fast_state_txt(int state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case PHASE1:
+               return "PHASE1";
+       case PHASE2_START:
+               return "PHASE2_START";
+       case PHASE2_ID:
+               return "PHASE2_ID";
+       case PHASE2_METHOD:
+               return "PHASE2_METHOD";
+       case CRYPTO_BINDING:
+               return "CRYPTO_BINDING";
+       case REQUEST_PAC:
+               return "REQUEST_PAC";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "Unknown?!";
+       }
+}
+
+
+static void eap_fast_state(struct eap_fast_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-FAST: %s -> %s",
+                  eap_fast_state_txt(data->state),
+                  eap_fast_state_txt(state));
+       data->state = state;
+}
+
+
+static EapType eap_fast_req_failure(struct eap_sm *sm,
+                                   struct eap_fast_data *data)
+{
+       /* TODO: send Result TLV(FAILURE) */
+       eap_fast_state(data, FAILURE);
+       return EAP_TYPE_NONE;
+}
+
+
+static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
+                                     const u8 *client_random,
+                                     const u8 *server_random,
+                                     u8 *master_secret)
+{
+       struct eap_fast_data *data = ctx;
+       const u8 *pac_opaque;
+       size_t pac_opaque_len;
+       u8 *buf, *pos, *end, *pac_key = NULL;
+       os_time_t lifetime = 0;
+       struct os_time now;
+       u8 *identity = NULL;
+       size_t identity_len = 0;
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: SessionTicket callback");
+       wpa_hexdump(MSG_DEBUG, "EAP-FAST: SessionTicket (PAC-Opaque)",
+                   ticket, len);
+
+       if (len < 4 || WPA_GET_BE16(ticket) != PAC_TYPE_PAC_OPAQUE) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid "
+                          "SessionTicket");
+               return 0;
+       }
+
+       pac_opaque_len = WPA_GET_BE16(ticket + 2);
+       pac_opaque = ticket + 4;
+       if (pac_opaque_len < 8 || pac_opaque_len % 8 ||
+           pac_opaque_len > len - 4) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Ignore invalid PAC-Opaque "
+                          "(len=%lu left=%lu)",
+                          (unsigned long) pac_opaque_len,
+                          (unsigned long) len);
+               return 0;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-FAST: Received PAC-Opaque",
+                   pac_opaque, pac_opaque_len);
+
+       buf = os_malloc(pac_opaque_len - 8);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to allocate memory "
+                          "for decrypting PAC-Opaque");
+               return 0;
+       }
+
+       if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8,
+                      pac_opaque, buf) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt "
+                          "PAC-Opaque");
+               os_free(buf);
+               /*
+                * This may have been caused by server changing the PAC-Opaque
+                * encryption key, so just ignore this PAC-Opaque instead of
+                * failing the authentication completely. Provisioning can now
+                * be used to provision a new PAC.
+                */
+               return 0;
+       }
+
+       end = buf + pac_opaque_len - 8;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted PAC-Opaque",
+                       buf, end - buf);
+
+       pos = buf;
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+
+               switch (*pos) {
+               case PAC_OPAQUE_TYPE_PAD:
+                       pos = end;
+                       break;
+               case PAC_OPAQUE_TYPE_KEY:
+                       if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
+                                          "PAC-Key length %d", pos[1]);
+                               os_free(buf);
+                               return -1;
+                       }
+                       pac_key = pos + 2;
+                       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: PAC-Key from "
+                                       "decrypted PAC-Opaque",
+                                       pac_key, EAP_FAST_PAC_KEY_LEN);
+                       break;
+               case PAC_OPAQUE_TYPE_LIFETIME:
+                       if (pos[1] != 4) {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
+                                          "PAC-Key lifetime length %d",
+                                          pos[1]);
+                               os_free(buf);
+                               return -1;
+                       }
+                       lifetime = WPA_GET_BE32(pos + 2);
+                       break;
+               case PAC_OPAQUE_TYPE_IDENTITY:
+                       identity = pos + 2;
+                       identity_len = pos[1];
+                       break;
+               }
+
+               pos += 2 + pos[1];
+       }
+
+       if (pac_key == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in "
+                          "PAC-Opaque");
+               os_free(buf);
+               return -1;
+       }
+
+       if (identity) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Identity from "
+                                 "PAC-Opaque", identity, identity_len);
+               os_free(data->identity);
+               data->identity = os_malloc(identity_len);
+               if (data->identity) {
+                       os_memcpy(data->identity, identity, identity_len);
+                       data->identity_len = identity_len;
+               }
+       }
+
+       if (os_get_time(&now) < 0 || lifetime <= 0 || now.sec > lifetime) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key not valid anymore "
+                          "(lifetime=%ld now=%ld)", lifetime, now.sec);
+               data->send_new_pac = 2;
+               /*
+                * Allow PAC to be used to allow a PAC update with some level
+                * of server authentication (i.e., do not fall back to full TLS
+                * handshake since we cannot be sure that the peer would be
+                * able to validate server certificate now). However, reject
+                * the authentication since the PAC was not valid anymore. Peer
+                * can connect again with the newly provisioned PAC after this.
+                */
+       } else if (lifetime - now.sec < data->pac_key_refresh_time) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Key soft timeout; send "
+                          "an update if authentication succeeds");
+               data->send_new_pac = 1;
+       }
+
+       eap_fast_derive_master_secret(pac_key, server_random, client_random,
+                                     master_secret);
+
+       os_free(buf);
+
+       return 1;
+}
+
+
+static void eap_fast_derive_key_auth(struct eap_sm *sm,
+                                    struct eap_fast_data *data)
+{
+       u8 *sks;
+
+       /* RFC 4851, Section 5.1:
+        * Extra key material after TLS key_block: session_key_seed[40]
+        */
+
+       sks = eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn, "key expansion",
+                                 EAP_FAST_SKS_LEN);
+       if (sks == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive "
+                          "session_key_seed");
+               return;
+       }
+
+       /*
+        * RFC 4851, Section 5.2:
+        * S-IMCK[0] = session_key_seed
+        */
+       wpa_hexdump_key(MSG_DEBUG,
+                       "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+                       sks, EAP_FAST_SKS_LEN);
+       data->simck_idx = 0;
+       os_memcpy(data->simck, sks, EAP_FAST_SIMCK_LEN);
+       os_free(sks);
+}
+
+
+static void eap_fast_derive_key_provisioning(struct eap_sm *sm,
+                                            struct eap_fast_data *data)
+{
+       os_free(data->key_block_p);
+       data->key_block_p = (struct eap_fast_key_block_provisioning *)
+               eap_fast_derive_key(sm->ssl_ctx, data->ssl.conn,
+                                   "key expansion",
+                                   sizeof(*data->key_block_p));
+       if (data->key_block_p == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to derive key block");
+               return;
+       }
+       /*
+        * RFC 4851, Section 5.2:
+        * S-IMCK[0] = session_key_seed
+        */
+       wpa_hexdump_key(MSG_DEBUG,
+                       "EAP-FAST: session_key_seed (SKS = S-IMCK[0])",
+                       data->key_block_p->session_key_seed,
+                       sizeof(data->key_block_p->session_key_seed));
+       data->simck_idx = 0;
+       os_memcpy(data->simck, data->key_block_p->session_key_seed,
+                 EAP_FAST_SIMCK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: server_challenge",
+                       data->key_block_p->server_challenge,
+                       sizeof(data->key_block_p->server_challenge));
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: client_challenge",
+                       data->key_block_p->client_challenge,
+                       sizeof(data->key_block_p->client_challenge));
+}
+
+
+static int eap_fast_get_phase2_key(struct eap_sm *sm,
+                                  struct eap_fast_data *data,
+                                  u8 *isk, size_t isk_len)
+{
+       u8 *key;
+       size_t key_len;
+
+       os_memset(isk, 0, isk_len);
+
+       if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+                          "available");
+               return -1;
+       }
+
+       if (data->phase2_method->getKey == NULL)
+               return 0;
+
+       if ((key = data->phase2_method->getKey(sm, data->phase2_priv,
+                                              &key_len)) == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Could not get key material "
+                          "from Phase 2");
+               return -1;
+       }
+
+       if (key_len > isk_len)
+               key_len = isk_len;
+       if (key_len == 32 &&
+           data->phase2_method->vendor == EAP_VENDOR_IETF &&
+           data->phase2_method->method == EAP_TYPE_MSCHAPV2) {
+               /*
+                * EAP-FAST uses reverse order for MS-MPPE keys when deriving
+                * MSK from EAP-MSCHAPv2. Swap the keys here to get the correct
+                * ISK for EAP-FAST cryptobinding.
+                */
+               os_memcpy(isk, key + 16, 16);
+               os_memcpy(isk + 16, key, 16);
+       } else
+               os_memcpy(isk, key, key_len);
+       os_free(key);
+
+       return 0;
+}
+
+
+static int eap_fast_update_icmk(struct eap_sm *sm, struct eap_fast_data *data)
+{
+       u8 isk[32], imck[60];
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Deriving ICMK[%d] (S-IMCK and CMK)",
+                  data->simck_idx + 1);
+
+       /*
+        * RFC 4851, Section 5.2:
+        * IMCK[j] = T-PRF(S-IMCK[j-1], "Inner Methods Compound Keys",
+        *                 MSK[j], 60)
+        * S-IMCK[j] = first 40 octets of IMCK[j]
+        * CMK[j] = last 20 octets of IMCK[j]
+        */
+
+       if (eap_fast_get_phase2_key(sm, data, isk, sizeof(isk)) < 0)
+               return -1;
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: ISK[j]", isk, sizeof(isk));
+       sha1_t_prf(data->simck, EAP_FAST_SIMCK_LEN,
+                  "Inner Methods Compound Keys",
+                  isk, sizeof(isk), imck, sizeof(imck));
+       data->simck_idx++;
+       os_memcpy(data->simck, imck, EAP_FAST_SIMCK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: S-IMCK[j]",
+                       data->simck, EAP_FAST_SIMCK_LEN);
+       os_memcpy(data->cmk, imck + EAP_FAST_SIMCK_LEN, EAP_FAST_CMK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-FAST: CMK[j]",
+                       data->cmk, EAP_FAST_CMK_LEN);
+
+       return 0;
+}
+
+
+static void * eap_fast_init(struct eap_sm *sm)
+{
+       struct eap_fast_data *data;
+       u8 ciphers[5] = {
+               TLS_CIPHER_ANON_DH_AES128_SHA,
+               TLS_CIPHER_AES128_SHA,
+               TLS_CIPHER_RSA_DHE_AES128_SHA,
+               TLS_CIPHER_RC4_SHA,
+               TLS_CIPHER_NONE
+       };
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->fast_version = EAP_FAST_VERSION;
+       data->force_version = -1;
+       if (sm->user && sm->user->force_version >= 0) {
+               data->force_version = sm->user->force_version;
+               wpa_printf(MSG_DEBUG, "EAP-FAST: forcing version %d",
+                          data->force_version);
+               data->fast_version = data->force_version;
+       }
+       data->state = START;
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to initialize SSL.");
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+
+       if (tls_connection_set_cipher_list(sm->ssl_ctx, data->ssl.conn,
+                                          ciphers) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to set TLS cipher "
+                          "suites");
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+
+       if (tls_connection_set_session_ticket_cb(sm->ssl_ctx, data->ssl.conn,
+                                                eap_fast_session_ticket_cb,
+                                                data) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to set SessionTicket "
+                          "callback");
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+
+       if (sm->pac_opaque_encr_key == NULL) {
+               wpa_printf(MSG_INFO, "EAP-FAST: No PAC-Opaque encryption key "
+                          "configured");
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+       os_memcpy(data->pac_opaque_encr, sm->pac_opaque_encr_key,
+                 sizeof(data->pac_opaque_encr));
+
+       if (sm->eap_fast_a_id == NULL) {
+               wpa_printf(MSG_INFO, "EAP-FAST: No A-ID configured");
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+       data->srv_id = os_malloc(sm->eap_fast_a_id_len);
+       if (data->srv_id == NULL) {
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+       os_memcpy(data->srv_id, sm->eap_fast_a_id, sm->eap_fast_a_id_len);
+       data->srv_id_len = sm->eap_fast_a_id_len;
+
+       if (sm->eap_fast_a_id_info == NULL) {
+               wpa_printf(MSG_INFO, "EAP-FAST: No A-ID-Info configured");
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+       data->srv_id_info = os_strdup(sm->eap_fast_a_id_info);
+       if (data->srv_id_info == NULL) {
+               eap_fast_reset(sm, data);
+               return NULL;
+       }
+
+       /* PAC-Key lifetime in seconds (hard limit) */
+       data->pac_key_lifetime = sm->pac_key_lifetime;
+
+       /*
+        * 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.
+        */
+       data->pac_key_refresh_time = sm->pac_key_refresh_time;
+
+       return data;
+}
+
+
+static void eap_fast_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       if (data == NULL)
+               return;
+       if (data->phase2_priv && data->phase2_method)
+               data->phase2_method->reset(sm, data->phase2_priv);
+       eap_server_tls_ssl_deinit(sm, &data->ssl);
+       os_free(data->srv_id);
+       os_free(data->srv_id_info);
+       os_free(data->key_block_p);
+       wpabuf_free(data->pending_phase2_resp);
+       os_free(data->identity);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_fast_build_start(struct eap_sm *sm,
+                                           struct eap_fast_data *data, u8 id)
+{
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_FAST,
+                           1 + sizeof(struct pac_tlv_hdr) + data->srv_id_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-FAST: Failed to allocate memory for"
+                          " request");
+               eap_fast_state(data, FAILURE);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->fast_version);
+
+       /* RFC 4851, 4.1.1. Authority ID Data */
+       eap_fast_put_tlv(req, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
+
+       eap_fast_state(data, PHASE1);
+
+       return req;
+}
+
+
+static int eap_fast_phase1_done(struct eap_sm *sm, struct eap_fast_data *data)
+{
+       char cipher[64];
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Phase1 done, starting Phase2");
+
+       if (tls_get_cipher(sm->ssl_ctx, data->ssl.conn, cipher, sizeof(cipher))
+           < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to get cipher "
+                          "information");
+               eap_fast_state(data, FAILURE);
+               return -1;
+       }
+       data->anon_provisioning = os_strstr(cipher, "ADH") != NULL;
+                   
+       if (data->anon_provisioning) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Anonymous provisioning");
+               eap_fast_derive_key_provisioning(sm, data);
+       } else
+               eap_fast_derive_key_auth(sm, data);
+
+       eap_fast_state(data, PHASE2_START);
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_fast_build_phase2_req(struct eap_sm *sm,
+                                                struct eap_fast_data *data,
+                                                u8 id)
+{
+       struct wpabuf *req;
+
+       if (data->phase2_priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase 2 method not "
+                          "initialized");
+               return NULL;
+       }
+       req = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+       if (req == NULL)
+               return NULL;
+
+       wpa_hexdump_buf_key(MSG_MSGDUMP, "EAP-FAST: Phase 2 EAP-Request", req);
+       return eap_fast_tlv_eap_payload(req);
+}
+
+
+static struct wpabuf * eap_fast_build_crypto_binding(
+       struct eap_sm *sm, struct eap_fast_data *data)
+{
+       struct wpabuf *buf;
+       struct eap_tlv_result_tlv *result;
+       struct eap_tlv_crypto_binding_tlv *binding;
+
+       buf = wpabuf_alloc(2 * sizeof(*result) + sizeof(*binding));
+       if (buf == NULL)
+               return NULL;
+
+       if (data->send_new_pac || data->anon_provisioning ||
+           data->phase2_method)
+               data->final_result = 0;
+       else
+               data->final_result = 1;
+
+       if (!data->final_result || data->eap_seq > 1) {
+               /* Intermediate-Result */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Add Intermediate-Result TLV "
+                          "(status=SUCCESS)");
+               result = wpabuf_put(buf, sizeof(*result));
+               result->tlv_type = host_to_be16(
+                       EAP_TLV_TYPE_MANDATORY |
+                       EAP_TLV_INTERMEDIATE_RESULT_TLV);
+               result->length = host_to_be16(2);
+               result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+       }
+
+       if (data->final_result) {
+               /* Result TLV */
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV "
+                          "(status=SUCCESS)");
+               result = wpabuf_put(buf, sizeof(*result));
+               result->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                               EAP_TLV_RESULT_TLV);
+               result->length = host_to_be16(2);
+               result->status = host_to_be16(EAP_TLV_RESULT_SUCCESS);
+       }
+
+       /* Crypto-Binding TLV */
+       binding = wpabuf_put(buf, sizeof(*binding));
+       binding->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                        EAP_TLV_CRYPTO_BINDING_TLV);
+       binding->length = host_to_be16(sizeof(*binding) -
+                                      sizeof(struct eap_tlv_hdr));
+       binding->version = EAP_FAST_VERSION;
+       binding->received_version = data->peer_version;
+       binding->subtype = EAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST;
+       if (os_get_random(binding->nonce, sizeof(binding->nonce)) < 0) {
+               wpabuf_free(buf);
+               return NULL;
+       }
+
+       /*
+        * RFC 4851, Section 4.2.8:
+        * The nonce in a request MUST have its least significant bit set to 0.
+        */
+       binding->nonce[sizeof(binding->nonce) - 1] &= ~0x01;
+
+       os_memcpy(data->crypto_binding_nonce, binding->nonce,
+                 sizeof(binding->nonce));
+
+       /*
+        * RFC 4851, Section 5.3:
+        * CMK = CMK[j]
+        * Compound-MAC = HMAC-SHA1( CMK, Crypto-Binding TLV )
+        */
+
+       hmac_sha1(data->cmk, EAP_FAST_CMK_LEN,
+                 (u8 *) binding, sizeof(*binding),
+                 binding->compound_mac);
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Add Crypto-Binding TLV: Version %d "
+                  "Received Version %d SubType %d",
+                  binding->version, binding->received_version,
+                  binding->subtype);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+                   binding->nonce, sizeof(binding->nonce));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+                   binding->compound_mac, sizeof(binding->compound_mac));
+
+       return buf;
+}
+
+
+static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
+                                         struct eap_fast_data *data)
+{
+       u8 pac_key[EAP_FAST_PAC_KEY_LEN];
+       u8 *pac_buf, *pac_opaque;
+       struct wpabuf *buf;
+       u8 *pos;
+       size_t buf_len, srv_id_info_len, pac_len;
+       struct eap_tlv_hdr *pac_tlv;
+       struct pac_tlv_hdr *pac_info;
+       struct eap_tlv_result_tlv *result;
+       struct os_time now;
+
+       if (os_get_random(pac_key, EAP_FAST_PAC_KEY_LEN) < 0 ||
+           os_get_time(&now) < 0)
+               return NULL;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Generated PAC-Key",
+                       pac_key, EAP_FAST_PAC_KEY_LEN);
+
+       pac_len = (2 + EAP_FAST_PAC_KEY_LEN) + (2 + 4) +
+               (2 + sm->identity_len) + 8;
+       pac_buf = os_malloc(pac_len);
+       if (pac_buf == NULL)
+               return NULL;
+
+       srv_id_info_len = os_strlen(data->srv_id_info);
+
+       pos = pac_buf;
+       *pos++ = PAC_OPAQUE_TYPE_KEY;
+       *pos++ = EAP_FAST_PAC_KEY_LEN;
+       os_memcpy(pos, pac_key, EAP_FAST_PAC_KEY_LEN);
+       pos += EAP_FAST_PAC_KEY_LEN;
+
+       *pos++ = PAC_OPAQUE_TYPE_LIFETIME;
+       *pos++ = 4;
+       WPA_PUT_BE32(pos, now.sec + data->pac_key_lifetime);
+       pos += 4;
+
+       if (sm->identity) {
+               *pos++ = PAC_OPAQUE_TYPE_IDENTITY;
+               *pos++ = sm->identity_len;
+               os_memcpy(pos, sm->identity, sm->identity_len);
+               pos += sm->identity_len;
+       }
+
+       pac_len = pos - pac_buf;
+       while (pac_len % 8) {
+               *pos++ = PAC_OPAQUE_TYPE_PAD;
+               pac_len++;
+       }
+
+       pac_opaque = os_malloc(pac_len + 8);
+       if (pac_opaque == NULL) {
+               os_free(pac_buf);
+               return NULL;
+       }
+       if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf,
+                    pac_opaque) < 0) {
+               os_free(pac_buf);
+               os_free(pac_opaque);
+               return NULL;
+       }
+       os_free(pac_buf);
+
+       pac_len += 8;
+       wpa_hexdump(MSG_DEBUG, "EAP-FAST: PAC-Opaque",
+                   pac_opaque, pac_len);
+
+       buf_len = sizeof(*pac_tlv) +
+               sizeof(struct pac_tlv_hdr) + EAP_FAST_PAC_KEY_LEN +
+               sizeof(struct pac_tlv_hdr) + pac_len +
+               data->srv_id_len + srv_id_info_len + 100 + sizeof(*result);
+       buf = wpabuf_alloc(buf_len);
+       if (buf == NULL) {
+               os_free(pac_opaque);
+               return NULL;
+       }
+
+       /* Result TLV */
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Add Result TLV (status=SUCCESS)");
+       result = wpabuf_put(buf, sizeof(*result));
+       WPA_PUT_BE16((u8 *) &result->tlv_type,
+                    EAP_TLV_TYPE_MANDATORY | EAP_TLV_RESULT_TLV);
+       WPA_PUT_BE16((u8 *) &result->length, 2);
+       WPA_PUT_BE16((u8 *) &result->status, EAP_TLV_RESULT_SUCCESS);
+
+       /* PAC TLV */
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Add PAC TLV");
+       pac_tlv = wpabuf_put(buf, sizeof(*pac_tlv));
+       pac_tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                        EAP_TLV_PAC_TLV);
+
+       /* PAC-Key */
+       eap_fast_put_tlv(buf, PAC_TYPE_PAC_KEY, pac_key, EAP_FAST_PAC_KEY_LEN);
+
+       /* PAC-Opaque */
+       eap_fast_put_tlv(buf, PAC_TYPE_PAC_OPAQUE, pac_opaque, pac_len);
+       os_free(pac_opaque);
+
+       /* PAC-Info */
+       pac_info = wpabuf_put(buf, sizeof(*pac_info));
+       pac_info->type = host_to_be16(PAC_TYPE_PAC_INFO);
+
+       /* PAC-Lifetime (inside PAC-Info) */
+       eap_fast_put_tlv_hdr(buf, PAC_TYPE_CRED_LIFETIME, 4);
+       wpabuf_put_be32(buf, now.sec + data->pac_key_lifetime);
+
+       /* A-ID (inside PAC-Info) */
+       eap_fast_put_tlv(buf, PAC_TYPE_A_ID, data->srv_id, data->srv_id_len);
+       
+       /* Note: headers may be misaligned after A-ID */
+
+       if (sm->identity) {
+               eap_fast_put_tlv(buf, PAC_TYPE_I_ID, sm->identity,
+                                sm->identity_len);
+       }
+
+       /* A-ID-Info (inside PAC-Info) */
+       eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info,
+                        srv_id_info_len);
+
+       /* PAC-Type (inside PAC-Info) */
+       eap_fast_put_tlv_hdr(buf, PAC_TYPE_PAC_TYPE, 2);
+       wpabuf_put_be16(buf, PAC_TYPE_TUNNEL_PAC);
+
+       /* Update PAC-Info and PAC TLV Length fields */
+       pos = wpabuf_put(buf, 0);
+       pac_info->len = host_to_be16(pos - (u8 *) (pac_info + 1));
+       pac_tlv->length = host_to_be16(pos - (u8 *) (pac_tlv + 1));
+
+       return buf;
+}
+
+
+static int eap_fast_encrypt_phase2(struct eap_sm *sm,
+                                  struct eap_fast_data *data,
+                                  struct wpabuf *plain, int piggyback)
+{
+       struct wpabuf *encr;
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs",
+                           plain);
+       encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
+       wpabuf_free(plain);
+
+       if (data->ssl.tls_out && piggyback) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data "
+                          "(len=%d) with last Phase 1 Message (len=%d "
+                          "used=%d)",
+                          (int) wpabuf_len(encr),
+                          (int) wpabuf_len(data->ssl.tls_out),
+                          (int) data->ssl.tls_out_pos);
+               if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) {
+                       wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize "
+                                  "output buffer");
+                       wpabuf_free(encr);
+                       return -1;
+               }
+               wpabuf_put_buf(data->ssl.tls_out, encr);
+               wpabuf_free(encr);
+       } else {
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = encr;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_fast_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_fast_data *data = priv;
+       struct wpabuf *req = NULL;
+       int piggyback = 0;
+
+       if (data->ssl.state == FRAG_ACK) {
+               return eap_server_tls_build_ack(id, EAP_TYPE_FAST,
+                                               data->fast_version);
+       }
+
+       if (data->ssl.state == WAIT_FRAG_ACK) {
+               return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
+                                               data->fast_version, id);
+       }
+
+       switch (data->state) {
+       case START:
+               return eap_fast_build_start(sm, data, id);
+       case PHASE1:
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+                       if (eap_fast_phase1_done(sm, data) < 0)
+                               return NULL;
+                       if (data->state == PHASE2_START) {
+                               /*
+                                * Try to generate Phase 2 data to piggyback
+                                * with the end of Phase 1 to avoid extra
+                                * roundtrip.
+                                */
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Try to start "
+                                          "Phase 2");
+                               if (eap_fast_process_phase2_start(sm, data))
+                                       break;
+                               req = eap_fast_build_phase2_req(sm, data, id);
+                               piggyback = 1;
+                       }
+               }
+               break;
+       case PHASE2_ID:
+       case PHASE2_METHOD:
+               req = eap_fast_build_phase2_req(sm, data, id);
+               break;
+       case CRYPTO_BINDING:
+               req = eap_fast_build_crypto_binding(sm, data);
+               if (data->phase2_method) {
+                       /*
+                        * Include the start of the next EAP method in the
+                        * sequence in the same message with Crypto-Binding to
+                        * save a round-trip.
+                        */
+                       struct wpabuf *eap;
+                       eap = eap_fast_build_phase2_req(sm, data, id);
+                       req = wpabuf_concat(req, eap);
+                       eap_fast_state(data, PHASE2_METHOD);
+               }
+               break;
+       case REQUEST_PAC:
+               req = eap_fast_build_pac(sm, data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d",
+                          __func__, data->state);
+               return NULL;
+       }
+
+       if (req &&
+           eap_fast_encrypt_phase2(sm, data, req, piggyback) < 0)
+               return NULL;
+
+       return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_FAST,
+                                       data->fast_version, id);
+}
+
+
+static Boolean eap_fast_check(struct eap_sm *sm, void *priv,
+                             struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_FAST, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static int eap_fast_phase2_init(struct eap_sm *sm, struct eap_fast_data *data,
+                               EapType eap_type)
+{
+       if (data->phase2_priv && data->phase2_method) {
+               data->phase2_method->reset(sm, data->phase2_priv);
+               data->phase2_method = NULL;
+               data->phase2_priv = NULL;
+       }
+       data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+                                                       eap_type);
+       if (!data->phase2_method)
+               return -1;
+
+       if (data->key_block_p) {
+               sm->auth_challenge = data->key_block_p->server_challenge;
+               sm->peer_challenge = data->key_block_p->client_challenge;
+       }
+       sm->init_phase2 = 1;
+       data->phase2_priv = data->phase2_method->init(sm);
+       sm->init_phase2 = 0;
+       sm->auth_challenge = NULL;
+       sm->peer_challenge = NULL;
+
+       return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static void eap_fast_process_phase2_response(struct eap_sm *sm,
+                                            struct eap_fast_data *data,
+                                            u8 *in_data, size_t in_len)
+{
+       u8 next_type = EAP_TYPE_NONE;
+       struct eap_hdr *hdr;
+       u8 *pos;
+       size_t left;
+       struct wpabuf buf;
+       const struct eap_method *m = data->phase2_method;
+       void *priv = data->phase2_priv;
+
+       if (priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: %s - Phase2 not "
+                          "initialized?!", __func__);
+               return;
+       }
+
+       hdr = (struct eap_hdr *) in_data;
+       pos = (u8 *) (hdr + 1);
+
+       if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+               left = in_len - sizeof(*hdr);
+               wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; "
+                           "allowed types", pos + 1, left - 1);
+#ifdef EAP_SERVER_TNC
+               if (m && m->vendor == EAP_VENDOR_IETF &&
+                   m->method == EAP_TYPE_TNC) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required "
+                                  "TNC negotiation");
+                       next_type = eap_fast_req_failure(sm, data);
+                       eap_fast_phase2_init(sm, data, next_type);
+                       return;
+               }
+#endif /* EAP_SERVER_TNC */
+               eap_sm_process_nak(sm, pos + 1, left - 1);
+               if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+                   sm->user->methods[sm->user_eap_method_index].method !=
+                   EAP_TYPE_NONE) {
+                       next_type = sm->user->methods[
+                               sm->user_eap_method_index++].method;
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d",
+                                  next_type);
+               } else {
+                       next_type = eap_fast_req_failure(sm, data);
+               }
+               eap_fast_phase2_init(sm, data, next_type);
+               return;
+       }
+
+       wpabuf_set(&buf, in_data, in_len);
+
+       if (m->check(sm, priv, &buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to "
+                          "ignore the packet");
+               next_type = eap_fast_req_failure(sm, data);
+               return;
+       }
+
+       m->process(sm, priv, &buf);
+
+       if (!m->isDone(sm, priv))
+               return;
+
+       if (!m->isSuccess(sm, priv)) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method failed");
+               next_type = eap_fast_req_failure(sm, data);
+               eap_fast_phase2_init(sm, data, next_type);
+               return;
+       }
+
+       switch (data->state) {
+       case PHASE2_ID:
+               if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: Phase2 "
+                                         "Identity not found in the user "
+                                         "database",
+                                         sm->identity, sm->identity_len);
+                       next_type = eap_fast_req_failure(sm, data);
+                       break;
+               }
+
+               eap_fast_state(data, PHASE2_METHOD);
+               if (data->anon_provisioning) {
+                       /*
+                        * Only EAP-MSCHAPv2 is allowed for anonymous
+                        * provisioning.
+                        */
+                       next_type = EAP_TYPE_MSCHAPV2;
+                       sm->user_eap_method_index = 0;
+               } else {
+                       next_type = sm->user->methods[0].method;
+                       sm->user_eap_method_index = 1;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: try EAP type %d", next_type);
+               break;
+       case PHASE2_METHOD:
+       case CRYPTO_BINDING:
+               eap_fast_update_icmk(sm, data);
+               eap_fast_state(data, CRYPTO_BINDING);
+               data->eap_seq++;
+               next_type = EAP_TYPE_NONE;
+#ifdef EAP_SERVER_TNC
+               if (sm->tnc && !data->tnc_started) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC");
+                       next_type = EAP_TYPE_TNC;
+                       data->tnc_started = 1;
+               }
+#endif /* EAP_SERVER_TNC */
+               break;
+       case FAILURE:
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-FAST: %s - unexpected state %d",
+                          __func__, data->state);
+               break;
+       }
+
+       eap_fast_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_fast_process_phase2_eap(struct eap_sm *sm,
+                                       struct eap_fast_data *data,
+                                       u8 *in_data, size_t in_len)
+{
+       struct eap_hdr *hdr;
+       size_t len;
+
+       hdr = (struct eap_hdr *) in_data;
+       if (in_len < (int) sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
+                          "EAP frame (len=%lu)", (unsigned long) in_len);
+               eap_fast_req_failure(sm, data);
+               return;
+       }
+       len = be_to_host16(hdr->length);
+       if (len > in_len) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Length mismatch in "
+                          "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+                          (unsigned long) in_len, (unsigned long) len);
+               eap_fast_req_failure(sm, data);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: code=%d "
+                  "identifier=%d length=%lu", hdr->code, hdr->identifier,
+                  (unsigned long) len);
+       switch (hdr->code) {
+       case EAP_CODE_RESPONSE:
+               eap_fast_process_phase2_response(sm, data, (u8 *) hdr, len);
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-FAST: Unexpected code=%d in "
+                          "Phase 2 EAP header", hdr->code);
+               break;
+       }
+}
+
+
+static int eap_fast_parse_tlvs(struct wpabuf *data,
+                              struct eap_fast_tlv_parse *tlv)
+{
+       int mandatory, tlv_type, len, res;
+       u8 *pos, *end;
+
+       os_memset(tlv, 0, sizeof(*tlv));
+
+       pos = wpabuf_mhead(data);
+       end = pos + wpabuf_len(data);
+       while (pos + 4 < end) {
+               mandatory = pos[0] & 0x80;
+               tlv_type = WPA_GET_BE16(pos) & 0x3fff;
+               pos += 2;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               if (pos + len > end) {
+                       wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
+                          "TLV type %d length %d%s",
+                          tlv_type, len, mandatory ? " (mandatory)" : "");
+
+               res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
+               if (res == -2)
+                       break;
+               if (res < 0) {
+                       if (mandatory) {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Nak unknown "
+                                          "mandatory TLV type %d", tlv_type);
+                               /* TODO: generate Nak TLV */
+                               break;
+                       } else {
+                               wpa_printf(MSG_DEBUG, "EAP-FAST: Ignored "
+                                          "unknown optional TLV type %d",
+                                          tlv_type);
+                       }
+               }
+
+               pos += len;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_validate_crypto_binding(
+       struct eap_fast_data *data, struct eap_tlv_crypto_binding_tlv *b,
+       size_t bind_len)
+{
+       u8 cmac[SHA1_MAC_LEN];
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Reply Crypto-Binding TLV: "
+                  "Version %d Received Version %d SubType %d",
+                  b->version, b->received_version, b->subtype);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: NONCE",
+                   b->nonce, sizeof(b->nonce));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Compound MAC",
+                   b->compound_mac, sizeof(b->compound_mac));
+
+       if (b->version != EAP_FAST_VERSION ||
+           b->received_version != EAP_FAST_VERSION) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected version "
+                          "in Crypto-Binding: version %d "
+                          "received_version %d", b->version,
+                          b->received_version);
+               return -1;
+       }
+
+       if (b->subtype != EAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected subtype in "
+                          "Crypto-Binding: %d", b->subtype);
+               return -1;
+       }
+
+       if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 ||
+           (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in "
+                          "Crypto-Binding");
+               return -1;
+       }
+
+       os_memcpy(cmac, b->compound_mac, sizeof(cmac));
+       os_memset(b->compound_mac, 0, sizeof(cmac));
+       wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Crypto-Binding TLV for "
+                   "Compound MAC calculation",
+                   (u8 *) b, bind_len);
+       hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len,
+                 b->compound_mac);
+       if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) {
+               wpa_hexdump(MSG_MSGDUMP,
+                           "EAP-FAST: Calculated Compound MAC",
+                           b->compound_mac, sizeof(cmac));
+               wpa_printf(MSG_INFO, "EAP-FAST: Compound MAC did not "
+                          "match");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_pac_type(u8 *pac, size_t len, u16 type)
+{
+       struct eap_tlv_pac_type_tlv *tlv;
+
+       if (pac == NULL || len != sizeof(*tlv))
+               return 0;
+
+       tlv = (struct eap_tlv_pac_type_tlv *) pac;
+
+       return be_to_host16(tlv->tlv_type) == PAC_TYPE_PAC_TYPE &&
+               be_to_host16(tlv->length) == 2 &&
+               be_to_host16(tlv->pac_type) == type;
+}
+
+
+static void eap_fast_process_phase2_tlvs(struct eap_sm *sm,
+                                        struct eap_fast_data *data,
+                                        struct wpabuf *in_data)
+{
+       struct eap_fast_tlv_parse tlv;
+       int check_crypto_binding = data->state == CRYPTO_BINDING;
+
+       if (eap_fast_parse_tlvs(in_data, &tlv) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received "
+                          "Phase 2 TLVs");
+               return;
+       }
+
+       if (tlv.result == EAP_TLV_RESULT_FAILURE) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Result TLV indicated "
+                          "failure");
+               eap_fast_state(data, FAILURE);
+               return;
+       }
+
+       if (data->state == REQUEST_PAC) {
+               u16 type, len, res;
+               if (tlv.pac == NULL || tlv.pac_len < 6) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC "
+                                  "Acknowledgement received");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               type = WPA_GET_BE16(tlv.pac);
+               len = WPA_GET_BE16(tlv.pac + 2);
+               res = WPA_GET_BE16(tlv.pac + 4);
+
+               if (type != PAC_TYPE_PAC_ACKNOWLEDGEMENT || len != 2 ||
+                   res != EAP_TLV_RESULT_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: PAC TLV did not "
+                                  "contain acknowledgement");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               wpa_printf(MSG_DEBUG, "EAP-FAST: PAC-Acknowledgement received "
+                          "- PAC provisioning succeeded");
+               eap_fast_state(data, (data->anon_provisioning ||
+                                     data->send_new_pac == 2) ?
+                              FAILURE : SUCCESS);
+               return;
+       }
+
+       if (check_crypto_binding) {
+               if (tlv.crypto_binding == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: No Crypto-Binding "
+                                  "TLV received");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               if (data->final_result &&
+                   tlv.result != EAP_TLV_RESULT_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV "
+                                  "without Success Result");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               if (!data->final_result &&
+                   tlv.iresult != EAP_TLV_RESULT_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Crypto-Binding TLV "
+                                  "without intermediate Success Result");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               if (eap_fast_validate_crypto_binding(data, tlv.crypto_binding,
+                                                    tlv.crypto_binding_len)) {
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Valid Crypto-Binding TLV "
+                          "received");
+               if (data->final_result) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
+                                  "completed successfully");
+               }
+
+               if (data->anon_provisioning &&
+                   sm->eap_fast_prov != ANON_PROV &&
+                   sm->eap_fast_prov != BOTH_PROV) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
+                                  "use unauthenticated provisioning which is "
+                                  "disabled");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               if (sm->eap_fast_prov != AUTH_PROV &&
+                   sm->eap_fast_prov != BOTH_PROV &&
+                   tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
+                   eap_fast_pac_type(tlv.pac, tlv.pac_len,
+                                     PAC_TYPE_TUNNEL_PAC)) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Client is trying to "
+                                  "use authenticated provisioning which is "
+                                  "disabled");
+                       eap_fast_state(data, FAILURE);
+                       return;
+               }
+
+               if (data->anon_provisioning ||
+                   (tlv.request_action == EAP_TLV_ACTION_PROCESS_TLV &&
+                    eap_fast_pac_type(tlv.pac, tlv.pac_len,
+                                      PAC_TYPE_TUNNEL_PAC))) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Requested a new "
+                                  "Tunnel PAC");
+                       eap_fast_state(data, REQUEST_PAC);
+               } else if (data->send_new_pac) {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Server triggered "
+                                  "re-keying of Tunnel PAC");
+                       eap_fast_state(data, REQUEST_PAC);
+               } else if (data->final_result)
+                       eap_fast_state(data, SUCCESS);
+       }
+
+       if (tlv.eap_payload_tlv) {
+               eap_fast_process_phase2_eap(sm, data, tlv.eap_payload_tlv,
+                                           tlv.eap_payload_tlv_len);
+       }
+}
+
+
+static void eap_fast_process_phase2(struct eap_sm *sm,
+                                   struct eap_fast_data *data,
+                                   struct wpabuf *in_buf)
+{
+       struct wpabuf *in_decrypted;
+
+       wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for"
+                  " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+       if (data->pending_phase2_resp) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - "
+                          "skip decryption and use old data");
+               eap_fast_process_phase2_tlvs(sm, data,
+                                            data->pending_phase2_resp);
+               wpabuf_free(data->pending_phase2_resp);
+               data->pending_phase2_resp = NULL;
+               return;
+       }
+
+       in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+                                             in_buf);
+       if (in_decrypted == NULL) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 "
+                          "data");
+               eap_fast_state(data, FAILURE);
+               return;
+       }
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs",
+                           in_decrypted);
+
+       eap_fast_process_phase2_tlvs(sm, data, in_decrypted);
+
+       if (sm->method_pending == METHOD_PENDING_WAIT) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in "
+                          "pending wait state - save decrypted response");
+               wpabuf_free(data->pending_phase2_resp);
+               data->pending_phase2_resp = in_decrypted;
+               return;
+       }
+
+       wpabuf_free(in_decrypted);
+}
+
+
+static int eap_fast_process_version(struct eap_sm *sm, void *priv,
+                                   int peer_version)
+{
+       struct eap_fast_data *data = priv;
+
+       data->peer_version = peer_version;
+
+       if (data->force_version >= 0 && peer_version != data->force_version) {
+               wpa_printf(MSG_INFO, "EAP-FAST: peer did not select the forced"
+                          " version (forced=%d peer=%d) - reject",
+                          data->force_version, peer_version);
+               return -1;
+       }
+
+       if (peer_version < data->fast_version) {
+               wpa_printf(MSG_DEBUG, "EAP-FAST: peer ver=%d, own ver=%d; "
+                          "use version %d",
+                          peer_version, data->fast_version, peer_version);
+               data->fast_version = peer_version;
+       }
+
+       return 0;
+}
+
+
+static int eap_fast_process_phase1(struct eap_sm *sm,
+                                  struct eap_fast_data *data)
+{
+       if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: TLS processing failed");
+               eap_fast_state(data, FAILURE);
+               return -1;
+       }
+
+       if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) ||
+           wpabuf_len(data->ssl.tls_out) > 0)
+               return 1;
+
+       /*
+        * Phase 1 was completed with the received message (e.g., when using
+        * abbreviated handshake), so Phase 2 can be started immediately
+        * without having to send through an empty message to the peer.
+        */
+
+       return eap_fast_phase1_done(sm, data);
+}
+
+
+static int eap_fast_process_phase2_start(struct eap_sm *sm,
+                                        struct eap_fast_data *data)
+{
+       u8 next_type;
+
+       if (data->identity) {
+               os_free(sm->identity);
+               sm->identity = data->identity;
+               data->identity = NULL;
+               sm->identity_len = data->identity_len;
+               data->identity_len = 0;
+               sm->require_identity_match = 1;
+               if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "EAP-FAST: "
+                                         "Phase2 Identity not found "
+                                         "in the user database",
+                                         sm->identity, sm->identity_len);
+                       next_type = eap_fast_req_failure(sm, data);
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-FAST: Identity already "
+                                  "known - skip Phase 2 Identity Request");
+                       next_type = sm->user->methods[0].method;
+                       sm->user_eap_method_index = 1;
+               }
+
+               eap_fast_state(data, PHASE2_METHOD);
+       } else {
+               eap_fast_state(data, PHASE2_ID);
+               next_type = EAP_TYPE_IDENTITY;
+       }
+
+       return eap_fast_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_fast_process_msg(struct eap_sm *sm, void *priv,
+                                const struct wpabuf *respData)
+{
+       struct eap_fast_data *data = priv;
+
+       switch (data->state) {
+       case PHASE1:
+               if (eap_fast_process_phase1(sm, data))
+                       break;
+
+               /* fall through to PHASE2_START */
+       case PHASE2_START:
+               eap_fast_process_phase2_start(sm, data);
+               break;
+       case PHASE2_ID:
+       case PHASE2_METHOD:
+       case CRYPTO_BINDING:
+       case REQUEST_PAC:
+               eap_fast_process_phase2(sm, data, data->ssl.tls_in);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s",
+                          data->state, __func__);
+               break;
+       }
+}
+
+
+static void eap_fast_process(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_fast_data *data = priv;
+       if (eap_server_tls_process(sm, &data->ssl, respData, data,
+                                  EAP_TYPE_FAST, eap_fast_process_version,
+                                  eap_fast_process_msg) < 0)
+               eap_fast_state(data, FAILURE);
+}
+
+
+static Boolean eap_fast_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_fast_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_fast_data *data = priv;
+       u8 *eapKeyData;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       eapKeyData = os_malloc(EAP_FAST_KEY_LEN);
+       if (eapKeyData == NULL)
+               return NULL;
+
+       eap_fast_derive_eap_msk(data->simck, eapKeyData);
+       *len = EAP_FAST_KEY_LEN;
+
+       return eapKeyData;
+}
+
+
+static u8 * eap_fast_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_fast_data *data = priv;
+       u8 *eapKeyData;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       eapKeyData = os_malloc(EAP_EMSK_LEN);
+       if (eapKeyData == NULL)
+               return NULL;
+
+       eap_fast_derive_eap_emsk(data->simck, eapKeyData);
+       *len = EAP_EMSK_LEN;
+
+       return eapKeyData;
+}
+
+
+static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_fast_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_fast_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_FAST, "FAST");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_fast_init;
+       eap->reset = eap_fast_reset;
+       eap->buildReq = eap_fast_buildReq;
+       eap->check = eap_fast_check;
+       eap->process = eap_fast_process;
+       eap->isDone = eap_fast_isDone;
+       eap->getKey = eap_fast_getKey;
+       eap->get_emsk = eap_fast_get_emsk;
+       eap->isSuccess = eap_fast_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
new file mode 100644 (file)
index 0000000..d0c7559
--- /dev/null
@@ -0,0 +1,633 @@
+/*
+ * hostapd / EAP-GPSK (RFC 5433) server
+ * Copyright (c) 2006-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 "eap_server/eap_i.h"
+#include "eap_common/eap_gpsk_common.h"
+
+
+struct eap_gpsk_data {
+       enum { GPSK_1, GPSK_3, SUCCESS, FAILURE } state;
+       u8 rand_server[EAP_GPSK_RAND_LEN];
+       u8 rand_peer[EAP_GPSK_RAND_LEN];
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 sk[EAP_GPSK_MAX_SK_LEN];
+       size_t sk_len;
+       u8 pk[EAP_GPSK_MAX_PK_LEN];
+       size_t pk_len;
+       u8 *id_peer;
+       size_t id_peer_len;
+       u8 *id_server;
+       size_t id_server_len;
+#define MAX_NUM_CSUITES 2
+       struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES];
+       size_t csuite_count;
+       int vendor; /* CSuite/Vendor */
+       int specifier; /* CSuite/Specifier */
+};
+
+
+static const char * eap_gpsk_state_txt(int state)
+{
+       switch (state) {
+       case GPSK_1:
+               return "GPSK-1";
+       case GPSK_3:
+               return "GPSK-3";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_gpsk_state(struct eap_gpsk_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s",
+                  eap_gpsk_state_txt(data->state),
+                  eap_gpsk_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_gpsk_init(struct eap_sm *sm)
+{
+       struct eap_gpsk_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = GPSK_1;
+
+       /* TODO: add support for configuring ID_Server */
+       data->id_server = (u8 *) os_strdup("hostapd");
+       if (data->id_server)
+               data->id_server_len = os_strlen((char *) data->id_server);
+
+       data->csuite_count = 0;
+       if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
+                                          EAP_GPSK_CIPHER_AES)) {
+               WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
+                            EAP_GPSK_VENDOR_IETF);
+               WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
+                            EAP_GPSK_CIPHER_AES);
+               data->csuite_count++;
+       }
+       if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
+                                          EAP_GPSK_CIPHER_SHA256)) {
+               WPA_PUT_BE32(data->csuite_list[data->csuite_count].vendor,
+                            EAP_GPSK_VENDOR_IETF);
+               WPA_PUT_BE16(data->csuite_list[data->csuite_count].specifier,
+                            EAP_GPSK_CIPHER_SHA256);
+               data->csuite_count++;
+       }
+
+       return data;
+}
+
+
+static void eap_gpsk_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_gpsk_data *data = priv;
+       os_free(data->id_server);
+       os_free(data->id_peer);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
+                                            struct eap_gpsk_data *data, u8 id)
+{
+       size_t len;
+       struct wpabuf *req;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1");
+
+       if (os_get_random(data->rand_server, EAP_GPSK_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data");
+               eap_gpsk_state(data, FAILURE);
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server",
+                   data->rand_server, EAP_GPSK_RAND_LEN);
+
+       len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 +
+               data->csuite_count * sizeof(struct eap_gpsk_csuite);
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
+                          "for request/GPSK-1");
+               eap_gpsk_state(data, FAILURE);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
+       wpabuf_put_be16(req, data->id_server_len);
+       wpabuf_put_data(req, data->id_server, data->id_server_len);
+       wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
+       wpabuf_put_be16(req,
+                       data->csuite_count * sizeof(struct eap_gpsk_csuite));
+       wpabuf_put_data(req, data->csuite_list,
+                       data->csuite_count * sizeof(struct eap_gpsk_csuite));
+
+       return req;
+}
+
+
+static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
+                                            struct eap_gpsk_data *data, u8 id)
+{
+       u8 *pos, *start;
+       size_t len, miclen;
+       struct eap_gpsk_csuite *csuite;
+       struct wpabuf *req;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
+
+       miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+       len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len +
+               sizeof(struct eap_gpsk_csuite) + 2 + miclen;
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory "
+                          "for request/GPSK-3");
+               eap_gpsk_state(data, FAILURE);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_3);
+       start = wpabuf_put(req, 0);
+
+       wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
+       wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
+       wpabuf_put_be16(req, data->id_server_len);
+       wpabuf_put_data(req, data->id_server, data->id_server_len);
+       csuite = wpabuf_put(req, sizeof(*csuite));
+       WPA_PUT_BE32(csuite->vendor, data->vendor);
+       WPA_PUT_BE16(csuite->specifier, data->specifier);
+
+       /* no PD_Payload_2 */
+       wpabuf_put_be16(req, 0);
+
+       pos = wpabuf_put(req, miclen);
+       if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+                                data->specifier, start, pos - start, pos) < 0)
+       {
+               os_free(req);
+               eap_gpsk_state(data, FAILURE);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+static struct wpabuf * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_gpsk_data *data = priv;
+
+       switch (data->state) {
+       case GPSK_1:
+               return eap_gpsk_build_gpsk_1(sm, data, id);
+       case GPSK_3:
+               return eap_gpsk_build_gpsk_3(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq",
+                          data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv,
+                             struct wpabuf *respData)
+{
+       struct eap_gpsk_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame");
+               return TRUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos);
+
+       if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2)
+               return FALSE;
+
+       if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4)
+               return FALSE;
+
+       wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d",
+                  *pos, data->state);
+
+       return TRUE;
+}
+
+
+static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
+                                   struct eap_gpsk_data *data,
+                                   const u8 *payload, size_t payloadlen)
+{
+       const u8 *pos, *end;
+       u16 alen;
+       const struct eap_gpsk_csuite *csuite;
+       size_t i, miclen;
+       u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+       if (data->state != GPSK_1)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2");
+
+       pos = payload;
+       end = payload + payloadlen;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "ID_Peer length");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "ID_Peer");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       os_free(data->id_peer);
+       data->id_peer = os_malloc(alen);
+       if (data->id_peer == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store "
+                          "%d-octet ID_Peer", alen);
+               return;
+       }
+       os_memcpy(data->id_peer, pos, alen);
+       data->id_peer_len = alen;
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Peer",
+                         data->id_peer, data->id_peer_len);
+       pos += alen;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "ID_Server length");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "ID_Server");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (alen != data->id_server_len ||
+           os_memcmp(pos, data->id_server, alen) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
+                          "GPSK-2 did not match");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       pos += alen;
+
+       if (end - pos < EAP_GPSK_RAND_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "RAND_Peer");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       os_memcpy(data->rand_peer, pos, EAP_GPSK_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Peer",
+                   data->rand_peer, EAP_GPSK_RAND_LEN);
+       pos += EAP_GPSK_RAND_LEN;
+
+       if (end - pos < EAP_GPSK_RAND_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "RAND_Server");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (os_memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and "
+                          "GPSK-2 did not match");
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1",
+                           data->rand_server, EAP_GPSK_RAND_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2",
+                           pos, EAP_GPSK_RAND_LEN);
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       pos += EAP_GPSK_RAND_LEN;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "CSuite_List length");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "CSuite_List");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) ||
+           os_memcmp(pos, data->csuite_list, alen) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and "
+                          "GPSK-2 did not match");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       pos += alen;
+
+       if (end - pos < (int) sizeof(*csuite)) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "CSuite_Sel");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       csuite = (const struct eap_gpsk_csuite *) pos;
+       for (i = 0; i < data->csuite_count; i++) {
+               if (os_memcmp(csuite, &data->csuite_list[i], sizeof(*csuite))
+                   == 0)
+                       break;
+       }
+       if (i == data->csuite_count) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported "
+                          "ciphersuite %d:%d",
+                          WPA_GET_BE32(csuite->vendor),
+                          WPA_GET_BE16(csuite->specifier));
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       data->vendor = WPA_GET_BE32(csuite->vendor);
+       data->specifier = WPA_GET_BE16(csuite->specifier);
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
+                  data->vendor, data->specifier);
+       pos += sizeof(*csuite); 
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "PD_Payload_1 length");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "PD_Payload_1");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
+       pos += alen;
+
+       if (sm->user == NULL || sm->user->password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured "
+                          "for the user");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+
+       if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len,
+                                data->vendor, data->specifier,
+                                data->rand_peer, data->rand_server,
+                                data->id_peer, data->id_peer_len,
+                                data->id_server, data->id_server_len,
+                                data->msk, data->emsk,
+                                data->sk, &data->sk_len,
+                                data->pk, &data->pk_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive keys");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+
+       miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+       if (end - pos < (int) miclen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+                          "(left=%lu miclen=%lu)",
+                          (unsigned long) (end - pos),
+                          (unsigned long) miclen);
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+                                data->specifier, payload, pos - payload, mic)
+           < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (os_memcmp(mic, pos, miclen) != 0) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2");
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       pos += miclen;
+
+       if (pos != end) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+                          "data in the end of GPSK-2",
+                          (unsigned long) (end - pos));
+       }
+
+       eap_gpsk_state(data, GPSK_3);
+}
+
+
+static void eap_gpsk_process_gpsk_4(struct eap_sm *sm,
+                                   struct eap_gpsk_data *data,
+                                   const u8 *payload, size_t payloadlen)
+{
+       const u8 *pos, *end;
+       u16 alen;
+       size_t miclen;
+       u8 mic[EAP_GPSK_MAX_MIC_LEN];
+
+       if (data->state != GPSK_3)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4");
+
+       pos = payload;
+       end = payload + payloadlen;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "PD_Payload_1 length");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       alen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < alen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
+                          "PD_Payload_1");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen);
+       pos += alen;
+
+       miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
+       if (end - pos < (int) miclen) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
+                          "(left=%lu miclen=%lu)",
+                          (unsigned long) (end - pos),
+                          (unsigned long) miclen);
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
+                                data->specifier, payload, pos - payload, mic)
+           < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       if (os_memcmp(mic, pos, miclen) != 0) {
+               wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4");
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
+               wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       pos += miclen;
+
+       if (pos != end) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %lu bytes of extra "
+                          "data in the end of GPSK-4",
+                          (unsigned long) (end - pos));
+       }
+
+       eap_gpsk_state(data, SUCCESS);
+}
+
+
+static void eap_gpsk_process(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_gpsk_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GPSK, respData, &len);
+       if (pos == NULL || len < 1)
+               return;
+
+       switch (*pos) {
+       case EAP_GPSK_OPCODE_GPSK_2:
+               eap_gpsk_process_gpsk_2(sm, data, pos + 1, len - 1);
+               break;
+       case EAP_GPSK_OPCODE_GPSK_4:
+               eap_gpsk_process_gpsk_4(sm, data, pos + 1, len - 1);
+               break;
+       }
+}
+
+
+static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_gpsk_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_gpsk_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_gpsk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_gpsk_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_gpsk_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_gpsk_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_gpsk_init;
+       eap->reset = eap_gpsk_reset;
+       eap->buildReq = eap_gpsk_buildReq;
+       eap->check = eap_gpsk_check;
+       eap->process = eap_gpsk_process;
+       eap->isDone = eap_gpsk_isDone;
+       eap->getKey = eap_gpsk_getKey;
+       eap->isSuccess = eap_gpsk_isSuccess;
+       eap->get_emsk = eap_gpsk_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_gtc.c b/src/eap_server/eap_server_gtc.c
new file mode 100644 (file)
index 0000000..79b9696
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * hostapd / EAP-GTC (RFC 3748)
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_gtc_data {
+       enum { CONTINUE, SUCCESS, FAILURE } state;
+       int prefix;
+};
+
+
+static void * eap_gtc_init(struct eap_sm *sm)
+{
+       struct eap_gtc_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = CONTINUE;
+
+#ifdef EAP_SERVER_FAST
+       if (sm->m && sm->m->vendor == EAP_VENDOR_IETF &&
+           sm->m->method == EAP_TYPE_FAST) {
+               wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix "
+                          "with challenge/response");
+               data->prefix = 1;
+       }
+#endif /* EAP_SERVER_FAST */
+
+       return data;
+}
+
+
+static void eap_gtc_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_gtc_data *data = priv;
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_gtc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_gtc_data *data = priv;
+       struct wpabuf *req;
+       char *msg;
+       size_t msg_len;
+
+       msg = data->prefix ? "CHALLENGE=Password" : "Password";
+
+       msg_len = os_strlen(msg);
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, msg_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for "
+                          "request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       wpabuf_put_data(req, msg, msg_len);
+
+       data->state = CONTINUE;
+
+       return req;
+}
+
+
+static Boolean eap_gtc_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_gtc_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_gtc_data *data = priv;
+       const u8 *pos;
+       size_t rlen;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, respData, &rlen);
+       if (pos == NULL || rlen < 1)
+               return; /* Should not happen - frame already validated */
+
+       wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen);
+
+#ifdef EAP_SERVER_FAST
+       if (data->prefix) {
+               const u8 *pos2, *end;
+               /* "RESPONSE=<user>\0<password>" */
+               if (rlen < 10) {
+                       wpa_printf(MSG_DEBUG, "EAP-GTC: Too short response "
+                                  "for EAP-FAST prefix");
+                       data->state = FAILURE;
+                       return;
+               }
+
+               end = pos + rlen;
+               pos += 9;
+               pos2 = pos;
+               while (pos2 < end && *pos2)
+                       pos2++;
+               if (pos2 == end) {
+                       wpa_printf(MSG_DEBUG, "EAP-GTC: No password in "
+                                  "response to EAP-FAST prefix");
+                       data->state = FAILURE;
+                       return;
+               }
+
+               wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Response user",
+                                 pos, pos2 - pos);
+               if (sm->identity && sm->require_identity_match &&
+                   (pos2 - pos != (int) sm->identity_len ||
+                    os_memcmp(pos, sm->identity, sm->identity_len))) {
+                       wpa_printf(MSG_DEBUG, "EAP-GTC: Phase 2 Identity did "
+                                  "not match with required Identity");
+                       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-GTC: Expected "
+                                         "identity",
+                                         sm->identity, sm->identity_len);
+                       data->state = FAILURE;
+                       return;
+               } else {
+                       os_free(sm->identity);
+                       sm->identity_len = pos2 - pos;
+                       sm->identity = os_malloc(sm->identity_len);
+                       if (sm->identity == NULL) {
+                               data->state = FAILURE;
+                               return;
+                       }
+                       os_memcpy(sm->identity, pos, sm->identity_len);
+               }
+
+               if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "EAP-GTC: Phase2 "
+                                         "Identity not found in the user "
+                                         "database",
+                                         sm->identity, sm->identity_len);
+                       data->state = FAILURE;
+                       return;
+               }
+
+               pos = pos2 + 1;
+               rlen = end - pos;
+               wpa_hexdump_ascii_key(MSG_MSGDUMP,
+                                     "EAP-GTC: Response password",
+                                     pos, rlen);
+       }
+#endif /* EAP_SERVER_FAST */
+
+       if (sm->user == NULL || sm->user->password == NULL ||
+           sm->user->password_hash) {
+               wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not "
+                          "configured");
+               data->state = FAILURE;
+               return;
+       }
+
+       if (rlen != sm->user->password_len ||
+           os_memcmp(pos, sm->user->password, rlen) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
+               data->state = FAILURE;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Success");
+               data->state = SUCCESS;
+       }
+}
+
+
+static Boolean eap_gtc_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_gtc_data *data = priv;
+       return data->state != CONTINUE;
+}
+
+
+static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_gtc_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_gtc_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_gtc_init;
+       eap->reset = eap_gtc_reset;
+       eap->buildReq = eap_gtc_buildReq;
+       eap->check = eap_gtc_check;
+       eap->process = eap_gtc_process;
+       eap->isDone = eap_gtc_isDone;
+       eap->isSuccess = eap_gtc_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_identity.c b/src/eap_server/eap_server_identity.c
new file mode 100644 (file)
index 0000000..cd8da2a
--- /dev/null
@@ -0,0 +1,180 @@
+/*
+ * hostapd / EAP-Identity
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eap_i.h"
+
+
+struct eap_identity_data {
+       enum { CONTINUE, SUCCESS, FAILURE } state;
+       int pick_up;
+};
+
+
+static void * eap_identity_init(struct eap_sm *sm)
+{
+       struct eap_identity_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = CONTINUE;
+
+       return data;
+}
+
+
+static void * eap_identity_initPickUp(struct eap_sm *sm)
+{
+       struct eap_identity_data *data;
+       data = eap_identity_init(sm);
+       if (data) {
+               data->pick_up = 1;
+       }
+       return data;
+}
+
+
+static void eap_identity_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_identity_data *data = priv;
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_identity_buildReq(struct eap_sm *sm, void *priv,
+                                            u8 id)
+{
+       struct eap_identity_data *data = priv;
+       struct wpabuf *req;
+       const char *req_data;
+       size_t req_data_len;
+
+       if (sm->eapol_cb->get_eap_req_id_text) {
+               req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx,
+                                                            &req_data_len);
+       } else {
+               req_data = NULL;
+               req_data_len = 0;
+       }
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, req_data_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate "
+                          "memory for request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       wpabuf_put_data(req, req_data, req_data_len);
+
+       return req;
+}
+
+
+static Boolean eap_identity_check(struct eap_sm *sm, void *priv,
+                                 struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+                              respData, &len);
+       if (pos == NULL) {
+               wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_identity_process(struct eap_sm *sm, void *priv,
+                                struct wpabuf *respData)
+{
+       struct eap_identity_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       if (data->pick_up) {
+               if (eap_identity_check(sm, data, respData)) {
+                       wpa_printf(MSG_DEBUG, "EAP-Identity: failed to pick "
+                                  "up already started negotiation");
+                       data->state = FAILURE;
+                       return;
+               }
+               data->pick_up = 0;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+                              respData, &len);
+       if (pos == NULL)
+               return; /* Should not happen - frame already validated */
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
+       if (sm->identity)
+               sm->update_user = TRUE;
+       os_free(sm->identity);
+       sm->identity = os_malloc(len ? len : 1);
+       if (sm->identity == NULL) {
+               data->state = FAILURE;
+       } else {
+               os_memcpy(sm->identity, pos, len);
+               sm->identity_len = len;
+               data->state = SUCCESS;
+       }
+}
+
+
+static Boolean eap_identity_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_identity_data *data = priv;
+       return data->state != CONTINUE;
+}
+
+
+static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_identity_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_identity_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_IDENTITY,
+                                     "Identity");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_identity_init;
+       eap->initPickUp = eap_identity_initPickUp;
+       eap->reset = eap_identity_reset;
+       eap->buildReq = eap_identity_buildReq;
+       eap->check = eap_identity_check;
+       eap->process = eap_identity_process;
+       eap->isDone = eap_identity_isDone;
+       eap->isSuccess = eap_identity_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_ikev2.c b/src/eap_server/eap_server_ikev2.c
new file mode 100644 (file)
index 0000000..06074ee
--- /dev/null
@@ -0,0 +1,538 @@
+/*
+ * EAP-IKEv2 server (RFC 5106)
+ * 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"
+
+#include "common.h"
+#include "eap_i.h"
+#include "eap_common/eap_ikev2_common.h"
+#include "ikev2.h"
+
+
+struct eap_ikev2_data {
+       struct ikev2_initiator_data ikev2;
+       enum { MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+       struct wpabuf *in_buf;
+       struct wpabuf *out_buf;
+       size_t out_used;
+       size_t fragment_size;
+       int keys_ready;
+       u8 keymat[EAP_MSK_LEN + EAP_EMSK_LEN];
+       int keymat_ok;
+};
+
+
+static const u8 * eap_ikev2_get_shared_secret(void *ctx, const u8 *IDr,
+                                             size_t IDr_len,
+                                             size_t *secret_len)
+{
+       struct eap_sm *sm = ctx;
+
+       if (IDr == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: No IDr received - default "
+                          "to user identity from EAP-Identity");
+               IDr = sm->identity;
+               IDr_len = sm->identity_len;
+       }
+
+       if (eap_user_get(sm, IDr, IDr_len, 0) < 0 || sm->user == NULL ||
+           sm->user->password == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: No user entry found");
+               return NULL;
+       }
+
+       *secret_len = sm->user->password_len;
+       return sm->user->password;
+}
+
+
+static const char * eap_ikev2_state_txt(int state)
+{
+       switch (state) {
+       case MSG:
+               return "MSG";
+       case FRAG_ACK:
+               return "FRAG_ACK";
+       case WAIT_FRAG_ACK:
+               return "WAIT_FRAG_ACK";
+       case DONE:
+               return "DONE";
+       case FAIL:
+               return "FAIL";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_ikev2_state(struct eap_ikev2_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: %s -> %s",
+                  eap_ikev2_state_txt(data->state),
+                  eap_ikev2_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_ikev2_init(struct eap_sm *sm)
+{
+       struct eap_ikev2_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = MSG;
+       data->fragment_size = IKEV2_FRAGMENT_SIZE;
+       data->ikev2.state = SA_INIT;
+       data->ikev2.peer_auth = PEER_AUTH_SECRET;
+       data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
+       if (data->ikev2.key_pad == NULL)
+               goto failed;
+       data->ikev2.key_pad_len = 21;
+
+       /* TODO: make proposals configurable */
+       data->ikev2.proposal.proposal_num = 1;
+       data->ikev2.proposal.integ = AUTH_HMAC_SHA1_96;
+       data->ikev2.proposal.prf = PRF_HMAC_SHA1;
+       data->ikev2.proposal.encr = ENCR_AES_CBC;
+       data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
+
+       data->ikev2.IDi = (u8 *) os_strdup("hostapd");
+       data->ikev2.IDi_len = 7;
+
+       data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
+       data->ikev2.cb_ctx = sm;
+
+       return data;
+
+failed:
+       ikev2_initiator_deinit(&data->ikev2);
+       os_free(data);
+       return NULL;
+}
+
+
+static void eap_ikev2_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_ikev2_data *data = priv;
+       wpabuf_free(data->in_buf);
+       wpabuf_free(data->out_buf);
+       ikev2_initiator_deinit(&data->ikev2);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data, u8 id)
+{
+       struct wpabuf *req;
+       u8 flags;
+       size_t send_len, plen, icv_len = 0;
+
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Generating Request");
+
+       flags = 0;
+       send_len = wpabuf_len(data->out_buf) - data->out_used;
+       if (1 + send_len > data->fragment_size) {
+               send_len = data->fragment_size - 1;
+               flags |= IKEV2_FLAGS_MORE_FRAGMENTS;
+               if (data->out_used == 0) {
+                       flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
+                       send_len -= 4;
+               }
+       }
+
+       plen = 1 + send_len;
+       if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+               plen += 4;
+       if (data->keys_ready) {
+               const struct ikev2_integ_alg *integ;
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Add Integrity Checksum "
+                          "Data");
+               flags |= IKEV2_FLAGS_ICV_INCLUDED;
+               integ = ikev2_get_integ(data->ikev2.proposal.integ);
+               if (integ == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unknown INTEG "
+                                  "transform / cannot generate ICV");
+                       return NULL;
+               }
+               icv_len = integ->hash_len;
+
+               plen += icv_len;
+       }
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, plen,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL)
+               return NULL;
+
+       wpabuf_put_u8(req, flags); /* Flags */
+       if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
+               wpabuf_put_be32(req, wpabuf_len(data->out_buf));
+
+       wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+                       send_len);
+       data->out_used += send_len;
+
+       if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+               const u8 *msg = wpabuf_head(req);
+               size_t len = wpabuf_len(req);
+               ikev2_integ_hash(data->ikev2.proposal.integ,
+                                data->ikev2.keys.SK_ai,
+                                data->ikev2.keys.SK_integ_len,
+                                msg, len, wpabuf_put(req, icv_len));
+       }
+
+       if (data->out_used == wpabuf_len(data->out_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->out_buf);
+               data->out_buf = NULL;
+               data->out_used = 0;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->out_buf) -
+                          data->out_used);
+               eap_ikev2_state(data, WAIT_FRAG_ACK);
+       }
+
+       return req;
+}
+
+
+static struct wpabuf * eap_ikev2_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_ikev2_data *data = priv;
+
+       switch (data->state) {
+       case MSG:
+               if (data->out_buf == NULL) {
+                       data->out_buf = ikev2_initiator_build(&data->ikev2);
+                       if (data->out_buf == NULL) {
+                               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to "
+                                          "generate IKEv2 message");
+                               return NULL;
+                       }
+                       data->out_used = 0;
+               }
+               /* pass through */
+       case WAIT_FRAG_ACK:
+               return eap_ikev2_build_msg(data, id);
+       case FRAG_ACK:
+               return eap_ikev2_build_frag_ack(id, EAP_CODE_REQUEST);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected state %d in "
+                          "buildReq", data->state);
+               return NULL;
+       }
+}
+
+
+static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
+                              struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
+                              &len);
+       if (pos == NULL) {
+               wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
+                                const struct wpabuf *respData,
+                                u8 flags, const u8 *pos, const u8 **end)
+{
+       if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
+               int icv_len = eap_ikev2_validate_icv(
+                       data->ikev2.proposal.integ, &data->ikev2.keys, 0,
+                       respData, pos, *end);
+               if (icv_len < 0)
+                       return -1;
+               /* Hide Integrity Checksum Data from further processing */
+               *end -= icv_len;
+       } else if (data->keys_ready) {
+               wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
+                          "included integrity checksum");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_ikev2_process_cont(struct eap_ikev2_data *data,
+                                 const u8 *buf, size_t len)
+{
+       /* Process continuation of a pending message */
+       if (len > wpabuf_tailroom(data->in_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment overflow");
+               eap_ikev2_state(data, FAIL);
+               return -1;
+       }
+
+       wpabuf_put_data(data->in_buf, buf, len);
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes, waiting for %lu "
+                  "bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->in_buf));
+
+       return 0;
+}
+
+
+static int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
+                                     u8 flags, u32 message_length,
+                                     const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->in_buf == NULL && !(flags & IKEV2_FLAGS_LENGTH_INCLUDED)) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: No Message Length field in "
+                          "a fragmented packet");
+               return -1;
+       }
+
+       if (data->in_buf == NULL) {
+               /* First fragment of the message */
+               data->in_buf = wpabuf_alloc(message_length);
+               if (data->in_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
+                                  "message");
+                       return -1;
+               }
+               wpabuf_put_data(data->in_buf, buf, len);
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received %lu bytes in first "
+                          "fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->in_buf));
+       }
+
+       return 0;
+}
+
+
+static int eap_ikev2_server_keymat(struct eap_ikev2_data *data)
+{
+       if (eap_ikev2_derive_keymat(
+                   data->ikev2.proposal.prf, &data->ikev2.keys,
+                   data->ikev2.i_nonce, data->ikev2.i_nonce_len,
+                   data->ikev2.r_nonce, data->ikev2.r_nonce_len,
+                   data->keymat) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Failed to derive "
+                          "key material");
+               return -1;
+       }
+       data->keymat_ok = 1;
+       return 0;
+}
+
+
+static void eap_ikev2_process(struct eap_sm *sm, void *priv,
+                             struct wpabuf *respData)
+{
+       struct eap_ikev2_data *data = priv;
+       const u8 *start, *pos, *end;
+       size_t len;
+       u8 flags;
+       u32 message_length = 0;
+       struct wpabuf tmpbuf;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, respData,
+                              &len);
+       if (pos == NULL)
+               return; /* Should not happen; message already verified */
+
+       start = pos;
+       end = start + len;
+
+       if (len == 0) {
+               /* fragment ack */
+               flags = 0;
+       } else
+               flags = *pos++;
+
+       if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) {
+               eap_ikev2_state(data, FAIL);
+               return;
+       }
+
+       if (flags & IKEV2_FLAGS_LENGTH_INCLUDED) {
+               if (end - pos < 4) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Message underflow");
+                       eap_ikev2_state(data, FAIL);
+                       return;
+               }
+               message_length = WPA_GET_BE32(pos);
+               pos += 4;
+
+               if (message_length < (u32) (end - pos)) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Invalid Message "
+                                  "Length (%d; %ld remaining in this msg)",
+                                  message_length, (long) (end - pos));
+                       eap_ikev2_state(data, FAIL);
+                       return;
+               }
+       }
+       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Received packet: Flags 0x%x "
+                  "Message Length %u", flags, message_length);
+
+       if (data->state == WAIT_FRAG_ACK) {
+               if (len != 0) {
+                       wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
+                                  "in WAIT_FRAG_ACK state");
+                       eap_ikev2_state(data, FAIL);
+                       return;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Fragment acknowledged");
+               eap_ikev2_state(data, MSG);
+               return;
+       }
+
+       if (data->in_buf && eap_ikev2_process_cont(data, pos, end - pos) < 0) {
+               eap_ikev2_state(data, FAIL);
+               return;
+       }
+               
+       if (flags & IKEV2_FLAGS_MORE_FRAGMENTS) {
+               if (eap_ikev2_process_fragment(data, flags, message_length,
+                                              pos, end - pos) < 0)
+                       eap_ikev2_state(data, FAIL);
+               else
+                       eap_ikev2_state(data, FRAG_ACK);
+               return;
+       } else if (data->state == FRAG_ACK) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
+               data->state = MSG;
+       }
+
+       if (data->in_buf == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&tmpbuf, pos, end - pos);
+               data->in_buf = &tmpbuf;
+       }
+
+       if (ikev2_initiator_process(&data->ikev2, data->in_buf) < 0) {
+               if (data->in_buf == &tmpbuf)
+                       data->in_buf = NULL;
+               eap_ikev2_state(data, FAIL);
+               return;
+       }
+
+       switch (data->ikev2.state) {
+       case SA_AUTH:
+               /* SA_INIT was sent out, so message have to be
+                * integrity protected from now on. */
+               data->keys_ready = 1;
+               break;
+       case IKEV2_DONE:
+               if (data->state == FAIL)
+                       break;
+               wpa_printf(MSG_DEBUG, "EAP-IKEV2: Authentication completed "
+                          "successfully");
+               if (eap_ikev2_server_keymat(data))
+                       break;
+               eap_ikev2_state(data, DONE);
+               break;
+       default:
+               break;
+       }
+
+       if (data->in_buf != &tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+}
+
+
+static Boolean eap_ikev2_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_ikev2_data *data = priv;
+       return data->state == DONE || data->state == FAIL;
+}
+
+
+static Boolean eap_ikev2_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_ikev2_data *data = priv;
+       return data->state == DONE && data->ikev2.state == IKEV2_DONE &&
+               data->keymat_ok;
+}
+
+
+static u8 * eap_ikev2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ikev2_data *data = priv;
+       u8 *key;
+
+       if (data->state != DONE || !data->keymat_ok)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key) {
+               os_memcpy(key, data->keymat, EAP_MSK_LEN);
+               *len = EAP_MSK_LEN;
+       }
+
+       return key;
+}
+
+
+static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ikev2_data *data = priv;
+       u8 *key;
+
+       if (data->state != DONE || !data->keymat_ok)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key) {
+               os_memcpy(key, data->keymat + EAP_MSK_LEN, EAP_EMSK_LEN);
+               *len = EAP_EMSK_LEN;
+       }
+
+       return key;
+}
+
+
+int eap_server_ikev2_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_IKEV2,
+                                     "IKEV2");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_ikev2_init;
+       eap->reset = eap_ikev2_reset;
+       eap->buildReq = eap_ikev2_buildReq;
+       eap->check = eap_ikev2_check;
+       eap->process = eap_ikev2_process;
+       eap->isDone = eap_ikev2_isDone;
+       eap->getKey = eap_ikev2_getKey;
+       eap->isSuccess = eap_ikev2_isSuccess;
+       eap->get_emsk = eap_ikev2_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_md5.c b/src/eap_server/eap_server_md5.c
new file mode 100644 (file)
index 0000000..dee2dc5
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * hostapd / EAP-MD5 server
+ * 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 "eap_i.h"
+#include "eap_common/chap.h"
+
+
+#define CHALLENGE_LEN 16
+
+struct eap_md5_data {
+       u8 challenge[CHALLENGE_LEN];
+       enum { CONTINUE, SUCCESS, FAILURE } state;
+};
+
+
+static void * eap_md5_init(struct eap_sm *sm)
+{
+       struct eap_md5_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = CONTINUE;
+
+       return data;
+}
+
+
+static void eap_md5_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_md5_data *data = priv;
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_md5_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_md5_data *data = priv;
+       struct wpabuf *req;
+
+       if (os_get_random(data->challenge, CHALLENGE_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-MD5: Failed to get random data");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHALLENGE_LEN,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for "
+                          "request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, CHALLENGE_LEN);
+       wpabuf_put_data(req, data->challenge, CHALLENGE_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", data->challenge,
+                   CHALLENGE_LEN);
+
+       data->state = CONTINUE;
+
+       return req;
+}
+
+
+static Boolean eap_md5_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame");
+               return TRUE;
+       }
+       if (*pos != CHAP_MD5_LEN || 1 + CHAP_MD5_LEN > len) {
+               wpa_printf(MSG_INFO, "EAP-MD5: Invalid response "
+                          "(response_len=%d payload_len=%lu",
+                          *pos, (unsigned long) len);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_md5_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_md5_data *data = priv;
+       const u8 *pos;
+       size_t plen;
+       u8 hash[CHAP_MD5_LEN], id;
+
+       if (sm->user == NULL || sm->user->password == NULL ||
+           sm->user->password_hash) {
+               wpa_printf(MSG_INFO, "EAP-MD5: Plaintext password not "
+                          "configured");
+               data->state = FAILURE;
+               return;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, respData, &plen);
+       if (pos == NULL || *pos != CHAP_MD5_LEN || plen < 1 + CHAP_MD5_LEN)
+               return; /* Should not happen - frame already validated */
+
+       pos++; /* Skip response len */
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, CHAP_MD5_LEN);
+
+       id = eap_get_id(respData);
+       chap_md5(id, sm->user->password, sm->user->password_len,
+                data->challenge, CHALLENGE_LEN, hash);
+
+       if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
+               data->state = SUCCESS;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Failure");
+               data->state = FAILURE;
+       }
+}
+
+
+static Boolean eap_md5_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_md5_data *data = priv;
+       return data->state != CONTINUE;
+}
+
+
+static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_md5_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_md5_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_md5_init;
+       eap->reset = eap_md5_reset;
+       eap->buildReq = eap_md5_buildReq;
+       eap->check = eap_md5_check;
+       eap->process = eap_md5_process;
+       eap->isDone = eap_md5_isDone;
+       eap->isSuccess = eap_md5_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_methods.c b/src/eap_server/eap_server_methods.c
new file mode 100644 (file)
index 0000000..900a5dd
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * EAP server method registration
+ * 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 "common.h"
+#include "eap_i.h"
+#include "eap_methods.h"
+
+
+static struct eap_method *eap_methods;
+
+
+/**
+ * eap_server_get_eap_method - Get EAP method based on type number
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @method: EAP type number
+ * Returns: Pointer to EAP method or %NULL if not found
+ */
+const struct eap_method * eap_server_get_eap_method(int vendor, EapType method)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (m->vendor == vendor && m->method == method)
+                       return m;
+       }
+       return NULL;
+}
+
+
+/**
+ * eap_server_get_type - Get EAP type for the given EAP method name
+ * @name: EAP method name, e.g., TLS
+ * @vendor: Buffer for returning EAP Vendor-Id
+ * Returns: EAP method type or %EAP_TYPE_NONE if not found
+ *
+ * This function maps EAP type names into EAP type numbers based on the list of
+ * EAP methods included in the build.
+ */
+EapType eap_server_get_type(const char *name, int *vendor)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (os_strcmp(m->name, name) == 0) {
+                       *vendor = m->vendor;
+                       return m->method;
+               }
+       }
+       *vendor = EAP_VENDOR_IETF;
+       return EAP_TYPE_NONE;
+}
+
+
+/**
+ * eap_server_method_alloc - Allocate EAP server method structure
+ * @version: Version of the EAP server method interface (set to
+ * EAP_SERVER_METHOD_INTERFACE_VERSION)
+ * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF)
+ * @method: EAP type number (EAP_TYPE_*)
+ * @name: Name of the method (e.g., "TLS")
+ * Returns: Allocated EAP method structure or %NULL on failure
+ *
+ * The returned structure should be freed with eap_server_method_free() when it
+ * is not needed anymore.
+ */
+struct eap_method * eap_server_method_alloc(int version, int vendor,
+                                           EapType method, const char *name)
+{
+       struct eap_method *eap;
+       eap = os_zalloc(sizeof(*eap));
+       if (eap == NULL)
+               return NULL;
+       eap->version = version;
+       eap->vendor = vendor;
+       eap->method = method;
+       eap->name = name;
+       return eap;
+}
+
+
+/**
+ * eap_server_method_free - Free EAP server method structure
+ * @method: Method structure allocated with eap_server_method_alloc()
+ */
+void eap_server_method_free(struct eap_method *method)
+{
+       os_free(method);
+}
+
+
+/**
+ * eap_server_method_register - Register an EAP server method
+ * @method: EAP method to register
+ * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method
+ * has already been registered
+ *
+ * Each EAP server method needs to call this function to register itself as a
+ * supported EAP method.
+ */
+int eap_server_method_register(struct eap_method *method)
+{
+       struct eap_method *m, *last = NULL;
+
+       if (method == NULL || method->name == NULL ||
+           method->version != EAP_SERVER_METHOD_INTERFACE_VERSION)
+               return -1;
+
+       for (m = eap_methods; m; m = m->next) {
+               if ((m->vendor == method->vendor &&
+                    m->method == method->method) ||
+                   os_strcmp(m->name, method->name) == 0)
+                       return -2;
+               last = m;
+       }
+
+       if (last)
+               last->next = method;
+       else
+               eap_methods = method;
+
+       return 0;
+}
+
+
+/**
+ * eap_server_unregister_methods - Unregister EAP server methods
+ *
+ * This function is called at program termination to unregister all EAP server
+ * methods.
+ */
+void eap_server_unregister_methods(void)
+{
+       struct eap_method *m;
+
+       while (eap_methods) {
+               m = eap_methods;
+               eap_methods = eap_methods->next;
+
+               if (m->free)
+                       m->free(m);
+               else
+                       eap_server_method_free(m);
+       }
+}
+
+
+/**
+ * eap_server_get_name - Get EAP method name for the given EAP type
+ * @vendor: EAP Vendor-Id (0 = IETF)
+ * @type: EAP method type
+ * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ *
+ * This function maps EAP type numbers into EAP type names based on the list of
+ * EAP methods included in the build.
+ */
+const char * eap_server_get_name(int vendor, EapType type)
+{
+       struct eap_method *m;
+       for (m = eap_methods; m; m = m->next) {
+               if (m->vendor == vendor && m->method == type)
+                       return m->name;
+       }
+       return NULL;
+}
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
new file mode 100644 (file)
index 0000000..39d1c6e
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server
+ * 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 "crypto/ms_funcs.h"
+#include "eap_i.h"
+
+
+struct eap_mschapv2_hdr {
+       u8 op_code; /* MSCHAPV2_OP_* */
+       u8 mschapv2_id; /* must be changed for challenges, but not for
+                        * success/failure */
+       u8 ms_length[2]; /* Note: misaligned; length - 5 */
+       /* followed by data */
+} STRUCT_PACKED;
+
+#define MSCHAPV2_OP_CHALLENGE 1
+#define MSCHAPV2_OP_RESPONSE 2
+#define MSCHAPV2_OP_SUCCESS 3
+#define MSCHAPV2_OP_FAILURE 4
+#define MSCHAPV2_OP_CHANGE_PASSWORD 7
+
+#define MSCHAPV2_RESP_LEN 49
+
+#define ERROR_RESTRICTED_LOGON_HOURS 646
+#define ERROR_ACCT_DISABLED 647
+#define ERROR_PASSWD_EXPIRED 648
+#define ERROR_NO_DIALIN_PERMISSION 649
+#define ERROR_AUTHENTICATION_FAILURE 691
+#define ERROR_CHANGING_PASSWORD 709
+
+#define PASSWD_CHANGE_CHAL_LEN 16
+#define MSCHAPV2_KEY_LEN 16
+
+
+#define CHALLENGE_LEN 16
+
+struct eap_mschapv2_data {
+       u8 auth_challenge[CHALLENGE_LEN];
+       int auth_challenge_from_tls;
+       u8 *peer_challenge;
+       u8 auth_response[20];
+       enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state;
+       u8 resp_mschapv2_id;
+       u8 master_key[16];
+       int master_key_valid;
+};
+
+
+static void * eap_mschapv2_init(struct eap_sm *sm)
+{
+       struct eap_mschapv2_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = CHALLENGE;
+
+       if (sm->auth_challenge) {
+               os_memcpy(data->auth_challenge, sm->auth_challenge,
+                         CHALLENGE_LEN);
+               data->auth_challenge_from_tls = 1;
+       }
+
+       if (sm->peer_challenge) {
+               data->peer_challenge = os_malloc(CHALLENGE_LEN);
+               if (data->peer_challenge == NULL) {
+                       os_free(data);
+                       return NULL;
+               }
+               os_memcpy(data->peer_challenge, sm->peer_challenge,
+                         CHALLENGE_LEN);
+       }
+
+       return data;
+}
+
+
+static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_mschapv2_data *data = priv;
+       if (data == NULL)
+               return;
+
+       os_free(data->peer_challenge);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_mschapv2_build_challenge(
+       struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_mschapv2_hdr *ms;
+       char *name = "hostapd"; /* TODO: make this configurable */
+       size_t ms_len;
+
+       if (!data->auth_challenge_from_tls &&
+           os_get_random(data->auth_challenge, CHALLENGE_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random "
+                          "data");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+                          " for request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       ms = wpabuf_put(req, sizeof(*ms));
+       ms->op_code = MSCHAPV2_OP_CHALLENGE;
+       ms->mschapv2_id = id;
+       WPA_PUT_BE16(ms->ms_length, ms_len);
+
+       wpabuf_put_u8(req, CHALLENGE_LEN);
+       if (!data->auth_challenge_from_tls)
+               wpabuf_put_data(req, data->auth_challenge, CHALLENGE_LEN);
+       else
+               wpabuf_put(req, CHALLENGE_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
+                   data->auth_challenge, CHALLENGE_LEN);
+       wpabuf_put_data(req, name, os_strlen(name));
+
+       return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_build_success_req(
+       struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_mschapv2_hdr *ms;
+       u8 *msg;
+       char *message = "OK";
+       size_t ms_len;
+
+       ms_len = sizeof(*ms) + 2 + 2 * sizeof(data->auth_response) + 1 + 2 +
+               os_strlen(message);
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+                          " for request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       ms = wpabuf_put(req, sizeof(*ms));
+       ms->op_code = MSCHAPV2_OP_SUCCESS;
+       ms->mschapv2_id = data->resp_mschapv2_id;
+       WPA_PUT_BE16(ms->ms_length, ms_len);
+       msg = (u8 *) (ms + 1);
+
+       wpabuf_put_u8(req, 'S');
+       wpabuf_put_u8(req, '=');
+       wpa_snprintf_hex_uppercase(
+               wpabuf_put(req, sizeof(data->auth_response) * 2),
+               sizeof(data->auth_response) * 2 + 1,
+               data->auth_response, sizeof(data->auth_response));
+       wpabuf_put_u8(req, ' ');
+       wpabuf_put_u8(req, 'M');
+       wpabuf_put_u8(req, '=');
+       wpabuf_put_data(req, message, os_strlen(message));
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message",
+                         msg, ms_len - sizeof(*ms));
+
+       return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_build_failure_req(
+       struct eap_sm *sm, struct eap_mschapv2_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_mschapv2_hdr *ms;
+       char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 "
+               "M=FAILED";
+       size_t ms_len;
+
+       ms_len = sizeof(*ms) + os_strlen(message);
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory"
+                          " for request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       ms = wpabuf_put(req, sizeof(*ms));
+       ms->op_code = MSCHAPV2_OP_FAILURE;
+       ms->mschapv2_id = data->resp_mschapv2_id;
+       WPA_PUT_BE16(ms->ms_length, ms_len);
+
+       wpabuf_put_data(req, message, os_strlen(message));
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message",
+                         (u8 *) message, os_strlen(message));
+
+       return req;
+}
+
+
+static struct wpabuf * eap_mschapv2_buildReq(struct eap_sm *sm, void *priv,
+                                            u8 id)
+{
+       struct eap_mschapv2_data *data = priv;
+
+       switch (data->state) {
+       case CHALLENGE:
+               return eap_mschapv2_build_challenge(sm, data, id);
+       case SUCCESS_REQ:
+               return eap_mschapv2_build_success_req(sm, data, id);
+       case FAILURE_REQ:
+               return eap_mschapv2_build_failure_req(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+                          "buildReq", data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv,
+                                 struct wpabuf *respData)
+{
+       struct eap_mschapv2_data *data = priv;
+       struct eap_mschapv2_hdr *resp;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+                              &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame");
+               return TRUE;
+       }
+
+       resp = (struct eap_mschapv2_hdr *) pos;
+       if (data->state == CHALLENGE &&
+           resp->op_code != MSCHAPV2_OP_RESPONSE) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - "
+                          "ignore op %d", resp->op_code);
+               return TRUE;
+       }
+
+       if (data->state == SUCCESS_REQ &&
+           resp->op_code != MSCHAPV2_OP_SUCCESS &&
+           resp->op_code != MSCHAPV2_OP_FAILURE) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Success or "
+                          "Failure - ignore op %d", resp->op_code);
+               return TRUE;
+       }
+
+       if (data->state == FAILURE_REQ &&
+           resp->op_code != MSCHAPV2_OP_FAILURE) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Failure "
+                          "- ignore op %d", resp->op_code);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_mschapv2_process_response(struct eap_sm *sm,
+                                         struct eap_mschapv2_data *data,
+                                         struct wpabuf *respData)
+{
+       struct eap_mschapv2_hdr *resp;
+       const u8 *pos, *end, *peer_challenge, *nt_response, *name;
+       u8 flags;
+       size_t len, name_len, i;
+       u8 expected[24];
+       const u8 *username, *user;
+       size_t username_len, user_len;
+       int res;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+                              &len);
+       if (pos == NULL || len < 1)
+               return; /* Should not happen - frame already validated */
+
+       end = pos + len;
+       resp = (struct eap_mschapv2_hdr *) pos;
+       pos = (u8 *) (resp + 1);
+
+       if (len < sizeof(*resp) + 1 + 49 ||
+           resp->op_code != MSCHAPV2_OP_RESPONSE ||
+           pos[0] != 49) {
+               wpa_hexdump_buf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response",
+                               respData);
+               data->state = FAILURE;
+               return;
+       }
+       data->resp_mschapv2_id = resp->mschapv2_id;
+       pos++;
+       peer_challenge = pos;
+       pos += 16 + 8;
+       nt_response = pos;
+       pos += 24;
+       flags = *pos++;
+       name = pos;
+       name_len = end - name;
+
+       if (data->peer_challenge) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Using pre-configured "
+                          "Peer-Challenge");
+               peer_challenge = data->peer_challenge;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge",
+                   peer_challenge, 16);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: NT-Response", nt_response, 24);
+       wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
+
+       /* MSCHAPv2 does not include optional domain name in the
+        * challenge-response calculation, so remove domain prefix
+        * (if present). */
+       username = sm->identity;
+       username_len = sm->identity_len;
+       for (i = 0; i < username_len; i++) {
+               if (username[i] == '\\') {
+                       username_len -= i + 1;
+                       username += i + 1;
+                       break;
+               }
+       }
+
+       user = name;
+       user_len = name_len;
+       for (i = 0; i < user_len; i++) {
+               if (user[i] == '\\') {
+                       user_len -= i + 1;
+                       user += i + 1;
+                       break;
+               }
+       }
+
+       if (username_len != user_len ||
+           os_memcmp(username, user, username_len) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Mismatch in user names");
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Expected user "
+                                 "name", username, username_len);
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-MSCHAPV2: Received user "
+                                 "name", user, user_len);
+               data->state = FAILURE;
+               return;
+       }
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name",
+                         username, username_len);
+
+       if (sm->user->password_hash) {
+               res = generate_nt_response_pwhash(data->auth_challenge,
+                                                 peer_challenge,
+                                                 username, username_len,
+                                                 sm->user->password,
+                                                 expected);
+       } else {
+               res = generate_nt_response(data->auth_challenge,
+                                          peer_challenge,
+                                          username, username_len,
+                                          sm->user->password,
+                                          sm->user->password_len,
+                                          expected);
+       }
+       if (res) {
+               data->state = FAILURE;
+               return;
+       }
+
+       if (os_memcmp(nt_response, expected, 24) == 0) {
+               const u8 *pw_hash;
+               u8 pw_hash_buf[16], pw_hash_hash[16];
+
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response");
+               data->state = SUCCESS_REQ;
+
+               /* Authenticator response is not really needed yet, but
+                * calculate it here so that peer_challenge and username need
+                * not be saved. */
+               if (sm->user->password_hash) {
+                       pw_hash = sm->user->password;
+               } else {
+                       nt_password_hash(sm->user->password,
+                                        sm->user->password_len,
+                                        pw_hash_buf);
+                       pw_hash = pw_hash_buf;
+               }
+               generate_authenticator_response_pwhash(
+                       pw_hash, peer_challenge, data->auth_challenge,
+                       username, username_len, nt_response,
+                       data->auth_response);
+
+               hash_nt_password_hash(pw_hash, pw_hash_hash);
+               get_master_key(pw_hash_hash, nt_response, data->master_key);
+               data->master_key_valid = 1;
+               wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
+                               data->master_key, MSCHAPV2_KEY_LEN);
+       } else {
+               wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response",
+                           expected, 24);
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Invalid NT-Response");
+               data->state = FAILURE_REQ;
+       }
+}
+
+
+static void eap_mschapv2_process_success_resp(struct eap_sm *sm,
+                                             struct eap_mschapv2_data *data,
+                                             struct wpabuf *respData)
+{
+       struct eap_mschapv2_hdr *resp;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+                              &len);
+       if (pos == NULL || len < 1)
+               return; /* Should not happen - frame already validated */
+
+       resp = (struct eap_mschapv2_hdr *) pos;
+
+       if (resp->op_code == MSCHAPV2_OP_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response"
+                          " - authentication completed successfully");
+               data->state = SUCCESS;
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Success "
+                          "Response - peer rejected authentication");
+               data->state = FAILURE;
+       }
+}
+
+
+static void eap_mschapv2_process_failure_resp(struct eap_sm *sm,
+                                             struct eap_mschapv2_data *data,
+                                             struct wpabuf *respData)
+{
+       struct eap_mschapv2_hdr *resp;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
+                              &len);
+       if (pos == NULL || len < 1)
+               return; /* Should not happen - frame already validated */
+
+       resp = (struct eap_mschapv2_hdr *) pos;
+
+       if (resp->op_code == MSCHAPV2_OP_FAILURE) {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response"
+                          " - authentication failed");
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Did not receive Failure "
+                          "Response - authentication failed");
+       }
+
+       data->state = FAILURE;
+}
+
+
+static void eap_mschapv2_process(struct eap_sm *sm, void *priv,
+                                struct wpabuf *respData)
+{
+       struct eap_mschapv2_data *data = priv;
+
+       if (sm->user == NULL || sm->user->password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured");
+               data->state = FAILURE;
+               return;
+       }
+
+       switch (data->state) {
+       case CHALLENGE:
+               eap_mschapv2_process_response(sm, data, respData);
+               break;
+       case SUCCESS_REQ:
+               eap_mschapv2_process_success_resp(sm, data, respData);
+               break;
+       case FAILURE_REQ:
+               eap_mschapv2_process_failure_resp(sm, data, respData);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Unknown state %d in "
+                          "process", data->state);
+               break;
+       }
+}
+
+
+static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_mschapv2_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_mschapv2_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS || !data->master_key_valid)
+               return NULL;
+
+       *len = 2 * MSCHAPV2_KEY_LEN;
+       key = os_malloc(*len);
+       if (key == NULL)
+               return NULL;
+       /* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
+       get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
+       get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+                               MSCHAPV2_KEY_LEN, 1, 1);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
+
+       return key;
+}
+
+
+static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_mschapv2_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_mschapv2_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2,
+                                     "MSCHAPV2");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_mschapv2_init;
+       eap->reset = eap_mschapv2_reset;
+       eap->buildReq = eap_mschapv2_buildReq;
+       eap->check = eap_mschapv2_check;
+       eap->process = eap_mschapv2_process;
+       eap->isDone = eap_mschapv2_isDone;
+       eap->getKey = eap_mschapv2_getKey;
+       eap->isSuccess = eap_mschapv2_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
new file mode 100644 (file)
index 0000000..1dc023b
--- /dev/null
@@ -0,0 +1,569 @@
+/*
+ * hostapd / EAP-PAX (RFC 4746) server
+ * Copyright (c) 2005-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 "eap_server/eap_i.h"
+#include "eap_common/eap_pax_common.h"
+
+/*
+ * Note: only PAX_STD subprotocol is currently supported
+ *
+ * TODO: Add support with PAX_SEC with the mandatory to implement ciphersuite
+ * (HMAC_SHA1_128, IANA DH Group 14 (2048 bits), RSA-PKCS1-V1_5) and
+ * recommended ciphersuite (HMAC_SHA256_128, IANA DH Group 15 (3072 bits),
+ * RSAES-OAEP).
+ */
+
+struct eap_pax_data {
+       enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state;
+       u8 mac_id;
+       union {
+               u8 e[2 * EAP_PAX_RAND_LEN];
+               struct {
+                       u8 x[EAP_PAX_RAND_LEN]; /* server rand */
+                       u8 y[EAP_PAX_RAND_LEN]; /* client rand */
+               } r;
+       } rand;
+       u8 ak[EAP_PAX_AK_LEN];
+       u8 mk[EAP_PAX_MK_LEN];
+       u8 ck[EAP_PAX_CK_LEN];
+       u8 ick[EAP_PAX_ICK_LEN];
+       int keys_set;
+       char *cid;
+       size_t cid_len;
+};
+
+
+static void * eap_pax_init(struct eap_sm *sm)
+{
+       struct eap_pax_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = PAX_STD_1;
+       /*
+        * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is
+        * supported
+        */
+       data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128;
+
+       return data;
+}
+
+
+static void eap_pax_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_pax_data *data = priv;
+       os_free(data->cid);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
+                                          struct eap_pax_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_pax_hdr *pax;
+       u8 *pos;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)");
+
+       if (os_get_random(data->rand.r.x, EAP_PAX_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+                           sizeof(*pax) + 2 + EAP_PAX_RAND_LEN +
+                           EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
+                          "request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       pax = wpabuf_put(req, sizeof(*pax));
+       pax->op_code = EAP_PAX_OP_STD_1;
+       pax->flags = 0;
+       pax->mac_id = data->mac_id;
+       pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
+       pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
+
+       wpabuf_put_be16(req, EAP_PAX_RAND_LEN);
+       wpabuf_put_data(req, data->rand.r.x, EAP_PAX_RAND_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)",
+                   data->rand.r.x, EAP_PAX_RAND_LEN);
+
+       pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
+       eap_pax_mac(data->mac_id, (u8 *) "", 0,
+                   wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+                   NULL, 0, NULL, 0, pos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
+                                          struct eap_pax_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_pax_hdr *pax;
+       u8 *pos;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)");
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PAX,
+                           sizeof(*pax) + 2 + EAP_PAX_MAC_LEN +
+                           EAP_PAX_ICV_LEN, EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory "
+                          "request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       pax = wpabuf_put(req, sizeof(*pax));
+       pax->op_code = EAP_PAX_OP_STD_3;
+       pax->flags = 0;
+       pax->mac_id = data->mac_id;
+       pax->dh_group_id = EAP_PAX_DH_GROUP_NONE;
+       pax->public_key_id = EAP_PAX_PUBLIC_KEY_NONE;
+
+       wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
+       pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
+       eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+                   data->rand.r.y, EAP_PAX_RAND_LEN,
+                   (u8 *) data->cid, data->cid_len, NULL, 0, pos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
+                   pos, EAP_PAX_MAC_LEN);
+       pos += EAP_PAX_MAC_LEN;
+
+       /* Optional ADE could be added here, if needed */
+
+       pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
+       eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+                   wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+                   NULL, 0, NULL, 0, pos);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_pax_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_pax_data *data = priv;
+
+       switch (data->state) {
+       case PAX_STD_1:
+               return eap_pax_build_std_1(sm, data, id);
+       case PAX_STD_3:
+               return eap_pax_build_std_3(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq",
+                          data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_pax_data *data = priv;
+       struct eap_pax_hdr *resp;
+       const u8 *pos;
+       size_t len, mlen;
+       u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
+       if (pos == NULL || len < sizeof(*resp)) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
+               return TRUE;
+       }
+
+       mlen = sizeof(struct eap_hdr) + 1 + len;
+       resp = (struct eap_pax_hdr *) pos;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x "
+                  "flags 0x%x mac_id 0x%x dh_group_id 0x%x "
+                  "public_key_id 0x%x",
+                  resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id,
+                  resp->public_key_id);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload",
+                   (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN);
+
+       if (data->state == PAX_STD_1 &&
+           resp->op_code != EAP_PAX_OP_STD_2) {
+               wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - "
+                          "ignore op %d", resp->op_code);
+               return TRUE;
+       }
+
+       if (data->state == PAX_STD_3 &&
+           resp->op_code != EAP_PAX_OP_ACK) {
+               wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - "
+                          "ignore op %d", resp->op_code);
+               return TRUE;
+       }
+
+       if (resp->op_code != EAP_PAX_OP_STD_2 &&
+           resp->op_code != EAP_PAX_OP_ACK) {
+               wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x",
+                          resp->op_code);
+       }
+
+       if (data->mac_id != resp->mac_id) {
+               wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, "
+                          "received 0x%x", data->mac_id, resp->mac_id);
+               return TRUE;
+       }
+
+       if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, "
+                          "received 0x%x", EAP_PAX_DH_GROUP_NONE,
+                          resp->dh_group_id);
+               return TRUE;
+       }
+
+       if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, "
+                          "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE,
+                          resp->public_key_id);
+               return TRUE;
+       }
+
+       if (resp->flags & EAP_PAX_FLAGS_MF) {
+               /* TODO: add support for reassembling fragments */
+               wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported");
+               return TRUE;
+       }
+
+       if (resp->flags & EAP_PAX_FLAGS_CE) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag");
+               return TRUE;
+       }
+
+       if (data->keys_set) {
+               if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) {
+                       wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet");
+                       return TRUE;
+               }
+               icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
+               eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+                           wpabuf_mhead(respData),
+                           wpabuf_len(respData) - EAP_PAX_ICV_LEN,
+                           NULL, 0, NULL, 0, icvbuf);
+               if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
+                       wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
+                       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
+                                   icvbuf, EAP_PAX_ICV_LEN);
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
+
+static void eap_pax_process_std_2(struct eap_sm *sm,
+                                 struct eap_pax_data *data,
+                                 struct wpabuf *respData)
+{
+       struct eap_pax_hdr *resp;
+       u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
+       const u8 *pos;
+       size_t len, left;
+       int i;
+
+       if (data->state != PAX_STD_1)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2");
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
+       if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN)
+               return;
+
+       resp = (struct eap_pax_hdr *) pos;
+       pos = (u8 *) (resp + 1);
+       left = len - sizeof(*resp);
+
+       if (left < 2 + EAP_PAX_RAND_LEN ||
+           WPA_GET_BE16(pos) != EAP_PAX_RAND_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)");
+               return;
+       }
+       pos += 2;
+       left -= 2;
+       os_memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)",
+                   data->rand.r.y, EAP_PAX_RAND_LEN);
+       pos += EAP_PAX_RAND_LEN;
+       left -= EAP_PAX_RAND_LEN;
+
+       if (left < 2 || (size_t) 2 + WPA_GET_BE16(pos) > left) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
+               return;
+       }
+       data->cid_len = WPA_GET_BE16(pos);
+       os_free(data->cid);
+       data->cid = os_malloc(data->cid_len);
+       if (data->cid == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for "
+                          "CID");
+               return;
+       }
+       os_memcpy(data->cid, pos + 2, data->cid_len);
+       pos += 2 + data->cid_len;
+       left -= 2 + data->cid_len;
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID",
+                         (u8 *) data->cid, data->cid_len);
+
+       if (left < 2 + EAP_PAX_MAC_LEN ||
+           WPA_GET_BE16(pos) != EAP_PAX_MAC_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)");
+               return;
+       }
+       pos += 2;
+       left -= 2;
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)",
+                   pos, EAP_PAX_MAC_LEN);
+
+       if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID",
+                                 (u8 *) data->cid, data->cid_len);
+               data->state = FAILURE;
+               return;
+       }
+
+       for (i = 0;
+            i < EAP_MAX_METHODS &&
+                    (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+                     sm->user->methods[i].method != EAP_TYPE_NONE);
+            i++) {
+               if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+                   sm->user->methods[i].method == EAP_TYPE_PAX)
+                       break;
+       }
+
+       if (i >= EAP_MAX_METHODS ||
+           sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+           sm->user->methods[i].method != EAP_TYPE_PAX) {
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP-PAX: EAP-PAX not enabled for CID",
+                                 (u8 *) data->cid, data->cid_len);
+               data->state = FAILURE;
+               return;
+       }
+
+       if (sm->user->password == NULL ||
+           sm->user->password_len != EAP_PAX_AK_LEN) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in "
+                                 "user database for CID",
+                                 (u8 *) data->cid, data->cid_len);
+               data->state = FAILURE;
+               return;
+       }
+       os_memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN);
+
+       if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
+                                          data->rand.e, data->mk, data->ck,
+                                          data->ick) < 0) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
+                          "key derivation");
+               data->state = FAILURE;
+               return;
+       }
+       data->keys_set = 1;
+
+       eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+                   data->rand.r.x, EAP_PAX_RAND_LEN,
+                   data->rand.r.y, EAP_PAX_RAND_LEN,
+                   (u8 *) data->cid, data->cid_len, mac);
+       if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
+                          "PAX_STD-2");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
+                           mac, EAP_PAX_MAC_LEN);
+               data->state = FAILURE;
+               return;
+       }
+
+       pos += EAP_PAX_MAC_LEN;
+       left -= EAP_PAX_MAC_LEN;
+
+       if (left < EAP_PAX_ICV_LEN) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in "
+                          "PAX_STD-2", (unsigned long) left);
+               return;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
+       eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+                   wpabuf_head(respData),
+                   wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
+                   icvbuf);
+       if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
+                           icvbuf, EAP_PAX_ICV_LEN);
+               return;
+       }
+       pos += EAP_PAX_ICV_LEN;
+       left -= EAP_PAX_ICV_LEN;
+
+       if (left > 0) {
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload",
+                           pos, left);
+       }
+
+       data->state = PAX_STD_3;
+}
+
+
+static void eap_pax_process_ack(struct eap_sm *sm,
+                               struct eap_pax_data *data,
+                               struct wpabuf *respData)
+{
+       if (data->state != PAX_STD_3)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication "
+                  "completed successfully");
+       data->state = SUCCESS;
+}
+
+
+static void eap_pax_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_pax_data *data = priv;
+       struct eap_pax_hdr *resp;
+       const u8 *pos;
+       size_t len;
+
+       if (sm->user == NULL || sm->user->password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not "
+                          "configured");
+               data->state = FAILURE;
+               return;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
+       if (pos == NULL || len < sizeof(*resp))
+               return;
+
+       resp = (struct eap_pax_hdr *) pos;
+
+       switch (resp->op_code) {
+       case EAP_PAX_OP_STD_2:
+               eap_pax_process_std_2(sm, data, respData);
+               break;
+       case EAP_PAX_OP_ACK:
+               eap_pax_process_ack(sm, data, respData);
+               break;
+       }
+}
+
+
+static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_pax_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pax_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_MSK_LEN;
+       eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+                   "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN,
+                   EAP_MSK_LEN, key);
+
+       return key;
+}
+
+
+static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pax_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN,
+                   "Extended Master Session Key",
+                   data->rand.e, 2 * EAP_PAX_RAND_LEN,
+                   EAP_EMSK_LEN, key);
+
+       return key;
+}
+
+
+static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_pax_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_pax_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_pax_init;
+       eap->reset = eap_pax_reset;
+       eap->buildReq = eap_pax_buildReq;
+       eap->check = eap_pax_check;
+       eap->process = eap_pax_process;
+       eap->isDone = eap_pax_isDone;
+       eap->getKey = eap_pax_getKey;
+       eap->isSuccess = eap_pax_isSuccess;
+       eap->get_emsk = eap_pax_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
new file mode 100644 (file)
index 0000000..674ecd2
--- /dev/null
@@ -0,0 +1,1386 @@
+/*
+ * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
+ * 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 "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_peap_common.h"
+#include "tncs.h"
+
+
+/* Maximum supported PEAP version
+ * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
+ * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
+ * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
+ */
+#define EAP_PEAP_VERSION 1
+
+
+static void eap_peap_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_peap_data {
+       struct eap_ssl_data ssl;
+       enum {
+               START, PHASE1, PHASE1_ID2, PHASE2_START, PHASE2_ID,
+               PHASE2_METHOD, PHASE2_SOH,
+               PHASE2_TLV, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE
+       } state;
+
+       int peap_version;
+       int recv_version;
+       const struct eap_method *phase2_method;
+       void *phase2_priv;
+       int force_version;
+       struct wpabuf *pending_phase2_resp;
+       enum { TLV_REQ_NONE, TLV_REQ_SUCCESS, TLV_REQ_FAILURE } tlv_request;
+       int crypto_binding_sent;
+       int crypto_binding_used;
+       enum { NO_BINDING, OPTIONAL_BINDING, REQUIRE_BINDING } crypto_binding;
+       u8 binding_nonce[32];
+       u8 ipmk[40];
+       u8 cmk[20];
+       u8 *phase2_key;
+       size_t phase2_key_len;
+       struct wpabuf *soh_response;
+};
+
+
+static const char * eap_peap_state_txt(int state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case PHASE1:
+               return "PHASE1";
+       case PHASE1_ID2:
+               return "PHASE1_ID2";
+       case PHASE2_START:
+               return "PHASE2_START";
+       case PHASE2_ID:
+               return "PHASE2_ID";
+       case PHASE2_METHOD:
+               return "PHASE2_METHOD";
+       case PHASE2_SOH:
+               return "PHASE2_SOH";
+       case PHASE2_TLV:
+               return "PHASE2_TLV";
+       case SUCCESS_REQ:
+               return "SUCCESS_REQ";
+       case FAILURE_REQ:
+               return "FAILURE_REQ";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "Unknown?!";
+       }
+}
+
+
+static void eap_peap_state(struct eap_peap_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: %s -> %s",
+                  eap_peap_state_txt(data->state),
+                  eap_peap_state_txt(state));
+       data->state = state;
+}
+
+
+static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
+{
+       struct wpabuf *e;
+       struct eap_tlv_hdr *tlv;
+
+       if (buf == NULL)
+               return NULL;
+
+       /* Encapsulate EAP packet in EAP-Payload TLV */
+       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
+       e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
+       if (e == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
+                          "for TLV encapsulation");
+               wpabuf_free(buf);
+               return NULL;
+       }
+       tlv = wpabuf_put(e, sizeof(*tlv));
+       tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
+                                    EAP_TLV_EAP_PAYLOAD_TLV);
+       tlv->length = host_to_be16(wpabuf_len(buf));
+       wpabuf_put_buf(e, buf);
+       wpabuf_free(buf);
+       return e;
+}
+
+
+static void eap_peap_req_success(struct eap_sm *sm,
+                                struct eap_peap_data *data)
+{
+       if (data->state == FAILURE || data->state == FAILURE_REQ) {
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+       if (data->peap_version == 0) {
+               data->tlv_request = TLV_REQ_SUCCESS;
+               eap_peap_state(data, PHASE2_TLV);
+       } else {
+               eap_peap_state(data, SUCCESS_REQ);
+       }
+}
+
+
+static void eap_peap_req_failure(struct eap_sm *sm,
+                                struct eap_peap_data *data)
+{
+       if (data->state == FAILURE || data->state == FAILURE_REQ ||
+           data->state == SUCCESS_REQ || data->tlv_request != TLV_REQ_NONE) {
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+       if (data->peap_version == 0) {
+               data->tlv_request = TLV_REQ_FAILURE;
+               eap_peap_state(data, PHASE2_TLV);
+       } else {
+               eap_peap_state(data, FAILURE_REQ);
+       }
+}
+
+
+static void * eap_peap_init(struct eap_sm *sm)
+{
+       struct eap_peap_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->peap_version = EAP_PEAP_VERSION;
+       data->force_version = -1;
+       if (sm->user && sm->user->force_version >= 0) {
+               data->force_version = sm->user->force_version;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: forcing version %d",
+                          data->force_version);
+               data->peap_version = data->force_version;
+       }
+       data->state = START;
+       data->crypto_binding = OPTIONAL_BINDING;
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Failed to initialize SSL.");
+               eap_peap_reset(sm, data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_peap_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       if (data == NULL)
+               return;
+       if (data->phase2_priv && data->phase2_method)
+               data->phase2_method->reset(sm, data->phase2_priv);
+       eap_server_tls_ssl_deinit(sm, &data->ssl);
+       wpabuf_free(data->pending_phase2_resp);
+       os_free(data->phase2_key);
+       wpabuf_free(data->soh_response);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_peap_build_start(struct eap_sm *sm,
+                                           struct eap_peap_data *data, u8 id)
+{
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PEAP, 1,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-PEAP: Failed to allocate memory for"
+                          " request");
+               eap_peap_state(data, FAILURE);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->peap_version);
+
+       eap_peap_state(data, PHASE1);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
+                                                struct eap_peap_data *data,
+                                                u8 id)
+{
+       struct wpabuf *buf, *encr_req, msgbuf;
+       const u8 *req;
+       size_t req_len;
+
+       if (data->phase2_method == NULL || data->phase2_priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 method not ready");
+               return NULL;
+       }
+       buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+       if (data->peap_version >= 2 && buf)
+               buf = eap_peapv2_tlv_eap_payload(buf);
+       if (buf == NULL)
+               return NULL;
+
+       req = wpabuf_head(buf);
+       req_len = wpabuf_len(buf);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+                       req, req_len);
+
+       if (data->peap_version == 0 &&
+           data->phase2_method->method != EAP_TYPE_TLV) {
+               req += sizeof(struct eap_hdr);
+               req_len -= sizeof(struct eap_hdr);
+       }
+
+       wpabuf_set(&msgbuf, req, req_len);
+       encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+       wpabuf_free(buf);
+
+       return encr_req;
+}
+
+
+#ifdef EAP_SERVER_TNC
+static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm,
+                                                struct eap_peap_data *data,
+                                                u8 id)
+{
+       struct wpabuf *buf1, *buf, *encr_req, msgbuf;
+       const u8 *req;
+       size_t req_len;
+
+       buf1 = tncs_build_soh_request();
+       if (buf1 == NULL)
+               return NULL;
+
+       buf = eap_msg_alloc(EAP_VENDOR_MICROSOFT, 0x21, wpabuf_len(buf1),
+                           EAP_CODE_REQUEST, id);
+       if (buf == NULL) {
+               wpabuf_free(buf1);
+               return NULL;
+       }
+       wpabuf_put_buf(buf, buf1);
+       wpabuf_free(buf1);
+
+       req = wpabuf_head(buf);
+       req_len = wpabuf_len(buf);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 SOH data",
+                       req, req_len);
+
+       req += sizeof(struct eap_hdr);
+       req_len -= sizeof(struct eap_hdr);
+       wpabuf_set(&msgbuf, req, req_len);
+
+       encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+       wpabuf_free(buf);
+
+       return encr_req;
+}
+#endif /* EAP_SERVER_TNC */
+
+
+static void eap_peap_get_isk(struct eap_peap_data *data,
+                            u8 *isk, size_t isk_len)
+{
+       size_t key_len;
+
+       os_memset(isk, 0, isk_len);
+       if (data->phase2_key == NULL)
+               return;
+
+       key_len = data->phase2_key_len;
+       if (key_len > isk_len)
+               key_len = isk_len;
+       os_memcpy(isk, data->phase2_key, key_len);
+}
+
+
+static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
+{
+       u8 *tk;
+       u8 isk[32], imck[60];
+
+       /*
+        * Tunnel key (TK) is the first 60 octets of the key generated by
+        * phase 1 of PEAP (based on TLS).
+        */
+       tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",
+                                      EAP_TLS_KEY_LEN);
+       if (tk == NULL)
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
+
+       eap_peap_get_isk(data, isk, sizeof(isk));
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: ISK", isk, sizeof(isk));
+
+       /*
+        * IPMK Seed = "Inner Methods Compound Keys" | ISK
+        * TempKey = First 40 octets of TK
+        * IPMK|CMK = PRF+(TempKey, IPMK Seed, 60)
+        * (note: draft-josefsson-pppext-eap-tls-eap-10.txt includes a space
+        * in the end of the label just before ISK; is that just a typo?)
+        */
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
+       peap_prfplus(data->peap_version, tk, 40, "Inner Methods Compound Keys",
+                    isk, sizeof(isk), imck, sizeof(imck));
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
+                       imck, sizeof(imck));
+
+       os_free(tk);
+
+       /* TODO: fast-connect: IPMK|CMK = TK */
+       os_memcpy(data->ipmk, imck, 40);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
+       os_memcpy(data->cmk, imck + 40, 20);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
+                                                struct eap_peap_data *data,
+                                                u8 id)
+{
+       struct wpabuf *buf, *encr_req;
+       size_t mlen;
+
+       mlen = 6; /* Result TLV */
+       if (data->crypto_binding != NO_BINDING)
+               mlen += 60; /* Cryptobinding TLV */
+#ifdef EAP_SERVER_TNC
+       if (data->soh_response)
+               mlen += wpabuf_len(data->soh_response);
+#endif /* EAP_SERVER_TNC */
+
+       buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
+                           EAP_CODE_REQUEST, id);
+       if (buf == NULL)
+               return NULL;
+
+       wpabuf_put_u8(buf, 0x80); /* Mandatory */
+       wpabuf_put_u8(buf, EAP_TLV_RESULT_TLV);
+       /* Length */
+       wpabuf_put_be16(buf, 2);
+       /* Status */
+       wpabuf_put_be16(buf, data->tlv_request == TLV_REQ_SUCCESS ?
+                       EAP_TLV_RESULT_SUCCESS : EAP_TLV_RESULT_FAILURE);
+
+       if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+           data->crypto_binding != NO_BINDING) {
+               u8 *mac;
+               u8 eap_type = EAP_TYPE_PEAP;
+               const u8 *addr[2];
+               size_t len[2];
+               u16 tlv_type;
+
+#ifdef EAP_SERVER_TNC
+               if (data->soh_response) {
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH "
+                                  "Response TLV");
+                       wpabuf_put_buf(buf, data->soh_response);
+                       wpabuf_free(data->soh_response);
+                       data->soh_response = NULL;
+               }
+#endif /* EAP_SERVER_TNC */
+
+               if (eap_peap_derive_cmk(sm, data) < 0 ||
+                   os_get_random(data->binding_nonce, 32)) {
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+
+               /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+               addr[0] = wpabuf_put(buf, 0);
+               len[0] = 60;
+               addr[1] = &eap_type;
+               len[1] = 1;
+
+               tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
+               if (data->peap_version >= 2)
+                       tlv_type |= EAP_TLV_TYPE_MANDATORY;
+               wpabuf_put_be16(buf, tlv_type);
+               wpabuf_put_be16(buf, 56);
+
+               wpabuf_put_u8(buf, 0); /* Reserved */
+               wpabuf_put_u8(buf, data->peap_version); /* Version */
+               wpabuf_put_u8(buf, data->recv_version); /* RecvVersion */
+               wpabuf_put_u8(buf, 0); /* SubType: 0 = Request, 1 = Response */
+               wpabuf_put_data(buf, data->binding_nonce, 32); /* Nonce */
+               mac = wpabuf_put(buf, 20); /* Compound_MAC */
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC CMK",
+                           data->cmk, 20);
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 1",
+                           addr[0], len[0]);
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC data 2",
+                           addr[1], len[1]);
+               hmac_sha1_vector(data->cmk, 20, 2, addr, len, mac);
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PEAP: Compound_MAC",
+                           mac, SHA1_MAC_LEN);
+               data->crypto_binding_sent = 1;
+       }
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data",
+                           buf);
+
+       encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
+       wpabuf_free(buf);
+
+       return encr_req;
+}
+
+
+static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm,
+                                                 struct eap_peap_data *data,
+                                                 u8 id, int success)
+{
+       struct wpabuf *encr_req, msgbuf;
+       size_t req_len;
+       struct eap_hdr *hdr;
+
+       req_len = sizeof(*hdr);
+       hdr = os_zalloc(req_len);
+       if (hdr == NULL)
+               return NULL;
+
+       hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE;
+       hdr->identifier = id;
+       hdr->length = host_to_be16(req_len);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data",
+                       (u8 *) hdr, req_len);
+
+       wpabuf_set(&msgbuf, hdr, req_len);
+       encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+       os_free(hdr);
+
+       return encr_req;
+}
+
+
+static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_peap_data *data = priv;
+
+       if (data->ssl.state == FRAG_ACK) {
+               return eap_server_tls_build_ack(id, EAP_TYPE_PEAP,
+                                               data->peap_version);
+       }
+
+       if (data->ssl.state == WAIT_FRAG_ACK) {
+               return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP,
+                                               data->peap_version, id);
+       }
+
+       switch (data->state) {
+       case START:
+               return eap_peap_build_start(sm, data, id);
+       case PHASE1:
+       case PHASE1_ID2:
+               if (data->peap_version < 2 &&
+                   tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, "
+                                  "starting Phase2");
+                       eap_peap_state(data, PHASE2_START);
+               }
+               break;
+       case PHASE2_ID:
+       case PHASE2_METHOD:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id);
+               break;
+#ifdef EAP_SERVER_TNC
+       case PHASE2_SOH:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id);
+               break;
+#endif /* EAP_SERVER_TNC */
+       case PHASE2_TLV:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id);
+               break;
+       case SUCCESS_REQ:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id,
+                                                              1);
+               break;
+       case FAILURE_REQ:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id,
+                                                              0);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
+                          __func__, data->state);
+               return NULL;
+       }
+
+       return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_PEAP,
+                                       data->peap_version, id);
+}
+
+
+static Boolean eap_peap_check(struct eap_sm *sm, void *priv,
+                             struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PEAP, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data,
+                               EapType eap_type)
+{
+       if (data->phase2_priv && data->phase2_method) {
+               data->phase2_method->reset(sm, data->phase2_priv);
+               data->phase2_method = NULL;
+               data->phase2_priv = NULL;
+       }
+       data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+                                                       eap_type);
+       if (!data->phase2_method)
+               return -1;
+
+       sm->init_phase2 = 1;
+       data->phase2_priv = data->phase2_method->init(sm);
+       sm->init_phase2 = 0;
+       return 0;
+}
+
+
+static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
+                                         struct eap_peap_data *data,
+                                         const u8 *crypto_tlv,
+                                         size_t crypto_tlv_len)
+{
+       u8 buf[61], mac[SHA1_MAC_LEN];
+       const u8 *pos;
+
+       if (crypto_tlv_len != 4 + 56) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid cryptobinding TLV "
+                          "length %d", (int) crypto_tlv_len);
+               return -1;
+       }
+
+       pos = crypto_tlv;
+       pos += 4; /* TLV header */
+       if (pos[1] != data->peap_version) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV Version "
+                          "mismatch (was %d; expected %d)",
+                          pos[1], data->peap_version);
+               return -1;
+       }
+
+       if (pos[3] != 1) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected Cryptobinding TLV "
+                          "SubType %d", pos[3]);
+               return -1;
+       }
+       pos += 4;
+       pos += 32; /* Nonce */
+
+       /* Compound_MAC: HMAC-SHA1-160(cryptobinding TLV | EAP type) */
+       os_memcpy(buf, crypto_tlv, 60);
+       os_memset(buf + 4 + 4 + 32, 0, 20); /* Compound_MAC */
+       buf[60] = EAP_TYPE_PEAP;
+       hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
+
+       if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
+                          "cryptobinding TLV");
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20);
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding seed data",
+                           buf, 61);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: Valid cryptobinding TLV received");
+
+       return 0;
+}
+
+
+static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
+                                       struct eap_peap_data *data,
+                                       struct wpabuf *in_data)
+{
+       const u8 *pos;
+       size_t left;
+       const u8 *result_tlv = NULL, *crypto_tlv = NULL;
+       size_t result_tlv_len = 0, crypto_tlv_len = 0;
+       int tlv_type, mandatory, tlv_len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLV, in_data, &left);
+       if (pos == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid EAP-TLV header");
+               return;
+       }
+
+       /* Parse TLVs */
+       wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs", pos, left);
+       while (left >= 4) {
+               mandatory = !!(pos[0] & 0x80);
+               tlv_type = pos[0] & 0x3f;
+               tlv_type = (tlv_type << 8) | pos[1];
+               tlv_len = ((int) pos[2] << 8) | pos[3];
+               pos += 4;
+               left -= 4;
+               if ((size_t) tlv_len > left) {
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
+                                  "(tlv_len=%d left=%lu)", tlv_len,
+                                  (unsigned long) left);
+                       eap_peap_state(data, FAILURE);
+                       return;
+               }
+               switch (tlv_type) {
+               case EAP_TLV_RESULT_TLV:
+                       result_tlv = pos;
+                       result_tlv_len = tlv_len;
+                       break;
+               case EAP_TLV_CRYPTO_BINDING_TLV:
+                       crypto_tlv = pos;
+                       crypto_tlv_len = tlv_len;
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
+                                  "%d%s", tlv_type,
+                                  mandatory ? " (mandatory)" : "");
+                       if (mandatory) {
+                               eap_peap_state(data, FAILURE);
+                               return;
+                       }
+                       /* Ignore this TLV, but process other TLVs */
+                       break;
+               }
+
+               pos += tlv_len;
+               left -= tlv_len;
+       }
+       if (left) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
+                          "Request (left=%lu)", (unsigned long) left);
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+       /* Process supported TLVs */
+       if (crypto_tlv && data->crypto_binding_sent) {
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Cryptobinding TLV",
+                           crypto_tlv, crypto_tlv_len);
+               if (eap_tlv_validate_cryptobinding(sm, data, crypto_tlv - 4,
+                                                  crypto_tlv_len + 4) < 0) {
+                       eap_peap_state(data, FAILURE);
+                       return;
+               }
+               data->crypto_binding_used = 1;
+       } else if (!crypto_tlv && data->crypto_binding_sent &&
+                  data->crypto_binding == REQUIRE_BINDING) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: No cryptobinding TLV");
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+       if (result_tlv) {
+               int status;
+               const char *requested;
+
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Result TLV",
+                           result_tlv, result_tlv_len);
+               if (result_tlv_len < 2) {
+                       wpa_printf(MSG_INFO, "EAP-PEAP: Too short Result TLV "
+                                  "(len=%lu)",
+                                  (unsigned long) result_tlv_len);
+                       eap_peap_state(data, FAILURE);
+                       return;
+               }
+               requested = data->tlv_request == TLV_REQ_SUCCESS ? "Success" :
+                       "Failure";
+               status = WPA_GET_BE16(result_tlv);
+               if (status == EAP_TLV_RESULT_SUCCESS) {
+                       wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Success "
+                                  "- requested %s", requested);
+                       if (data->tlv_request == TLV_REQ_SUCCESS)
+                               eap_peap_state(data, SUCCESS);
+                       else
+                               eap_peap_state(data, FAILURE);
+                       
+               } else if (status == EAP_TLV_RESULT_FAILURE) {
+                       wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
+                                  "- requested %s", requested);
+                       eap_peap_state(data, FAILURE);
+               } else {
+                       wpa_printf(MSG_INFO, "EAP-PEAP: Unknown TLV Result "
+                                  "Status %d", status);
+                       eap_peap_state(data, FAILURE);
+               }
+       }
+}
+
+
+#ifdef EAP_SERVER_TNC
+static void eap_peap_process_phase2_soh(struct eap_sm *sm,
+                                       struct eap_peap_data *data,
+                                       struct wpabuf *in_data)
+{
+       const u8 *pos, *vpos;
+       size_t left;
+       const u8 *soh_tlv = NULL;
+       size_t soh_tlv_len = 0;
+       int tlv_type, mandatory, tlv_len, vtlv_len;
+       u8 next_type;
+       u32 vendor_id;
+
+       pos = eap_hdr_validate(EAP_VENDOR_MICROSOFT, 0x21, in_data, &left);
+       if (pos == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Not a valid SoH EAP "
+                          "Extensions Method header - skip TNC");
+               goto auth_method;
+       }
+
+       /* Parse TLVs */
+       wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received TLVs (SoH)", pos, left);
+       while (left >= 4) {
+               mandatory = !!(pos[0] & 0x80);
+               tlv_type = pos[0] & 0x3f;
+               tlv_type = (tlv_type << 8) | pos[1];
+               tlv_len = ((int) pos[2] << 8) | pos[3];
+               pos += 4;
+               left -= 4;
+               if ((size_t) tlv_len > left) {
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: TLV underrun "
+                                  "(tlv_len=%d left=%lu)", tlv_len,
+                                  (unsigned long) left);
+                       eap_peap_state(data, FAILURE);
+                       return;
+               }
+               switch (tlv_type) {
+               case EAP_TLV_VENDOR_SPECIFIC_TLV:
+                       if (tlv_len < 4) {
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Too short "
+                                          "vendor specific TLV (len=%d)",
+                                          (int) tlv_len);
+                               eap_peap_state(data, FAILURE);
+                               return;
+                       }
+
+                       vendor_id = WPA_GET_BE32(pos);
+                       if (vendor_id != EAP_VENDOR_MICROSOFT) {
+                               if (mandatory) {
+                                       eap_peap_state(data, FAILURE);
+                                       return;
+                               }
+                               break;
+                       }
+
+                       vpos = pos + 4;
+                       mandatory = !!(vpos[0] & 0x80);
+                       tlv_type = vpos[0] & 0x3f;
+                       tlv_type = (tlv_type << 8) | vpos[1];
+                       vtlv_len = ((int) vpos[2] << 8) | vpos[3];
+                       vpos += 4;
+                       if (vpos + vtlv_len > pos + left) {
+                               wpa_printf(MSG_DEBUG, "EAP-PEAP: Vendor TLV "
+                                          "underrun");
+                               eap_peap_state(data, FAILURE);
+                               return;
+                       }
+
+                       if (tlv_type == 1) {
+                               soh_tlv = vpos;
+                               soh_tlv_len = vtlv_len;
+                               break;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported MS-TLV "
+                                  "Type %d%s", tlv_type,
+                                  mandatory ? " (mandatory)" : "");
+                       if (mandatory) {
+                               eap_peap_state(data, FAILURE);
+                               return;
+                       }
+                       /* Ignore this TLV, but process other TLVs */
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Unsupported TLV Type "
+                                  "%d%s", tlv_type,
+                                  mandatory ? " (mandatory)" : "");
+                       if (mandatory) {
+                               eap_peap_state(data, FAILURE);
+                               return;
+                       }
+                       /* Ignore this TLV, but process other TLVs */
+                       break;
+               }
+
+               pos += tlv_len;
+               left -= tlv_len;
+       }
+       if (left) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Last TLV too short in "
+                          "Request (left=%lu)", (unsigned long) left);
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+       /* Process supported TLVs */
+       if (soh_tlv) {
+               int failure = 0;
+               wpabuf_free(data->soh_response);
+               data->soh_response = tncs_process_soh(soh_tlv, soh_tlv_len,
+                                                     &failure);
+               if (failure) {
+                       eap_peap_state(data, FAILURE);
+                       return;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: No SoH TLV received");
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+auth_method:
+       eap_peap_state(data, PHASE2_METHOD);
+       next_type = sm->user->methods[0].method;
+       sm->user_eap_method_index = 1;
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
+       eap_peap_phase2_init(sm, data, next_type);
+}
+#endif /* EAP_SERVER_TNC */
+
+
+static void eap_peap_process_phase2_response(struct eap_sm *sm,
+                                            struct eap_peap_data *data,
+                                            struct wpabuf *in_data)
+{
+       u8 next_type = EAP_TYPE_NONE;
+       const struct eap_hdr *hdr;
+       const u8 *pos;
+       size_t left;
+
+       if (data->state == PHASE2_TLV) {
+               eap_peap_process_phase2_tlv(sm, data, in_data);
+               return;
+       }
+
+#ifdef EAP_SERVER_TNC
+       if (data->state == PHASE2_SOH) {
+               eap_peap_process_phase2_soh(sm, data, in_data);
+               return;
+       }
+#endif /* EAP_SERVER_TNC */
+
+       if (data->phase2_priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not "
+                          "initialized?!", __func__);
+               return;
+       }
+
+       hdr = wpabuf_head(in_data);
+       pos = (const u8 *) (hdr + 1);
+
+       if (wpabuf_len(in_data) > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+               left = wpabuf_len(in_data) - sizeof(*hdr);
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Phase2 type Nak'ed; "
+                           "allowed types", pos + 1, left - 1);
+               eap_sm_process_nak(sm, pos + 1, left - 1);
+               if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+                   sm->user->methods[sm->user_eap_method_index].method !=
+                   EAP_TYPE_NONE) {
+                       next_type = sm->user->methods[
+                               sm->user_eap_method_index++].method;
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d",
+                                  next_type);
+               } else {
+                       eap_peap_req_failure(sm, data);
+                       next_type = EAP_TYPE_NONE;
+               }
+               eap_peap_phase2_init(sm, data, next_type);
+               return;
+       }
+
+       if (data->phase2_method->check(sm, data->phase2_priv, in_data)) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 check() asked to "
+                          "ignore the packet");
+               return;
+       }
+
+       data->phase2_method->process(sm, data->phase2_priv, in_data);
+
+       if (sm->method_pending == METHOD_PENDING_WAIT) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method is in "
+                          "pending wait state - save decrypted response");
+               wpabuf_free(data->pending_phase2_resp);
+               data->pending_phase2_resp = wpabuf_dup(in_data);
+       }
+
+       if (!data->phase2_method->isDone(sm, data->phase2_priv))
+               return;
+
+       if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 method failed");
+               eap_peap_req_failure(sm, data);
+               next_type = EAP_TYPE_NONE;
+               eap_peap_phase2_init(sm, data, next_type);
+               return;
+       }
+
+       os_free(data->phase2_key);
+       if (data->phase2_method->getKey) {
+               data->phase2_key = data->phase2_method->getKey(
+                       sm, data->phase2_priv, &data->phase2_key_len);
+               if (data->phase2_key == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase2 getKey "
+                                  "failed");
+                       eap_peap_req_failure(sm, data);
+                       eap_peap_phase2_init(sm, data, EAP_TYPE_NONE);
+                       return;
+               }
+       }
+
+       switch (data->state) {
+       case PHASE1_ID2:
+       case PHASE2_ID:
+       case PHASE2_SOH:
+               if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "EAP_PEAP: Phase2 "
+                                         "Identity not found in the user "
+                                         "database",
+                                         sm->identity, sm->identity_len);
+                       eap_peap_req_failure(sm, data);
+                       next_type = EAP_TYPE_NONE;
+                       break;
+               }
+
+#ifdef EAP_SERVER_TNC
+               if (data->state != PHASE2_SOH && sm->tnc &&
+                   data->peap_version == 0) {
+                       eap_peap_state(data, PHASE2_SOH);
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Try to initialize "
+                                  "TNC (NAP SOH)");
+                       next_type = EAP_TYPE_NONE;
+                       break;
+               }
+#endif /* EAP_SERVER_TNC */
+
+               eap_peap_state(data, PHASE2_METHOD);
+               next_type = sm->user->methods[0].method;
+               sm->user_eap_method_index = 1;
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type);
+               break;
+       case PHASE2_METHOD:
+               eap_peap_req_success(sm, data);
+               next_type = EAP_TYPE_NONE;
+               break;
+       case FAILURE:
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - unexpected state %d",
+                          __func__, data->state);
+               break;
+       }
+
+       eap_peap_phase2_init(sm, data, next_type);
+}
+
+
+static void eap_peap_process_phase2(struct eap_sm *sm,
+                                   struct eap_peap_data *data,
+                                   const struct wpabuf *respData,
+                                   struct wpabuf *in_buf)
+{
+       struct wpabuf *in_decrypted;
+       const struct eap_hdr *hdr;
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for"
+                  " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+       if (data->pending_phase2_resp) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - "
+                          "skip decryption and use old data");
+               eap_peap_process_phase2_response(sm, data,
+                                                data->pending_phase2_resp);
+               wpabuf_free(data->pending_phase2_resp);
+               data->pending_phase2_resp = NULL;
+               return;
+       }
+
+       in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+                                             in_buf);
+       if (in_decrypted == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 "
+                          "data");
+               eap_peap_state(data, FAILURE);
+               return;
+       }
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP",
+                           in_decrypted);
+
+       hdr = wpabuf_head(in_decrypted);
+
+       if (data->peap_version == 0 && data->state != PHASE2_TLV) {
+               const struct eap_hdr *resp;
+               struct eap_hdr *nhdr;
+               struct wpabuf *nbuf =
+                       wpabuf_alloc(sizeof(struct eap_hdr) +
+                                    wpabuf_len(in_decrypted));
+               if (nbuf == NULL) {
+                       wpabuf_free(in_decrypted);
+                       return;
+               }
+
+               resp = wpabuf_head(respData);
+               nhdr = wpabuf_put(nbuf, sizeof(*nhdr));
+               nhdr->code = resp->code;
+               nhdr->identifier = resp->identifier;
+               nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
+                                           wpabuf_len(in_decrypted));
+               wpabuf_put_buf(nbuf, in_decrypted);
+               wpabuf_free(in_decrypted);
+
+               in_decrypted = nbuf;
+       } else if (data->peap_version >= 2) {
+               struct eap_tlv_hdr *tlv;
+               struct wpabuf *nmsg;
+
+               if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
+                                  "EAP TLV");
+                       wpabuf_free(in_decrypted);
+                       return;
+               }
+               tlv = wpabuf_mhead(in_decrypted);
+               if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) !=
+                   EAP_TLV_EAP_PAYLOAD_TLV) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
+                       wpabuf_free(in_decrypted);
+                       return;
+               }
+               if (sizeof(*tlv) + be_to_host16(tlv->length) >
+                   wpabuf_len(in_decrypted)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
+                                  "length");
+                       wpabuf_free(in_decrypted);
+                       return;
+               }
+               hdr = (struct eap_hdr *) (tlv + 1);
+               if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
+                       wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
+                                  "EAP packet in EAP TLV");
+                       wpabuf_free(in_decrypted);
+                       return;
+               }
+
+               nmsg = wpabuf_alloc(be_to_host16(hdr->length));
+               if (nmsg == NULL) {
+                       wpabuf_free(in_decrypted);
+                       return;
+               }
+
+               wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
+               wpabuf_free(in_decrypted);
+               in_decrypted = nmsg;
+       }
+
+       hdr = wpabuf_head(in_decrypted);
+       if (wpabuf_len(in_decrypted) < (int) sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
+                          "EAP frame (len=%lu)",
+                          (unsigned long) wpabuf_len(in_decrypted));
+               wpabuf_free(in_decrypted);
+               eap_peap_req_failure(sm, data);
+               return;
+       }
+       len = be_to_host16(hdr->length);
+       if (len > wpabuf_len(in_decrypted)) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: Length mismatch in "
+                          "Phase 2 EAP frame (len=%lu hdr->length=%lu)",
+                          (unsigned long) wpabuf_len(in_decrypted),
+                          (unsigned long) len);
+               wpabuf_free(in_decrypted);
+               eap_peap_req_failure(sm, data);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-PEAP: received Phase 2: code=%d "
+                  "identifier=%d length=%lu", hdr->code, hdr->identifier,
+                  (unsigned long) len);
+       switch (hdr->code) {
+       case EAP_CODE_RESPONSE:
+               eap_peap_process_phase2_response(sm, data, in_decrypted);
+               break;
+       case EAP_CODE_SUCCESS:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Success");
+               if (data->state == SUCCESS_REQ) {
+                       eap_peap_state(data, SUCCESS);
+               }
+               break;
+       case EAP_CODE_FAILURE:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase 2 Failure");
+               eap_peap_state(data, FAILURE);
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-PEAP: Unexpected code=%d in "
+                          "Phase 2 EAP header", hdr->code);
+               break;
+       }
+
+       wpabuf_free(in_decrypted);
+}
+
+
+static int eap_peapv2_start_phase2(struct eap_sm *sm,
+                                  struct eap_peap_data *data)
+{
+       struct wpabuf *buf, *buf2;
+
+       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 "
+                  "payload in the same message");
+       eap_peap_state(data, PHASE1_ID2);
+       if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY))
+               return -1;
+
+       /* TODO: which Id to use here? */
+       buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6);
+       if (buf == NULL)
+               return -1;
+
+       buf2 = eap_peapv2_tlv_eap_payload(buf);
+       if (buf2 == NULL)
+               return -1;
+
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2);
+
+       buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
+                                    buf2);
+       wpabuf_free(buf2);
+
+       if (buf == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 "
+                          "data");
+               return -1;
+       }
+
+       wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request",
+                       buf);
+
+       /* Append TLS data into the pending buffer after the Server Finished */
+       if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) {
+               wpabuf_free(buf);
+               return -1;
+       }
+       wpabuf_put_buf(data->ssl.tls_out, buf);
+       wpabuf_free(buf);
+
+       return 0;
+}
+
+
+static int eap_peap_process_version(struct eap_sm *sm, void *priv,
+                                   int peer_version)
+{
+       struct eap_peap_data *data = priv;
+
+       data->recv_version = peer_version;
+       if (data->force_version >= 0 && peer_version != data->force_version) {
+               wpa_printf(MSG_INFO, "EAP-PEAP: peer did not select the forced"
+                          " version (forced=%d peer=%d) - reject",
+                          data->force_version, peer_version);
+               return -1;
+       }
+       if (peer_version < data->peap_version) {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: peer ver=%d, own ver=%d; "
+                          "use version %d",
+                          peer_version, data->peap_version, peer_version);
+               data->peap_version = peer_version;
+       }
+
+       return 0;
+}
+
+
+static void eap_peap_process_msg(struct eap_sm *sm, void *priv,
+                                const struct wpabuf *respData)
+{
+       struct eap_peap_data *data = priv;
+
+       switch (data->state) {
+       case PHASE1:
+               if (eap_server_tls_phase1(sm, &data->ssl) < 0) {
+                       eap_peap_state(data, FAILURE);
+                       break;
+               }
+
+               if (data->peap_version >= 2 &&
+                   tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+                       if (eap_peapv2_start_phase2(sm, data)) {
+                               eap_peap_state(data, FAILURE);
+                               break;
+                       }
+               }
+               break;
+       case PHASE2_START:
+               eap_peap_state(data, PHASE2_ID);
+               eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY);
+               break;
+       case PHASE1_ID2:
+       case PHASE2_ID:
+       case PHASE2_METHOD:
+       case PHASE2_SOH:
+       case PHASE2_TLV:
+               eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in);
+               break;
+       case SUCCESS_REQ:
+               eap_peap_state(data, SUCCESS);
+               break;
+       case FAILURE_REQ:
+               eap_peap_state(data, FAILURE);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Unexpected state %d in %s",
+                          data->state, __func__);
+               break;
+       }
+}
+
+
+static void eap_peap_process(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_peap_data *data = priv;
+       if (eap_server_tls_process(sm, &data->ssl, respData, data,
+                                  EAP_TYPE_PEAP, eap_peap_process_version,
+                                  eap_peap_process_msg) < 0)
+               eap_peap_state(data, FAILURE);
+}
+
+
+static Boolean eap_peap_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_peap_data *data = priv;
+       u8 *eapKeyData;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       if (data->crypto_binding_used) {
+               u8 csk[128];
+               /*
+                * Note: It looks like Microsoft implementation requires null
+                * termination for this label while the one used for deriving
+                * IPMK|CMK did not use null termination.
+                */
+               peap_prfplus(data->peap_version, data->ipmk, 40,
+                            "Session Key Generating Function",
+                            (u8 *) "\00", 1, csk, sizeof(csk));
+               wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CSK", csk, sizeof(csk));
+               eapKeyData = os_malloc(EAP_TLS_KEY_LEN);
+               if (eapKeyData) {
+                       os_memcpy(eapKeyData, csk, EAP_TLS_KEY_LEN);
+                       *len = EAP_TLS_KEY_LEN;
+                       wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+                                   eapKeyData, EAP_TLS_KEY_LEN);
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive "
+                                  "key");
+               }
+
+               return eapKeyData;
+       }
+
+       /* TODO: PEAPv1 - different label in some cases */
+       eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+                                              "client EAP encryption",
+                                              EAP_TLS_KEY_LEN);
+       if (eapKeyData) {
+               *len = EAP_TLS_KEY_LEN;
+               wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
+                           eapKeyData, EAP_TLS_KEY_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive key");
+       }
+
+       return eapKeyData;
+}
+
+
+static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_peap_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_peap_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_PEAP, "PEAP");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_peap_init;
+       eap->reset = eap_peap_reset;
+       eap->buildReq = eap_peap_buildReq;
+       eap->check = eap_peap_check;
+       eap->process = eap_peap_process;
+       eap->isDone = eap_peap_isDone;
+       eap->getKey = eap_peap_getKey;
+       eap->isSuccess = eap_peap_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_psk.c b/src/eap_server/eap_server_psk.c
new file mode 100644 (file)
index 0000000..4c30346
--- /dev/null
@@ -0,0 +1,517 @@
+/*
+ * hostapd / EAP-PSK (RFC 4764) server
+ * Copyright (c) 2005-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: EAP-PSK is an EAP authentication method and as such, completely
+ * different from WPA-PSK. This file is not needed for WPA-PSK functionality.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "eap_common/eap_psk_common.h"
+#include "eap_server/eap_i.h"
+
+
+struct eap_psk_data {
+       enum { PSK_1, PSK_3, SUCCESS, FAILURE } state;
+       u8 rand_s[EAP_PSK_RAND_LEN];
+       u8 rand_p[EAP_PSK_RAND_LEN];
+       u8 *id_p, *id_s;
+       size_t id_p_len, id_s_len;
+       u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+};
+
+
+static void * eap_psk_init(struct eap_sm *sm)
+{
+       struct eap_psk_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = PSK_1;
+       data->id_s = (u8 *) "hostapd";
+       data->id_s_len = 7;
+
+       return data;
+}
+
+
+static void eap_psk_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_psk_data *data = priv;
+       os_free(data->id_p);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
+                                      struct eap_psk_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_psk_hdr_1 *psk;
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)");
+
+       if (os_get_random(data->rand_s, EAP_PSK_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data");
+               data->state = FAILURE;
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)",
+                   data->rand_s, EAP_PSK_RAND_LEN);
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+                           sizeof(*psk) + data->id_s_len,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
+                          "request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       psk = wpabuf_put(req, sizeof(*psk));
+       psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */
+       os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
+       wpabuf_put_data(req, data->id_s, data->id_s_len);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
+                                      struct eap_psk_data *data, u8 id)
+{
+       struct wpabuf *req;
+       struct eap_psk_hdr_3 *psk;
+       u8 *buf, *pchannel, nonce[16];
+       size_t buflen;
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)");
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
+                           sizeof(*psk) + 4 + 16 + 1, EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
+                          "request");
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       psk = wpabuf_put(req, sizeof(*psk));
+       psk->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */
+       os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
+
+       /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
+       buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               goto fail;
+
+       os_memcpy(buf, data->id_s, data->id_s_len);
+       os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+       if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s))
+               goto fail;
+       os_free(buf);
+
+       if (eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk,
+                               data->emsk))
+               goto fail;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: TEK", data->tek, EAP_PSK_TEK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: MSK", data->msk, EAP_MSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: EMSK", data->emsk, EAP_EMSK_LEN);
+
+       os_memset(nonce, 0, sizeof(nonce));
+       pchannel = wpabuf_put(req, 4 + 16 + 1);
+       os_memcpy(pchannel, nonce + 12, 4);
+       os_memset(pchannel + 4, 0, 16); /* Tag */
+       pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6;
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)",
+                   pchannel, 4 + 16 + 1);
+       if (aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce),
+                               wpabuf_head(req), 22,
+                               pchannel + 4 + 16, 1, pchannel + 4))
+               goto fail;
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)",
+                   pchannel, 4 + 16 + 1);
+
+       return req;
+
+fail:
+       wpabuf_free(req);
+       data->state = FAILURE;
+       return NULL;
+}
+
+
+static struct wpabuf * eap_psk_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_psk_data *data = priv;
+
+       switch (data->state) {
+       case PSK_1:
+               return eap_psk_build_1(sm, data, id);
+       case PSK_3:
+               return eap_psk_build_3(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq",
+                          data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_psk_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_psk_data *data = priv;
+       size_t len;
+       u8 t;
+       const u8 *pos;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+               return TRUE;
+       }
+       t = EAP_PSK_FLAGS_GET_T(*pos);
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t);
+
+       if (data->state == PSK_1 && t != 1) {
+               wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - "
+                          "ignore T=%d", t);
+               return TRUE;
+       }
+
+       if (data->state == PSK_3 && t != 3) {
+               wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - "
+                          "ignore T=%d", t);
+               return TRUE;
+       }
+
+       if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) ||
+           (t == 3 && len < sizeof(struct eap_psk_hdr_4))) {
+               wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_psk_process_2(struct eap_sm *sm,
+                             struct eap_psk_data *data,
+                             struct wpabuf *respData)
+{
+       const struct eap_psk_hdr_2 *resp;
+       u8 *pos, mac[EAP_PSK_MAC_LEN], *buf;
+       size_t left, buflen;
+       int i;
+       const u8 *cpos;
+
+       if (data->state != PSK_1)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2");
+
+       cpos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData,
+                               &left);
+       if (cpos == NULL || left < sizeof(*resp)) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+               return;
+       }
+       resp = (const struct eap_psk_hdr_2 *) cpos;
+       cpos = (const u8 *) (resp + 1);
+       left -= sizeof(*resp);
+
+       os_free(data->id_p);
+       data->id_p = os_malloc(left);
+       if (data->id_p == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for "
+                          "ID_P");
+               return;
+       }
+       os_memcpy(data->id_p, cpos, left);
+       data->id_p_len = left;
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P",
+                         data->id_p, data->id_p_len);
+
+       if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P",
+                                 data->id_p, data->id_p_len);
+               data->state = FAILURE;
+               return;
+       }
+
+       for (i = 0;
+            i < EAP_MAX_METHODS &&
+                    (sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+                     sm->user->methods[i].method != EAP_TYPE_NONE);
+            i++) {
+               if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+                   sm->user->methods[i].method == EAP_TYPE_PSK)
+                       break;
+       }
+
+       if (i >= EAP_MAX_METHODS ||
+           sm->user->methods[i].vendor != EAP_VENDOR_IETF ||
+           sm->user->methods[i].method != EAP_TYPE_PSK) {
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP-PSK: EAP-PSK not enabled for ID_P",
+                                 data->id_p, data->id_p_len);
+               data->state = FAILURE;
+               return;
+       }
+
+       if (sm->user->password == NULL ||
+           sm->user->password_len != EAP_PSK_PSK_LEN) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in "
+                                 "user database for ID_P",
+                                 data->id_p, data->id_p_len);
+               data->state = FAILURE;
+               return;
+       }
+       if (eap_psk_key_setup(sm->user->password, data->ak, data->kdk)) {
+               data->state = FAILURE;
+               return;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN);
+
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)",
+                   resp->rand_p, EAP_PSK_RAND_LEN);
+       os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN);
+
+       /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
+       buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               data->state = FAILURE;
+               return;
+       }
+       os_memcpy(buf, data->id_p, data->id_p_len);
+       pos = buf + data->id_p_len;
+       os_memcpy(pos, data->id_s, data->id_s_len);
+       pos += data->id_s_len;
+       os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
+       pos += EAP_PSK_RAND_LEN;
+       os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
+       if (omac1_aes_128(data->ak, buf, buflen, mac)) {
+               os_free(buf);
+               data->state = FAILURE;
+               return;
+       }
+       os_free(buf);
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN);
+       if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P",
+                           mac, EAP_PSK_MAC_LEN);
+               data->state = FAILURE;
+               return;
+       }
+
+       data->state = PSK_3;
+}
+
+
+static void eap_psk_process_4(struct eap_sm *sm,
+                             struct eap_psk_data *data,
+                             struct wpabuf *respData)
+{
+       const struct eap_psk_hdr_4 *resp;
+       u8 *decrypted, nonce[16];
+       size_t left;
+       const u8 *pos, *tag;
+
+       if (data->state != PSK_3)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4");
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &left);
+       if (pos == NULL || left < sizeof(*resp)) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame");
+               return;
+       }
+       resp = (const struct eap_psk_hdr_4 *) pos;
+       pos = (const u8 *) (resp + 1);
+       left -= sizeof(*resp);
+
+       wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left);
+
+       if (left < 4 + 16 + 1) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in "
+                          "PSK-4 (len=%lu, expected 21)",
+                          (unsigned long) left);
+               return;
+       }
+
+       if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase");
+               return;
+       }
+
+       os_memset(nonce, 0, 12);
+       os_memcpy(nonce + 12, pos, 4);
+       pos += 4;
+       left -= 4;
+       tag = pos;
+       pos += 16;
+       left -= 16;
+
+       decrypted = os_malloc(left);
+       if (decrypted == NULL)
+               return;
+       os_memcpy(decrypted, pos, left);
+
+       if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce),
+                               wpabuf_head(respData), 22, decrypted, left,
+                               tag)) {
+               wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed");
+               os_free(decrypted);
+               data->state = FAILURE;
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message",
+                   decrypted, left);
+
+       /* Verify R flag */
+       switch (decrypted[0] >> 6) {
+       case EAP_PSK_R_FLAG_CONT:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported");
+               data->state = FAILURE;
+               break;
+       case EAP_PSK_R_FLAG_DONE_SUCCESS:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS");
+               data->state = SUCCESS;
+               break;
+       case EAP_PSK_R_FLAG_DONE_FAILURE:
+               wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE");
+               data->state = FAILURE;
+               break;
+       }
+       os_free(decrypted);
+}
+
+
+static void eap_psk_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_psk_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       if (sm->user == NULL || sm->user->password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not "
+                          "configured");
+               data->state = FAILURE;
+               return;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PSK, respData, &len);
+       if (pos == NULL || len < 1)
+               return;
+
+       switch (EAP_PSK_FLAGS_GET_T(*pos)) {
+       case 1:
+               eap_psk_process_2(sm, data, respData);
+               break;
+       case 3:
+               eap_psk_process_4(sm, data, respData);
+               break;
+       }
+}
+
+
+static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_psk_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_psk_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_psk_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_psk_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_psk_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_psk_init;
+       eap->reset = eap_psk_reset;
+       eap->buildReq = eap_psk_buildReq;
+       eap->check = eap_psk_check;
+       eap->process = eap_psk_process;
+       eap->isDone = eap_psk_isDone;
+       eap->getKey = eap_psk_getKey;
+       eap->isSuccess = eap_psk_isSuccess;
+       eap->get_emsk = eap_psk_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
new file mode 100644 (file)
index 0000000..ce4848f
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * hostapd / EAP-SAKE (RFC 4763) server
+ * Copyright (c) 2006-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 "eap_server/eap_i.h"
+#include "eap_common/eap_sake_common.h"
+
+
+struct eap_sake_data {
+       enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state;
+       u8 rand_s[EAP_SAKE_RAND_LEN];
+       u8 rand_p[EAP_SAKE_RAND_LEN];
+       struct {
+               u8 auth[EAP_SAKE_TEK_AUTH_LEN];
+               u8 cipher[EAP_SAKE_TEK_CIPHER_LEN];
+       } tek;
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 session_id;
+       u8 *peerid;
+       size_t peerid_len;
+       u8 *serverid;
+       size_t serverid_len;
+};
+
+
+static const char * eap_sake_state_txt(int state)
+{
+       switch (state) {
+       case IDENTITY:
+               return "IDENTITY";
+       case CHALLENGE:
+               return "CHALLENGE";
+       case CONFIRM:
+               return "CONFIRM";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_sake_state(struct eap_sake_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: %s -> %s",
+                  eap_sake_state_txt(data->state),
+                  eap_sake_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_sake_init(struct eap_sm *sm)
+{
+       struct eap_sake_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = CHALLENGE;
+
+       if (os_get_random(&data->session_id, 1)) {
+               wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+               os_free(data);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
+                  data->session_id);
+
+       /* TODO: add support for configuring SERVERID */
+       data->serverid = (u8 *) os_strdup("hostapd");
+       if (data->serverid)
+               data->serverid_len = os_strlen((char *) data->serverid);
+
+       return data;
+}
+
+
+static void eap_sake_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_sake_data *data = priv;
+       os_free(data->serverid);
+       os_free(data->peerid);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_sake_build_msg(struct eap_sake_data *data,
+                                         u8 id, size_t length, u8 subtype)
+{
+       struct eap_sake_hdr *sake;
+       struct wpabuf *msg;
+       size_t plen;
+
+       plen = sizeof(struct eap_sake_hdr) + length;
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_SAKE, plen,
+                           EAP_CODE_REQUEST, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to allocate memory "
+                          "request");
+               return NULL;
+       }
+
+       sake = wpabuf_put(msg, sizeof(*sake));
+       sake->version = EAP_SAKE_VERSION;
+       sake->session_id = data->session_id;
+       sake->subtype = subtype;
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
+                                              struct eap_sake_data *data,
+                                              u8 id)
+{
+       struct wpabuf *msg;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
+
+       plen = 4;
+       if (data->serverid)
+               plen += 2 + data->serverid_len;
+       msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
+       if (msg == NULL) {
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
+       eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
+
+       if (data->serverid) {
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+               eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+                                 data->serverid, data->serverid_len);
+       }
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
+                                               struct eap_sake_data *data,
+                                               u8 id)
+{
+       struct wpabuf *msg;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge");
+
+       if (os_get_random(data->rand_s, EAP_SAKE_RAND_LEN)) {
+               wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data");
+               data->state = FAILURE;
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
+                   data->rand_s, EAP_SAKE_RAND_LEN);
+
+       plen = 2 + EAP_SAKE_RAND_LEN;
+       if (data->serverid)
+               plen += 2 + data->serverid_len;
+       msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
+       if (msg == NULL) {
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_RAND_S");
+       eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
+                         data->rand_s, EAP_SAKE_RAND_LEN);
+
+       if (data->serverid) {
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+               eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+                                 data->serverid, data->serverid_len);
+       }
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
+                                             struct eap_sake_data *data,
+                                             u8 id)
+{
+       struct wpabuf *msg;
+       u8 *mic;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm");
+
+       msg = eap_sake_build_msg(data, id, 2 + EAP_SAKE_MIC_LEN,
+                                EAP_SAKE_SUBTYPE_CONFIRM);
+       if (msg == NULL) {
+               data->state = FAILURE;
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S");
+       wpabuf_put_u8(msg, EAP_SAKE_AT_MIC_S);
+       wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
+       mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
+       if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+                                data->serverid, data->serverid_len,
+                                data->peerid, data->peerid_len, 0,
+                                wpabuf_head(msg), wpabuf_len(msg), mic, mic))
+       {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+               data->state = FAILURE;
+               os_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_sake_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_sake_data *data = priv;
+
+       switch (data->state) {
+       case IDENTITY:
+               return eap_sake_build_identity(sm, data, id);
+       case CHALLENGE:
+               return eap_sake_build_challenge(sm, data, id);
+       case CONFIRM:
+               return eap_sake_build_confirm(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq",
+                          data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_sake_check(struct eap_sm *sm, void *priv,
+                             struct wpabuf *respData)
+{
+       struct eap_sake_data *data = priv;
+       struct eap_sake_hdr *resp;
+       size_t len;
+       u8 version, session_id, subtype;
+       const u8 *pos;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
+       if (pos == NULL || len < sizeof(struct eap_sake_hdr)) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame");
+               return TRUE;
+       }
+
+       resp = (struct eap_sake_hdr *) pos;
+       version = resp->version;
+       session_id = resp->session_id;
+       subtype = resp->subtype;
+
+       if (version != EAP_SAKE_VERSION) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version);
+               return TRUE;
+       }
+
+       if (session_id != data->session_id) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)",
+                          session_id, data->session_id);
+               return TRUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype);
+
+       if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY)
+               return FALSE;
+
+       if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE)
+               return FALSE;
+
+       if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM)
+               return FALSE;
+
+       if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT)
+               return FALSE;
+
+       wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d",
+                  subtype, data->state);
+
+       return TRUE;
+}
+
+
+static void eap_sake_process_identity(struct eap_sm *sm,
+                                     struct eap_sake_data *data,
+                                     const struct wpabuf *respData,
+                                     const u8 *payload, size_t payloadlen)
+{
+       if (data->state != IDENTITY)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity");
+       /* TODO: update identity and select new user data */
+       eap_sake_state(data, CHALLENGE);
+}
+
+
+static void eap_sake_process_challenge(struct eap_sm *sm,
+                                      struct eap_sake_data *data,
+                                      const struct wpabuf *respData,
+                                      const u8 *payload, size_t payloadlen)
+{
+       struct eap_sake_parse_attr attr;
+       u8 mic_p[EAP_SAKE_MIC_LEN];
+
+       if (data->state != CHALLENGE)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge");
+
+       if (eap_sake_parse_attributes(payload, payloadlen, &attr))
+               return;
+
+       if (!attr.rand_p || !attr.mic_p) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not "
+                          "include AT_RAND_P or AT_MIC_P");
+               return;
+       }
+
+       os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN);
+
+       os_free(data->peerid);
+       data->peerid = NULL;
+       data->peerid_len = 0;
+       if (attr.peerid) {
+               data->peerid = os_malloc(attr.peerid_len);
+               if (data->peerid == NULL)
+                       return;
+               os_memcpy(data->peerid, attr.peerid, attr.peerid_len);
+               data->peerid_len = attr.peerid_len;
+       }
+
+       if (sm->user == NULL || sm->user->password == NULL ||
+           sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with "
+                          "%d-byte key not configured",
+                          2 * EAP_SAKE_ROOT_SECRET_LEN);
+               data->state = FAILURE;
+               return;
+       }
+       eap_sake_derive_keys(sm->user->password,
+                            sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
+                            data->rand_s, data->rand_p,
+                            (u8 *) &data->tek, data->msk, data->emsk);
+
+       eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+                            data->serverid, data->serverid_len,
+                            data->peerid, data->peerid_len, 1,
+                            wpabuf_head(respData), wpabuf_len(respData),
+                            attr.mic_p, mic_p);
+       if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
+               eap_sake_state(data, FAILURE);
+               return;
+       }
+
+       eap_sake_state(data, CONFIRM);
+}
+
+
+static void eap_sake_process_confirm(struct eap_sm *sm,
+                                    struct eap_sake_data *data,
+                                    const struct wpabuf *respData,
+                                    const u8 *payload, size_t payloadlen)
+{
+       struct eap_sake_parse_attr attr;
+       u8 mic_p[EAP_SAKE_MIC_LEN];
+
+       if (data->state != CONFIRM)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm");
+
+       if (eap_sake_parse_attributes(payload, payloadlen, &attr))
+               return;
+
+       if (!attr.mic_p) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not "
+                          "include AT_MIC_P");
+               return;
+       }
+
+       eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+                            data->serverid, data->serverid_len,
+                            data->peerid, data->peerid_len, 1,
+                            wpabuf_head(respData), wpabuf_len(respData),
+                            attr.mic_p, mic_p);
+       if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
+               eap_sake_state(data, FAILURE);
+       } else
+               eap_sake_state(data, SUCCESS);
+}
+
+
+static void eap_sake_process_auth_reject(struct eap_sm *sm,
+                                        struct eap_sake_data *data,
+                                        const struct wpabuf *respData,
+                                        const u8 *payload, size_t payloadlen)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject");
+       eap_sake_state(data, FAILURE);
+}
+
+
+static void eap_sake_process(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_sake_data *data = priv;
+       struct eap_sake_hdr *resp;
+       u8 subtype;
+       size_t len;
+       const u8 *pos, *end;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SAKE, respData, &len);
+       if (pos == NULL || len < sizeof(struct eap_sake_hdr))
+               return;
+
+       resp = (struct eap_sake_hdr *) pos;
+       end = pos + len;
+       subtype = resp->subtype;
+       pos = (u8 *) (resp + 1);
+
+       wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Received attributes",
+                   pos, end - pos);
+
+       switch (subtype) {
+       case EAP_SAKE_SUBTYPE_IDENTITY:
+               eap_sake_process_identity(sm, data, respData, pos, end - pos);
+               break;
+       case EAP_SAKE_SUBTYPE_CHALLENGE:
+               eap_sake_process_challenge(sm, data, respData, pos, end - pos);
+               break;
+       case EAP_SAKE_SUBTYPE_CONFIRM:
+               eap_sake_process_confirm(sm, data, respData, pos, end - pos);
+               break;
+       case EAP_SAKE_SUBTYPE_AUTH_REJECT:
+               eap_sake_process_auth_reject(sm, data, respData, pos,
+                                            end - pos);
+               break;
+       }
+}
+
+
+static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_sake_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sake_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sake_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_sake_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_sake_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_sake_init;
+       eap->reset = eap_sake_reset;
+       eap->buildReq = eap_sake_buildReq;
+       eap->check = eap_sake_check;
+       eap->process = eap_sake_process;
+       eap->isDone = eap_sake_isDone;
+       eap->getKey = eap_sake_getKey;
+       eap->isSuccess = eap_sake_isSuccess;
+       eap->get_emsk = eap_sake_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
new file mode 100644 (file)
index 0000000..436c655
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * hostapd / EAP-SIM (RFC 4186)
+ * Copyright (c) 2005-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 "eap_server/eap_i.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_sim_db.h"
+
+
+struct eap_sim_data {
+       u8 mk[EAP_SIM_MK_LEN];
+       u8 nonce_mt[EAP_SIM_NONCE_MT_LEN];
+       u8 nonce_s[EAP_SIM_NONCE_S_LEN];
+       u8 k_aut[EAP_SIM_K_AUT_LEN];
+       u8 k_encr[EAP_SIM_K_ENCR_LEN];
+       u8 msk[EAP_SIM_KEYING_DATA_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
+       u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
+       u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+       int num_chal;
+       enum {
+               START, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE
+       } state;
+       char *next_pseudonym;
+       char *next_reauth_id;
+       u16 counter;
+       struct eap_sim_reauth *reauth;
+       u16 notification;
+       int use_result_ind;
+};
+
+
+static const char * eap_sim_state_txt(int state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case CHALLENGE:
+               return "CHALLENGE";
+       case REAUTH:
+               return "REAUTH";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       case NOTIFICATION:
+               return "NOTIFICATION";
+       default:
+               return "Unknown?!";
+       }
+}
+
+
+static void eap_sim_state(struct eap_sim_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s",
+                  eap_sim_state_txt(data->state),
+                  eap_sim_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_sim_init(struct eap_sm *sm)
+{
+       struct eap_sim_data *data;
+
+       if (sm->eap_sim_db_priv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: eap_sim_db not configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = START;
+
+       return data;
+}
+
+
+static void eap_sim_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       os_free(data->next_pseudonym);
+       os_free(data->next_reauth_id);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
+                                          struct eap_sim_data *data, u8 id)
+{
+       struct eap_sim_msg *msg;
+       u8 ver[2];
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start");
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_START);
+       if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity,
+                                     sm->identity_len)) {
+               wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
+               eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
+       } else {
+               /*
+                * RFC 4186, Chap. 4.2.4 recommends that identity from EAP is
+                * ignored and the SIM/Start is used to request the identity.
+                */
+               wpa_printf(MSG_DEBUG, "   AT_ANY_ID_REQ");
+               eap_sim_msg_add(msg, EAP_SIM_AT_ANY_ID_REQ, 0, NULL, 0);
+       }
+       wpa_printf(MSG_DEBUG, "   AT_VERSION_LIST");
+       ver[0] = 0;
+       ver[1] = EAP_SIM_VERSION;
+       eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
+                       ver, sizeof(ver));
+       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+}
+
+
+static int eap_sim_build_encr(struct eap_sm *sm, struct eap_sim_data *data,
+                             struct eap_sim_msg *msg, u16 counter,
+                             const u8 *nonce_s)
+{
+       os_free(data->next_pseudonym);
+       data->next_pseudonym =
+               eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0);
+       os_free(data->next_reauth_id);
+       if (data->counter <= EAP_SIM_MAX_FAST_REAUTHS) {
+               data->next_reauth_id =
+                       eap_sim_db_get_next_reauth_id(sm->eap_sim_db_priv, 0);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication "
+                          "count exceeded - force full authentication");
+               data->next_reauth_id = NULL;
+       }
+
+       if (data->next_pseudonym == NULL && data->next_reauth_id == NULL &&
+           counter == 0 && nonce_s == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "   AT_IV");
+       wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+       eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA);
+
+       if (counter > 0) {
+               wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)", counter);
+               eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0);
+       }
+
+       if (nonce_s) {
+               wpa_printf(MSG_DEBUG, "   *AT_NONCE_S");
+               eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s,
+                               EAP_SIM_NONCE_S_LEN);
+       }
+
+       if (data->next_pseudonym) {
+               wpa_printf(MSG_DEBUG, "   *AT_NEXT_PSEUDONYM (%s)",
+                          data->next_pseudonym);
+               eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM,
+                               os_strlen(data->next_pseudonym),
+                               (u8 *) data->next_pseudonym,
+                               os_strlen(data->next_pseudonym));
+       }
+
+       if (data->next_reauth_id) {
+               wpa_printf(MSG_DEBUG, "   *AT_NEXT_REAUTH_ID (%s)",
+                          data->next_reauth_id);
+               eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_REAUTH_ID,
+                               os_strlen(data->next_reauth_id),
+                               (u8 *) data->next_reauth_id,
+                               os_strlen(data->next_reauth_id));
+       }
+
+       if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt "
+                          "AT_ENCR_DATA");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
+                                              struct eap_sim_data *data,
+                                              u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge");
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_CHALLENGE);
+       wpa_printf(MSG_DEBUG, "   AT_RAND");
+       eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand,
+                       data->num_chal * GSM_RAND_LEN);
+
+       if (eap_sim_build_encr(sm, data, msg, 0, NULL)) {
+               eap_sim_msg_free(msg);
+               return NULL;
+       }
+
+       if (sm->eap_sim_aka_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt,
+                                 EAP_SIM_NONCE_MT_LEN);
+}
+
+
+static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
+                                           struct eap_sim_data *data, u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Re-authentication");
+
+       if (os_get_random(data->nonce_s, EAP_SIM_NONCE_S_LEN))
+               return NULL;
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S",
+                       data->nonce_s, EAP_SIM_NONCE_S_LEN);
+
+       eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+                           data->emsk);
+       eap_sim_derive_keys_reauth(data->counter, sm->identity,
+                                  sm->identity_len, data->nonce_s, data->mk,
+                                  data->msk, data->emsk);
+
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_REAUTHENTICATION);
+
+       if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) {
+               eap_sim_msg_free(msg);
+               return NULL;
+       }
+
+       if (sm->eap_sim_aka_result_ind) {
+               wpa_printf(MSG_DEBUG, "   AT_RESULT_IND");
+               eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0);
+       }
+
+       wpa_printf(MSG_DEBUG, "   AT_MAC");
+       eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
+                                                 struct eap_sim_data *data,
+                                                 u8 id)
+{
+       struct eap_sim_msg *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Notification");
+       msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM,
+                              EAP_SIM_SUBTYPE_NOTIFICATION);
+       wpa_printf(MSG_DEBUG, "   AT_NOTIFICATION (%d)", data->notification);
+       eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification,
+                       NULL, 0);
+       if (data->use_result_ind) {
+               if (data->reauth) {
+                       wpa_printf(MSG_DEBUG, "   AT_IV");
+                       wpa_printf(MSG_DEBUG, "   AT_ENCR_DATA");
+                       eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV,
+                                                  EAP_SIM_AT_ENCR_DATA);
+                       wpa_printf(MSG_DEBUG, "   *AT_COUNTER (%u)",
+                                  data->counter);
+                       eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, data->counter,
+                                       NULL, 0);
+
+                       if (eap_sim_msg_add_encr_end(msg, data->k_encr,
+                                                    EAP_SIM_AT_PADDING)) {
+                               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to "
+                                          "encrypt AT_ENCR_DATA");
+                               eap_sim_msg_free(msg);
+                               return NULL;
+                       }
+               }
+
+               wpa_printf(MSG_DEBUG, "   AT_MAC");
+               eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
+       }
+       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+}
+
+
+static struct wpabuf * eap_sim_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_sim_data *data = priv;
+
+       switch (data->state) {
+       case START:
+               return eap_sim_build_start(sm, data, id);
+       case CHALLENGE:
+               return eap_sim_build_challenge(sm, data, id);
+       case REAUTH:
+               return eap_sim_build_reauth(sm, data, id);
+       case NOTIFICATION:
+               return eap_sim_build_notification(sm, data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+                          "buildReq", data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_sim_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_sim_data *data = priv;
+       const u8 *pos;
+       size_t len;
+       u8 subtype;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
+       if (pos == NULL || len < 3) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame");
+               return TRUE;
+       }
+       subtype = *pos;
+
+       if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR)
+               return FALSE;
+
+       switch (data->state) {
+       case START:
+               if (subtype != EAP_SIM_SUBTYPE_START) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       case CHALLENGE:
+               if (subtype != EAP_SIM_SUBTYPE_CHALLENGE) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       case REAUTH:
+               if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       case NOTIFICATION:
+               if (subtype != EAP_SIM_SUBTYPE_NOTIFICATION) {
+                       wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response "
+                                  "subtype %d", subtype);
+                       return TRUE;
+               }
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for "
+                          "processing a response", data->state);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static int eap_sim_supported_ver(struct eap_sim_data *data, int version)
+{
+       return version == EAP_SIM_VERSION;
+}
+
+
+static void eap_sim_process_start(struct eap_sm *sm,
+                                 struct eap_sim_data *data,
+                                 struct wpabuf *respData,
+                                 struct eap_sim_attrs *attr)
+{
+       const u8 *identity;
+       size_t identity_len;
+       u8 ver_list[2];
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response");
+
+       if (attr->identity) {
+               os_free(sm->identity);
+               sm->identity = os_malloc(attr->identity_len);
+               if (sm->identity) {
+                       os_memcpy(sm->identity, attr->identity,
+                                 attr->identity_len);
+                       sm->identity_len = attr->identity_len;
+               }
+       }
+
+       identity = NULL;
+       identity_len = 0;
+
+       if (sm->identity && sm->identity_len > 0 &&
+           sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) {
+               identity = sm->identity;
+               identity_len = sm->identity_len;
+       } else {
+               identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv,
+                                                   sm->identity,
+                                                   sm->identity_len,
+                                                   &identity_len);
+               if (identity == NULL) {
+                       data->reauth = eap_sim_db_get_reauth_entry(
+                               sm->eap_sim_db_priv, sm->identity,
+                               sm->identity_len);
+                       if (data->reauth) {
+                               wpa_printf(MSG_DEBUG, "EAP-SIM: Using fast "
+                                          "re-authentication");
+                               identity = data->reauth->identity;
+                               identity_len = data->reauth->identity_len;
+                               data->counter = data->reauth->counter;
+                               os_memcpy(data->mk, data->reauth->mk,
+                                         EAP_SIM_MK_LEN);
+                       }
+               }
+       }
+
+       if (identity == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent"
+                          " user name");
+               eap_sim_state(data, FAILURE);
+               return;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity",
+                         identity, identity_len);
+
+       if (data->reauth) {
+               eap_sim_state(data, REAUTH);
+               return;
+       }
+
+       if (attr->nonce_mt == NULL || attr->selected_version < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Start/Response missing "
+                          "required attributes");
+               eap_sim_state(data, FAILURE);
+               return;
+       }
+
+       if (!eap_sim_supported_ver(data, attr->selected_version)) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Peer selected unsupported "
+                          "version %d", attr->selected_version);
+               eap_sim_state(data, FAILURE);
+               return;
+       }
+
+       data->counter = 0; /* reset re-auth counter since this is full auth */
+       data->reauth = NULL;
+
+       data->num_chal = eap_sim_db_get_gsm_triplets(
+               sm->eap_sim_db_priv, identity, identity_len,
+               EAP_SIM_MAX_CHAL,
+               (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm);
+       if (data->num_chal == EAP_SIM_DB_PENDING) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets "
+                          "not yet available - pending request");
+               sm->method_pending = METHOD_PENDING_WAIT;
+               return;
+       }
+       if (data->num_chal < 2) {
+               wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM "
+                          "authentication triplets for the peer");
+               eap_sim_state(data, FAILURE);
+               return;
+       }
+
+       identity_len = sm->identity_len;
+       while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
+                          "character from identity");
+               identity_len--;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation",
+                         sm->identity, identity_len);
+
+       os_memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN);
+       WPA_PUT_BE16(ver_list, EAP_SIM_VERSION);
+       eap_sim_derive_mk(sm->identity, identity_len, attr->nonce_mt,
+                         attr->selected_version, ver_list, sizeof(ver_list),
+                         data->num_chal, (const u8 *) data->kc, data->mk);
+       eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk,
+                           data->emsk);
+
+       eap_sim_state(data, CHALLENGE);
+}
+
+
+static void eap_sim_process_challenge(struct eap_sm *sm,
+                                     struct eap_sim_data *data,
+                                     struct wpabuf *respData,
+                                     struct eap_sim_attrs *attr)
+{
+       const u8 *identity;
+       size_t identity_len;
+
+       if (attr->mac == NULL ||
+           eap_sim_verify_mac(data->k_aut, respData, attr->mac,
+                              (u8 *) data->sres,
+                              data->num_chal * EAP_SIM_SRES_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message "
+                          "did not include valid AT_MAC");
+               eap_sim_state(data, FAILURE);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Challenge response includes the "
+                  "correct AT_MAC");
+       if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+               data->use_result_ind = 1;
+               data->notification = EAP_SIM_SUCCESS;
+               eap_sim_state(data, NOTIFICATION);
+       } else
+               eap_sim_state(data, SUCCESS);
+
+       identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity,
+                                           sm->identity_len, &identity_len);
+       if (identity == NULL) {
+               identity = sm->identity;
+               identity_len = sm->identity_len;
+       }
+
+       if (data->next_pseudonym) {
+               eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+                                        identity_len,
+                                        data->next_pseudonym);
+               data->next_pseudonym = NULL;
+       }
+       if (data->next_reauth_id) {
+               eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+                                     identity_len,
+                                     data->next_reauth_id, data->counter + 1,
+                                     data->mk);
+               data->next_reauth_id = NULL;
+       }
+}
+
+
+static void eap_sim_process_reauth(struct eap_sm *sm,
+                                  struct eap_sim_data *data,
+                                  struct wpabuf *respData,
+                                  struct eap_sim_attrs *attr)
+{
+       struct eap_sim_attrs eattr;
+       u8 *decrypted = NULL;
+       const u8 *identity, *id2;
+       size_t identity_len, id2_len;
+
+       if (attr->mac == NULL ||
+           eap_sim_verify_mac(data->k_aut, respData, attr->mac, data->nonce_s,
+                              EAP_SIM_NONCE_S_LEN)) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
+                          "did not include valid AT_MAC");
+               goto fail;
+       }
+
+       if (attr->encr_data == NULL || attr->iv == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication "
+                          "message did not include encrypted data");
+               goto fail;
+       }
+
+       decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data,
+                                      attr->encr_data_len, attr->iv, &eattr,
+                                      0);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted "
+                          "data from reauthentication message");
+               goto fail;
+       }
+
+       if (eattr.counter != data->counter) {
+               wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message "
+                          "used incorrect counter %u, expected %u",
+                          eattr.counter, data->counter);
+               goto fail;
+       }
+       os_free(decrypted);
+       decrypted = NULL;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes "
+                  "the correct AT_MAC");
+       if (sm->eap_sim_aka_result_ind && attr->result_ind) {
+               data->use_result_ind = 1;
+               data->notification = EAP_SIM_SUCCESS;
+               eap_sim_state(data, NOTIFICATION);
+       } else
+               eap_sim_state(data, SUCCESS);
+
+       if (data->reauth) {
+               identity = data->reauth->identity;
+               identity_len = data->reauth->identity_len;
+       } else {
+               identity = sm->identity;
+               identity_len = sm->identity_len;
+       }
+
+       id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity,
+                                      identity_len, &id2_len);
+       if (id2) {
+               identity = id2;
+               identity_len = id2_len;
+       }
+
+       if (data->next_pseudonym) {
+               eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity,
+                                        identity_len, data->next_pseudonym);
+               data->next_pseudonym = NULL;
+       }
+       if (data->next_reauth_id) {
+               eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity,
+                                     identity_len, data->next_reauth_id,
+                                     data->counter + 1, data->mk);
+               data->next_reauth_id = NULL;
+       } else {
+               eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+               data->reauth = NULL;
+       }
+
+       return;
+
+fail:
+       eap_sim_state(data, FAILURE);
+       eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth);
+       data->reauth = NULL;
+       os_free(decrypted);
+}
+
+
+static void eap_sim_process_client_error(struct eap_sm *sm,
+                                        struct eap_sim_data *data,
+                                        struct wpabuf *respData,
+                                        struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Client reported error %d",
+                  attr->client_error_code);
+       if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+               eap_sim_state(data, SUCCESS);
+       else
+               eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process_notification(struct eap_sm *sm,
+                                        struct eap_sim_data *data,
+                                        struct wpabuf *respData,
+                                        struct eap_sim_attrs *attr)
+{
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Client replied to notification");
+       if (data->notification == EAP_SIM_SUCCESS && data->use_result_ind)
+               eap_sim_state(data, SUCCESS);
+       else
+               eap_sim_state(data, FAILURE);
+}
+
+
+static void eap_sim_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_sim_data *data = priv;
+       const u8 *pos, *end;
+       u8 subtype;
+       size_t len;
+       struct eap_sim_attrs attr;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_SIM, respData, &len);
+       if (pos == NULL || len < 3)
+               return;
+
+       end = pos + len;
+       subtype = *pos;
+       pos += 3;
+
+       if (eap_sim_parse_attr(pos, end, &attr, 0, 0)) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Failed to parse attributes");
+               eap_sim_state(data, FAILURE);
+               return;
+       }
+
+       if (subtype == EAP_SIM_SUBTYPE_CLIENT_ERROR) {
+               eap_sim_process_client_error(sm, data, respData, &attr);
+               return;
+       }
+
+       switch (data->state) {
+       case START:
+               eap_sim_process_start(sm, data, respData, &attr);
+               break;
+       case CHALLENGE:
+               eap_sim_process_challenge(sm, data, respData, &attr);
+               break;
+       case REAUTH:
+               eap_sim_process_reauth(sm, data, respData, &attr);
+               break;
+       case NOTIFICATION:
+               eap_sim_process_notification(sm, data, respData, &attr);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in "
+                          "process", data->state);
+               break;
+       }
+}
+
+
+static Boolean eap_sim_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sim_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_SIM_KEYING_DATA_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN);
+       *len = EAP_SIM_KEYING_DATA_LEN;
+       return key;
+}
+
+
+static u8 * eap_sim_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sim_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+       return key;
+}
+
+
+static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_sim_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_sim_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_sim_init;
+       eap->reset = eap_sim_reset;
+       eap->buildReq = eap_sim_buildReq;
+       eap->check = eap_sim_check;
+       eap->process = eap_sim_process;
+       eap->isDone = eap_sim_isDone;
+       eap->getKey = eap_sim_getKey;
+       eap->isSuccess = eap_sim_isSuccess;
+       eap->get_emsk = eap_sim_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
new file mode 100644 (file)
index 0000000..c98fa18
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * hostapd / EAP-TLS (RFC 2716)
+ * 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 "eap_i.h"
+#include "eap_tls_common.h"
+#include "crypto/tls.h"
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_tls_data {
+       struct eap_ssl_data ssl;
+       enum { START, CONTINUE, SUCCESS, FAILURE } state;
+       int established;
+};
+
+
+static const char * eap_tls_state_txt(int state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case CONTINUE:
+               return "CONTINUE";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "Unknown?!";
+       }
+}
+
+
+static void eap_tls_state(struct eap_tls_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TLS: %s -> %s",
+                  eap_tls_state_txt(data->state),
+                  eap_tls_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_tls_init(struct eap_sm *sm)
+{
+       struct eap_tls_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = START;
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 1)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_reset(sm, data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_tls_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       if (data == NULL)
+               return;
+       eap_server_tls_ssl_deinit(sm, &data->ssl);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_tls_build_start(struct eap_sm *sm,
+                                          struct eap_tls_data *data, u8 id)
+{
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLS, 1, EAP_CODE_REQUEST,
+                           id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-TLS: Failed to allocate memory for "
+                          "request");
+               eap_tls_state(data, FAILURE);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_TLS_FLAGS_START);
+
+       eap_tls_state(data, CONTINUE);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_tls_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_tls_data *data = priv;
+       struct wpabuf *res;
+
+       if (data->ssl.state == FRAG_ACK) {
+               return eap_server_tls_build_ack(id, EAP_TYPE_TLS, 0);
+       }
+
+       if (data->ssl.state == WAIT_FRAG_ACK) {
+               res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0,
+                                              id);
+               goto check_established;
+       }
+
+       switch (data->state) {
+       case START:
+               return eap_tls_build_start(sm, data, id);
+       case CONTINUE:
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn))
+                       data->established = 1;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-TLS: %s - unexpected state %d",
+                          __func__, data->state);
+               return NULL;
+       }
+
+       res = eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TLS, 0, id);
+
+check_established:
+       if (data->established && data->ssl.state != WAIT_FRAG_ACK) {
+               /* TLS handshake has been completed and there are no more
+                * fragments waiting to be sent out. */
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
+               eap_tls_state(data, SUCCESS);
+       }
+
+       return res;
+}
+
+
+static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TLS, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_tls_process_msg(struct eap_sm *sm, void *priv,
+                               const struct wpabuf *respData)
+{
+       struct eap_tls_data *data = priv;
+       if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS "
+                          "handshake message");
+               return;
+       }
+       if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+               eap_tls_state(data, FAILURE);
+}
+
+
+static void eap_tls_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_tls_data *data = priv;
+       if (eap_server_tls_process(sm, &data->ssl, respData, data,
+                                  EAP_TYPE_TLS, NULL, eap_tls_process_msg) <
+           0)
+               eap_tls_state(data, FAILURE);
+}
+
+
+static Boolean eap_tls_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+       u8 *eapKeyData;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+                                              "client EAP encryption",
+                                              EAP_TLS_KEY_LEN);
+       if (eapKeyData) {
+               *len = EAP_TLS_KEY_LEN;
+               wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived key",
+                           eapKeyData, EAP_TLS_KEY_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive key");
+       }
+
+       return eapKeyData;
+}
+
+
+static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+       u8 *eapKeyData, *emsk;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+                                              "client EAP encryption",
+                                              EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+       if (eapKeyData) {
+               emsk = os_malloc(EAP_EMSK_LEN);
+               if (emsk)
+                       os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
+                                 EAP_EMSK_LEN);
+               os_free(eapKeyData);
+       } else
+               emsk = NULL;
+
+       if (emsk) {
+               *len = EAP_EMSK_LEN;
+               wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK",
+                           emsk, EAP_EMSK_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK");
+       }
+
+       return emsk;
+}
+
+
+static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_tls_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_tls_init;
+       eap->reset = eap_tls_reset;
+       eap->buildReq = eap_tls_buildReq;
+       eap->check = eap_tls_check;
+       eap->process = eap_tls_process;
+       eap->isDone = eap_tls_isDone;
+       eap->getKey = eap_tls_getKey;
+       eap->isSuccess = eap_tls_isSuccess;
+       eap->get_emsk = eap_tls_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
new file mode 100644 (file)
index 0000000..25ae683
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * EAP-TLS/PEAP/TTLS/FAST server common functions
+ * 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 "common.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_i.h"
+#include "eap_tls_common.h"
+
+
+static void eap_server_tls_free_in_buf(struct eap_ssl_data *data);
+
+
+int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+                           int verify_peer)
+{
+       data->eap = sm;
+       data->phase2 = sm->init_phase2;
+
+       data->conn = tls_connection_init(sm->ssl_ctx);
+       if (data->conn == NULL) {
+               wpa_printf(MSG_INFO, "SSL: Failed to initialize new TLS "
+                          "connection");
+               return -1;
+       }
+
+       if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
+               wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
+                          "of TLS peer certificate");
+               tls_connection_deinit(sm->ssl_ctx, data->conn);
+               data->conn = NULL;
+               return -1;
+       }
+
+       /* TODO: make this configurable */
+       data->tls_out_limit = 1398;
+       if (data->phase2) {
+               /* Limit the fragment size in the inner TLS authentication
+                * since the outer authentication with EAP-PEAP does not yet
+                * support fragmentation */
+               if (data->tls_out_limit > 100)
+                       data->tls_out_limit -= 100;
+       }
+       return 0;
+}
+
+
+void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+       tls_connection_deinit(sm->ssl_ctx, data->conn);
+       eap_server_tls_free_in_buf(data);
+       wpabuf_free(data->tls_out);
+       data->tls_out = NULL;
+}
+
+
+u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+                              char *label, size_t len)
+{
+       struct tls_keys keys;
+       u8 *rnd = NULL, *out;
+
+       out = os_malloc(len);
+       if (out == NULL)
+               return NULL;
+
+       if (tls_connection_prf(sm->ssl_ctx, data->conn, label, 0, out, len) ==
+           0)
+               return out;
+
+       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+               goto fail;
+
+       if (keys.client_random == NULL || keys.server_random == NULL ||
+           keys.master_key == NULL)
+               goto fail;
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       if (rnd == NULL)
+               goto fail;
+       os_memcpy(rnd, keys.client_random, keys.client_random_len);
+       os_memcpy(rnd + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       if (tls_prf(keys.master_key, keys.master_key_len,
+                   label, rnd, keys.client_random_len +
+                   keys.server_random_len, out, len))
+               goto fail;
+
+       os_free(rnd);
+       return out;
+
+fail:
+       os_free(out);
+       os_free(rnd);
+       return NULL;
+}
+
+
+struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
+                                        int eap_type, int version, u8 id)
+{
+       struct wpabuf *req;
+       u8 flags;
+       size_t send_len, plen;
+
+       wpa_printf(MSG_DEBUG, "SSL: Generating Request");
+       if (data->tls_out == NULL) {
+               wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__);
+               return NULL;
+       }
+
+       flags = version;
+       send_len = wpabuf_len(data->tls_out) - data->tls_out_pos;
+       if (1 + send_len > data->tls_out_limit) {
+               send_len = data->tls_out_limit - 1;
+               flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS;
+               if (data->tls_out_pos == 0) {
+                       flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED;
+                       send_len -= 4;
+               }
+       }
+
+       plen = 1 + send_len;
+       if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+               plen += 4;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, plen,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL)
+               return NULL;
+
+       wpabuf_put_u8(req, flags); /* Flags */
+       if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)
+               wpabuf_put_be32(req, wpabuf_len(data->tls_out));
+
+       wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos,
+                       send_len);
+       data->tls_out_pos += send_len;
+
+       if (data->tls_out_pos == wpabuf_len(data->tls_out)) {
+               wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->tls_out);
+               data->tls_out = NULL;
+               data->tls_out_pos = 0;
+               data->state = MSG;
+       } else {
+               wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->tls_out) -
+                          data->tls_out_pos);
+               data->state = WAIT_FRAG_ACK;
+       }
+
+       return req;
+}
+
+
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version)
+{
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, eap_type, 1, EAP_CODE_REQUEST,
+                           id);
+       if (req == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "SSL: Building ACK");
+       wpabuf_put_u8(req, version); /* Flags */
+       return req;
+}
+
+
+static int eap_server_tls_process_cont(struct eap_ssl_data *data,
+                                      const u8 *buf, size_t len)
+{
+       /* Process continuation of a pending message */
+       if (len > wpabuf_tailroom(data->tls_in)) {
+               wpa_printf(MSG_DEBUG, "SSL: Fragment overflow");
+               return -1;
+       }
+
+       wpabuf_put_data(data->tls_in, buf, len);
+       wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu "
+                  "bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->tls_in));
+
+       return 0;
+}
+
+
+static int eap_server_tls_process_fragment(struct eap_ssl_data *data,
+                                          u8 flags, u32 message_length,
+                                          const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) {
+               wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a "
+                          "fragmented packet");
+               return -1;
+       }
+
+       if (data->tls_in == NULL) {
+               /* First fragment of the message */
+
+               /* Limit length to avoid rogue peers from causing large
+                * memory allocations. */
+               if (message_length > 65536) {
+                       wpa_printf(MSG_INFO, "SSL: Too long TLS fragment (size"
+                                  " over 64 kB)");
+                       return -1;
+               }
+
+               data->tls_in = wpabuf_alloc(message_length);
+               if (data->tls_in == NULL) {
+                       wpa_printf(MSG_DEBUG, "SSL: No memory for message");
+                       return -1;
+               }
+               wpabuf_put_data(data->tls_in, buf, len);
+               wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first "
+                          "fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->tls_in));
+       }
+
+       return 0;
+}
+
+
+int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data)
+{
+       if (data->tls_out) {
+               /* This should not happen.. */
+               wpa_printf(MSG_INFO, "SSL: pending tls_out data when "
+                          "processing new message");
+               wpabuf_free(data->tls_out);
+               WPA_ASSERT(data->tls_out == NULL);
+       }
+
+       data->tls_out = tls_connection_server_handshake(sm->ssl_ctx,
+                                                       data->conn,
+                                                       data->tls_in, NULL);
+       if (data->tls_out == NULL) {
+               wpa_printf(MSG_INFO, "SSL: TLS processing failed");
+               return -1;
+       }
+       if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) {
+               /* TLS processing has failed - return error */
+               wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to "
+                          "report error");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags,
+                                    const u8 **pos, size_t *left)
+{
+       unsigned int tls_msg_len = 0;
+       const u8 *end = *pos + *left;
+
+       if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) {
+               if (*left < 4) {
+                       wpa_printf(MSG_INFO, "SSL: Short frame with TLS "
+                                  "length");
+                       return -1;
+               }
+               tls_msg_len = WPA_GET_BE32(*pos);
+               wpa_printf(MSG_DEBUG, "SSL: TLS Message Length: %d",
+                          tls_msg_len);
+               *pos += 4;
+               *left -= 4;
+       }
+
+       wpa_printf(MSG_DEBUG, "SSL: Received packet: Flags 0x%x "
+                  "Message Length %u", flags, tls_msg_len);
+
+       if (data->state == WAIT_FRAG_ACK) {
+               if (*left != 0) {
+                       wpa_printf(MSG_DEBUG, "SSL: Unexpected payload in "
+                                  "WAIT_FRAG_ACK state");
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "SSL: Fragment acknowledged");
+               return 1;
+       }
+
+       if (data->tls_in &&
+           eap_server_tls_process_cont(data, *pos, end - *pos) < 0)
+               return -1;
+               
+       if (flags & EAP_TLS_FLAGS_MORE_FRAGMENTS) {
+               if (eap_server_tls_process_fragment(data, flags, tls_msg_len,
+                                                   *pos, end - *pos) < 0)
+                       return -1;
+
+               data->state = FRAG_ACK;
+               return 1;
+       }
+
+       if (data->state == FRAG_ACK) {
+               wpa_printf(MSG_DEBUG, "SSL: All fragments received");
+               data->state = MSG;
+       }
+
+       if (data->tls_in == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&data->tmpbuf, *pos, end - *pos);
+               data->tls_in = &data->tmpbuf;
+       }
+
+       return 0;
+}
+
+
+static void eap_server_tls_free_in_buf(struct eap_ssl_data *data)
+{
+       if (data->tls_in != &data->tmpbuf)
+               wpabuf_free(data->tls_in);
+       data->tls_in = NULL;
+}
+
+
+struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
+                                      struct eap_ssl_data *data,
+                                      const struct wpabuf *plain)
+{
+       struct wpabuf *buf;
+
+       buf = tls_connection_encrypt(sm->ssl_ctx, data->conn,
+                                    plain);
+       if (buf == NULL) {
+               wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data");
+               return NULL;
+       }
+
+       return buf;
+}
+
+
+int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
+                          struct wpabuf *respData, void *priv, int eap_type,
+                          int (*proc_version)(struct eap_sm *sm, void *priv,
+                                              int peer_version),
+                          void (*proc_msg)(struct eap_sm *sm, void *priv,
+                                           const struct wpabuf *respData))
+{
+       const u8 *pos;
+       u8 flags;
+       size_t left;
+       int ret, res = 0;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData, &left);
+       if (pos == NULL || left < 1)
+               return 0; /* Should not happen - frame already validated */
+       flags = *pos++;
+       left--;
+       wpa_printf(MSG_DEBUG, "SSL: Received packet(len=%lu) - Flags 0x%02x",
+                  (unsigned long) wpabuf_len(respData), flags);
+
+       if (proc_version &&
+           proc_version(sm, priv, flags & EAP_TLS_VERSION_MASK) < 0)
+               return -1;
+
+       ret = eap_server_tls_reassemble(data, flags, &pos, &left);
+       if (ret < 0) {
+               res = -1;
+               goto done;
+       } else if (ret == 1)
+               return 0;
+
+       if (proc_msg)
+               proc_msg(sm, priv, respData);
+
+       if (tls_connection_get_write_alerts(sm->ssl_ctx, data->conn) > 1) {
+               wpa_printf(MSG_INFO, "SSL: Locally detected fatal error in "
+                          "TLS processing");
+               res = -1;
+       }
+
+done:
+       eap_server_tls_free_in_buf(data);
+
+       return res;
+}
diff --git a/src/eap_server/eap_server_tnc.c b/src/eap_server/eap_server_tnc.c
new file mode 100644 (file)
index 0000000..f3b70ed
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * EAP server method: EAP-TNC (Trusted Network Connect)
+ * Copyright (c) 2007-2010, 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 "base64.h"
+#include "eap_i.h"
+#include "tncs.h"
+
+
+struct eap_tnc_data {
+       enum eap_tnc_state {
+               START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
+               FAIL
+       } state;
+       enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
+       struct tncs_data *tncs;
+       struct wpabuf *in_buf;
+       struct wpabuf *out_buf;
+       size_t out_used;
+       size_t fragment_size;
+       unsigned int was_done:1;
+       unsigned int was_fail:1;
+};
+
+
+/* EAP-TNC Flags */
+#define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TNC_FLAGS_START 0x20
+#define EAP_TNC_VERSION_MASK 0x07
+
+#define EAP_TNC_VERSION 1
+
+
+static const char * eap_tnc_state_txt(enum eap_tnc_state state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case CONTINUE:
+               return "CONTINUE";
+       case RECOMMENDATION:
+               return "RECOMMENDATION";
+       case FRAG_ACK:
+               return "FRAG_ACK";
+       case WAIT_FRAG_ACK:
+               return "WAIT_FRAG_ACK";
+       case DONE:
+               return "DONE";
+       case FAIL:
+               return "FAIL";
+       }
+       return "??";
+}
+
+
+static void eap_tnc_set_state(struct eap_tnc_data *data,
+                             enum eap_tnc_state new_state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
+                  eap_tnc_state_txt(data->state),
+                  eap_tnc_state_txt(new_state));
+       data->state = new_state;
+}
+
+
+static void * eap_tnc_init(struct eap_sm *sm)
+{
+       struct eap_tnc_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       eap_tnc_set_state(data, START);
+       data->tncs = tncs_init();
+       if (data->tncs == NULL) {
+               os_free(data);
+               return NULL;
+       }
+
+       data->fragment_size = 1300;
+
+       return data;
+}
+
+
+static void eap_tnc_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_tnc_data *data = priv;
+       wpabuf_free(data->in_buf);
+       wpabuf_free(data->out_buf);
+       tncs_deinit(data->tncs);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
+                                          struct eap_tnc_data *data, u8 id)
+{
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
+                           id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
+                          "request");
+               eap_tnc_set_state(data, FAIL);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
+
+       eap_tnc_set_state(data, CONTINUE);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
+                                    struct eap_tnc_data *data)
+{
+       struct wpabuf *req;
+       u8 *rpos, *rpos1;
+       size_t rlen;
+       char *start_buf, *end_buf;
+       size_t start_len, end_len;
+       size_t imv_len;
+
+       imv_len = tncs_total_send_len(data->tncs);
+
+       start_buf = tncs_if_tnccs_start(data->tncs);
+       if (start_buf == NULL)
+               return NULL;
+       start_len = os_strlen(start_buf);
+       end_buf = tncs_if_tnccs_end();
+       if (end_buf == NULL) {
+               os_free(start_buf);
+               return NULL;
+       }
+       end_len = os_strlen(end_buf);
+
+       rlen = start_len + imv_len + end_len;
+       req = wpabuf_alloc(rlen);
+       if (req == NULL) {
+               os_free(start_buf);
+               os_free(end_buf);
+               return NULL;
+       }
+
+       wpabuf_put_data(req, start_buf, start_len);
+       os_free(start_buf);
+
+       rpos1 = wpabuf_put(req, 0);
+       rpos = tncs_copy_send_buf(data->tncs, rpos1);
+       wpabuf_put(req, rpos - rpos1);
+
+       wpabuf_put_data(req, end_buf, end_len);
+       os_free(end_buf);
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
+                         wpabuf_head(req), wpabuf_len(req));
+
+       return req;
+}
+
+
+static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
+                                                   struct eap_tnc_data *data)
+{
+       switch (data->recommendation) {
+       case ALLOW:
+               eap_tnc_set_state(data, DONE);
+               break;
+       case ISOLATE:
+               eap_tnc_set_state(data, FAIL);
+               /* TODO: support assignment to a different VLAN */
+               break;
+       case NO_ACCESS:
+               eap_tnc_set_state(data, FAIL);
+               break;
+       case NO_RECOMMENDATION:
+               eap_tnc_set_state(data, DONE);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
+               return NULL;
+       }
+
+       return eap_tnc_build(sm, data);
+}
+
+
+static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
+{
+       struct wpabuf *msg;
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
+                          "for fragment ack");
+               return NULL;
+       }
+       wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
+
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
+{
+       struct wpabuf *req;
+       u8 flags;
+       size_t send_len, plen;
+
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
+
+       flags = EAP_TNC_VERSION;
+       send_len = wpabuf_len(data->out_buf) - data->out_used;
+       if (1 + send_len > data->fragment_size) {
+               send_len = data->fragment_size - 1;
+               flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
+               if (data->out_used == 0) {
+                       flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
+                       send_len -= 4;
+               }
+       }
+
+       plen = 1 + send_len;
+       if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+               plen += 4;
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL)
+               return NULL;
+
+       wpabuf_put_u8(req, flags); /* Flags */
+       if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
+               wpabuf_put_be32(req, wpabuf_len(data->out_buf));
+
+       wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+                       send_len);
+       data->out_used += send_len;
+
+       if (data->out_used == wpabuf_len(data->out_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->out_buf);
+               data->out_buf = NULL;
+               data->out_used = 0;
+               if (data->was_fail)
+                       eap_tnc_set_state(data, FAIL);
+               else if (data->was_done)
+                       eap_tnc_set_state(data, DONE);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->out_buf) -
+                          data->out_used);
+               if (data->state == FAIL)
+                       data->was_fail = 1;
+               else if (data->state == DONE)
+                       data->was_done = 1;
+               eap_tnc_set_state(data, WAIT_FRAG_ACK);
+       }
+
+       return req;
+}
+
+
+static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_tnc_data *data = priv;
+
+       switch (data->state) {
+       case START:
+               tncs_init_connection(data->tncs);
+               return eap_tnc_build_start(sm, data, id);
+       case CONTINUE:
+               if (data->out_buf == NULL) {
+                       data->out_buf = eap_tnc_build(sm, data);
+                       if (data->out_buf == NULL) {
+                               wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
+                                          "generate message");
+                               return NULL;
+                       }
+                       data->out_used = 0;
+               }
+               return eap_tnc_build_msg(data, id);
+       case RECOMMENDATION:
+               if (data->out_buf == NULL) {
+                       data->out_buf = eap_tnc_build_recommendation(sm, data);
+                       if (data->out_buf == NULL) {
+                               wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
+                                          "generate recommendation message");
+                               return NULL;
+                       }
+                       data->out_used = 0;
+               }
+               return eap_tnc_build_msg(data, id);
+       case WAIT_FRAG_ACK:
+               return eap_tnc_build_msg(data, id);
+       case FRAG_ACK:
+               return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
+       case DONE:
+       case FAIL:
+               return NULL;
+       }
+
+       return NULL;
+}
+
+
+static Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_tnc_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
+                              &len);
+       if (pos == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
+               return TRUE;
+       }
+
+       if (len == 0 && data->state != WAIT_FRAG_ACK) {
+               wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
+               return TRUE;
+       }
+
+       if (len == 0)
+               return FALSE; /* Fragment ACK does not include flags */
+
+       if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
+                          *pos & EAP_TNC_VERSION_MASK);
+               return TRUE;
+       }
+
+       if (*pos & EAP_TNC_FLAGS_START) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
+{
+       enum tncs_process_res res;
+
+       res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
+                                   wpabuf_len(inbuf));
+       switch (res) {
+       case TNCCS_RECOMMENDATION_ALLOW:
+               wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
+               eap_tnc_set_state(data, RECOMMENDATION);
+               data->recommendation = ALLOW;
+               break;
+       case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
+               wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
+               eap_tnc_set_state(data, RECOMMENDATION);
+               data->recommendation = NO_RECOMMENDATION;
+               break;
+       case TNCCS_RECOMMENDATION_ISOLATE:
+               wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
+               eap_tnc_set_state(data, RECOMMENDATION);
+               data->recommendation = ISOLATE;
+               break;
+       case TNCCS_RECOMMENDATION_NO_ACCESS:
+               wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
+               eap_tnc_set_state(data, RECOMMENDATION);
+               data->recommendation = NO_ACCESS;
+               break;
+       case TNCCS_PROCESS_ERROR:
+               wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
+               eap_tnc_set_state(data, FAIL);
+               break;
+       default:
+               break;
+       }
+}
+
+
+static int eap_tnc_process_cont(struct eap_tnc_data *data,
+                               const u8 *buf, size_t len)
+{
+       /* Process continuation of a pending message */
+       if (len > wpabuf_tailroom(data->in_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
+               eap_tnc_set_state(data, FAIL);
+               return -1;
+       }
+
+       wpabuf_put_data(data->in_buf, buf, len);
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
+                  "bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->in_buf));
+
+       return 0;
+}
+
+
+static int eap_tnc_process_fragment(struct eap_tnc_data *data,
+                                   u8 flags, u32 message_length,
+                                   const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
+                          "fragmented packet");
+               return -1;
+       }
+
+       if (data->in_buf == NULL) {
+               /* First fragment of the message */
+               data->in_buf = wpabuf_alloc(message_length);
+               if (data->in_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
+                                  "message");
+                       return -1;
+               }
+               wpabuf_put_data(data->in_buf, buf, len);
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
+                          "fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->in_buf));
+       }
+
+       return 0;
+}
+
+
+static void eap_tnc_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_tnc_data *data = priv;
+       const u8 *pos, *end;
+       size_t len;
+       u8 flags;
+       u32 message_length = 0;
+       struct wpabuf tmpbuf;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
+       if (pos == NULL)
+               return; /* Should not happen; message already verified */
+
+       end = pos + len;
+
+       if (len == 1 && (data->state == DONE || data->state == FAIL)) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
+                          "message");
+               return;
+       }
+
+       if (len == 0) {
+               /* fragment ack */
+               flags = 0;
+       } else
+               flags = *pos++;
+
+       if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
+               if (end - pos < 4) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
+                       eap_tnc_set_state(data, FAIL);
+                       return;
+               }
+               message_length = WPA_GET_BE32(pos);
+               pos += 4;
+
+               if (message_length < (u32) (end - pos)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
+                                  "Length (%d; %ld remaining in this msg)",
+                                  message_length, (long) (end - pos));
+                       eap_tnc_set_state(data, FAIL);
+                       return;
+               }
+       }
+       wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
+                  "Message Length %u", flags, message_length);
+
+       if (data->state == WAIT_FRAG_ACK) {
+               if (len > 1) {
+                       wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
+                                  "in WAIT_FRAG_ACK state");
+                       eap_tnc_set_state(data, FAIL);
+                       return;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
+               eap_tnc_set_state(data, CONTINUE);
+               return;
+       }
+
+       if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
+               eap_tnc_set_state(data, FAIL);
+               return;
+       }
+               
+       if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
+               if (eap_tnc_process_fragment(data, flags, message_length,
+                                            pos, end - pos) < 0)
+                       eap_tnc_set_state(data, FAIL);
+               else
+                       eap_tnc_set_state(data, FRAG_ACK);
+               return;
+       } else if (data->state == FRAG_ACK) {
+               wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
+               eap_tnc_set_state(data, CONTINUE);
+       }
+
+       if (data->in_buf == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&tmpbuf, pos, end - pos);
+               data->in_buf = &tmpbuf;
+       }
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
+                         wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
+       tncs_process(data, data->in_buf);
+
+       if (data->in_buf != &tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+}
+
+
+static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_tnc_data *data = priv;
+       return data->state == DONE || data->state == FAIL;
+}
+
+
+static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_tnc_data *data = priv;
+       return data->state == DONE;
+}
+
+
+int eap_server_tnc_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_tnc_init;
+       eap->reset = eap_tnc_reset;
+       eap->buildReq = eap_tnc_buildReq;
+       eap->check = eap_tnc_check;
+       eap->process = eap_tnc_process;
+       eap->isDone = eap_tnc_isDone;
+       eap->isSuccess = eap_tnc_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
new file mode 100644 (file)
index 0000000..702c50c
--- /dev/null
@@ -0,0 +1,1430 @@
+/*
+ * hostapd / EAP-TTLS (RFC 5281)
+ * 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 "crypto/ms_funcs.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "eap_server/eap_i.h"
+#include "eap_server/eap_tls_common.h"
+#include "eap_common/chap.h"
+#include "eap_common/eap_ttls.h"
+
+
+/* Maximum supported TTLS version
+ * 0 = RFC 5281
+ * 1 = draft-funk-eap-ttls-v1-00.txt
+ */
+#ifndef EAP_TTLS_VERSION
+#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */
+#endif /* EAP_TTLS_VERSION */
+
+
+#define MSCHAPV2_KEY_LEN 16
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv);
+
+
+struct eap_ttls_data {
+       struct eap_ssl_data ssl;
+       enum {
+               START, PHASE1, PHASE2_START, PHASE2_METHOD,
+               PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE
+       } state;
+
+       int ttls_version;
+       int force_version;
+       const struct eap_method *phase2_method;
+       void *phase2_priv;
+       int mschapv2_resp_ok;
+       u8 mschapv2_auth_response[20];
+       u8 mschapv2_ident;
+       int tls_ia_configured;
+       struct wpabuf *pending_phase2_eap_resp;
+       int tnc_started;
+};
+
+
+static const char * eap_ttls_state_txt(int state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case PHASE1:
+               return "PHASE1";
+       case PHASE2_START:
+               return "PHASE2_START";
+       case PHASE2_METHOD:
+               return "PHASE2_METHOD";
+       case PHASE2_MSCHAPV2_RESP:
+               return "PHASE2_MSCHAPV2_RESP";
+       case PHASE_FINISHED:
+               return "PHASE_FINISHED";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "Unknown?!";
+       }
+}
+
+
+static void eap_ttls_state(struct eap_ttls_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: %s -> %s",
+                  eap_ttls_state_txt(data->state),
+                  eap_ttls_state_txt(state));
+       data->state = state;
+}
+
+
+static u8 * eap_ttls_avp_hdr(u8 *avphdr, u32 avp_code, u32 vendor_id,
+                            int mandatory, size_t len)
+{
+       struct ttls_avp_vendor *avp;
+       u8 flags;
+       size_t hdrlen;
+
+       avp = (struct ttls_avp_vendor *) avphdr;
+       flags = mandatory ? AVP_FLAGS_MANDATORY : 0;
+       if (vendor_id) {
+               flags |= AVP_FLAGS_VENDOR;
+               hdrlen = sizeof(*avp);
+               avp->vendor_id = host_to_be32(vendor_id);
+       } else {
+               hdrlen = sizeof(struct ttls_avp);
+       }
+
+       avp->avp_code = host_to_be32(avp_code);
+       avp->avp_length = host_to_be32((flags << 24) | (hdrlen + len));
+
+       return avphdr + hdrlen;
+}
+
+
+static struct wpabuf * eap_ttls_avp_encapsulate(struct wpabuf *resp,
+                                               u32 avp_code, int mandatory)
+{
+       struct wpabuf *avp;
+       u8 *pos;
+
+       avp = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(resp) + 4);
+       if (avp == NULL) {
+               wpabuf_free(resp);
+               return NULL;
+       }
+
+       pos = eap_ttls_avp_hdr(wpabuf_mhead(avp), avp_code, 0, mandatory,
+                              wpabuf_len(resp));
+       os_memcpy(pos, wpabuf_head(resp), wpabuf_len(resp));
+       pos += wpabuf_len(resp);
+       AVP_PAD((const u8 *) wpabuf_head(avp), pos);
+       wpabuf_free(resp);
+       wpabuf_put(avp, pos - (u8 *) wpabuf_head(avp));
+       return avp;
+}
+
+
+struct eap_ttls_avp {
+        /* Note: eap is allocated memory; caller is responsible for freeing
+         * it. All the other pointers are pointing to the packet data, i.e.,
+         * they must not be freed separately. */
+       u8 *eap;
+       size_t eap_len;
+       u8 *user_name;
+       size_t user_name_len;
+       u8 *user_password;
+       size_t user_password_len;
+       u8 *chap_challenge;
+       size_t chap_challenge_len;
+       u8 *chap_password;
+       size_t chap_password_len;
+       u8 *mschap_challenge;
+       size_t mschap_challenge_len;
+       u8 *mschap_response;
+       size_t mschap_response_len;
+       u8 *mschap2_response;
+       size_t mschap2_response_len;
+};
+
+
+static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse)
+{
+       struct ttls_avp *avp;
+       u8 *pos;
+       int left;
+
+       pos = wpabuf_mhead(buf);
+       left = wpabuf_len(buf);
+       os_memset(parse, 0, sizeof(*parse));
+
+       while (left > 0) {
+               u32 avp_code, avp_length, vendor_id = 0;
+               u8 avp_flags, *dpos;
+               size_t pad, dlen;
+               avp = (struct ttls_avp *) pos;
+               avp_code = be_to_host32(avp->avp_code);
+               avp_length = be_to_host32(avp->avp_length);
+               avp_flags = (avp_length >> 24) & 0xff;
+               avp_length &= 0xffffff;
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP: code=%d flags=0x%02x "
+                          "length=%d", (int) avp_code, avp_flags,
+                          (int) avp_length);
+               if ((int) avp_length > left) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow "
+                                  "(len=%d, left=%d) - dropped",
+                                  (int) avp_length, left);
+                       goto fail;
+               }
+               if (avp_length < sizeof(*avp)) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length "
+                                  "%d", avp_length);
+                       goto fail;
+               }
+               dpos = (u8 *) (avp + 1);
+               dlen = avp_length - sizeof(*avp);
+               if (avp_flags & AVP_FLAGS_VENDOR) {
+                       if (dlen < 4) {
+                               wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP "
+                                          "underflow");
+                               goto fail;
+                       }
+                       vendor_id = be_to_host32(* (be32 *) dpos);
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d",
+                                  (int) vendor_id);
+                       dpos += 4;
+                       dlen -= 4;
+               }
+
+               wpa_hexdump(MSG_DEBUG, "EAP-TTLS: AVP data", dpos, dlen);
+
+               if (vendor_id == 0 && avp_code == RADIUS_ATTR_EAP_MESSAGE) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP - EAP Message");
+                       if (parse->eap == NULL) {
+                               parse->eap = os_malloc(dlen);
+                               if (parse->eap == NULL) {
+                                       wpa_printf(MSG_WARNING, "EAP-TTLS: "
+                                                  "failed to allocate memory "
+                                                  "for Phase 2 EAP data");
+                                       goto fail;
+                               }
+                               os_memcpy(parse->eap, dpos, dlen);
+                               parse->eap_len = dlen;
+                       } else {
+                               u8 *neweap = os_realloc(parse->eap,
+                                                       parse->eap_len + dlen);
+                               if (neweap == NULL) {
+                                       wpa_printf(MSG_WARNING, "EAP-TTLS: "
+                                                  "failed to allocate memory "
+                                                  "for Phase 2 EAP data");
+                                       goto fail;
+                               }
+                               os_memcpy(neweap + parse->eap_len, dpos, dlen);
+                               parse->eap = neweap;
+                               parse->eap_len += dlen;
+                       }
+               } else if (vendor_id == 0 &&
+                          avp_code == RADIUS_ATTR_USER_NAME) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "EAP-TTLS: User-Name",
+                                         dpos, dlen);
+                       parse->user_name = dpos;
+                       parse->user_name_len = dlen;
+               } else if (vendor_id == 0 &&
+                          avp_code == RADIUS_ATTR_USER_PASSWORD) {
+                       u8 *password = dpos;
+                       size_t password_len = dlen;
+                       while (password_len > 0 &&
+                              password[password_len - 1] == '\0') {
+                               password_len--;
+                       }
+                       wpa_hexdump_ascii_key(MSG_DEBUG, "EAP-TTLS: "
+                                             "User-Password (PAP)",
+                                             password, password_len);
+                       parse->user_password = password;
+                       parse->user_password_len = password_len;
+               } else if (vendor_id == 0 &&
+                          avp_code == RADIUS_ATTR_CHAP_CHALLENGE) {
+                       wpa_hexdump(MSG_DEBUG,
+                                   "EAP-TTLS: CHAP-Challenge (CHAP)",
+                                   dpos, dlen);
+                       parse->chap_challenge = dpos;
+                       parse->chap_challenge_len = dlen;
+               } else if (vendor_id == 0 &&
+                          avp_code == RADIUS_ATTR_CHAP_PASSWORD) {
+                       wpa_hexdump(MSG_DEBUG,
+                                   "EAP-TTLS: CHAP-Password (CHAP)",
+                                   dpos, dlen);
+                       parse->chap_password = dpos;
+                       parse->chap_password_len = dlen;
+               } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+                          avp_code == RADIUS_ATTR_MS_CHAP_CHALLENGE) {
+                       wpa_hexdump(MSG_DEBUG,
+                                   "EAP-TTLS: MS-CHAP-Challenge",
+                                   dpos, dlen);
+                       parse->mschap_challenge = dpos;
+                       parse->mschap_challenge_len = dlen;
+               } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+                          avp_code == RADIUS_ATTR_MS_CHAP_RESPONSE) {
+                       wpa_hexdump(MSG_DEBUG,
+                                   "EAP-TTLS: MS-CHAP-Response (MSCHAP)",
+                                   dpos, dlen);
+                       parse->mschap_response = dpos;
+                       parse->mschap_response_len = dlen;
+               } else if (vendor_id == RADIUS_VENDOR_ID_MICROSOFT &&
+                          avp_code == RADIUS_ATTR_MS_CHAP2_RESPONSE) {
+                       wpa_hexdump(MSG_DEBUG,
+                                   "EAP-TTLS: MS-CHAP2-Response (MSCHAPV2)",
+                                   dpos, dlen);
+                       parse->mschap2_response = dpos;
+                       parse->mschap2_response_len = dlen;
+               } else if (avp_flags & AVP_FLAGS_MANDATORY) {
+                       wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported "
+                                  "mandatory AVP code %d vendor_id %d - "
+                                  "dropped", (int) avp_code, (int) vendor_id);
+                       goto fail;
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported "
+                                  "AVP code %d vendor_id %d",
+                                  (int) avp_code, (int) vendor_id);
+               }
+
+               pad = (4 - (avp_length & 3)) & 3;
+               pos += avp_length + pad;
+               left -= avp_length + pad;
+       }
+
+       return 0;
+
+fail:
+       os_free(parse->eap);
+       parse->eap = NULL;
+       return -1;
+}
+
+
+static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
+                                       struct eap_ttls_data *data, size_t len)
+{
+       struct tls_keys keys;
+       u8 *challenge, *rnd;
+
+       if (data->ttls_version == 0) {
+               return eap_server_tls_derive_key(sm, &data->ssl,
+                                                "ttls challenge", len);
+       }
+
+       os_memset(&keys, 0, sizeof(keys));
+       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+           keys.client_random == NULL || keys.server_random == NULL ||
+           keys.inner_secret == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+                          "client random, or server random to derive "
+                          "implicit challenge");
+               return NULL;
+       }
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       challenge = os_malloc(len);
+       if (rnd == NULL || challenge == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit "
+                          "challenge derivation");
+               os_free(rnd);
+               os_free(challenge);
+               return NULL;
+       }
+       os_memcpy(rnd, keys.server_random, keys.server_random_len);
+       os_memcpy(rnd + keys.server_random_len, keys.client_random,
+                 keys.client_random_len);
+
+       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+                   "inner application challenge", rnd,
+                   keys.client_random_len + keys.server_random_len,
+                   challenge, len)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive implicit "
+                          "challenge");
+               os_free(rnd);
+               os_free(challenge);
+               return NULL;
+       }
+
+       os_free(rnd);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge",
+                       challenge, len);
+
+       return challenge;
+}
+
+
+static void * eap_ttls_init(struct eap_sm *sm)
+{
+       struct eap_ttls_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->ttls_version = EAP_TTLS_VERSION;
+       data->force_version = -1;
+       if (sm->user && sm->user->force_version >= 0) {
+               data->force_version = sm->user->force_version;
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: forcing version %d",
+                          data->force_version);
+               data->ttls_version = data->force_version;
+       }
+       data->state = START;
+
+       if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) &&
+           data->ttls_version > 0) {
+               if (data->force_version > 0) {
+                       wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and "
+                                  "TLS library does not support TLS/IA.",
+                                  data->force_version);
+                       eap_ttls_reset(sm, data);
+                       return NULL;
+               }
+               data->ttls_version = 0;
+       }
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL.");
+               eap_ttls_reset(sm, data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static void eap_ttls_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       if (data == NULL)
+               return;
+       if (data->phase2_priv && data->phase2_method)
+               data->phase2_method->reset(sm, data->phase2_priv);
+       eap_server_tls_ssl_deinit(sm, &data->ssl);
+       wpabuf_free(data->pending_phase2_eap_resp);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_ttls_build_start(struct eap_sm *sm,
+                                           struct eap_ttls_data *data, u8 id)
+{      
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-TTLS: Failed to allocate memory for"
+                          " request");
+               eap_ttls_state(data, FAILURE);
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, EAP_TLS_FLAGS_START | data->ttls_version);
+
+       eap_ttls_state(data, PHASE1);
+
+       return req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase2_eap_req(
+       struct eap_sm *sm, struct eap_ttls_data *data, u8 id)
+{
+       struct wpabuf *buf, *encr_req;
+
+
+       buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
+       if (buf == NULL)
+               return NULL;
+
+       wpa_hexdump_buf_key(MSG_DEBUG,
+                           "EAP-TTLS/EAP: Encapsulate Phase 2 data", buf);
+
+       buf = eap_ttls_avp_encapsulate(buf, RADIUS_ATTR_EAP_MESSAGE, 1);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Failed to encapsulate "
+                          "packet");
+               return NULL;
+       }
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated "
+                           "Phase 2 data", buf);
+
+       encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf);
+       wpabuf_free(buf);
+
+       return encr_req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase2_mschapv2(
+       struct eap_sm *sm, struct eap_ttls_data *data)
+{
+       struct wpabuf *encr_req, msgbuf;
+       u8 *req, *pos, *end;
+       int ret;
+
+       pos = req = os_malloc(100);
+       if (req == NULL)
+               return NULL;
+       end = req + 100;
+
+       if (data->mschapv2_resp_ok) {
+               pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS,
+                                      RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
+               *pos++ = data->mschapv2_ident;
+               ret = os_snprintf((char *) pos, end - pos, "S=");
+               if (ret >= 0 && ret < end - pos)
+                       pos += ret;
+               pos += wpa_snprintf_hex_uppercase(
+                       (char *) pos, end - pos, data->mschapv2_auth_response,
+                       sizeof(data->mschapv2_auth_response));
+       } else {
+               pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR,
+                                      RADIUS_VENDOR_ID_MICROSOFT, 1, 6);
+               os_memcpy(pos, "Failed", 6);
+               pos += 6;
+               AVP_PAD(req, pos);
+       }
+
+       wpabuf_set(&msgbuf, req, pos - req);
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 "
+                           "data", &msgbuf);
+
+       encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf);
+       os_free(req);
+
+       return encr_req;
+}
+
+
+static struct wpabuf * eap_ttls_build_phase_finished(
+       struct eap_sm *sm, struct eap_ttls_data *data, int final)
+{
+       return tls_connection_ia_send_phase_finished(sm->ssl_ctx,
+                                                    data->ssl.conn, final);
+}
+
+
+static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_ttls_data *data = priv;
+
+       if (data->ssl.state == FRAG_ACK) {
+               return eap_server_tls_build_ack(id, EAP_TYPE_TTLS,
+                                               data->ttls_version);
+       }
+
+       if (data->ssl.state == WAIT_FRAG_ACK) {
+               return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+                                               data->ttls_version, id);
+       }
+
+       switch (data->state) {
+       case START:
+               return eap_ttls_build_start(sm, data, id);
+       case PHASE1:
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase1 done, "
+                                  "starting Phase2");
+                       eap_ttls_state(data, PHASE2_START);
+               }
+               break;
+       case PHASE2_METHOD:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data,
+                                                                 id);
+               break;
+       case PHASE2_MSCHAPV2_RESP:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data);
+               break;
+       case PHASE_FINISHED:
+               wpabuf_free(data->ssl.tls_out);
+               data->ssl.tls_out_pos = 0;
+               data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+                          __func__, data->state);
+               return NULL;
+       }
+
+       return eap_server_tls_build_msg(&data->ssl, EAP_TYPE_TTLS,
+                                       data->ttls_version, id);
+}
+
+
+static Boolean eap_ttls_check(struct eap_sm *sm, void *priv,
+                             struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TTLS, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static int eap_ttls_ia_permute_inner_secret(struct eap_sm *sm,
+                                           struct eap_ttls_data *data,
+                                           const u8 *key, size_t key_len)
+{
+       u8 *buf;
+       size_t buf_len;
+       int ret;
+
+       if (key) {
+               buf_len = 2 + key_len;
+               buf = os_malloc(buf_len);
+               if (buf == NULL)
+                       return -1;
+               WPA_PUT_BE16(buf, key_len);
+               os_memcpy(buf + 2, key, key_len);
+       } else {
+               buf = NULL;
+               buf_len = 0;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Session keys for TLS/IA inner "
+                       "secret permutation", buf, buf_len);
+       ret = tls_connection_ia_permute_inner_secret(sm->ssl_ctx,
+                                                    data->ssl.conn,
+                                                    buf, buf_len);
+       os_free(buf);
+
+       return ret;
+}
+
+
+static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
+                                       struct eap_ttls_data *data,
+                                       const u8 *user_password,
+                                       size_t user_password_len)
+{
+       if (!sm->user || !sm->user->password || sm->user->password_hash ||
+           !(sm->user->ttls_auth & EAP_TTLS_AUTH_PAP)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user "
+                          "password configured");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (sm->user->password_len != user_password_len ||
+           os_memcmp(sm->user->password, user_password, user_password_len) !=
+           0) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password");
+       eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
+                      SUCCESS);
+}
+
+
+static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
+                                        struct eap_ttls_data *data,
+                                        const u8 *challenge,
+                                        size_t challenge_len,
+                                        const u8 *password,
+                                        size_t password_len)
+{
+       u8 *chal, hash[CHAP_MD5_LEN];
+
+       if (challenge == NULL || password == NULL ||
+           challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN ||
+           password_len != 1 + EAP_TTLS_CHAP_PASSWORD_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid CHAP attributes "
+                          "(challenge len %lu password len %lu)",
+                          (unsigned long) challenge_len,
+                          (unsigned long) password_len);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (!sm->user || !sm->user->password || sm->user->password_hash ||
+           !(sm->user->ttls_auth & EAP_TTLS_AUTH_CHAP)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user "
+                          "password configured");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       chal = eap_ttls_implicit_challenge(sm, data,
+                                          EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
+       if (chal == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate "
+                          "challenge from TLS data");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 ||
+           password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch");
+               os_free(chal);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+       os_free(chal);
+
+       /* MD5(Ident + Password + Challenge) */
+       chap_md5(password[0], sm->user->password, sm->user->password_len,
+                challenge, challenge_len, hash);
+
+       if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
+               eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
+                              SUCCESS);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password");
+               eap_ttls_state(data, FAILURE);
+       }
+}
+
+
+static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
+                                          struct eap_ttls_data *data,
+                                          u8 *challenge, size_t challenge_len,
+                                          u8 *response, size_t response_len)
+{
+       u8 *chal, nt_response[24];
+
+       if (challenge == NULL || response == NULL ||
+           challenge_len != EAP_TTLS_MSCHAP_CHALLENGE_LEN ||
+           response_len != EAP_TTLS_MSCHAP_RESPONSE_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid MS-CHAP "
+                          "attributes (challenge len %lu response len %lu)",
+                          (unsigned long) challenge_len,
+                          (unsigned long) response_len);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (!sm->user || !sm->user->password ||
+           !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAP)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: No user password "
+                          "configured");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       chal = eap_ttls_implicit_challenge(sm, data,
+                                          EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
+       if (chal == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate "
+                          "challenge from TLS data");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 ||
+           response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch");
+               os_free(chal);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+       os_free(chal);
+
+       if (sm->user->password_hash)
+               challenge_response(challenge, sm->user->password, nt_response);
+       else
+               nt_challenge_response(challenge, sm->user->password,
+                                     sm->user->password_len, nt_response);
+
+       if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
+               eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED :
+                              SUCCESS);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received",
+                           response + 2 + 24, 24);
+               wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Expected",
+                           nt_response, 24);
+               eap_ttls_state(data, FAILURE);
+       }
+}
+
+
+static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
+                                            struct eap_ttls_data *data,
+                                            u8 *challenge,
+                                            size_t challenge_len,
+                                            u8 *response, size_t response_len)
+{
+       u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge,
+               *auth_challenge;
+       size_t username_len, i;
+
+       if (challenge == NULL || response == NULL ||
+           challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN ||
+           response_len != EAP_TTLS_MSCHAPV2_RESPONSE_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid MS-CHAP2 "
+                          "attributes (challenge len %lu response len %lu)",
+                          (unsigned long) challenge_len,
+                          (unsigned long) response_len);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (!sm->user || !sm->user->password ||
+           !(sm->user->ttls_auth & EAP_TTLS_AUTH_MSCHAPV2)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: No user password "
+                          "configured");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       /* MSCHAPv2 does not include optional domain name in the
+        * challenge-response calculation, so remove domain prefix
+        * (if present). */
+       username = sm->identity;
+       username_len = sm->identity_len;
+       for (i = 0; i < username_len; i++) {
+               if (username[i] == '\\') {
+                       username_len -= i + 1;
+                       username += i + 1;
+                       break;
+               }
+       }
+
+       chal = eap_ttls_implicit_challenge(
+               sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
+       if (chal == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate "
+                          "challenge from TLS data");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 ||
+           response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch");
+               os_free(chal);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+       os_free(chal);
+
+       auth_challenge = challenge;
+       peer_challenge = response + 2;
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: User",
+                         username, username_len);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: auth_challenge",
+                   auth_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+       wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge",
+                   peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN);
+
+       if (sm->user->password_hash) {
+               generate_nt_response_pwhash(auth_challenge, peer_challenge,
+                                           username, username_len,
+                                           sm->user->password,
+                                           nt_response);
+       } else {
+               generate_nt_response(auth_challenge, peer_challenge,
+                                    username, username_len,
+                                    sm->user->password,
+                                    sm->user->password_len,
+                                    nt_response);
+       }
+
+       rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
+       if (os_memcmp(nt_response, rx_resp, 24) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
+                          "NT-Response");
+               data->mschapv2_resp_ok = 1;
+               if (data->ttls_version > 0) {
+                       const u8 *pw_hash;
+                       u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16];
+                       u8 session_key[2 * MSCHAPV2_KEY_LEN];
+
+                       if (sm->user->password_hash)
+                               pw_hash = sm->user->password;
+                       else {
+                               nt_password_hash(sm->user->password,
+                                                sm->user->password_len,
+                                                pw_hash_buf);
+                               pw_hash = pw_hash_buf;
+                       }
+                       hash_nt_password_hash(pw_hash, pw_hash_hash);
+                       get_master_key(pw_hash_hash, nt_response, master_key);
+                       get_asymetric_start_key(master_key, session_key,
+                                               MSCHAPV2_KEY_LEN, 0, 0);
+                       get_asymetric_start_key(master_key,
+                                               session_key + MSCHAPV2_KEY_LEN,
+                                               MSCHAPV2_KEY_LEN, 1, 0);
+                       eap_ttls_ia_permute_inner_secret(sm, data,
+                                                        session_key,
+                                                        sizeof(session_key));
+               }
+
+               if (sm->user->password_hash) {
+                       generate_authenticator_response_pwhash(
+                               sm->user->password,
+                               peer_challenge, auth_challenge,
+                               username, username_len, nt_response,
+                               data->mschapv2_auth_response);
+               } else {
+                       generate_authenticator_response(
+                               sm->user->password, sm->user->password_len,
+                               peer_challenge, auth_challenge,
+                               username, username_len, nt_response,
+                               data->mschapv2_auth_response);
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid "
+                          "NT-Response");
+               wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Received",
+                           rx_resp, 24);
+               wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: Expected",
+                           nt_response, 24);
+               data->mschapv2_resp_ok = 0;
+       }
+       eap_ttls_state(data, PHASE2_MSCHAPV2_RESP);
+       data->mschapv2_ident = response[0];
+}
+
+
+static int eap_ttls_phase2_eap_init(struct eap_sm *sm,
+                                   struct eap_ttls_data *data,
+                                   EapType eap_type)
+{
+       if (data->phase2_priv && data->phase2_method) {
+               data->phase2_method->reset(sm, data->phase2_priv);
+               data->phase2_method = NULL;
+               data->phase2_priv = NULL;
+       }
+       data->phase2_method = eap_server_get_eap_method(EAP_VENDOR_IETF,
+                                                       eap_type);
+       if (!data->phase2_method)
+               return -1;
+
+       sm->init_phase2 = 1;
+       data->phase2_priv = data->phase2_method->init(sm);
+       sm->init_phase2 = 0;
+       return data->phase2_priv == NULL ? -1 : 0;
+}
+
+
+static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm,
+                                                struct eap_ttls_data *data,
+                                                u8 *in_data, size_t in_len)
+{
+       u8 next_type = EAP_TYPE_NONE;
+       struct eap_hdr *hdr;
+       u8 *pos;
+       size_t left;
+       struct wpabuf buf;
+       const struct eap_method *m = data->phase2_method;
+       void *priv = data->phase2_priv;
+
+       if (priv == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not "
+                          "initialized?!", __func__);
+               return;
+       }
+
+       hdr = (struct eap_hdr *) in_data;
+       pos = (u8 *) (hdr + 1);
+
+       if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) {
+               left = in_len - sizeof(*hdr);
+               wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; "
+                           "allowed types", pos + 1, left - 1);
+               eap_sm_process_nak(sm, pos + 1, left - 1);
+               if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS &&
+                   sm->user->methods[sm->user_eap_method_index].method !=
+                   EAP_TYPE_NONE) {
+                       next_type = sm->user->methods[
+                               sm->user_eap_method_index++].method;
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d",
+                                  next_type);
+                       if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+                               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to "
+                                          "initialize EAP type %d",
+                                          next_type);
+                               eap_ttls_state(data, FAILURE);
+                               return;
+                       }
+               } else {
+                       eap_ttls_state(data, FAILURE);
+               }
+               return;
+       }
+
+       wpabuf_set(&buf, in_data, in_len);
+
+       if (m->check(sm, priv, &buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to "
+                          "ignore the packet");
+               return;
+       }
+
+       m->process(sm, priv, &buf);
+
+       if (sm->method_pending == METHOD_PENDING_WAIT) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method is in "
+                          "pending wait state - save decrypted response");
+               wpabuf_free(data->pending_phase2_eap_resp);
+               data->pending_phase2_eap_resp = wpabuf_dup(&buf);
+       }
+
+       if (!m->isDone(sm, priv))
+               return;
+
+       if (!m->isSuccess(sm, priv)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       switch (data->state) {
+       case PHASE2_START:
+               if (eap_user_get(sm, sm->identity, sm->identity_len, 1) != 0) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "EAP_TTLS: Phase2 "
+                                         "Identity not found in the user "
+                                         "database",
+                                         sm->identity, sm->identity_len);
+                       eap_ttls_state(data, FAILURE);
+                       break;
+               }
+
+               eap_ttls_state(data, PHASE2_METHOD);
+               next_type = sm->user->methods[0].method;
+               sm->user_eap_method_index = 1;
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type);
+               if (eap_ttls_phase2_eap_init(sm, data, next_type)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize "
+                                  "EAP type %d", next_type);
+                       eap_ttls_state(data, FAILURE);
+               }
+               break;
+       case PHASE2_METHOD:
+               if (data->ttls_version > 0) {
+                       if (m->getKey) {
+                               u8 *key;
+                               size_t key_len;
+                               key = m->getKey(sm, priv, &key_len);
+                               eap_ttls_ia_permute_inner_secret(sm, data,
+                                                                key, key_len);
+                       }
+                       eap_ttls_state(data, PHASE_FINISHED);
+               } else
+                       eap_ttls_state(data, SUCCESS);
+               break;
+       case FAILURE:
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d",
+                          __func__, data->state);
+               break;
+       }
+}
+
+
+static void eap_ttls_process_phase2_eap(struct eap_sm *sm,
+                                       struct eap_ttls_data *data,
+                                       const u8 *eap, size_t eap_len)
+{
+       struct eap_hdr *hdr;
+       size_t len;
+
+       if (data->state == PHASE2_START) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: initializing Phase 2");
+               if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_IDENTITY) < 0)
+               {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: failed to "
+                                  "initialize EAP-Identity");
+                       return;
+               }
+       }
+
+       if (eap_len < sizeof(*hdr)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: too short Phase 2 EAP "
+                          "packet (len=%lu)", (unsigned long) eap_len);
+               return;
+       }
+
+       hdr = (struct eap_hdr *) eap;
+       len = be_to_host16(hdr->length);
+       wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: received Phase 2 EAP: code=%d "
+                  "identifier=%d length=%lu", hdr->code, hdr->identifier,
+                  (unsigned long) len);
+       if (len > eap_len) {
+               wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Length mismatch in Phase 2"
+                          " EAP frame (hdr len=%lu, data len in AVP=%lu)",
+                          (unsigned long) len, (unsigned long) eap_len);
+               return;
+       }
+
+       switch (hdr->code) {
+       case EAP_CODE_RESPONSE:
+               eap_ttls_process_phase2_eap_response(sm, data, (u8 *) hdr,
+                                                    len);
+               break;
+       default:
+               wpa_printf(MSG_INFO, "EAP-TTLS/EAP: Unexpected code=%d in "
+                          "Phase 2 EAP header", hdr->code);
+               break;
+       }
+}
+
+
+static void eap_ttls_process_phase2(struct eap_sm *sm,
+                                   struct eap_ttls_data *data,
+                                   struct wpabuf *in_buf)
+{
+       struct wpabuf *in_decrypted;
+       struct eap_ttls_avp parse;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for"
+                  " Phase 2", (unsigned long) wpabuf_len(in_buf));
+
+       if (data->pending_phase2_eap_resp) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response "
+                          "- skip decryption and use old data");
+               eap_ttls_process_phase2_eap(
+                       sm, data, wpabuf_head(data->pending_phase2_eap_resp),
+                       wpabuf_len(data->pending_phase2_eap_resp));
+               wpabuf_free(data->pending_phase2_eap_resp);
+               data->pending_phase2_eap_resp = NULL;
+               return;
+       }
+
+       in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn,
+                                             in_buf);
+       if (in_decrypted == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 "
+                          "data");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (data->state == PHASE_FINISHED) {
+               if (wpabuf_len(in_decrypted) == 0 &&
+                   tls_connection_ia_final_phase_finished(sm->ssl_ctx,
+                                                          data->ssl.conn)) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished "
+                                  "received");
+                       eap_ttls_state(data, SUCCESS);
+               } else {
+                       wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid "
+                                  "FinalPhaseFinished");
+                       eap_ttls_state(data, FAILURE);
+               }
+
+               wpabuf_free(in_decrypted);
+               return;
+       }
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP",
+                           in_decrypted);
+
+       if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs");
+               wpabuf_free(in_decrypted);
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       if (parse.user_name) {
+               os_free(sm->identity);
+               sm->identity = os_malloc(parse.user_name_len);
+               if (sm->identity) {
+                       os_memcpy(sm->identity, parse.user_name,
+                                 parse.user_name_len);
+                       sm->identity_len = parse.user_name_len;
+               }
+               if (eap_user_get(sm, parse.user_name, parse.user_name_len, 1)
+                   != 0) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS: Phase2 Identity not "
+                                  "found in the user database");
+                       eap_ttls_state(data, FAILURE);
+                       goto done;
+               }
+       }
+
+#ifdef EAP_SERVER_TNC
+       if (data->tnc_started && parse.eap == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP "
+                          "response from peer");
+               eap_ttls_state(data, FAILURE);
+               goto done;
+       }
+#endif /* EAP_SERVER_TNC */
+
+       if (parse.eap) {
+               eap_ttls_process_phase2_eap(sm, data, parse.eap,
+                                           parse.eap_len);
+       } else if (parse.user_password) {
+               eap_ttls_process_phase2_pap(sm, data, parse.user_password,
+                                           parse.user_password_len);
+       } else if (parse.chap_password) {
+               eap_ttls_process_phase2_chap(sm, data,
+                                            parse.chap_challenge,
+                                            parse.chap_challenge_len,
+                                            parse.chap_password,
+                                            parse.chap_password_len);
+       } else if (parse.mschap_response) {
+               eap_ttls_process_phase2_mschap(sm, data,
+                                              parse.mschap_challenge,
+                                              parse.mschap_challenge_len,
+                                              parse.mschap_response,
+                                              parse.mschap_response_len);
+       } else if (parse.mschap2_response) {
+               eap_ttls_process_phase2_mschapv2(sm, data,
+                                                parse.mschap_challenge,
+                                                parse.mschap_challenge_len,
+                                                parse.mschap2_response,
+                                                parse.mschap2_response_len);
+       }
+
+done:
+       wpabuf_free(in_decrypted);
+       os_free(parse.eap);
+}
+
+
+static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data)
+{
+#ifdef EAP_SERVER_TNC
+       if (!sm->tnc || data->state != SUCCESS || data->tnc_started)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-TTLS: Initialize TNC");
+       if (eap_ttls_phase2_eap_init(sm, data, EAP_TYPE_TNC)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to initialize TNC");
+               eap_ttls_state(data, FAILURE);
+               return;
+       }
+
+       data->tnc_started = 1;
+       eap_ttls_state(data, PHASE2_METHOD);
+#endif /* EAP_SERVER_TNC */
+}
+
+
+static int eap_ttls_process_version(struct eap_sm *sm, void *priv,
+                                   int peer_version)
+{
+       struct eap_ttls_data *data = priv;
+       if (peer_version < data->ttls_version) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: peer ver=%d, own ver=%d; "
+                          "use version %d",
+                          peer_version, data->ttls_version, peer_version);
+               data->ttls_version = peer_version;
+       }
+
+       if (data->ttls_version > 0 && !data->tls_ia_configured) {
+               if (tls_connection_set_ia(sm->ssl_ctx, data->ssl.conn, 1)) {
+                       wpa_printf(MSG_INFO, "EAP-TTLS: Failed to enable "
+                                  "TLS/IA");
+                       return -1;
+               }
+               data->tls_ia_configured = 1;
+       }
+
+       return 0;
+}
+
+
+static void eap_ttls_process_msg(struct eap_sm *sm, void *priv,
+                                const struct wpabuf *respData)
+{
+       struct eap_ttls_data *data = priv;
+
+       switch (data->state) {
+       case PHASE1:
+               if (eap_server_tls_phase1(sm, &data->ssl) < 0)
+                       eap_ttls_state(data, FAILURE);
+               break;
+       case PHASE2_START:
+       case PHASE2_METHOD:
+       case PHASE_FINISHED:
+               eap_ttls_process_phase2(sm, data, data->ssl.tls_in);
+               eap_ttls_start_tnc(sm, data);
+               break;
+       case PHASE2_MSCHAPV2_RESP:
+               if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) ==
+                   0) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+                                  "acknowledged response");
+                       eap_ttls_state(data, data->ttls_version > 0 ?
+                                      PHASE_FINISHED : SUCCESS);
+               } else if (!data->mschapv2_resp_ok) {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer "
+                                  "acknowledged error");
+                       eap_ttls_state(data, FAILURE);
+               } else {
+                       wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Unexpected "
+                                  "frame from peer (payload len %lu, "
+                                  "expected empty frame)",
+                                  (unsigned long)
+                                  wpabuf_len(data->ssl.tls_in));
+                       eap_ttls_state(data, FAILURE);
+               }
+               eap_ttls_start_tnc(sm, data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Unexpected state %d in %s",
+                          data->state, __func__);
+               break;
+       }
+}
+
+
+static void eap_ttls_process(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_ttls_data *data = priv;
+       if (eap_server_tls_process(sm, &data->ssl, respData, data,
+                                  EAP_TYPE_TTLS, eap_ttls_process_version,
+                                  eap_ttls_process_msg) < 0)
+               eap_ttls_state(data, FAILURE);
+}
+
+
+static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm,
+                                  struct eap_ttls_data *data)
+{
+       struct tls_keys keys;
+       u8 *rnd, *key;
+
+       os_memset(&keys, 0, sizeof(keys));
+       if (tls_connection_get_keys(sm->ssl_ctx, data->ssl.conn, &keys) ||
+           keys.client_random == NULL || keys.server_random == NULL ||
+           keys.inner_secret == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: Could not get inner secret, "
+                          "client random, or server random to derive keying "
+                          "material");
+               return NULL;
+       }
+
+       rnd = os_malloc(keys.client_random_len + keys.server_random_len);
+       key = os_malloc(EAP_TLS_KEY_LEN);
+       if (rnd == NULL || key == NULL) {
+               wpa_printf(MSG_INFO, "EAP-TTLS: No memory for key derivation");
+               os_free(rnd);
+               os_free(key);
+               return NULL;
+       }
+       os_memcpy(rnd, keys.client_random, keys.client_random_len);
+       os_memcpy(rnd + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       if (tls_prf(keys.inner_secret, keys.inner_secret_len,
+                   "ttls v1 keying material", rnd, keys.client_random_len +
+                   keys.server_random_len, key, EAP_TLS_KEY_LEN)) {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+               os_free(rnd);
+               os_free(key);
+               return NULL;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-TTLS: client/server random",
+                   rnd, keys.client_random_len + keys.server_random_len);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: TLS/IA inner secret",
+                       keys.inner_secret, keys.inner_secret_len);
+
+       os_free(rnd);
+
+       return key;
+}
+
+
+static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+       u8 *eapKeyData;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       if (data->ttls_version == 0) {
+               eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+                                                      "ttls keying material",
+                                                      EAP_TLS_KEY_LEN);
+       } else {
+               eapKeyData = eap_ttls_v1_derive_key(sm, data);
+       }
+
+       if (eapKeyData) {
+               *len = EAP_TLS_KEY_LEN;
+               wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
+                               eapKeyData, EAP_TLS_KEY_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key");
+       }
+
+       return eapKeyData;
+}
+
+
+static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_ttls_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_ttls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_ttls_init;
+       eap->reset = eap_ttls_reset;
+       eap->buildReq = eap_ttls_buildReq;
+       eap->check = eap_ttls_check;
+       eap->process = eap_ttls_process;
+       eap->isDone = eap_ttls_isDone;
+       eap->getKey = eap_ttls_getKey;
+       eap->isSuccess = eap_ttls_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_vendor_test.c b/src/eap_server/eap_server_vendor_test.c
new file mode 100644 (file)
index 0000000..0dd0aca
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * hostapd / Test method for vendor specific (expanded) EAP type
+ * Copyright (c) 2005-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 "eap_i.h"
+
+
+#define EAP_VENDOR_ID 0xfffefd
+#define EAP_VENDOR_TYPE 0xfcfbfaf9
+
+
+struct eap_vendor_test_data {
+       enum { INIT, CONFIRM, SUCCESS, FAILURE } state;
+};
+
+
+static const char * eap_vendor_test_state_txt(int state)
+{
+       switch (state) {
+       case INIT:
+               return "INIT";
+       case CONFIRM:
+               return "CONFIRM";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_vendor_test_state(struct eap_vendor_test_data *data,
+                                 int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s",
+                  eap_vendor_test_state_txt(data->state),
+                  eap_vendor_test_state_txt(state));
+       data->state = state;
+}
+
+
+static void * eap_vendor_test_init(struct eap_sm *sm)
+{
+       struct eap_vendor_test_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = INIT;
+
+       return data;
+}
+
+
+static void eap_vendor_test_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_vendor_test_data *data = priv;
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv,
+                                               u8 id)
+{
+       struct eap_vendor_test_data *data = priv;
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_ID, EAP_VENDOR_TYPE, 1,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-VENDOR-TEST: Failed to allocate "
+                          "memory for request");
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, data->state == INIT ? 1 : 3);
+
+       return req;
+}
+
+
+static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv,
+                                    struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-VENDOR-TEST: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static void eap_vendor_test_process(struct eap_sm *sm, void *priv,
+                                   struct wpabuf *respData)
+{
+       struct eap_vendor_test_data *data = priv;
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_ID, EAP_VENDOR_TYPE, respData, &len);
+       if (pos == NULL || len < 1)
+               return;
+
+       if (data->state == INIT) {
+               if (*pos == 2)
+                       eap_vendor_test_state(data, CONFIRM);
+               else
+                       eap_vendor_test_state(data, FAILURE);
+       } else if (data->state == CONFIRM) {
+               if (*pos == 4)
+                       eap_vendor_test_state(data, SUCCESS);
+               else
+                       eap_vendor_test_state(data, FAILURE);
+       } else
+               eap_vendor_test_state(data, FAILURE);
+}
+
+
+static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_vendor_test_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_vendor_test_data *data = priv;
+       u8 *key;
+       const int key_len = 64;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(key_len);
+       if (key == NULL)
+               return NULL;
+
+       os_memset(key, 0x11, key_len / 2);
+       os_memset(key + key_len / 2, 0x22, key_len / 2);
+       *len = key_len;
+
+       return key;
+}
+
+
+static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_vendor_test_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_vendor_test_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_ID, EAP_VENDOR_TYPE,
+                                     "VENDOR-TEST");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_vendor_test_init;
+       eap->reset = eap_vendor_test_reset;
+       eap->buildReq = eap_vendor_test_buildReq;
+       eap->check = eap_vendor_test_check;
+       eap->process = eap_vendor_test_process;
+       eap->isDone = eap_vendor_test_isDone;
+       eap->getKey = eap_vendor_test_getKey;
+       eap->isSuccess = eap_vendor_test_isSuccess;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_server_wsc.c b/src/eap_server/eap_server_wsc.c
new file mode 100644 (file)
index 0000000..77cf9e2
--- /dev/null
@@ -0,0 +1,509 @@
+/*
+ * EAP-WSC server for Wi-Fi Protected Setup
+ * Copyright (c) 2007-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 "eap_i.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+
+
+struct eap_wsc_data {
+       enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state;
+       int registrar;
+       struct wpabuf *in_buf;
+       struct wpabuf *out_buf;
+       enum wsc_op_code in_op_code, out_op_code;
+       size_t out_used;
+       size_t fragment_size;
+       struct wps_data *wps;
+       int ext_reg_timeout;
+};
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static const char * eap_wsc_state_txt(int state)
+{
+       switch (state) {
+       case START:
+               return "START";
+       case MESG:
+               return "MESG";
+       case FRAG_ACK:
+               return "FRAG_ACK";
+       case WAIT_FRAG_ACK:
+               return "WAIT_FRAG_ACK";
+       case DONE:
+               return "DONE";
+       case FAIL:
+               return "FAIL";
+       default:
+               return "?";
+       }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+static void eap_wsc_state(struct eap_wsc_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
+                  eap_wsc_state_txt(data->state),
+                  eap_wsc_state_txt(state));
+       data->state = state;
+}
+
+
+static void eap_wsc_ext_reg_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct eap_sm *sm = eloop_ctx;
+       struct eap_wsc_data *data = timeout_ctx;
+
+       if (sm->method_pending != METHOD_PENDING_WAIT)
+               return;
+
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Timeout while waiting for an External "
+                  "Registrar");
+       data->ext_reg_timeout = 1;
+       eap_sm_pending_cb(sm);
+}
+
+
+static void * eap_wsc_init(struct eap_sm *sm)
+{
+       struct eap_wsc_data *data;
+       int registrar;
+       struct wps_config cfg;
+
+       if (sm->identity && sm->identity_len == WSC_ID_REGISTRAR_LEN &&
+           os_memcmp(sm->identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) ==
+           0)
+               registrar = 0; /* Supplicant is Registrar */
+       else if (sm->identity && sm->identity_len == WSC_ID_ENROLLEE_LEN &&
+                os_memcmp(sm->identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN)
+                == 0)
+               registrar = 1; /* Supplicant is Enrollee */
+       else {
+               wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
+                                 sm->identity, sm->identity_len);
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = registrar ? START : MESG;
+       data->registrar = registrar;
+
+       os_memset(&cfg, 0, sizeof(cfg));
+       cfg.wps = sm->wps;
+       cfg.registrar = registrar;
+       if (registrar) {
+               if (sm->wps == NULL || sm->wps->registrar == NULL) {
+                       wpa_printf(MSG_INFO, "EAP-WSC: WPS Registrar not "
+                                  "initialized");
+                       os_free(data);
+                       return NULL;
+               }
+       } else {
+               if (sm->user == NULL || sm->user->password == NULL) {
+                       /*
+                        * In theory, this should not really be needed, but
+                        * Windows 7 uses Registrar mode to probe AP's WPS
+                        * capabilities before trying to use Enrollee and fails
+                        * if the AP does not allow that probing to happen..
+                        */
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) "
+                                  "configured for Enrollee functionality - "
+                                  "allow for probing capabilities (M1)");
+               } else {
+                       cfg.pin = sm->user->password;
+                       cfg.pin_len = sm->user->password_len;
+               }
+       }
+       cfg.assoc_wps_ie = sm->assoc_wps_ie;
+       cfg.peer_addr = sm->peer_addr;
+       if (0 /* TODO: could provide option for forcing PSK format */)
+                cfg.use_psk_key = 1;
+       data->wps = wps_init(&cfg);
+       if (data->wps == NULL) {
+               os_free(data);
+               return NULL;
+       }
+       data->fragment_size = WSC_FRAGMENT_SIZE;
+
+       return data;
+}
+
+
+static void eap_wsc_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_wsc_data *data = priv;
+       eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+       wpabuf_free(data->in_buf);
+       wpabuf_free(data->out_buf);
+       wps_deinit(data->wps);
+       os_free(data);
+}
+
+
+static struct wpabuf * eap_wsc_build_start(struct eap_sm *sm,
+                                          struct eap_wsc_data *data, u8 id)
+{
+       struct wpabuf *req;
+
+       req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 2,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+                          "request");
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Send WSC/Start");
+       wpabuf_put_u8(req, WSC_Start); /* Op-Code */
+       wpabuf_put_u8(req, 0); /* Flags */
+
+       return req;
+}
+
+
+static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id)
+{
+       struct wpabuf *req;
+       u8 flags;
+       size_t send_len, plen;
+
+       flags = 0;
+       send_len = wpabuf_len(data->out_buf) - data->out_used;
+       if (2 + send_len > data->fragment_size) {
+               send_len = data->fragment_size - 2;
+               flags |= WSC_FLAGS_MF;
+               if (data->out_used == 0) {
+                       flags |= WSC_FLAGS_LF;
+                       send_len -= 2;
+               }
+       }
+       plen = 2 + send_len;
+       if (flags & WSC_FLAGS_LF)
+               plen += 2;
+       req = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
+                           EAP_CODE_REQUEST, id);
+       if (req == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-WSC: Failed to allocate memory for "
+                          "request");
+               return NULL;
+       }
+
+       wpabuf_put_u8(req, data->out_op_code); /* Op-Code */
+       wpabuf_put_u8(req, flags); /* Flags */
+       if (flags & WSC_FLAGS_LF)
+               wpabuf_put_be16(req, wpabuf_len(data->out_buf));
+
+       wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
+                       send_len);
+       data->out_used += send_len;
+
+       if (data->out_used == wpabuf_len(data->out_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+                          "(message sent completely)",
+                          (unsigned long) send_len);
+               wpabuf_free(data->out_buf);
+               data->out_buf = NULL;
+               data->out_used = 0;
+               eap_wsc_state(data, MESG);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
+                          "(%lu more to send)", (unsigned long) send_len,
+                          (unsigned long) wpabuf_len(data->out_buf) -
+                          data->out_used);
+               eap_wsc_state(data, WAIT_FRAG_ACK);
+       }
+
+       return req;
+}
+
+
+static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_wsc_data *data = priv;
+
+       switch (data->state) {
+       case START:
+               return eap_wsc_build_start(sm, data, id);
+       case MESG:
+               if (data->out_buf == NULL) {
+                       data->out_buf = wps_get_msg(data->wps,
+                                                   &data->out_op_code);
+                       if (data->out_buf == NULL) {
+                               wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to "
+                                          "receive message from WPS");
+                               return NULL;
+                       }
+                       data->out_used = 0;
+               }
+               /* pass through */
+       case WAIT_FRAG_ACK:
+               return eap_wsc_build_msg(data, id);
+       case FRAG_ACK:
+               return eap_wsc_build_frag_ack(id, EAP_CODE_REQUEST);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected state %d in "
+                          "buildReq", data->state);
+               return NULL;
+       }
+}
+
+
+static Boolean eap_wsc_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       const u8 *pos;
+       size_t len;
+
+       pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+                              respData, &len);
+       if (pos == NULL || len < 2) {
+               wpa_printf(MSG_INFO, "EAP-WSC: Invalid frame");
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static int eap_wsc_process_cont(struct eap_wsc_data *data,
+                               const u8 *buf, size_t len, u8 op_code)
+{
+       /* Process continuation of a pending message */
+       if (op_code != data->in_op_code) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
+                          "fragment (expected %d)",
+                          op_code, data->in_op_code);
+               eap_wsc_state(data, FAIL);
+               return -1;
+       }
+
+       if (len > wpabuf_tailroom(data->in_buf)) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
+               eap_wsc_state(data, FAIL);
+               return -1;
+       }
+
+       wpabuf_put_data(data->in_buf, buf, len);
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting for %lu "
+                  "bytes more", (unsigned long) len,
+                  (unsigned long) wpabuf_tailroom(data->in_buf));
+
+       return 0;
+}
+
+
+static int eap_wsc_process_fragment(struct eap_wsc_data *data,
+                                   u8 flags, u8 op_code, u16 message_length,
+                                   const u8 *buf, size_t len)
+{
+       /* Process a fragment that is not the last one of the message */
+       if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length "
+                          "field in a fragmented packet");
+               return -1;
+       }
+
+       if (data->in_buf == NULL) {
+               /* First fragment of the message */
+               data->in_buf = wpabuf_alloc(message_length);
+               if (data->in_buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
+                                  "message");
+                       return -1;
+               }
+               data->in_op_code = op_code;
+               wpabuf_put_data(data->in_buf, buf, len);
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in "
+                          "first fragment, waiting for %lu bytes more",
+                          (unsigned long) len,
+                          (unsigned long) wpabuf_tailroom(data->in_buf));
+       }
+
+       return 0;
+}
+
+
+static void eap_wsc_process(struct eap_sm *sm, void *priv,
+                           struct wpabuf *respData)
+{
+       struct eap_wsc_data *data = priv;
+       const u8 *start, *pos, *end;
+       size_t len;
+       u8 op_code, flags;
+       u16 message_length = 0;
+       enum wps_process_res res;
+       struct wpabuf tmpbuf;
+
+       eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+       if (data->ext_reg_timeout) {
+               eap_wsc_state(data, FAIL);
+               return;
+       }
+
+       pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+                              respData, &len);
+       if (pos == NULL || len < 2)
+               return; /* Should not happen; message already verified */
+
+       start = pos;
+       end = start + len;
+
+       op_code = *pos++;
+       flags = *pos++;
+       if (flags & WSC_FLAGS_LF) {
+               if (end - pos < 2) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
+                       return;
+               }
+               message_length = WPA_GET_BE16(pos);
+               pos += 2;
+
+               if (message_length < end - pos) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
+                                  "Length");
+                       return;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
+                  "Flags 0x%x Message Length %d",
+                  op_code, flags, message_length);
+
+       if (data->state == WAIT_FRAG_ACK) {
+               if (op_code != WSC_FRAG_ACK) {
+                       wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
+                                  "in WAIT_FRAG_ACK state", op_code);
+                       eap_wsc_state(data, FAIL);
+                       return;
+               }
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
+               eap_wsc_state(data, MESG);
+               return;
+       }
+
+       if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
+           op_code != WSC_Done) {
+               wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
+                          op_code);
+               eap_wsc_state(data, FAIL);
+               return;
+       }
+
+       if (data->in_buf &&
+           eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
+               eap_wsc_state(data, FAIL);
+               return;
+       }
+
+       if (flags & WSC_FLAGS_MF) {
+               if (eap_wsc_process_fragment(data, flags, op_code,
+                                            message_length, pos, end - pos) <
+                   0)
+                       eap_wsc_state(data, FAIL);
+               else
+                       eap_wsc_state(data, FRAG_ACK);
+               return;
+       }
+
+       if (data->in_buf == NULL) {
+               /* Wrap unfragmented messages as wpabuf without extra copy */
+               wpabuf_set(&tmpbuf, pos, end - pos);
+               data->in_buf = &tmpbuf;
+       }
+
+       res = wps_process_msg(data->wps, op_code, data->in_buf);
+       switch (res) {
+       case WPS_DONE:
+               wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
+                          "successfully - report EAP failure");
+               eap_wsc_state(data, FAIL);
+               break;
+       case WPS_CONTINUE:
+               eap_wsc_state(data, MESG);
+               break;
+       case WPS_FAILURE:
+               wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
+               eap_wsc_state(data, FAIL);
+               break;
+       case WPS_PENDING:
+               eap_wsc_state(data, MESG);
+               sm->method_pending = METHOD_PENDING_WAIT;
+               eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data);
+               eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout,
+                                      sm, data);
+               break;
+       }
+
+       if (data->in_buf != &tmpbuf)
+               wpabuf_free(data->in_buf);
+       data->in_buf = NULL;
+}
+
+
+static Boolean eap_wsc_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_wsc_data *data = priv;
+       return data->state == FAIL;
+}
+
+
+static Boolean eap_wsc_isSuccess(struct eap_sm *sm, void *priv)
+{
+       /* EAP-WSC will always result in EAP-Failure */
+       return FALSE;
+}
+
+
+static int eap_wsc_getTimeout(struct eap_sm *sm, void *priv)
+{
+       /* Recommended retransmit times: retransmit timeout 5 seconds,
+        * per-message timeout 15 seconds, i.e., 3 tries. */
+       sm->MaxRetrans = 2; /* total 3 attempts */
+       return 5;
+}
+
+
+int eap_server_wsc_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
+                                     "WSC");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_wsc_init;
+       eap->reset = eap_wsc_reset;
+       eap->buildReq = eap_wsc_buildReq;
+       eap->check = eap_wsc_check;
+       eap->process = eap_wsc_process;
+       eap->isDone = eap_wsc_isDone;
+       eap->isSuccess = eap_wsc_isSuccess;
+       eap->getTimeout = eap_wsc_getTimeout;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
diff --git a/src/eap_server/eap_sim_db.c b/src/eap_server/eap_sim_db.c
new file mode 100644 (file)
index 0000000..aba919a
--- /dev/null
@@ -0,0 +1,1337 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 2005-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.
+ *
+ * This is an example implementation of the EAP-SIM/AKA database/authentication
+ * gateway interface that is using an external program as an SS7 gateway to
+ * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example
+ * implementation of such a gateway program. This eap_sim_db.c takes care of
+ * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different
+ * gateway implementations for HLR/AuC access. Alternatively, it can also be
+ * completely replaced if the in-memory database of pseudonyms/re-auth
+ * identities is not suitable for some cases.
+ */
+
+#include "includes.h"
+#include <sys/un.h>
+
+#include "common.h"
+#include "eap_common/eap_sim_common.h"
+#include "eap_server/eap_sim_db.h"
+#include "eloop.h"
+
+struct eap_sim_pseudonym {
+       struct eap_sim_pseudonym *next;
+       u8 *identity;
+       size_t identity_len;
+       char *pseudonym;
+};
+
+struct eap_sim_db_pending {
+       struct eap_sim_db_pending *next;
+       u8 imsi[20];
+       size_t imsi_len;
+       enum { PENDING, SUCCESS, FAILURE } state;
+       void *cb_session_ctx;
+       struct os_time timestamp;
+       int aka;
+       union {
+               struct {
+                       u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN];
+                       u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN];
+                       u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN];
+                       int num_chal;
+               } sim;
+               struct {
+                       u8 rand[EAP_AKA_RAND_LEN];
+                       u8 autn[EAP_AKA_AUTN_LEN];
+                       u8 ik[EAP_AKA_IK_LEN];
+                       u8 ck[EAP_AKA_CK_LEN];
+                       u8 res[EAP_AKA_RES_MAX_LEN];
+                       size_t res_len;
+               } aka;
+       } u;
+};
+
+struct eap_sim_db_data {
+       int sock;
+       char *fname;
+       char *local_sock;
+       void (*get_complete_cb)(void *ctx, void *session_ctx);
+       void *ctx;
+       struct eap_sim_pseudonym *pseudonyms;
+       struct eap_sim_reauth *reauths;
+       struct eap_sim_db_pending *pending;
+};
+
+
+static struct eap_sim_db_pending *
+eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi,
+                      size_t imsi_len, int aka)
+{
+       struct eap_sim_db_pending *entry, *prev = NULL;
+
+       entry = data->pending;
+       while (entry) {
+               if (entry->aka == aka && entry->imsi_len == imsi_len &&
+                   os_memcmp(entry->imsi, imsi, imsi_len) == 0) {
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               data->pending = entry->next;
+                       break;
+               }
+               prev = entry;
+               entry = entry->next;
+       }
+       return entry;
+}
+
+
+static void eap_sim_db_add_pending(struct eap_sim_db_data *data,
+                                  struct eap_sim_db_pending *entry)
+{
+       entry->next = data->pending;
+       data->pending = entry;
+}
+
+
+static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data,
+                                    const char *imsi, char *buf)
+{
+       char *start, *end, *pos;
+       struct eap_sim_db_pending *entry;
+       int num_chal;
+
+       /*
+        * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ...
+        * SIM-RESP-AUTH <IMSI> FAILURE
+        * (IMSI = ASCII string, Kc/SRES/RAND = hex string)
+        */
+
+       entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 0);
+       if (entry == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
+                          "received message found");
+               return;
+       }
+
+       start = buf;
+       if (os_strncmp(start, "FAILURE", 7) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
+                          "failure");
+               entry->state = FAILURE;
+               eap_sim_db_add_pending(data, entry);
+               data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+               return;
+       }
+
+       num_chal = 0;
+       while (num_chal < EAP_SIM_MAX_CHAL) {
+               end = os_strchr(start, ' ');
+               if (end)
+                       *end = '\0';
+
+               pos = os_strchr(start, ':');
+               if (pos == NULL)
+                       goto parse_fail;
+               *pos = '\0';
+               if (hexstr2bin(start, entry->u.sim.kc[num_chal],
+                              EAP_SIM_KC_LEN))
+                       goto parse_fail;
+
+               start = pos + 1;
+               pos = os_strchr(start, ':');
+               if (pos == NULL)
+                       goto parse_fail;
+               *pos = '\0';
+               if (hexstr2bin(start, entry->u.sim.sres[num_chal],
+                              EAP_SIM_SRES_LEN))
+                       goto parse_fail;
+
+               start = pos + 1;
+               if (hexstr2bin(start, entry->u.sim.rand[num_chal],
+                              GSM_RAND_LEN))
+                       goto parse_fail;
+
+               num_chal++;
+               if (end == NULL)
+                       break;
+               else
+                       start = end + 1;
+       }
+       entry->u.sim.num_chal = num_chal;
+
+       entry->state = SUCCESS;
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
+                  "successfully - callback");
+       eap_sim_db_add_pending(data, entry);
+       data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+       return;
+
+parse_fail:
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+       os_free(entry);
+}
+
+
+static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data,
+                                    const char *imsi, char *buf)
+{
+       char *start, *end;
+       struct eap_sim_db_pending *entry;
+
+       /*
+        * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES>
+        * AKA-RESP-AUTH <IMSI> FAILURE
+        * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string)
+        */
+
+       entry = eap_sim_db_get_pending(data, (u8 *) imsi, os_strlen(imsi), 1);
+       if (entry == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the "
+                          "received message found");
+               return;
+       }
+
+       start = buf;
+       if (os_strncmp(start, "FAILURE", 7) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported "
+                          "failure");
+               entry->state = FAILURE;
+               eap_sim_db_add_pending(data, entry);
+               data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+               return;
+       }
+
+       end = os_strchr(start, ' ');
+       if (end == NULL)
+               goto parse_fail;
+       *end = '\0';
+       if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN))
+               goto parse_fail;
+
+       start = end + 1;
+       end = os_strchr(start, ' ');
+       if (end == NULL)
+               goto parse_fail;
+       *end = '\0';
+       if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN))
+               goto parse_fail;
+
+       start = end + 1;
+       end = os_strchr(start, ' ');
+       if (end == NULL)
+               goto parse_fail;
+       *end = '\0';
+       if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN))
+               goto parse_fail;
+
+       start = end + 1;
+       end = os_strchr(start, ' ');
+       if (end == NULL)
+               goto parse_fail;
+       *end = '\0';
+       if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN))
+               goto parse_fail;
+
+       start = end + 1;
+       end = os_strchr(start, ' ');
+       if (end)
+               *end = '\0';
+       else {
+               end = start;
+               while (*end)
+                       end++;
+       }
+       entry->u.aka.res_len = (end - start) / 2;
+       if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES");
+               entry->u.aka.res_len = 0;
+               goto parse_fail;
+       }
+       if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len))
+               goto parse_fail;
+
+       entry->state = SUCCESS;
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed "
+                  "successfully - callback");
+       eap_sim_db_add_pending(data, entry);
+       data->get_complete_cb(data->ctx, entry->cb_session_ctx);
+       return;
+
+parse_fail:
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+       os_free(entry);
+}
+
+
+static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct eap_sim_db_data *data = eloop_ctx;
+       char buf[1000], *pos, *cmd, *imsi;
+       int res;
+
+       res = recv(sock, buf, sizeof(buf), 0);
+       if (res < 0)
+               return;
+       wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
+                             "external source", (u8 *) buf, res);
+       if (res == 0)
+               return;
+       if (res >= (int) sizeof(buf))
+               res = sizeof(buf) - 1;
+       buf[res] = '\0';
+
+       if (data->get_complete_cb == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
+                          "registered");
+               return;
+       }
+
+       /* <cmd> <IMSI> ... */
+
+       cmd = buf;
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               goto parse_fail;
+       *pos = '\0';
+       imsi = pos + 1;
+       pos = os_strchr(imsi, ' ');
+       if (pos == NULL)
+               goto parse_fail;
+       *pos = '\0';
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: External response=%s for IMSI %s",
+                  cmd, imsi);
+
+       if (os_strcmp(cmd, "SIM-RESP-AUTH") == 0)
+               eap_sim_db_sim_resp_auth(data, imsi, pos + 1);
+       else if (os_strcmp(cmd, "AKA-RESP-AUTH") == 0)
+               eap_sim_db_aka_resp_auth(data, imsi, pos + 1);
+       else
+               wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response "
+                          "'%s'", cmd);
+       return;
+
+parse_fail:
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string");
+}
+
+
+static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
+{
+       struct sockaddr_un addr;
+       static int counter = 0;
+
+       if (os_strncmp(data->fname, "unix:", 5) != 0)
+               return -1;
+
+       data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (data->sock < 0) {
+               perror("socket(eap_sim_db)");
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_snprintf(addr.sun_path, sizeof(addr.sun_path),
+                   "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
+       data->local_sock = os_strdup(addr.sun_path);
+       if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind(eap_sim_db)");
+               close(data->sock);
+               data->sock = -1;
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
+       if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("connect(eap_sim_db)");
+               wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
+                                 (u8 *) addr.sun_path,
+                                 os_strlen(addr.sun_path));
+               close(data->sock);
+               data->sock = -1;
+               return -1;
+       }
+
+       eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL);
+
+       return 0;
+}
+
+
+static void eap_sim_db_close_socket(struct eap_sim_db_data *data)
+{
+       if (data->sock >= 0) {
+               eloop_unregister_read_sock(data->sock);
+               close(data->sock);
+               data->sock = -1;
+       }
+       if (data->local_sock) {
+               unlink(data->local_sock);
+               os_free(data->local_sock);
+               data->local_sock = NULL;
+       }
+}
+
+
+/**
+ * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface
+ * @config: Configuration data (e.g., file name)
+ * @get_complete_cb: Callback function for reporting availability of triplets
+ * @ctx: Context pointer for get_complete_cb
+ * Returns: Pointer to a private data structure or %NULL on failure
+ */
+void * eap_sim_db_init(const char *config,
+                      void (*get_complete_cb)(void *ctx, void *session_ctx),
+                      void *ctx)
+{
+       struct eap_sim_db_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->sock = -1;
+       data->get_complete_cb = get_complete_cb;
+       data->ctx = ctx;
+       data->fname = os_strdup(config);
+       if (data->fname == NULL)
+               goto fail;
+
+       if (os_strncmp(data->fname, "unix:", 5) == 0) {
+               if (eap_sim_db_open_socket(data))
+                       goto fail;
+       }
+
+       return data;
+
+fail:
+       eap_sim_db_close_socket(data);
+       os_free(data->fname);
+       os_free(data);
+       return NULL;
+}
+
+
+static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p)
+{
+       os_free(p->identity);
+       os_free(p->pseudonym);
+       os_free(p);
+}
+
+
+static void eap_sim_db_free_reauth(struct eap_sim_reauth *r)
+{
+       os_free(r->identity);
+       os_free(r->reauth_id);
+       os_free(r);
+}
+
+
+/**
+ * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface
+ * @priv: Private data pointer from eap_sim_db_init()
+ */
+void eap_sim_db_deinit(void *priv)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_pseudonym *p, *prev;
+       struct eap_sim_reauth *r, *prevr;
+       struct eap_sim_db_pending *pending, *prev_pending;
+
+       eap_sim_db_close_socket(data);
+       os_free(data->fname);
+
+       p = data->pseudonyms;
+       while (p) {
+               prev = p;
+               p = p->next;
+               eap_sim_db_free_pseudonym(prev);
+       }
+
+       r = data->reauths;
+       while (r) {
+               prevr = r;
+               r = r->next;
+               eap_sim_db_free_reauth(prevr);
+       }
+
+       pending = data->pending;
+       while (pending) {
+               prev_pending = pending;
+               pending = pending->next;
+               os_free(prev_pending);
+       }
+
+       os_free(data);
+}
+
+
+static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
+                          size_t len)
+{
+       int _errno = 0;
+
+       if (send(data->sock, msg, len, 0) < 0) {
+               _errno = errno;
+               perror("send[EAP-SIM DB UNIX]");
+       }
+
+       if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
+           _errno == ECONNREFUSED) {
+               /* Try to reconnect */
+               eap_sim_db_close_socket(data);
+               if (eap_sim_db_open_socket(data) < 0)
+                       return -1;
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
+                          "external server");
+               if (send(data->sock, msg, len, 0) < 0) {
+                       perror("send[EAP-SIM DB UNIX]");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+static void eap_sim_db_expire_pending(struct eap_sim_db_data *data)
+{
+       /* TODO: add limit for maximum length for pending list; remove latest
+        * (i.e., last) entry from the list if the limit is reached; could also
+        * use timeout to expire pending entries */
+}
+
+
+/**
+ * eap_sim_db_get_gsm_triplets - Get GSM triplets
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * @max_chal: Maximum number of triplets
+ * @_rand: Buffer for RAND values
+ * @kc: Buffer for Kc values
+ * @sres: Buffer for SRES values
+ * @cb_session_ctx: Session callback context for get_complete_cb()
+ * Returns: Number of triplets received (has to be less than or equal to
+ * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or
+ * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the
+ * callback function registered with eap_sim_db_init() will be called once the
+ * results become available.
+ *
+ * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in
+ * ASCII format.
+ *
+ * When using an external server for GSM triplets, this function can always
+ * start a request and return EAP_SIM_DB_PENDING immediately if authentication
+ * triplets are not available. Once the triplets are received, callback
+ * function registered with eap_sim_db_init() is called to notify EAP state
+ * machine to reprocess the message. This eap_sim_db_get_gsm_triplets()
+ * function will then be called again and the newly received triplets will then
+ * be given to the caller.
+ */
+int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
+                               size_t identity_len, int max_chal,
+                               u8 *_rand, u8 *kc, u8 *sres,
+                               void *cb_session_ctx)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_db_pending *entry;
+       int len, ret;
+       size_t i;
+       char msg[40];
+
+       if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+                                 identity, identity_len);
+               return EAP_SIM_DB_FAILURE;
+       }
+       identity++;
+       identity_len--;
+       for (i = 0; i < identity_len; i++) {
+               if (identity[i] == '@') {
+                       identity_len = i;
+                       break;
+               }
+       }
+       if (identity_len + 1 > sizeof(entry->imsi)) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+                                 identity, identity_len);
+               return EAP_SIM_DB_FAILURE;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI",
+                         identity, identity_len);
+
+       entry = eap_sim_db_get_pending(data, identity, identity_len, 0);
+       if (entry) {
+               int num_chal;
+               if (entry->state == FAILURE) {
+                       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+                                  "failure");
+                       os_free(entry);
+                       return EAP_SIM_DB_FAILURE;
+               }
+
+               if (entry->state == PENDING) {
+                       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+                                  "still pending");
+                       eap_sim_db_add_pending(data, entry);
+                       return EAP_SIM_DB_PENDING;
+               }
+
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> "
+                          "%d challenges", entry->u.sim.num_chal);
+               num_chal = entry->u.sim.num_chal;
+               if (num_chal > max_chal)
+                       num_chal = max_chal;
+               os_memcpy(_rand, entry->u.sim.rand, num_chal * GSM_RAND_LEN);
+               os_memcpy(sres, entry->u.sim.sres,
+                         num_chal * EAP_SIM_SRES_LEN);
+               os_memcpy(kc, entry->u.sim.kc, num_chal * EAP_SIM_KC_LEN);
+               os_free(entry);
+               return num_chal;
+       }
+
+       if (data->sock < 0) {
+               if (eap_sim_db_open_socket(data) < 0)
+                       return EAP_SIM_DB_FAILURE;
+       }
+
+       len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
+       if (len < 0 || len + identity_len >= sizeof(msg))
+               return EAP_SIM_DB_FAILURE;
+       os_memcpy(msg + len, identity, identity_len);
+       len += identity_len;
+       ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
+       if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+               return EAP_SIM_DB_FAILURE;
+       len += ret;
+
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication "
+                   "data for IMSI", identity, identity_len);
+       if (eap_sim_db_send(data, msg, len) < 0)
+               return EAP_SIM_DB_FAILURE;
+
+       entry = os_zalloc(sizeof(*entry));
+       if (entry == NULL)
+               return EAP_SIM_DB_FAILURE;
+
+       os_get_time(&entry->timestamp);
+       os_memcpy(entry->imsi, identity, identity_len);
+       entry->imsi_len = identity_len;
+       entry->cb_session_ctx = cb_session_ctx;
+       entry->state = PENDING;
+       eap_sim_db_add_pending(data, entry);
+       eap_sim_db_expire_pending(data);
+
+       return EAP_SIM_DB_PENDING;
+}
+
+
+static struct eap_sim_pseudonym *
+eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity,
+                        size_t identity_len)
+{
+       char *pseudonym;
+       size_t len;
+       struct eap_sim_pseudonym *p;
+
+       if (identity_len == 0 ||
+           (identity[0] != EAP_SIM_PSEUDONYM_PREFIX &&
+            identity[0] != EAP_AKA_PSEUDONYM_PREFIX))
+               return NULL;
+
+       /* Remove possible realm from identity */
+       len = 0;
+       while (len < identity_len) {
+               if (identity[len] == '@')
+                       break;
+               len++;
+       }
+
+       pseudonym = os_malloc(len + 1);
+       if (pseudonym == NULL)
+               return NULL;
+       os_memcpy(pseudonym, identity, len);
+       pseudonym[len] = '\0';
+
+       p = data->pseudonyms;
+       while (p) {
+               if (os_strcmp(p->pseudonym, pseudonym) == 0)
+                       break;
+               p = p->next;
+       }
+
+       os_free(pseudonym);
+
+       return p;
+}
+
+
+static struct eap_sim_pseudonym *
+eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity,
+                           size_t identity_len)
+{
+       struct eap_sim_pseudonym *p;
+
+       if (identity_len == 0 ||
+           (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
+            identity[0] != EAP_AKA_PERMANENT_PREFIX))
+               return NULL;
+
+       p = data->pseudonyms;
+       while (p) {
+               if (identity_len == p->identity_len &&
+                   os_memcmp(p->identity, identity, identity_len) == 0)
+                       break;
+               p = p->next;
+       }
+
+       return p;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity,
+                     size_t identity_len)
+{
+       char *reauth_id;
+       size_t len;
+       struct eap_sim_reauth *r;
+
+       if (identity_len == 0 ||
+           (identity[0] != EAP_SIM_REAUTH_ID_PREFIX &&
+            identity[0] != EAP_AKA_REAUTH_ID_PREFIX))
+               return NULL;
+
+       /* Remove possible realm from identity */
+       len = 0;
+       while (len < identity_len) {
+               if (identity[len] == '@')
+                       break;
+               len++;
+       }
+
+       reauth_id = os_malloc(len + 1);
+       if (reauth_id == NULL)
+               return NULL;
+       os_memcpy(reauth_id, identity, len);
+       reauth_id[len] = '\0';
+
+       r = data->reauths;
+       while (r) {
+               if (os_strcmp(r->reauth_id, reauth_id) == 0)
+                       break;
+               r = r->next;
+       }
+
+       os_free(reauth_id);
+
+       return r;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity,
+                        size_t identity_len)
+{
+       struct eap_sim_pseudonym *p;
+       struct eap_sim_reauth *r;
+
+       if (identity_len == 0)
+               return NULL;
+
+       p = eap_sim_db_get_pseudonym(data, identity, identity_len);
+       if (p == NULL)
+               p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
+       if (p) {
+               identity = p->identity;
+               identity_len = p->identity_len;
+       }
+
+       r = data->reauths;
+       while (r) {
+               if (identity_len == r->identity_len &&
+                   os_memcmp(r->identity, identity, identity_len) == 0)
+                       break;
+               r = r->next;
+       }
+
+       return r;
+}
+
+
+/**
+ * eap_sim_db_identity_known - Verify whether the given identity is known
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes 
+ * Returns: 0 if the user is found or -1 on failure
+ *
+ * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the
+ * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id.
+ */
+int eap_sim_db_identity_known(void *priv, const u8 *identity,
+                             size_t identity_len)
+{
+       struct eap_sim_db_data *data = priv;
+
+       if (identity == NULL || identity_len < 2)
+               return -1;
+
+       if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX ||
+           identity[0] == EAP_AKA_PSEUDONYM_PREFIX) {
+               struct eap_sim_pseudonym *p =
+                       eap_sim_db_get_pseudonym(data, identity, identity_len);
+               return p ? 0 : -1;
+       }
+
+       if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX ||
+           identity[0] == EAP_AKA_REAUTH_ID_PREFIX) {
+               struct eap_sim_reauth *r =
+                       eap_sim_db_get_reauth(data, identity, identity_len);
+               return r ? 0 : -1;
+       }
+
+       if (identity[0] != EAP_SIM_PERMANENT_PREFIX &&
+           identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+               /* Unknown identity prefix */
+               return -1;
+       }
+
+       /* TODO: Should consider asking HLR/AuC gateway whether this permanent
+        * identity is known. If it is, EAP-SIM/AKA can skip identity request.
+        * In case of EAP-AKA, this would reduce number of needed round-trips.
+        * Ideally, this would be done with one wait, i.e., just request
+        * authentication data and store it for the next use. This would then
+        * need to use similar pending-request functionality as the normal
+        * request for authentication data at later phase.
+        */
+       return -1;
+}
+
+
+static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
+{
+       char *id, *pos, *end;
+       u8 buf[10];
+
+       if (os_get_random(buf, sizeof(buf)))
+               return NULL;
+       id = os_malloc(sizeof(buf) * 2 + 2);
+       if (id == NULL)
+               return NULL;
+
+       pos = id;
+       end = id + sizeof(buf) * 2 + 2;
+       *pos++ = prefix;
+       pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
+       
+       return id;
+}
+
+
+/**
+ * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @aka: Using EAP-AKA instead of EAP-SIM
+ * Returns: Next pseudonym (allocated string) or %NULL on failure
+ *
+ * This function is used to generate a pseudonym for EAP-SIM. The returned
+ * pseudonym is not added to database at this point; it will need to be added
+ * with eap_sim_db_add_pseudonym() once the authentication has been completed
+ * successfully. Caller is responsible for freeing the returned buffer.
+ */
+char * eap_sim_db_get_next_pseudonym(void *priv, int aka)
+{
+       struct eap_sim_db_data *data = priv;
+       return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX :
+                                  EAP_SIM_PSEUDONYM_PREFIX);
+}
+
+
+/**
+ * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @aka: Using EAP-AKA instead of EAP-SIM
+ * Returns: Next reauth_id (allocated string) or %NULL on failure
+ *
+ * This function is used to generate a fast re-authentication identity for
+ * EAP-SIM. The returned reauth_id is not added to database at this point; it
+ * will need to be added with eap_sim_db_add_reauth() once the authentication
+ * has been completed successfully. Caller is responsible for freeing the
+ * returned buffer.
+ */
+char * eap_sim_db_get_next_reauth_id(void *priv, int aka)
+{
+       struct eap_sim_db_data *data = priv;
+       return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX :
+                                  EAP_SIM_REAUTH_ID_PREFIX);
+}
+
+
+/**
+ * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not
+ * free it.
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is
+ * responsible of freeing pseudonym buffer once it is not needed anymore.
+ */
+int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
+                            size_t identity_len, char *pseudonym)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_pseudonym *p;
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity",
+                         identity, identity_len);
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym);
+
+       /* TODO: could store last two pseudonyms */
+       p = eap_sim_db_get_pseudonym(data, identity, identity_len);
+       if (p == NULL)
+               p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
+
+       if (p) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
+                          "pseudonym: %s", p->pseudonym);
+               os_free(p->pseudonym);
+               p->pseudonym = pseudonym;
+               return 0;
+       }
+
+       p = os_zalloc(sizeof(*p));
+       if (p == NULL) {
+               os_free(pseudonym);
+               return -1;
+       }
+
+       p->next = data->pseudonyms;
+       p->identity = os_malloc(identity_len);
+       if (p->identity == NULL) {
+               os_free(p);
+               os_free(pseudonym);
+               return -1;
+       }
+       os_memcpy(p->identity, identity, identity_len);
+       p->identity_len = identity_len;
+       p->pseudonym = pseudonym;
+       data->pseudonyms = p;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry");
+       return 0;
+}
+
+
+static struct eap_sim_reauth *
+eap_sim_db_add_reauth_data(struct eap_sim_db_data *data, const u8 *identity,
+                          size_t identity_len, char *reauth_id, u16 counter)
+{
+       struct eap_sim_reauth *r;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add reauth_id for identity",
+                         identity, identity_len);
+       wpa_printf(MSG_DEBUG, "EAP-SIM DB: reauth_id: %s", reauth_id);
+
+       r = eap_sim_db_get_reauth(data, identity, identity_len);
+       if (r == NULL)
+               r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+
+       if (r) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous "
+                          "reauth_id: %s", r->reauth_id);
+               os_free(r->reauth_id);
+               r->reauth_id = reauth_id;
+       } else {
+               r = os_zalloc(sizeof(*r));
+               if (r == NULL) {
+                       os_free(reauth_id);
+                       return NULL;
+               }
+
+               r->next = data->reauths;
+               r->identity = os_malloc(identity_len);
+               if (r->identity == NULL) {
+                       os_free(r);
+                       os_free(reauth_id);
+                       return NULL;
+               }
+               os_memcpy(r->identity, identity, identity_len);
+               r->identity_len = identity_len;
+               r->reauth_id = reauth_id;
+               data->reauths = r;
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry");
+       }
+
+       r->counter = counter;
+
+       return r;
+}
+
+
+/**
+ * eap_sim_db_add_reauth - EAP-SIM DB: Add new re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
+ * free it.
+ * @counter: AT_COUNTER value for fast re-authentication
+ * @mk: 16-byte MK from the previous full authentication or %NULL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new re-authentication entry for an EAP-SIM user.
+ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
+ * anymore.
+ */
+int eap_sim_db_add_reauth(void *priv, const u8 *identity,
+                         size_t identity_len, char *reauth_id, u16 counter,
+                         const u8 *mk)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_reauth *r;
+
+       r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
+                                      counter);
+       if (r == NULL)
+               return -1;
+
+       os_memcpy(r->mk, mk, EAP_SIM_MK_LEN);
+       r->aka_prime = 0;
+
+       return 0;
+}
+
+
+#ifdef EAP_SERVER_AKA_PRIME
+/**
+ * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @reauth_id: reauth_id for this user. This needs to be an allocated buffer,
+ * e.g., return value from eap_sim_db_get_next_reauth_id(). Caller must not
+ * free it.
+ * @counter: AT_COUNTER value for fast re-authentication
+ * @k_encr: K_encr from the previous full authentication
+ * @k_aut: K_aut from the previous full authentication
+ * @k_re: 32-byte K_re from the previous full authentication
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds a new re-authentication entry for an EAP-AKA' user.
+ * EAP-SIM DB is responsible of freeing reauth_id buffer once it is not needed
+ * anymore.
+ */
+int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
+                               size_t identity_len, char *reauth_id,
+                               u16 counter, const u8 *k_encr, const u8 *k_aut,
+                               const u8 *k_re)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_reauth *r;
+
+       r = eap_sim_db_add_reauth_data(data, identity, identity_len, reauth_id,
+                                      counter);
+       if (r == NULL)
+               return -1;
+
+       r->aka_prime = 1;
+       os_memcpy(r->k_encr, k_encr, EAP_SIM_K_ENCR_LEN);
+       os_memcpy(r->k_aut, k_aut, EAP_AKA_PRIME_K_AUT_LEN);
+       os_memcpy(r->k_re, k_re, EAP_AKA_PRIME_K_RE_LEN);
+
+       return 0;
+}
+#endif /* EAP_SERVER_AKA_PRIME */
+
+
+/**
+ * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity or pseudonym)
+ * @identity_len: Length of identity
+ * @len: Buffer for length of the returned permanent identity
+ * Returns: Pointer to the permanent identity, or %NULL if not found
+ */
+const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
+                                   size_t identity_len, size_t *len)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_pseudonym *p;
+
+       if (identity == NULL)
+               return NULL;
+
+       p = eap_sim_db_get_pseudonym(data, identity, identity_len);
+       if (p == NULL)
+               p = eap_sim_db_get_pseudonym_id(data, identity, identity_len);
+       if (p == NULL)
+               return NULL;
+
+       *len = p->identity_len;
+       return p->identity;
+}
+
+
+/**
+ * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: Identity of the user (may be permanent identity, pseudonym, or
+ * reauth_id)
+ * @identity_len: Length of identity
+ * Returns: Pointer to the re-auth entry, or %NULL if not found
+ */
+struct eap_sim_reauth *
+eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
+                           size_t identity_len)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_reauth *r;
+
+       if (identity == NULL)
+               return NULL;
+       r = eap_sim_db_get_reauth(data, identity, identity_len);
+       if (r == NULL)
+               r = eap_sim_db_get_reauth_id(data, identity, identity_len);
+       return r;
+}
+
+
+/**
+ * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @reauth: Pointer to re-authentication entry from
+ * eap_sim_db_get_reauth_entry()
+ */
+void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_reauth *r, *prev = NULL;
+       r = data->reauths;
+       while (r) {
+               if (r == reauth) {
+                       if (prev)
+                               prev->next = r->next;
+                       else
+                               data->reauths = r->next;
+                       eap_sim_db_free_reauth(r);
+                       return;
+               }
+               prev = r;
+               r = r->next;
+       }
+}
+
+
+/**
+ * eap_sim_db_get_aka_auth - Get AKA authentication values
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * @_rand: Buffer for RAND value
+ * @autn: Buffer for AUTN value
+ * @ik: Buffer for IK value
+ * @ck: Buffer for CK value
+ * @res: Buffer for RES value
+ * @res_len: Buffer for RES length
+ * @cb_session_ctx: Session callback context for get_complete_cb()
+ * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not
+ * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this
+ * case, the callback function registered with eap_sim_db_init() will be
+ * called once the results become available.
+ *
+ * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in
+ * ASCII format.
+ *
+ * When using an external server for AKA authentication, this function can
+ * always start a request and return EAP_SIM_DB_PENDING immediately if
+ * authentication triplets are not available. Once the authentication data are
+ * received, callback function registered with eap_sim_db_init() is called to
+ * notify EAP state machine to reprocess the message. This
+ * eap_sim_db_get_aka_auth() function will then be called again and the newly
+ * received triplets will then be given to the caller.
+ */
+int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
+                           size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
+                           u8 *ck, u8 *res, size_t *res_len,
+                           void *cb_session_ctx)
+{
+       struct eap_sim_db_data *data = priv;
+       struct eap_sim_db_pending *entry;
+       int len;
+       size_t i;
+       char msg[40];
+
+       if (identity_len < 2 || identity == NULL ||
+           identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+                                 identity, identity_len);
+               return EAP_SIM_DB_FAILURE;
+       }
+       identity++;
+       identity_len--;
+       for (i = 0; i < identity_len; i++) {
+               if (identity[i] == '@') {
+                       identity_len = i;
+                       break;
+               }
+       }
+       if (identity_len + 1 > sizeof(entry->imsi)) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+                                 identity, identity_len);
+               return EAP_SIM_DB_FAILURE;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI",
+                         identity, identity_len);
+
+       entry = eap_sim_db_get_pending(data, identity, identity_len, 1);
+       if (entry) {
+               if (entry->state == FAILURE) {
+                       os_free(entry);
+                       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure");
+                       return EAP_SIM_DB_FAILURE;
+               }
+
+               if (entry->state == PENDING) {
+                       eap_sim_db_add_pending(data, entry);
+                       wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending");
+                       return EAP_SIM_DB_PENDING;
+               }
+
+               wpa_printf(MSG_DEBUG, "EAP-SIM DB: Returning successfully "
+                          "received authentication data");
+               os_memcpy(_rand, entry->u.aka.rand, EAP_AKA_RAND_LEN);
+               os_memcpy(autn, entry->u.aka.autn, EAP_AKA_AUTN_LEN);
+               os_memcpy(ik, entry->u.aka.ik, EAP_AKA_IK_LEN);
+               os_memcpy(ck, entry->u.aka.ck, EAP_AKA_CK_LEN);
+               os_memcpy(res, entry->u.aka.res, EAP_AKA_RES_MAX_LEN);
+               *res_len = entry->u.aka.res_len;
+               os_free(entry);
+               return 0;
+       }
+
+       if (data->sock < 0) {
+               if (eap_sim_db_open_socket(data) < 0)
+                       return EAP_SIM_DB_FAILURE;
+       }
+
+       len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
+       if (len < 0 || len + identity_len >= sizeof(msg))
+               return EAP_SIM_DB_FAILURE;
+       os_memcpy(msg + len, identity, identity_len);
+       len += identity_len;
+
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication "
+                   "data for IMSI", identity, identity_len);
+       if (eap_sim_db_send(data, msg, len) < 0)
+               return EAP_SIM_DB_FAILURE;
+
+       entry = os_zalloc(sizeof(*entry));
+       if (entry == NULL)
+               return EAP_SIM_DB_FAILURE;
+
+       os_get_time(&entry->timestamp);
+       entry->aka = 1;
+       os_memcpy(entry->imsi, identity, identity_len);
+       entry->imsi_len = identity_len;
+       entry->cb_session_ctx = cb_session_ctx;
+       entry->state = PENDING;
+       eap_sim_db_add_pending(data, entry);
+       eap_sim_db_expire_pending(data);
+
+       return EAP_SIM_DB_PENDING;
+}
+
+
+/**
+ * eap_sim_db_resynchronize - Resynchronize AKA AUTN
+ * @priv: Private data pointer from eap_sim_db_init()
+ * @identity: User name identity
+ * @identity_len: Length of identity in bytes
+ * @auts: AUTS value from the peer
+ * @_rand: RAND value used in the rejected message
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when the peer reports synchronization failure in the
+ * AUTN value by sending AUTS. The AUTS and RAND values should be sent to
+ * HLR/AuC to allow it to resynchronize with the peer. After this,
+ * eap_sim_db_get_aka_auth() will be called again to to fetch updated
+ * RAND/AUTN values for the next challenge.
+ */
+int eap_sim_db_resynchronize(void *priv, const u8 *identity,
+                            size_t identity_len, const u8 *auts,
+                            const u8 *_rand)
+{
+       struct eap_sim_db_data *data = priv;
+       size_t i;
+
+       if (identity_len < 2 || identity == NULL ||
+           identity[0] != EAP_AKA_PERMANENT_PREFIX) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+                                 identity, identity_len);
+               return -1;
+       }
+       identity++;
+       identity_len--;
+       for (i = 0; i < identity_len; i++) {
+               if (identity[i] == '@') {
+                       identity_len = i;
+                       break;
+               }
+       }
+       if (identity_len > 20) {
+               wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity",
+                                 identity, identity_len);
+               return -1;
+       }
+
+       if (data->sock >= 0) {
+               char msg[100];
+               int len, ret;
+
+               len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
+               if (len < 0 || len + identity_len >= sizeof(msg))
+                       return -1;
+               os_memcpy(msg + len, identity, identity_len);
+               len += identity_len;
+
+               ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
+               if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+                       return -1;
+               len += ret;
+               len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
+                                       auts, EAP_AKA_AUTS_LEN);
+               ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
+               if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+                       return -1;
+               len += ret;
+               len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
+                                       _rand, EAP_AKA_RAND_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for "
+                           "IMSI", identity, identity_len);
+               if (eap_sim_db_send(data, msg, len) < 0)
+                       return -1;
+       }
+
+       return 0;
+}
diff --git a/src/eap_server/eap_sim_db.h b/src/eap_server/eap_sim_db.h
new file mode 100644 (file)
index 0000000..ab89ae9
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * hostapd / EAP-SIM database/authenticator gateway
+ * Copyright (c) 2005-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 EAP_SIM_DB_H
+#define EAP_SIM_DB_H
+
+#include "eap_common/eap_sim_common.h"
+
+/* Identity prefixes */
+#define EAP_SIM_PERMANENT_PREFIX '1'
+#define EAP_SIM_PSEUDONYM_PREFIX '3'
+#define EAP_SIM_REAUTH_ID_PREFIX '5'
+#define EAP_AKA_PERMANENT_PREFIX '0'
+#define EAP_AKA_PSEUDONYM_PREFIX '2'
+#define EAP_AKA_REAUTH_ID_PREFIX '4'
+
+void * eap_sim_db_init(const char *config,
+                      void (*get_complete_cb)(void *ctx, void *session_ctx),
+                      void *ctx);
+
+void eap_sim_db_deinit(void *priv);
+
+int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity,
+                               size_t identity_len, int max_chal,
+                               u8 *_rand, u8 *kc, u8 *sres,
+                               void *cb_session_ctx);
+
+#define EAP_SIM_DB_FAILURE -1
+#define EAP_SIM_DB_PENDING -2
+
+int eap_sim_db_identity_known(void *priv, const u8 *identity,
+                             size_t identity_len);
+
+char * eap_sim_db_get_next_pseudonym(void *priv, int aka);
+
+char * eap_sim_db_get_next_reauth_id(void *priv, int aka);
+
+int eap_sim_db_add_pseudonym(void *priv, const u8 *identity,
+                            size_t identity_len, char *pseudonym);
+
+int eap_sim_db_add_reauth(void *priv, const u8 *identity,
+                         size_t identity_len, char *reauth_id, u16 counter,
+                         const u8 *mk);
+int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity,
+                               size_t identity_len, char *reauth_id,
+                               u16 counter, const u8 *k_encr, const u8 *k_aut,
+                               const u8 *k_re);
+
+const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity,
+                                   size_t identity_len, size_t *len);
+
+struct eap_sim_reauth {
+       struct eap_sim_reauth *next;
+       u8 *identity;
+       size_t identity_len;
+       char *reauth_id;
+       u16 counter;
+       int aka_prime;
+       u8 mk[EAP_SIM_MK_LEN];
+       u8 k_encr[EAP_SIM_K_ENCR_LEN];
+       u8 k_aut[EAP_AKA_PRIME_K_AUT_LEN];
+       u8 k_re[EAP_AKA_PRIME_K_RE_LEN];
+};
+
+struct eap_sim_reauth *
+eap_sim_db_get_reauth_entry(void *priv, const u8 *identity,
+                           size_t identity_len);
+
+void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth);
+
+int eap_sim_db_get_aka_auth(void *priv, const u8 *identity,
+                           size_t identity_len, u8 *_rand, u8 *autn, u8 *ik,
+                           u8 *ck, u8 *res, size_t *res_len,
+                           void *cb_session_ctx);
+
+int eap_sim_db_resynchronize(void *priv, const u8 *identity,
+                            size_t identity_len, const u8 *auts,
+                            const u8 *_rand);
+
+#endif /* EAP_SIM_DB_H */
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
new file mode 100644 (file)
index 0000000..c34c401
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * EAP-TLS/PEAP/TTLS/FAST server common functions
+ * 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.
+ */
+
+#ifndef EAP_TLS_COMMON_H
+#define EAP_TLS_COMMON_H
+
+/**
+ * struct eap_ssl_data - TLS data for EAP methods
+ */
+struct eap_ssl_data {
+       /**
+        * conn - TLS connection context data from tls_connection_init()
+        */
+       struct tls_connection *conn;
+
+       /**
+        * tls_out - TLS message to be sent out in fragments
+        */
+       struct wpabuf *tls_out;
+
+       /**
+        * tls_out_pos - The current position in the outgoing TLS message
+        */
+       size_t tls_out_pos;
+
+       /**
+        * tls_out_limit - Maximum fragment size for outgoing TLS messages
+        */
+       size_t tls_out_limit;
+
+       /**
+        * tls_in - Received TLS message buffer for re-assembly
+        */
+       struct wpabuf *tls_in;
+
+       /**
+        * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel)
+        */
+       int phase2;
+
+       /**
+        * eap - EAP state machine allocated with eap_server_sm_init()
+        */
+       struct eap_sm *eap;
+
+       enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state;
+       struct wpabuf tmpbuf;
+};
+
+
+/* EAP TLS Flags */
+#define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80
+#define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40
+#define EAP_TLS_FLAGS_START 0x20
+#define EAP_TLS_VERSION_MASK 0x07
+
+ /* could be up to 128 bytes, but only the first 64 bytes are used */
+#define EAP_TLS_KEY_LEN 64
+
+
+int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
+                           int verify_peer);
+void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
+u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
+                              char *label, size_t len);
+struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
+                                        int eap_type, int version, u8 id);
+struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
+int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data);
+struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm,
+                                      struct eap_ssl_data *data,
+                                      const struct wpabuf *plain);
+int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
+                          struct wpabuf *respData, void *priv, int eap_type,
+                          int (*proc_version)(struct eap_sm *sm, void *priv,
+                                              int peer_version),
+                          void (*proc_msg)(struct eap_sm *sm, void *priv,
+                                           const struct wpabuf *respData));
+
+#endif /* EAP_TLS_COMMON_H */
diff --git a/src/eap_server/ikev2.c b/src/eap_server/ikev2.c
new file mode 100644 (file)
index 0000000..435ba26
--- /dev/null
@@ -0,0 +1,1205 @@
+/*
+ * IKEv2 initiator (RFC 4306) for EAP-IKEV2
+ * 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"
+
+#include "common.h"
+#include "crypto/dh_groups.h"
+#include "ikev2.h"
+
+
+static int ikev2_process_idr(struct ikev2_initiator_data *data,
+                            const u8 *idr, size_t idr_len);
+
+
+void ikev2_initiator_deinit(struct ikev2_initiator_data *data)
+{
+       ikev2_free_keys(&data->keys);
+       wpabuf_free(data->r_dh_public);
+       wpabuf_free(data->i_dh_private);
+       os_free(data->IDi);
+       os_free(data->IDr);
+       os_free(data->shared_secret);
+       wpabuf_free(data->i_sign_msg);
+       wpabuf_free(data->r_sign_msg);
+       os_free(data->key_pad);
+}
+
+
+static int ikev2_derive_keys(struct ikev2_initiator_data *data)
+{
+       u8 *buf, *pos, *pad, skeyseed[IKEV2_MAX_HASH_LEN];
+       size_t buf_len, pad_len;
+       struct wpabuf *shared;
+       const struct ikev2_integ_alg *integ;
+       const struct ikev2_prf_alg *prf;
+       const struct ikev2_encr_alg *encr;
+       int ret;
+       const u8 *addr[2];
+       size_t len[2];
+
+       /* RFC 4306, Sect. 2.14 */
+
+       integ = ikev2_get_integ(data->proposal.integ);
+       prf = ikev2_get_prf(data->proposal.prf);
+       encr = ikev2_get_encr(data->proposal.encr);
+       if (integ == NULL || prf == NULL || encr == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported proposal");
+               return -1;
+       }
+
+       shared = dh_derive_shared(data->r_dh_public, data->i_dh_private,
+                                 data->dh);
+       if (shared == NULL)
+               return -1;
+
+       /* Construct Ni | Nr | SPIi | SPIr */
+
+       buf_len = data->i_nonce_len + data->r_nonce_len + 2 * IKEV2_SPI_LEN;
+       buf = os_malloc(buf_len);
+       if (buf == NULL) {
+               wpabuf_free(shared);
+               return -1;
+       }
+
+       pos = buf;
+       os_memcpy(pos, data->i_nonce, data->i_nonce_len);
+       pos += data->i_nonce_len;
+       os_memcpy(pos, data->r_nonce, data->r_nonce_len);
+       pos += data->r_nonce_len;
+       os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
+       pos += IKEV2_SPI_LEN;
+       os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
+
+       /* SKEYSEED = prf(Ni | Nr, g^ir) */
+
+       /* Use zero-padding per RFC 4306, Sect. 2.14 */
+       pad_len = data->dh->prime_len - wpabuf_len(shared);
+       pad = os_zalloc(pad_len ? pad_len : 1);
+       if (pad == NULL) {
+               wpabuf_free(shared);
+               os_free(buf);
+               return -1;
+       }
+       addr[0] = pad;
+       len[0] = pad_len;
+       addr[1] = wpabuf_head(shared);
+       len[1] = wpabuf_len(shared);
+       if (ikev2_prf_hash(prf->id, buf, data->i_nonce_len + data->r_nonce_len,
+                          2, addr, len, skeyseed) < 0) {
+               wpabuf_free(shared);
+               os_free(buf);
+               os_free(pad);
+               return -1;
+       }
+       os_free(pad);
+       wpabuf_free(shared);
+
+       /* DH parameters are not needed anymore, so free them */
+       wpabuf_free(data->r_dh_public);
+       data->r_dh_public = NULL;
+       wpabuf_free(data->i_dh_private);
+       data->i_dh_private = NULL;
+
+       wpa_hexdump_key(MSG_DEBUG, "IKEV2: SKEYSEED",
+                       skeyseed, prf->hash_len);
+
+       ret = ikev2_derive_sk_keys(prf, integ, encr, skeyseed, buf, buf_len,
+                                  &data->keys);
+       os_free(buf);
+       return ret;
+}
+
+
+static int ikev2_parse_transform(struct ikev2_initiator_data *data,
+                                struct ikev2_proposal_data *prop,
+                                const u8 *pos, const u8 *end)
+{
+       int transform_len;
+       const struct ikev2_transform *t;
+       u16 transform_id;
+       const u8 *tend;
+
+       if (end - pos < (int) sizeof(*t)) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short transform");
+               return -1;
+       }
+
+       t = (const struct ikev2_transform *) pos;
+       transform_len = WPA_GET_BE16(t->transform_length);
+       if (transform_len < (int) sizeof(*t) || pos + transform_len > end) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid transform length %d",
+                          transform_len);
+               return -1;
+       }
+       tend = pos + transform_len;
+
+       transform_id = WPA_GET_BE16(t->transform_id);
+
+       wpa_printf(MSG_DEBUG, "IKEV2:   Transform:");
+       wpa_printf(MSG_DEBUG, "IKEV2:     Type: %d  Transform Length: %d  "
+                  "Transform Type: %d  Transform ID: %d",
+                  t->type, transform_len, t->transform_type, transform_id);
+
+       if (t->type != 0 && t->type != 3) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected Transform type");
+               return -1;
+       }
+
+       pos = (const u8 *) (t + 1);
+       if (pos < tend) {
+               wpa_hexdump(MSG_DEBUG, "IKEV2:     Transform Attributes",
+                           pos, tend - pos);
+       }
+
+       switch (t->transform_type) {
+       case IKEV2_TRANSFORM_ENCR:
+               if (ikev2_get_encr(transform_id) &&
+                   transform_id == data->proposal.encr) {
+                       if (transform_id == ENCR_AES_CBC) {
+                               if (tend - pos != 4) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: No "
+                                                  "Transform Attr for AES");
+                                       break;
+                               }
+                               if (WPA_GET_BE16(pos) != 0x800e) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: Not a "
+                                                  "Key Size attribute for "
+                                                  "AES");
+                                       break;
+                               }
+                               if (WPA_GET_BE16(pos + 2) != 128) {
+                                       wpa_printf(MSG_DEBUG, "IKEV2: "
+                                                  "Unsupported AES key size "
+                                                  "%d bits",
+                                                  WPA_GET_BE16(pos + 2));
+                                       break;
+                               }
+                       }
+                       prop->encr = transform_id;
+               }
+               break;
+       case IKEV2_TRANSFORM_PRF:
+               if (ikev2_get_prf(transform_id) &&
+                   transform_id == data->proposal.prf)
+                       prop->prf = transform_id;
+               break;
+       case IKEV2_TRANSFORM_INTEG:
+               if (ikev2_get_integ(transform_id) &&
+                   transform_id == data->proposal.integ)
+                       prop->integ = transform_id;
+               break;
+       case IKEV2_TRANSFORM_DH:
+               if (dh_groups_get(transform_id) &&
+                   transform_id == data->proposal.dh)
+                       prop->dh = transform_id;
+               break;
+       }
+
+       return transform_len;
+}
+
+
+static int ikev2_parse_proposal(struct ikev2_initiator_data *data,
+                               struct ikev2_proposal_data *prop,
+                               const u8 *pos, const u8 *end)
+{
+       const u8 *pend, *ppos;
+       int proposal_len, i;
+       const struct ikev2_proposal *p;
+
+       if (end - pos < (int) sizeof(*p)) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short proposal");
+               return -1;
+       }
+
+       p = (const struct ikev2_proposal *) pos;
+       proposal_len = WPA_GET_BE16(p->proposal_length);
+       if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
+                          proposal_len);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "IKEV2: SAi1 Proposal # %d",
+                  p->proposal_num);
+       wpa_printf(MSG_DEBUG, "IKEV2:   Type: %d  Proposal Length: %d "
+                  " Protocol ID: %d",
+                  p->type, proposal_len, p->protocol_id);
+       wpa_printf(MSG_DEBUG, "IKEV2:   SPI Size: %d  Transforms: %d",
+                  p->spi_size, p->num_transforms);
+
+       if (p->type != 0 && p->type != 2) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal type");
+               return -1;
+       }
+
+       if (p->protocol_id != IKEV2_PROTOCOL_IKE) {
+               wpa_printf(MSG_DEBUG, "IKEV2: Unexpected Protocol ID "
+                          "(only IKE allowed for EAP-IKEv2)");
+               return -1;
+       }
+
+       if (p->proposal_num != prop->proposal_num) {
+               if (p->proposal_num == prop->proposal_num + 1)
+                       prop->proposal_num = p->proposal_num;
+               else {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Proposal #");
+                       return -1;
+               }
+       }
+
+       ppos = (const u8 *) (p + 1);
+       pend = pos + proposal_len;
+       if (ppos + p->spi_size > pend) {
+               wpa_printf(MSG_INFO, "IKEV2: Not enough room for SPI "
+                          "in proposal");
+               return -1;
+       }
+       if (p->spi_size) {
+               wpa_hexdump(MSG_DEBUG, "IKEV2:    SPI",
+                           ppos, p->spi_size);
+               ppos += p->spi_size;
+       }
+
+       /*
+        * For initial IKE_SA negotiation, SPI Size MUST be zero; for
+        * subsequent negotiations, it must be 8 for IKE. We only support
+        * initial case for now.
+        */
+       if (p->spi_size != 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected SPI Size");
+               return -1;
+       }
+
+       if (p->num_transforms == 0) {
+               wpa_printf(MSG_INFO, "IKEV2: At least one transform required");
+               return -1;
+       }
+
+       for (i = 0; i < (int) p->num_transforms; i++) {
+               int tlen = ikev2_parse_transform(data, prop, ppos, pend);
+               if (tlen < 0)
+                       return -1;
+               ppos += tlen;
+       }
+
+       if (ppos != pend) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected data after "
+                          "transforms");
+               return -1;
+       }
+
+       return proposal_len;
+}
+
+
+static int ikev2_process_sar1(struct ikev2_initiator_data *data,
+                             const u8 *sar1, size_t sar1_len)
+{
+       struct ikev2_proposal_data prop;
+       const u8 *pos, *end;
+       int found = 0;
+
+       /* Security Association Payloads: <Proposals> */
+
+       if (sar1 == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: SAr1 not received");
+               return -1;
+       }
+
+       os_memset(&prop, 0, sizeof(prop));
+       prop.proposal_num = 1;
+
+       pos = sar1;
+       end = sar1 + sar1_len;
+
+       while (pos < end) {
+               int plen;
+
+               prop.integ = -1;
+               prop.prf = -1;
+               prop.encr = -1;
+               prop.dh = -1;
+               plen = ikev2_parse_proposal(data, &prop, pos, end);
+               if (plen < 0)
+                       return -1;
+
+               if (!found && prop.integ != -1 && prop.prf != -1 &&
+                   prop.encr != -1 && prop.dh != -1) {
+                       found = 1;
+               }
+
+               pos += plen;
+
+               /* Only one proposal expected in SAr */
+               break;
+       }
+
+       if (pos != end) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected data after proposal");
+               return -1;
+       }
+
+       if (!found) {
+               wpa_printf(MSG_INFO, "IKEV2: No acceptable proposal found");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Accepted proposal #%d: ENCR:%d PRF:%d "
+                  "INTEG:%d D-H:%d", data->proposal.proposal_num,
+                  data->proposal.encr, data->proposal.prf,
+                  data->proposal.integ, data->proposal.dh);
+
+       return 0;
+}
+
+
+static int ikev2_process_ker(struct ikev2_initiator_data *data,
+                            const u8 *ker, size_t ker_len)
+{
+       u16 group;
+
+       /*
+        * Key Exchange Payload:
+        * DH Group # (16 bits)
+        * RESERVED (16 bits)
+        * Key Exchange Data (Diffie-Hellman public value)
+        */
+
+       if (ker == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: KEr not received");
+               return -1;
+       }
+
+       if (ker_len < 4 + 96) {
+               wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+               return -1;
+       }
+
+       group = WPA_GET_BE16(ker);
+       wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u", group);
+
+       if (group != data->proposal.dh) {
+               wpa_printf(MSG_DEBUG, "IKEV2: KEr DH Group #%u does not match "
+                          "with the selected proposal (%u)",
+                          group, data->proposal.dh);
+               return -1;
+       }
+
+       if (data->dh == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported DH group");
+               return -1;
+       }
+
+       /* RFC 4306, Section 3.4:
+        * The length of DH public value MUST be equal to the lenght of the
+        * prime modulus.
+        */
+       if (ker_len - 4 != data->dh->prime_len) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid DH public value length "
+                          "%ld (expected %ld)",
+                          (long) (ker_len - 4), (long) data->dh->prime_len);
+               return -1;
+       }
+
+       wpabuf_free(data->r_dh_public);
+       data->r_dh_public = wpabuf_alloc_copy(ker + 4, ker_len - 4);
+       if (data->r_dh_public == NULL)
+               return -1;
+
+       wpa_hexdump_buf(MSG_DEBUG, "IKEV2: KEr Diffie-Hellman Public Value",
+                       data->r_dh_public);
+       
+       return 0;
+}
+
+
+static int ikev2_process_nr(struct ikev2_initiator_data *data,
+                           const u8 *nr, size_t nr_len)
+{
+       if (nr == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Nr not received");
+               return -1;
+       }
+
+       if (nr_len < IKEV2_NONCE_MIN_LEN || nr_len > IKEV2_NONCE_MAX_LEN) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid Nr length %ld",
+                          (long) nr_len);
+               return -1;
+       }
+
+       data->r_nonce_len = nr_len;
+       os_memcpy(data->r_nonce, nr, nr_len);
+       wpa_hexdump(MSG_MSGDUMP, "IKEV2: Nr",
+                   data->r_nonce, data->r_nonce_len);
+
+       return 0;
+}
+
+
+static int ikev2_process_sa_init_encr(struct ikev2_initiator_data *data,
+                                     const struct ikev2_hdr *hdr,
+                                     const u8 *encrypted,
+                                     size_t encrypted_len, u8 next_payload)
+{
+       u8 *decrypted;
+       size_t decrypted_len;
+       struct ikev2_payloads pl;
+       int ret = 0;
+
+       decrypted = ikev2_decrypt_payload(data->proposal.encr,
+                                         data->proposal.integ, &data->keys, 0,
+                                         hdr, encrypted, encrypted_len,
+                                         &decrypted_len);
+       if (decrypted == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+       if (ikev2_parse_payloads(&pl, next_payload, decrypted,
+                                decrypted + decrypted_len) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+                          "payloads");
+               return -1;
+       }
+
+       if (pl.idr)
+               ret = ikev2_process_idr(data, pl.idr, pl.idr_len);
+
+       os_free(decrypted);
+
+       return ret;
+}
+
+
+static int ikev2_process_sa_init(struct ikev2_initiator_data *data,
+                                const struct ikev2_hdr *hdr,
+                                struct ikev2_payloads *pl)
+{
+       if (ikev2_process_sar1(data, pl->sa, pl->sa_len) < 0 ||
+           ikev2_process_ker(data, pl->ke, pl->ke_len) < 0 ||
+           ikev2_process_nr(data, pl->nonce, pl->nonce_len) < 0)
+               return -1;
+
+       os_memcpy(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN);
+
+       if (ikev2_derive_keys(data) < 0)
+               return -1;
+
+       if (pl->encrypted) {
+               wpa_printf(MSG_DEBUG, "IKEV2: Encrypted payload in SA_INIT - "
+                          "try to get IDr from it");
+               if (ikev2_process_sa_init_encr(data, hdr, pl->encrypted,
+                                              pl->encrypted_len,
+                                              pl->encr_next_payload) < 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Failed to process "
+                                  "encrypted payload");
+                       return -1;
+               }
+       }
+
+       data->state = SA_AUTH;
+
+       return 0;
+}
+
+
+static int ikev2_process_idr(struct ikev2_initiator_data *data,
+                            const u8 *idr, size_t idr_len)
+{
+       u8 id_type;
+
+       if (idr == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No IDr received");
+               return -1;
+       }
+
+       if (idr_len < 4) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short IDr payload");
+               return -1;
+       }
+
+       id_type = idr[0];
+       idr += 4;
+       idr_len -= 4;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: IDr ID Type %d", id_type);
+       wpa_hexdump_ascii(MSG_DEBUG, "IKEV2: IDr", idr, idr_len);
+       if (data->IDr) {
+               if (id_type != data->IDr_type || idr_len != data->IDr_len ||
+                   os_memcmp(idr, data->IDr, idr_len) != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: IDr differs from the one "
+                                  "received earlier");
+                       wpa_printf(MSG_DEBUG, "IKEV2: Previous IDr ID Type %d",
+                                  id_type);
+                       wpa_hexdump_ascii(MSG_DEBUG, "Previous IKEV2: IDr",
+                                         data->IDr, data->IDr_len);
+                       return -1;
+               }
+               os_free(data->IDr);
+       }
+       data->IDr = os_malloc(idr_len);
+       if (data->IDr == NULL)
+               return -1;
+       os_memcpy(data->IDr, idr, idr_len);
+       data->IDr_len = idr_len;
+       data->IDr_type = id_type;
+
+       return 0;
+}
+
+
+static int ikev2_process_cert(struct ikev2_initiator_data *data,
+                             const u8 *cert, size_t cert_len)
+{
+       u8 cert_encoding;
+
+       if (cert == NULL) {
+               if (data->peer_auth == PEER_AUTH_CERT) {
+                       wpa_printf(MSG_INFO, "IKEV2: No Certificate received");
+                       return -1;
+               }
+               return 0;
+       }
+
+       if (cert_len < 1) {
+               wpa_printf(MSG_INFO, "IKEV2: No Cert Encoding field");
+               return -1;
+       }
+
+       cert_encoding = cert[0];
+       cert++;
+       cert_len--;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Cert Encoding %d", cert_encoding);
+       wpa_hexdump(MSG_MSGDUMP, "IKEV2: Certificate Data", cert, cert_len);
+
+       /* TODO: validate certificate */
+
+       return 0;
+}
+
+
+static int ikev2_process_auth_cert(struct ikev2_initiator_data *data,
+                                  u8 method, const u8 *auth, size_t auth_len)
+{
+       if (method != AUTH_RSA_SIGN) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+                          "method %d", method);
+               return -1;
+       }
+
+       /* TODO: validate AUTH */
+       return 0;
+}
+
+
+static int ikev2_process_auth_secret(struct ikev2_initiator_data *data,
+                                    u8 method, const u8 *auth,
+                                    size_t auth_len)
+{
+       u8 auth_data[IKEV2_MAX_HASH_LEN];
+       const struct ikev2_prf_alg *prf;
+
+       if (method != AUTH_SHARED_KEY_MIC) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported authentication "
+                          "method %d", method);
+               return -1;
+       }
+
+       /* msg | Ni | prf(SK_pr,IDr') */
+       if (ikev2_derive_auth_data(data->proposal.prf, data->r_sign_msg,
+                                  data->IDr, data->IDr_len, data->IDr_type,
+                                  &data->keys, 0, data->shared_secret,
+                                  data->shared_secret_len,
+                                  data->i_nonce, data->i_nonce_len,
+                                  data->key_pad, data->key_pad_len,
+                                  auth_data) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+               return -1;
+       }
+
+       wpabuf_free(data->r_sign_msg);
+       data->r_sign_msg = NULL;
+
+       prf = ikev2_get_prf(data->proposal.prf);
+       if (prf == NULL)
+               return -1;
+
+       if (auth_len != prf->hash_len ||
+           os_memcmp(auth, auth_data, auth_len) != 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
+               wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
+                           auth, auth_len);
+               wpa_hexdump(MSG_DEBUG, "IKEV2: Expected Authentication Data",
+                           auth_data, prf->hash_len);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Peer authenticated successfully "
+                  "using shared keys");
+
+       return 0;
+}
+
+
+static int ikev2_process_auth(struct ikev2_initiator_data *data,
+                             const u8 *auth, size_t auth_len)
+{
+       u8 auth_method;
+
+       if (auth == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No Authentication Payload");
+               return -1;
+       }
+
+       if (auth_len < 4) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short Authentication "
+                          "Payload");
+               return -1;
+       }
+
+       auth_method = auth[0];
+       auth += 4;
+       auth_len -= 4;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Auth Method %d", auth_method);
+       wpa_hexdump(MSG_MSGDUMP, "IKEV2: Authentication Data", auth, auth_len);
+
+       switch (data->peer_auth) {
+       case PEER_AUTH_CERT:
+               return ikev2_process_auth_cert(data, auth_method, auth,
+                                              auth_len);
+       case PEER_AUTH_SECRET:
+               return ikev2_process_auth_secret(data, auth_method, auth,
+                                                auth_len);
+       }
+
+       return -1;
+}
+
+
+static int ikev2_process_sa_auth_decrypted(struct ikev2_initiator_data *data,
+                                          u8 next_payload,
+                                          u8 *payload, size_t payload_len)
+{
+       struct ikev2_payloads pl;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Processing decrypted payloads");
+
+       if (ikev2_parse_payloads(&pl, next_payload, payload, payload +
+                                payload_len) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Failed to parse decrypted "
+                          "payloads");
+               return -1;
+       }
+
+       if (ikev2_process_idr(data, pl.idr, pl.idr_len) < 0 ||
+           ikev2_process_cert(data, pl.cert, pl.cert_len) < 0 ||
+           ikev2_process_auth(data, pl.auth, pl.auth_len) < 0)
+               return -1;
+
+       return 0;
+}
+
+
+static int ikev2_process_sa_auth(struct ikev2_initiator_data *data,
+                                const struct ikev2_hdr *hdr,
+                                struct ikev2_payloads *pl)
+{
+       u8 *decrypted;
+       size_t decrypted_len;
+       int ret;
+
+       decrypted = ikev2_decrypt_payload(data->proposal.encr,
+                                         data->proposal.integ,
+                                         &data->keys, 0, hdr, pl->encrypted,
+                                         pl->encrypted_len, &decrypted_len);
+       if (decrypted == NULL)
+               return -1;
+
+       ret = ikev2_process_sa_auth_decrypted(data, pl->encr_next_payload,
+                                             decrypted, decrypted_len);
+       os_free(decrypted);
+
+       if (ret == 0 && !data->unknown_user) {
+               wpa_printf(MSG_DEBUG, "IKEV2: Authentication completed");
+               data->state = IKEV2_DONE;
+       }
+
+       return ret;
+}
+
+
+static int ikev2_validate_rx_state(struct ikev2_initiator_data *data,
+                                  u8 exchange_type, u32 message_id)
+{
+       switch (data->state) {
+       case SA_INIT:
+               /* Expect to receive IKE_SA_INIT: HDR, SAr, KEr, Nr, [CERTREQ],
+                * [SK{IDr}] */
+               if (exchange_type != IKE_SA_INIT) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+                                  "%u in SA_INIT state", exchange_type);
+                       return -1;
+               }
+               if (message_id != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+                                  "in SA_INIT state", message_id);
+                       return -1;
+               }
+               break;
+       case SA_AUTH:
+               /* Expect to receive IKE_SA_AUTH:
+                * HDR, SK {IDr, [CERT,] [CERTREQ,] [NFID,] AUTH}
+                */
+               if (exchange_type != IKE_SA_AUTH) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+                                  "%u in SA_AUTH state", exchange_type);
+                       return -1;
+               }
+               if (message_id != 1) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+                                  "in SA_AUTH state", message_id);
+                       return -1;
+               }
+               break;
+       case CHILD_SA:
+               if (exchange_type != CREATE_CHILD_SA) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Exchange Type "
+                                  "%u in CHILD_SA state", exchange_type);
+                       return -1;
+               }
+               if (message_id != 2) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected Message ID %u "
+                                  "in CHILD_SA state", message_id);
+                       return -1;
+               }
+               break;
+       case IKEV2_DONE:
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int ikev2_initiator_process(struct ikev2_initiator_data *data,
+                           const struct wpabuf *buf)
+{
+       const struct ikev2_hdr *hdr;
+       u32 length, message_id;
+       const u8 *pos, *end;
+       struct ikev2_payloads pl;
+
+       wpa_printf(MSG_MSGDUMP, "IKEV2: Received message (len %lu)",
+                  (unsigned long) wpabuf_len(buf));
+
+       if (wpabuf_len(buf) < sizeof(*hdr)) {
+               wpa_printf(MSG_INFO, "IKEV2: Too short frame to include HDR");
+               return -1;
+       }
+
+       hdr = (const struct ikev2_hdr *) wpabuf_head(buf);
+       end = wpabuf_head_u8(buf) + wpabuf_len(buf);
+       message_id = WPA_GET_BE32(hdr->message_id);
+       length = WPA_GET_BE32(hdr->length);
+
+       wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
+                   hdr->i_spi, IKEV2_SPI_LEN);
+       wpa_hexdump(MSG_DEBUG, "IKEV2:   IKE_SA Initiator's SPI",
+                   hdr->r_spi, IKEV2_SPI_LEN);
+       wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Version: 0x%x  "
+                  "Exchange Type: %u",
+                  hdr->next_payload, hdr->version, hdr->exchange_type);
+       wpa_printf(MSG_DEBUG, "IKEV2:   Message ID: %u  Length: %u",
+                  message_id, length);
+
+       if (hdr->version != IKEV2_VERSION) {
+               wpa_printf(MSG_INFO, "IKEV2: Unsupported HDR version 0x%x "
+                          "(expected 0x%x)", hdr->version, IKEV2_VERSION);
+               return -1;
+       }
+
+       if (length != wpabuf_len(buf)) {
+               wpa_printf(MSG_INFO, "IKEV2: Invalid length (HDR: %lu != "
+                          "RX: %lu)", (unsigned long) length,
+                          (unsigned long) wpabuf_len(buf));
+               return -1;
+       }
+
+       if (ikev2_validate_rx_state(data, hdr->exchange_type, message_id) < 0)
+               return -1;
+
+       if ((hdr->flags & (IKEV2_HDR_INITIATOR | IKEV2_HDR_RESPONSE)) !=
+           IKEV2_HDR_RESPONSE) {
+               wpa_printf(MSG_INFO, "IKEV2: Unexpected Flags value 0x%x",
+                          hdr->flags);
+               return -1;
+       }
+
+       if (data->state != SA_INIT) {
+               if (os_memcmp(data->i_spi, hdr->i_spi, IKEV2_SPI_LEN) != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+                                  "Initiator's SPI");
+                       return -1;
+               }
+               if (os_memcmp(data->r_spi, hdr->r_spi, IKEV2_SPI_LEN) != 0) {
+                       wpa_printf(MSG_INFO, "IKEV2: Unexpected IKE_SA "
+                                  "Responder's SPI");
+                       return -1;
+               }
+       }
+
+       pos = (const u8 *) (hdr + 1);
+       if (ikev2_parse_payloads(&pl, hdr->next_payload, pos, end) < 0)
+               return -1;
+
+       switch (data->state) {
+       case SA_INIT:
+               if (ikev2_process_sa_init(data, hdr, &pl) < 0)
+                       return -1;
+               wpabuf_free(data->r_sign_msg);
+               data->r_sign_msg = wpabuf_dup(buf);
+               break;
+       case SA_AUTH:
+               if (ikev2_process_sa_auth(data, hdr, &pl) < 0)
+                       return -1;
+               break;
+       case CHILD_SA:
+       case IKEV2_DONE:
+               break;
+       }
+
+       return 0;
+}
+
+
+static void ikev2_build_hdr(struct ikev2_initiator_data *data,
+                           struct wpabuf *msg, u8 exchange_type,
+                           u8 next_payload, u32 message_id)
+{
+       struct ikev2_hdr *hdr;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding HDR");
+
+       /* HDR - RFC 4306, Sect. 3.1 */
+       hdr = wpabuf_put(msg, sizeof(*hdr));
+       os_memcpy(hdr->i_spi, data->i_spi, IKEV2_SPI_LEN);
+       os_memcpy(hdr->r_spi, data->r_spi, IKEV2_SPI_LEN);
+       hdr->next_payload = next_payload;
+       hdr->version = IKEV2_VERSION;
+       hdr->exchange_type = exchange_type;
+       hdr->flags = IKEV2_HDR_INITIATOR;
+       WPA_PUT_BE32(hdr->message_id, message_id);
+}
+
+
+static int ikev2_build_sai(struct ikev2_initiator_data *data,
+                           struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       struct ikev2_proposal *p;
+       struct ikev2_transform *t;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding SAi payload");
+
+       /* SAi1 - RFC 4306, Sect. 2.7 and 3.3 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+
+       /* TODO: support for multiple proposals */
+       p = wpabuf_put(msg, sizeof(*p));
+       p->proposal_num = data->proposal.proposal_num;
+       p->protocol_id = IKEV2_PROTOCOL_IKE;
+       p->num_transforms = 4;
+
+       t = wpabuf_put(msg, sizeof(*t));
+       t->type = 3;
+       t->transform_type = IKEV2_TRANSFORM_ENCR;
+       WPA_PUT_BE16(t->transform_id, data->proposal.encr);
+       if (data->proposal.encr == ENCR_AES_CBC) {
+               /* Transform Attribute: Key Len = 128 bits */
+               wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
+               wpabuf_put_be16(msg, 128); /* 128-bit key */
+       }
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
+       WPA_PUT_BE16(t->transform_length, plen);
+
+       t = wpabuf_put(msg, sizeof(*t));
+       t->type = 3;
+       WPA_PUT_BE16(t->transform_length, sizeof(*t));
+       t->transform_type = IKEV2_TRANSFORM_PRF;
+       WPA_PUT_BE16(t->transform_id, data->proposal.prf);
+
+       t = wpabuf_put(msg, sizeof(*t));
+       t->type = 3;
+       WPA_PUT_BE16(t->transform_length, sizeof(*t));
+       t->transform_type = IKEV2_TRANSFORM_INTEG;
+       WPA_PUT_BE16(t->transform_id, data->proposal.integ);
+
+       t = wpabuf_put(msg, sizeof(*t));
+       WPA_PUT_BE16(t->transform_length, sizeof(*t));
+       t->transform_type = IKEV2_TRANSFORM_DH;
+       WPA_PUT_BE16(t->transform_id, data->proposal.dh);
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) p;
+       WPA_PUT_BE16(p->proposal_length, plen);
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+
+       return 0;
+}
+
+
+static int ikev2_build_kei(struct ikev2_initiator_data *data,
+                          struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       struct wpabuf *pv;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding KEi payload");
+
+       data->dh = dh_groups_get(data->proposal.dh);
+       pv = dh_init(data->dh, &data->i_dh_private);
+       if (pv == NULL) {
+               wpa_printf(MSG_DEBUG, "IKEV2: Failed to initialize DH");
+               return -1;
+       }
+
+       /* KEi - RFC 4306, Sect. 3.4 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+
+       wpabuf_put_be16(msg, data->proposal.dh); /* DH Group # */
+       wpabuf_put(msg, 2); /* RESERVED */
+       /*
+        * RFC 4306, Sect. 3.4: possible zero padding for public value to
+        * match the length of the prime.
+        */
+       wpabuf_put(msg, data->dh->prime_len - wpabuf_len(pv));
+       wpabuf_put_buf(msg, pv);
+       os_free(pv);
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_ni(struct ikev2_initiator_data *data,
+                         struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding Ni payload");
+
+       /* Ni - RFC 4306, Sect. 3.9 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+       wpabuf_put_data(msg, data->i_nonce, data->i_nonce_len);
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_idi(struct ikev2_initiator_data *data,
+                          struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding IDi payload");
+
+       if (data->IDi == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: No IDi available");
+               return -1;
+       }
+
+       /* IDi - RFC 4306, Sect. 3.5 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+       wpabuf_put_u8(msg, ID_KEY_ID);
+       wpabuf_put(msg, 3); /* RESERVED */
+       wpabuf_put_data(msg, data->IDi, data->IDi_len);
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static int ikev2_build_auth(struct ikev2_initiator_data *data,
+                           struct wpabuf *msg, u8 next_payload)
+{
+       struct ikev2_payload_hdr *phdr;
+       size_t plen;
+       const struct ikev2_prf_alg *prf;
+
+       wpa_printf(MSG_DEBUG, "IKEV2: Adding AUTH payload");
+
+       prf = ikev2_get_prf(data->proposal.prf);
+       if (prf == NULL)
+               return -1;
+
+       /* Authentication - RFC 4306, Sect. 3.8 */
+       phdr = wpabuf_put(msg, sizeof(*phdr));
+       phdr->next_payload = next_payload;
+       phdr->flags = 0;
+       wpabuf_put_u8(msg, AUTH_SHARED_KEY_MIC);
+       wpabuf_put(msg, 3); /* RESERVED */
+
+       /* msg | Nr | prf(SK_pi,IDi') */
+       if (ikev2_derive_auth_data(data->proposal.prf, data->i_sign_msg,
+                                  data->IDi, data->IDi_len, ID_KEY_ID,
+                                  &data->keys, 1, data->shared_secret,
+                                  data->shared_secret_len,
+                                  data->r_nonce, data->r_nonce_len,
+                                  data->key_pad, data->key_pad_len,
+                                  wpabuf_put(msg, prf->hash_len)) < 0) {
+               wpa_printf(MSG_INFO, "IKEV2: Could not derive AUTH data");
+               return -1;
+       }
+       wpabuf_free(data->i_sign_msg);
+       data->i_sign_msg = NULL;
+
+       plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) phdr;
+       WPA_PUT_BE16(phdr->payload_length, plen);
+       return 0;
+}
+
+
+static struct wpabuf * ikev2_build_sa_init(struct ikev2_initiator_data *data)
+{
+       struct wpabuf *msg;
+
+       /* build IKE_SA_INIT: HDR, SAi, KEi, Ni */
+
+       if (os_get_random(data->i_spi, IKEV2_SPI_LEN))
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "IKEV2: IKE_SA Initiator's SPI",
+                   data->i_spi, IKEV2_SPI_LEN);
+
+       data->i_nonce_len = IKEV2_NONCE_MIN_LEN;
+       if (os_get_random(data->i_nonce, data->i_nonce_len))
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "IKEV2: Ni", data->i_nonce, data->i_nonce_len);
+
+       msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + 1000);
+       if (msg == NULL)
+               return NULL;
+
+       ikev2_build_hdr(data, msg, IKE_SA_INIT, IKEV2_PAYLOAD_SA, 0);
+       if (ikev2_build_sai(data, msg, IKEV2_PAYLOAD_KEY_EXCHANGE) ||
+           ikev2_build_kei(data, msg, IKEV2_PAYLOAD_NONCE) ||
+           ikev2_build_ni(data, msg, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       ikev2_update_hdr(msg);
+
+       wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_INIT)", msg);
+
+       wpabuf_free(data->i_sign_msg);
+       data->i_sign_msg = wpabuf_dup(msg);
+
+       return msg;
+}
+
+
+static struct wpabuf * ikev2_build_sa_auth(struct ikev2_initiator_data *data)
+{
+       struct wpabuf *msg, *plain;
+       const u8 *secret;
+       size_t secret_len;
+
+       secret = data->get_shared_secret(data->cb_ctx, data->IDr,
+                                        data->IDr_len, &secret_len);
+       if (secret == NULL) {
+               wpa_printf(MSG_INFO, "IKEV2: Could not get shared secret - "
+                          "use fake value");
+               /* RFC 5106, Sect. 7:
+                * Use a random key to fake AUTH generation in order to prevent
+                * probing of user identities.
+                */
+               data->unknown_user = 1;
+               os_free(data->shared_secret);
+               data->shared_secret = os_malloc(16);
+               if (data->shared_secret == NULL)
+                       return NULL;
+               data->shared_secret_len = 16;
+               if (os_get_random(data->shared_secret, 16))
+                       return NULL;
+       } else {
+               os_free(data->shared_secret);
+               data->shared_secret = os_malloc(secret_len);
+               if (data->shared_secret == NULL)
+                       return NULL;
+               os_memcpy(data->shared_secret, secret, secret_len);
+               data->shared_secret_len = secret_len;
+       }
+
+       /* build IKE_SA_AUTH: HDR, SK {IDi, [CERT,] [CERTREQ,] AUTH} */
+
+       msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1000);
+       if (msg == NULL)
+               return NULL;
+       ikev2_build_hdr(data, msg, IKE_SA_AUTH, IKEV2_PAYLOAD_ENCRYPTED, 1);
+
+       plain = wpabuf_alloc(data->IDr_len + 1000);
+       if (plain == NULL) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (ikev2_build_idi(data, plain, IKEV2_PAYLOAD_AUTHENTICATION) ||
+           ikev2_build_auth(data, plain, IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) ||
+           ikev2_build_encrypted(data->proposal.encr, data->proposal.integ,
+                                 &data->keys, 1, msg, plain,
+                                 IKEV2_PAYLOAD_IDi)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wpa_hexdump_buf(MSG_MSGDUMP, "IKEV2: Sending message (SA_AUTH)", msg);
+
+       return msg;
+}
+
+
+struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data)
+{
+       switch (data->state) {
+       case SA_INIT:
+               return ikev2_build_sa_init(data);
+       case SA_AUTH:
+               return ikev2_build_sa_auth(data);
+       case CHILD_SA:
+               return NULL;
+       case IKEV2_DONE:
+               return NULL;
+       }
+       return NULL;
+}
diff --git a/src/eap_server/ikev2.h b/src/eap_server/ikev2.h
new file mode 100644 (file)
index 0000000..8349fbe
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * IKEv2 initiator (RFC 4306) for EAP-IKEV2
+ * 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.
+ */
+
+#ifndef IKEV2_H
+#define IKEV2_H
+
+#include "eap_common/ikev2_common.h"
+
+struct ikev2_proposal_data {
+       u8 proposal_num;
+       int integ;
+       int prf;
+       int encr;
+       int dh;
+};
+
+
+struct ikev2_initiator_data {
+       enum { SA_INIT, SA_AUTH, CHILD_SA, IKEV2_DONE } state;
+       u8 i_spi[IKEV2_SPI_LEN];
+       u8 r_spi[IKEV2_SPI_LEN];
+       u8 i_nonce[IKEV2_NONCE_MAX_LEN];
+       size_t i_nonce_len;
+       u8 r_nonce[IKEV2_NONCE_MAX_LEN];
+       size_t r_nonce_len;
+       struct wpabuf *r_dh_public;
+       struct wpabuf *i_dh_private;
+       struct ikev2_proposal_data proposal;
+       const struct dh_group *dh;
+       struct ikev2_keys keys;
+       u8 *IDi;
+       size_t IDi_len;
+       u8 *IDr;
+       size_t IDr_len;
+       u8 IDr_type;
+       struct wpabuf *r_sign_msg;
+       struct wpabuf *i_sign_msg;
+       u8 *shared_secret;
+       size_t shared_secret_len;
+       enum { PEER_AUTH_CERT, PEER_AUTH_SECRET } peer_auth;
+       u8 *key_pad;
+       size_t key_pad_len;
+
+       const u8 * (*get_shared_secret)(void *ctx, const u8 *IDr,
+                                       size_t IDr_len, size_t *secret_len);
+       void *cb_ctx;
+       int unknown_user;
+};
+
+
+void ikev2_initiator_deinit(struct ikev2_initiator_data *data);
+int ikev2_initiator_process(struct ikev2_initiator_data *data,
+                           const struct wpabuf *buf);
+struct wpabuf * ikev2_initiator_build(struct ikev2_initiator_data *data);
+
+#endif /* IKEV2_H */
diff --git a/src/eap_server/tncs.c b/src/eap_server/tncs.c
new file mode 100644 (file)
index 0000000..497b51a
--- /dev/null
@@ -0,0 +1,1273 @@
+/*
+ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
+ * Copyright (c) 2007-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 <dlfcn.h>
+
+#include "common.h"
+#include "base64.h"
+#include "tncs.h"
+#include "eap_common/eap_tlv_common.h"
+#include "eap_common/eap_defs.h"
+
+
+/* TODO: TNCS must be thread-safe; review the code and add locking etc. if
+ * needed.. */
+
+#define TNC_CONFIG_FILE "/etc/tnc_config"
+#define IF_TNCCS_START \
+"<?xml version=\"1.0\"?>\n" \
+"<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
+"xmlns=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/IF_TNCCS#\" " \
+"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " \
+"xsi:schemaLocation=\"http://www.trustedcomputinggroup.org/IWG/TNC/1_0/" \
+"IF_TNCCS# https://www.trustedcomputinggroup.org/XML/SCHEMA/TNCCS_1.0.xsd\">\n"
+#define IF_TNCCS_END "\n</TNCCS-Batch>"
+
+/* TNC IF-IMV */
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMVID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
+typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_Subtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+typedef TNC_UInt32 TNC_AttributeID;
+
+typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
+       TNC_IMVID imvID,
+       char *functionName,
+       void **pOutfunctionPointer);
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_IFIMV_VERSION_1 1
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION               0x00000001
+#define TNC_TNCCS_ERROR                                0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE            0x00000003
+#define TNC_TNCCS_REASONSTRINGS                        0x00000004
+
+/* Possible TNC_IMV_Action_Recommendation values: */
+enum IMV_Action_Recommendation {
+       TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+       TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
+       TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
+       TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+/* Possible TNC_IMV_Evaluation_Result values: */
+enum IMV_Evaluation_Result {
+       TNC_IMV_EVALUATION_RESULT_COMPLIANT,
+       TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
+       TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
+       TNC_IMV_EVALUATION_RESULT_ERROR,
+       TNC_IMV_EVALUATION_RESULT_DONT_KNOW
+};
+
+struct tnc_if_imv {
+       struct tnc_if_imv *next;
+       char *name;
+       char *path;
+       void *dlhandle; /* from dlopen() */
+       TNC_IMVID imvID;
+       TNC_MessageTypeList supported_types;
+       size_t num_supported_types;
+
+       /* Functions implemented by IMVs (with TNC_IMV_ prefix) */
+       TNC_Result (*Initialize)(
+               TNC_IMVID imvID,
+               TNC_Version minVersion,
+               TNC_Version maxVersion,
+               TNC_Version *pOutActualVersion);
+       TNC_Result (*NotifyConnectionChange)(
+               TNC_IMVID imvID,
+               TNC_ConnectionID connectionID,
+               TNC_ConnectionState newState);
+       TNC_Result (*ReceiveMessage)(
+               TNC_IMVID imvID,
+               TNC_ConnectionID connectionID,
+               TNC_BufferReference message,
+               TNC_UInt32 messageLength,
+               TNC_MessageType messageType);
+       TNC_Result (*SolicitRecommendation)(
+               TNC_IMVID imvID,
+               TNC_ConnectionID connectionID);
+       TNC_Result (*BatchEnding)(
+               TNC_IMVID imvID,
+               TNC_ConnectionID connectionID);
+       TNC_Result (*Terminate)(TNC_IMVID imvID);
+       TNC_Result (*ProvideBindFunction)(
+               TNC_IMVID imvID,
+               TNC_TNCS_BindFunctionPointer bindFunction);
+};
+
+
+#define TNC_MAX_IMV_ID 10
+
+struct tncs_data {
+       struct tncs_data *next;
+       struct tnc_if_imv *imv; /* local copy of tncs_global_data->imv */
+       TNC_ConnectionID connectionID;
+       unsigned int last_batchid;
+       enum IMV_Action_Recommendation recommendation;
+       int done;
+
+       struct conn_imv {
+               u8 *imv_send;
+               size_t imv_send_len;
+               enum IMV_Action_Recommendation recommendation;
+               int recommendation_set;
+       } imv_data[TNC_MAX_IMV_ID];
+
+       char *tncs_message;
+};
+
+
+struct tncs_global {
+       struct tnc_if_imv *imv;
+       TNC_ConnectionID next_conn_id;
+       struct tncs_data *connections;
+};
+
+static struct tncs_global *tncs_global_data = NULL;
+
+
+static struct tnc_if_imv * tncs_get_imv(TNC_IMVID imvID)
+{
+       struct tnc_if_imv *imv;
+
+       if (imvID >= TNC_MAX_IMV_ID || tncs_global_data == NULL)
+               return NULL;
+       imv = tncs_global_data->imv;
+       while (imv) {
+               if (imv->imvID == imvID)
+                       return imv;
+               imv = imv->next;
+       }
+       return NULL;
+}
+
+
+static struct tncs_data * tncs_get_conn(TNC_ConnectionID connectionID)
+{
+       struct tncs_data *tncs;
+
+       if (tncs_global_data == NULL)
+               return NULL;
+
+       tncs = tncs_global_data->connections;
+       while (tncs) {
+               if (tncs->connectionID == connectionID)
+                       return tncs;
+               tncs = tncs->next;
+       }
+
+       wpa_printf(MSG_DEBUG, "TNC: Connection ID %lu not found",
+                  (unsigned long) connectionID);
+
+       return NULL;
+}
+
+
+/* TNCS functions that IMVs can call */
+TNC_Result TNC_TNCS_ReportMessageTypes(
+       TNC_IMVID imvID,
+       TNC_MessageTypeList supportedTypes,
+       TNC_UInt32 typeCount)
+{
+       TNC_UInt32 i;
+       struct tnc_if_imv *imv;
+
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ReportMessageTypes(imvID=%lu "
+                  "typeCount=%lu)",
+                  (unsigned long) imvID, (unsigned long) typeCount);
+
+       for (i = 0; i < typeCount; i++) {
+               wpa_printf(MSG_DEBUG, "TNC: supportedTypes[%lu] = %lu",
+                          i, supportedTypes[i]);
+       }
+
+       imv = tncs_get_imv(imvID);
+       if (imv == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+       os_free(imv->supported_types);
+       imv->supported_types =
+               os_malloc(typeCount * sizeof(TNC_MessageTypeList));
+       if (imv->supported_types == NULL)
+               return TNC_RESULT_FATAL;
+       os_memcpy(imv->supported_types, supportedTypes,
+                 typeCount * sizeof(TNC_MessageTypeList));
+       imv->num_supported_types = typeCount;
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_SendMessage(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_BufferReference message,
+       TNC_UInt32 messageLength,
+       TNC_MessageType messageType)
+{
+       struct tncs_data *tncs;
+       unsigned char *b64;
+       size_t b64len;
+
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage(imvID=%lu "
+                  "connectionID=%lu messageType=%lu)",
+                  imvID, connectionID, messageType);
+       wpa_hexdump_ascii(MSG_DEBUG, "TNC: TNC_TNCS_SendMessage",
+                         message, messageLength);
+
+       if (tncs_get_imv(imvID) == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       tncs = tncs_get_conn(connectionID);
+       if (tncs == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       b64 = base64_encode(message, messageLength, &b64len);
+       if (b64 == NULL)
+               return TNC_RESULT_FATAL;
+
+       os_free(tncs->imv_data[imvID].imv_send);
+       tncs->imv_data[imvID].imv_send_len = 0;
+       tncs->imv_data[imvID].imv_send = os_zalloc(b64len + 100);
+       if (tncs->imv_data[imvID].imv_send == NULL) {
+               os_free(b64);
+               return TNC_RESULT_OTHER;
+       }
+
+       tncs->imv_data[imvID].imv_send_len =
+               os_snprintf((char *) tncs->imv_data[imvID].imv_send,
+                           b64len + 100,
+                           "<IMC-IMV-Message><Type>%08X</Type>"
+                           "<Base64>%s</Base64></IMC-IMV-Message>",
+                           (unsigned int) messageType, b64);
+
+       os_free(b64);
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_RequestHandshakeRetry(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_RetryReason reason)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_RequestHandshakeRetry");
+       /* TODO */
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_ProvideRecommendation(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_IMV_Action_Recommendation recommendation,
+       TNC_IMV_Evaluation_Result evaluation)
+{
+       struct tncs_data *tncs;
+
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_ProvideRecommendation(imvID=%lu "
+                  "connectionID=%lu recommendation=%lu evaluation=%lu)",
+                  (unsigned long) imvID, (unsigned long) connectionID,
+                  (unsigned long) recommendation, (unsigned long) evaluation);
+
+       if (tncs_get_imv(imvID) == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       tncs = tncs_get_conn(connectionID);
+       if (tncs == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       tncs->imv_data[imvID].recommendation = recommendation;
+       tncs->imv_data[imvID].recommendation_set = 1;
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_GetAttribute(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_AttributeID attribureID,
+       TNC_UInt32 bufferLength,
+       TNC_BufferReference buffer,
+       TNC_UInt32 *pOutValueLength)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_GetAttribute");
+       /* TODO */
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_SetAttribute(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_AttributeID attribureID,
+       TNC_UInt32 bufferLength,
+       TNC_BufferReference buffer)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_SetAttribute");
+       /* TODO */
+       return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_TNCS_BindFunction(
+       TNC_IMVID imvID,
+       char *functionName,
+       void **pOutFunctionPointer)
+{
+       wpa_printf(MSG_DEBUG, "TNC: TNC_TNCS_BindFunction(imcID=%lu, "
+                  "functionName='%s')", (unsigned long) imvID, functionName);
+
+       if (tncs_get_imv(imvID) == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       if (pOutFunctionPointer == NULL)
+               return TNC_RESULT_INVALID_PARAMETER;
+
+       if (os_strcmp(functionName, "TNC_TNCS_ReportMessageTypes") == 0)
+               *pOutFunctionPointer = TNC_TNCS_ReportMessageTypes;
+       else if (os_strcmp(functionName, "TNC_TNCS_SendMessage") == 0)
+               *pOutFunctionPointer = TNC_TNCS_SendMessage;
+       else if (os_strcmp(functionName, "TNC_TNCS_RequestHandshakeRetry") ==
+                0)
+               *pOutFunctionPointer = TNC_TNCS_RequestHandshakeRetry;
+       else if (os_strcmp(functionName, "TNC_TNCS_ProvideRecommendation") ==
+                0)
+               *pOutFunctionPointer = TNC_TNCS_ProvideRecommendation;
+       else if (os_strcmp(functionName, "TNC_TNCS_GetAttribute") == 0)
+               *pOutFunctionPointer = TNC_TNCS_GetAttribute;
+       else if (os_strcmp(functionName, "TNC_TNCS_SetAttribute") == 0)
+               *pOutFunctionPointer = TNC_TNCS_SetAttribute;
+       else
+               *pOutFunctionPointer = NULL;
+
+       return TNC_RESULT_SUCCESS;
+}
+
+
+static void * tncs_get_sym(void *handle, char *func)
+{
+       void *fptr;
+
+       fptr = dlsym(handle, func);
+
+       return fptr;
+}
+
+
+static int tncs_imv_resolve_funcs(struct tnc_if_imv *imv)
+{
+       void *handle = imv->dlhandle;
+
+       /* Mandatory IMV functions */
+       imv->Initialize = tncs_get_sym(handle, "TNC_IMV_Initialize");
+       if (imv->Initialize == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+                          "TNC_IMV_Initialize");
+               return -1;
+       }
+
+       imv->SolicitRecommendation = tncs_get_sym(
+               handle, "TNC_IMV_SolicitRecommendation");
+       if (imv->SolicitRecommendation == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+                          "TNC_IMV_SolicitRecommendation");
+               return -1;
+       }
+
+       imv->ProvideBindFunction =
+               tncs_get_sym(handle, "TNC_IMV_ProvideBindFunction");
+       if (imv->ProvideBindFunction == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: IMV does not export "
+                          "TNC_IMV_ProvideBindFunction");
+               return -1;
+       }
+
+       /* Optional IMV functions */
+       imv->NotifyConnectionChange =
+               tncs_get_sym(handle, "TNC_IMV_NotifyConnectionChange");
+       imv->ReceiveMessage = tncs_get_sym(handle, "TNC_IMV_ReceiveMessage");
+       imv->BatchEnding = tncs_get_sym(handle, "TNC_IMV_BatchEnding");
+       imv->Terminate = tncs_get_sym(handle, "TNC_IMV_Terminate");
+
+       return 0;
+}
+
+
+static int tncs_imv_initialize(struct tnc_if_imv *imv)
+{
+       TNC_Result res;
+       TNC_Version imv_ver;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Initialize for IMV '%s'",
+                  imv->name);
+       res = imv->Initialize(imv->imvID, TNC_IFIMV_VERSION_1,
+                             TNC_IFIMV_VERSION_1, &imv_ver);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Initialize: res=%lu imv_ver=%lu",
+                  (unsigned long) res, (unsigned long) imv_ver);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_terminate(struct tnc_if_imv *imv)
+{
+       TNC_Result res;
+
+       if (imv->Terminate == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_Terminate for IMV '%s'",
+                  imv->name);
+       res = imv->Terminate(imv->imvID);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_Terminate: %lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_provide_bind_function(struct tnc_if_imv *imv)
+{
+       TNC_Result res;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_ProvideBindFunction for "
+                  "IMV '%s'", imv->name);
+       res = imv->ProvideBindFunction(imv->imvID, TNC_TNCS_BindFunction);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMV_ProvideBindFunction: res=%lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_imv_notify_connection_change(struct tnc_if_imv *imv,
+                                            TNC_ConnectionID conn,
+                                            TNC_ConnectionState state)
+{
+       TNC_Result res;
+
+       if (imv->NotifyConnectionChange == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "TNC: Calling TNC_IMV_NotifyConnectionChange(%d)"
+                  " for IMV '%s'", (int) state, imv->name);
+       res = imv->NotifyConnectionChange(imv->imvID, conn, state);
+       wpa_printf(MSG_DEBUG, "TNC: TNC_IMC_NotifyConnectionChange: %lu",
+                  (unsigned long) res);
+
+       return res == TNC_RESULT_SUCCESS ? 0 : -1;
+}
+
+
+static int tncs_load_imv(struct tnc_if_imv *imv)
+{
+       if (imv->path == NULL) {
+               wpa_printf(MSG_DEBUG, "TNC: No IMV configured");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TNC: Opening IMV: %s (%s)",
+                  imv->name, imv->path);
+       imv->dlhandle = dlopen(imv->path, RTLD_LAZY);
+       if (imv->dlhandle == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to open IMV '%s' (%s): %s",
+                          imv->name, imv->path, dlerror());
+               return -1;
+       }
+
+       if (tncs_imv_resolve_funcs(imv) < 0) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to resolve IMV functions");
+               return -1;
+       }
+
+       if (tncs_imv_initialize(imv) < 0 ||
+           tncs_imv_provide_bind_function(imv) < 0) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to initialize IMV");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void tncs_free_imv(struct tnc_if_imv *imv)
+{
+       os_free(imv->name);
+       os_free(imv->path);
+       os_free(imv->supported_types);
+}
+
+static void tncs_unload_imv(struct tnc_if_imv *imv)
+{
+       tncs_imv_terminate(imv);
+
+       if (imv->dlhandle)
+               dlclose(imv->dlhandle);
+
+       tncs_free_imv(imv);
+}
+
+
+static int tncs_supported_type(struct tnc_if_imv *imv, unsigned int type)
+{
+       size_t i;
+       unsigned int vendor, subtype;
+
+       if (imv == NULL || imv->supported_types == NULL)
+               return 0;
+
+       vendor = type >> 8;
+       subtype = type & 0xff;
+
+       for (i = 0; i < imv->num_supported_types; i++) {
+               unsigned int svendor, ssubtype;
+               svendor = imv->supported_types[i] >> 8;
+               ssubtype = imv->supported_types[i] & 0xff;
+               if ((vendor == svendor || svendor == TNC_VENDORID_ANY) &&
+                   (subtype == ssubtype || ssubtype == TNC_SUBTYPE_ANY))
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static void tncs_send_to_imvs(struct tncs_data *tncs, unsigned int type,
+                             const u8 *msg, size_t len)
+{
+       struct tnc_if_imv *imv;
+       TNC_Result res;
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "TNC: Message to IMV(s)", msg, len);
+
+       for (imv = tncs->imv; imv; imv = imv->next) {
+               if (imv->ReceiveMessage == NULL ||
+                   !tncs_supported_type(imv, type))
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "TNC: Call ReceiveMessage for IMV '%s'",
+                          imv->name);
+               res = imv->ReceiveMessage(imv->imvID, tncs->connectionID,
+                                         (TNC_BufferReference) msg, len,
+                                         type);
+               wpa_printf(MSG_DEBUG, "TNC: ReceiveMessage: %lu",
+                          (unsigned long) res);
+       }
+}
+
+
+static void tncs_batch_ending(struct tncs_data *tncs)
+{
+       struct tnc_if_imv *imv;
+       TNC_Result res;
+
+       for (imv = tncs->imv; imv; imv = imv->next) {
+               if (imv->BatchEnding == NULL)
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "TNC: Call BatchEnding for IMV '%s'",
+                          imv->name);
+               res = imv->BatchEnding(imv->imvID, tncs->connectionID);
+               wpa_printf(MSG_DEBUG, "TNC: BatchEnding: %lu",
+                          (unsigned long) res);
+       }
+}
+
+
+static void tncs_solicit_recommendation(struct tncs_data *tncs)
+{
+       struct tnc_if_imv *imv;
+       TNC_Result res;
+
+       for (imv = tncs->imv; imv; imv = imv->next) {
+               if (tncs->imv_data[imv->imvID].recommendation_set)
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "TNC: Call SolicitRecommendation for "
+                          "IMV '%s'", imv->name);
+               res = imv->SolicitRecommendation(imv->imvID,
+                                                tncs->connectionID);
+               wpa_printf(MSG_DEBUG, "TNC: SolicitRecommendation: %lu",
+                          (unsigned long) res);
+       }
+}
+
+
+void tncs_init_connection(struct tncs_data *tncs)
+{
+       struct tnc_if_imv *imv;
+       int i;
+
+       for (imv = tncs->imv; imv; imv = imv->next) {
+               tncs_imv_notify_connection_change(
+                       imv, tncs->connectionID, TNC_CONNECTION_STATE_CREATE);
+               tncs_imv_notify_connection_change(
+                       imv, tncs->connectionID,
+                       TNC_CONNECTION_STATE_HANDSHAKE);
+       }
+
+       for (i = 0; i < TNC_MAX_IMV_ID; i++) {
+               os_free(tncs->imv_data[i].imv_send);
+               tncs->imv_data[i].imv_send = NULL;
+               tncs->imv_data[i].imv_send_len = 0;
+       }
+}
+
+
+size_t tncs_total_send_len(struct tncs_data *tncs)
+{
+       int i;
+       size_t len = 0;
+
+       for (i = 0; i < TNC_MAX_IMV_ID; i++)
+               len += tncs->imv_data[i].imv_send_len;
+       if (tncs->tncs_message)
+               len += os_strlen(tncs->tncs_message);
+       return len;
+}
+
+
+u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos)
+{
+       int i;
+
+       for (i = 0; i < TNC_MAX_IMV_ID; i++) {
+               if (tncs->imv_data[i].imv_send == NULL)
+                       continue;
+
+               os_memcpy(pos, tncs->imv_data[i].imv_send,
+                         tncs->imv_data[i].imv_send_len);
+               pos += tncs->imv_data[i].imv_send_len;
+               os_free(tncs->imv_data[i].imv_send);
+               tncs->imv_data[i].imv_send = NULL;
+               tncs->imv_data[i].imv_send_len = 0;
+       }
+
+       if (tncs->tncs_message) {
+               size_t len = os_strlen(tncs->tncs_message);
+               os_memcpy(pos, tncs->tncs_message, len);
+               pos += len;
+               os_free(tncs->tncs_message);
+               tncs->tncs_message = NULL;
+       }
+
+       return pos;
+}
+
+
+char * tncs_if_tnccs_start(struct tncs_data *tncs)
+{
+       char *buf = os_malloc(1000);
+       if (buf == NULL)
+               return NULL;
+       tncs->last_batchid++;
+       os_snprintf(buf, 1000, IF_TNCCS_START, tncs->last_batchid);
+       return buf;
+}
+
+
+char * tncs_if_tnccs_end(void)
+{
+       char *buf = os_malloc(100);
+       if (buf == NULL)
+               return NULL;
+       os_snprintf(buf, 100, IF_TNCCS_END);
+       return buf;
+}
+
+
+static int tncs_get_type(char *start, unsigned int *type)
+{
+       char *pos = os_strstr(start, "<Type>");
+       if (pos == NULL)
+               return -1;
+       pos += 6;
+       *type = strtoul(pos, NULL, 16);
+       return 0;
+}
+
+
+static unsigned char * tncs_get_base64(char *start, size_t *decoded_len)
+{
+       char *pos, *pos2;
+       unsigned char *decoded;
+
+       pos = os_strstr(start, "<Base64>");
+       if (pos == NULL)
+               return NULL;
+
+       pos += 8;
+       pos2 = os_strstr(pos, "</Base64>");
+       if (pos2 == NULL)
+               return NULL;
+       *pos2 = '\0';
+
+       decoded = base64_decode((unsigned char *) pos, os_strlen(pos),
+                               decoded_len);
+       *pos2 = '<';
+       if (decoded == NULL) {
+               wpa_printf(MSG_DEBUG, "TNC: Failed to decode Base64 data");
+       }
+
+       return decoded;
+}
+
+
+static enum tncs_process_res tncs_derive_recommendation(struct tncs_data *tncs)
+{
+       enum IMV_Action_Recommendation rec;
+       struct tnc_if_imv *imv;
+       TNC_ConnectionState state;
+       char *txt;
+
+       wpa_printf(MSG_DEBUG, "TNC: No more messages from IMVs");
+
+       if (tncs->done)
+               return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+
+       tncs_solicit_recommendation(tncs);
+
+       /* Select the most restrictive recommendation */
+       rec = TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION;
+       for (imv = tncs->imv; imv; imv = imv->next) {
+               TNC_IMV_Action_Recommendation irec;
+               irec = tncs->imv_data[imv->imvID].recommendation;
+               if (irec == TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
+                       rec = TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS;
+               if (irec == TNC_IMV_ACTION_RECOMMENDATION_ISOLATE &&
+                   rec != TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS)
+                       rec = TNC_IMV_ACTION_RECOMMENDATION_ISOLATE;
+               if (irec == TNC_IMV_ACTION_RECOMMENDATION_ALLOW &&
+                   rec == TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION)
+                       rec = TNC_IMV_ACTION_RECOMMENDATION_ALLOW;
+       }
+
+       wpa_printf(MSG_DEBUG, "TNC: Recommendation: %d", rec);
+       tncs->recommendation = rec;
+       tncs->done = 1;
+
+       txt = NULL;
+       switch (rec) {
+       case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+       case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+               txt = "allow";
+               state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+               break;
+       case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+               txt = "isolate";
+               state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
+               break;
+       case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+               txt = "none";
+               state = TNC_CONNECTION_STATE_ACCESS_NONE;
+               break;
+       default:
+               state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
+               break;
+       }
+
+       if (txt) {
+               os_free(tncs->tncs_message);
+               tncs->tncs_message = os_zalloc(200);
+               if (tncs->tncs_message) {
+                       os_snprintf(tncs->tncs_message, 199,
+                                   "<TNCC-TNCS-Message><Type>%08X</Type>"
+                                   "<XML><TNCCS-Recommendation type=\"%s\">"
+                                   "</TNCCS-Recommendation></XML>"
+                                   "</TNCC-TNCS-Message>",
+                                   TNC_TNCCS_RECOMMENDATION, txt);
+               }
+       }
+
+       for (imv = tncs->imv; imv; imv = imv->next) {
+               tncs_imv_notify_connection_change(imv, tncs->connectionID,
+                                                 state);
+       }
+
+       switch (rec) {
+       case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
+               return TNCCS_RECOMMENDATION_ALLOW;
+       case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
+               return TNCCS_RECOMMENDATION_NO_ACCESS;
+       case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
+               return TNCCS_RECOMMENDATION_ISOLATE;
+       case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
+               return TNCCS_RECOMMENDATION_NO_RECOMMENDATION;
+       default:
+               return TNCCS_PROCESS_ERROR;
+       }
+}
+
+
+enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
+                                           const u8 *msg, size_t len)
+{
+       char *buf, *start, *end, *pos, *pos2, *payload;
+       unsigned int batch_id;
+       unsigned char *decoded;
+       size_t decoded_len;
+
+       buf = os_malloc(len + 1);
+       if (buf == NULL)
+               return TNCCS_PROCESS_ERROR;
+
+       os_memcpy(buf, msg, len);
+       buf[len] = '\0';
+       start = os_strstr(buf, "<TNCCS-Batch ");
+       end = os_strstr(buf, "</TNCCS-Batch>");
+       if (start == NULL || end == NULL || start > end) {
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+
+       start += 13;
+       while (*start == ' ')
+               start++;
+       *end = '\0';
+
+       pos = os_strstr(start, "BatchId=");
+       if (pos == NULL) {
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+
+       pos += 8;
+       if (*pos == '"')
+               pos++;
+       batch_id = atoi(pos);
+       wpa_printf(MSG_DEBUG, "TNC: Received IF-TNCCS BatchId=%u",
+                  batch_id);
+       if (batch_id != tncs->last_batchid + 1) {
+               wpa_printf(MSG_DEBUG, "TNC: Unexpected IF-TNCCS BatchId "
+                          "%u (expected %u)",
+                          batch_id, tncs->last_batchid + 1);
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+       tncs->last_batchid = batch_id;
+
+       while (*pos != '\0' && *pos != '>')
+               pos++;
+       if (*pos == '\0') {
+               os_free(buf);
+               return TNCCS_PROCESS_ERROR;
+       }
+       pos++;
+       payload = start;
+
+       /*
+        * <IMC-IMV-Message>
+        * <Type>01234567</Type>
+        * <Base64>foo==</Base64>
+        * </IMC-IMV-Message>
+        */
+
+       while (*start) {
+               char *endpos;
+               unsigned int type;
+
+               pos = os_strstr(start, "<IMC-IMV-Message>");
+               if (pos == NULL)
+                       break;
+               start = pos + 17;
+               end = os_strstr(start, "</IMC-IMV-Message>");
+               if (end == NULL)
+                       break;
+               *end = '\0';
+               endpos = end;
+               end += 18;
+
+               if (tncs_get_type(start, &type) < 0) {
+                       *endpos = '<';
+                       start = end;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "TNC: IMC-IMV-Message Type 0x%x", type);
+
+               decoded = tncs_get_base64(start, &decoded_len);
+               if (decoded == NULL) {
+                       *endpos = '<';
+                       start = end;
+                       continue;
+               }
+
+               tncs_send_to_imvs(tncs, type, decoded, decoded_len);
+
+               os_free(decoded);
+
+               start = end;
+       }
+
+       /*
+        * <TNCC-TNCS-Message>
+        * <Type>01234567</Type>
+        * <XML><TNCCS-Foo type="foo"></TNCCS-Foo></XML>
+        * <Base64>foo==</Base64>
+        * </TNCC-TNCS-Message>
+        */
+
+       start = payload;
+       while (*start) {
+               unsigned int type;
+               char *xml, *xmlend, *endpos;
+
+               pos = os_strstr(start, "<TNCC-TNCS-Message>");
+               if (pos == NULL)
+                       break;
+               start = pos + 19;
+               end = os_strstr(start, "</TNCC-TNCS-Message>");
+               if (end == NULL)
+                       break;
+               *end = '\0';
+               endpos = end;
+               end += 20;
+
+               if (tncs_get_type(start, &type) < 0) {
+                       *endpos = '<';
+                       start = end;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "TNC: TNCC-TNCS-Message Type 0x%x",
+                          type);
+
+               /* Base64 OR XML */
+               decoded = NULL;
+               xml = NULL;
+               xmlend = NULL;
+               pos = os_strstr(start, "<XML>");
+               if (pos) {
+                       pos += 5;
+                       pos2 = os_strstr(pos, "</XML>");
+                       if (pos2 == NULL) {
+                               *endpos = '<';
+                               start = end;
+                               continue;
+                       }
+                       xmlend = pos2;
+                       xml = pos;
+               } else {
+                       decoded = tncs_get_base64(start, &decoded_len);
+                       if (decoded == NULL) {
+                               *endpos = '<';
+                               start = end;
+                               continue;
+                       }
+               }
+
+               if (decoded) {
+                       wpa_hexdump_ascii(MSG_MSGDUMP,
+                                         "TNC: TNCC-TNCS-Message Base64",
+                                         decoded, decoded_len);
+                       os_free(decoded);
+               }
+
+               if (xml) {
+                       wpa_hexdump_ascii(MSG_MSGDUMP,
+                                         "TNC: TNCC-TNCS-Message XML",
+                                         (unsigned char *) xml,
+                                         xmlend - xml);
+               }
+
+               start = end;
+       }
+
+       os_free(buf);
+
+       tncs_batch_ending(tncs);
+
+       if (tncs_total_send_len(tncs) == 0)
+               return tncs_derive_recommendation(tncs);
+
+       return TNCCS_PROCESS_OK_NO_RECOMMENDATION;
+}
+
+
+static struct tnc_if_imv * tncs_parse_imv(int id, char *start, char *end,
+                                         int *error)
+{
+       struct tnc_if_imv *imv;
+       char *pos, *pos2;
+
+       if (id >= TNC_MAX_IMV_ID) {
+               wpa_printf(MSG_DEBUG, "TNC: Too many IMVs");
+               return NULL;
+       }
+
+       imv = os_zalloc(sizeof(*imv));
+       if (imv == NULL) {
+               *error = 1;
+               return NULL;
+       }
+
+       imv->imvID = id;
+
+       pos = start;
+       wpa_printf(MSG_DEBUG, "TNC: Configured IMV: %s", pos);
+       if (pos + 1 >= end || *pos != '"') {
+               wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+                          "(no starting quotation mark)", start);
+               os_free(imv);
+               return NULL;
+       }
+
+       pos++;
+       pos2 = pos;
+       while (pos2 < end && *pos2 != '"')
+               pos2++;
+       if (pos2 >= end) {
+               wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+                          "(no ending quotation mark)", start);
+               os_free(imv);
+               return NULL;
+       }
+       *pos2 = '\0';
+       wpa_printf(MSG_DEBUG, "TNC: Name: '%s'", pos);
+       imv->name = os_strdup(pos);
+
+       pos = pos2 + 1;
+       if (pos >= end || *pos != ' ') {
+               wpa_printf(MSG_ERROR, "TNC: Ignoring invalid IMV line '%s' "
+                          "(no space after name)", start);
+               os_free(imv);
+               return NULL;
+       }
+
+       pos++;
+       wpa_printf(MSG_DEBUG, "TNC: IMV file: '%s'", pos);
+       imv->path = os_strdup(pos);
+
+       return imv;
+}
+
+
+static int tncs_read_config(struct tncs_global *global)
+{
+       char *config, *end, *pos, *line_end;
+       size_t config_len;
+       struct tnc_if_imv *imv, *last;
+       int id = 0;
+
+       last = NULL;
+
+       config = os_readfile(TNC_CONFIG_FILE, &config_len);
+       if (config == NULL) {
+               wpa_printf(MSG_ERROR, "TNC: Could not open TNC configuration "
+                          "file '%s'", TNC_CONFIG_FILE);
+               return -1;
+       }
+
+       end = config + config_len;
+       for (pos = config; pos < end; pos = line_end + 1) {
+               line_end = pos;
+               while (*line_end != '\n' && *line_end != '\r' &&
+                      line_end < end)
+                       line_end++;
+               *line_end = '\0';
+
+               if (os_strncmp(pos, "IMV ", 4) == 0) {
+                       int error = 0;
+
+                       imv = tncs_parse_imv(id++, pos + 4, line_end, &error);
+                       if (error)
+                               return -1;
+                       if (imv) {
+                               if (last == NULL)
+                                       global->imv = imv;
+                               else
+                                       last->next = imv;
+                               last = imv;
+                       }
+               }
+       }
+
+       os_free(config);
+
+       return 0;
+}
+
+
+struct tncs_data * tncs_init(void)
+{
+       struct tncs_data *tncs;
+
+       if (tncs_global_data == NULL)
+               return NULL;
+
+       tncs = os_zalloc(sizeof(*tncs));
+       if (tncs == NULL)
+               return NULL;
+       tncs->imv = tncs_global_data->imv;
+       tncs->connectionID = tncs_global_data->next_conn_id++;
+       tncs->next = tncs_global_data->connections;
+       tncs_global_data->connections = tncs;
+
+       return tncs;
+}
+
+
+void tncs_deinit(struct tncs_data *tncs)
+{
+       int i;
+       struct tncs_data *prev, *conn;
+
+       if (tncs == NULL)
+               return;
+
+       for (i = 0; i < TNC_MAX_IMV_ID; i++)
+               os_free(tncs->imv_data[i].imv_send);
+
+       prev = NULL;
+       conn = tncs_global_data->connections;
+       while (conn) {
+               if (conn == tncs) {
+                       if (prev)
+                               prev->next = tncs->next;
+                       else
+                               tncs_global_data->connections = tncs->next;
+                       break;
+               }
+               prev = conn;
+               conn = conn->next;
+       }
+
+       os_free(tncs->tncs_message);
+       os_free(tncs);
+}
+
+
+int tncs_global_init(void)
+{
+       struct tnc_if_imv *imv;
+
+       tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
+       if (tncs_global_data == NULL)
+               return -1;
+
+       if (tncs_read_config(tncs_global_data) < 0) {
+               wpa_printf(MSG_ERROR, "TNC: Failed to read TNC configuration");
+               goto failed;
+       }
+
+       for (imv = tncs_global_data->imv; imv; imv = imv->next) {
+               if (tncs_load_imv(imv)) {
+                       wpa_printf(MSG_ERROR, "TNC: Failed to load IMV '%s'",
+                                  imv->name);
+                       goto failed;
+               }
+       }
+
+       return 0;
+
+failed:
+       tncs_global_deinit();
+       return -1;
+}
+
+
+void tncs_global_deinit(void)
+{
+       struct tnc_if_imv *imv, *prev;
+
+       if (tncs_global_data == NULL)
+               return;
+
+       imv = tncs_global_data->imv;
+       while (imv) {
+               tncs_unload_imv(imv);
+
+               prev = imv;
+               imv = imv->next;
+               os_free(prev);
+       }
+
+       os_free(tncs_global_data);
+       tncs_global_data = NULL;
+}
+
+
+struct wpabuf * tncs_build_soh_request(void)
+{
+       struct wpabuf *buf;
+
+       /*
+        * Build a SoH Request TLV (to be used inside SoH EAP Extensions
+        * Method)
+        */
+
+       buf = wpabuf_alloc(8 + 4);
+       if (buf == NULL)
+               return NULL;
+
+       /* Vendor-Specific TLV (Microsoft) - SoH Request */
+       wpabuf_put_be16(buf, EAP_TLV_VENDOR_SPECIFIC_TLV); /* TLV Type */
+       wpabuf_put_be16(buf, 8); /* Length */
+
+       wpabuf_put_be32(buf, EAP_VENDOR_MICROSOFT); /* Vendor_Id */
+
+       wpabuf_put_be16(buf, 0x02); /* TLV Type - SoH Request TLV */
+       wpabuf_put_be16(buf, 0); /* Length */
+
+       return buf;
+}
+
+
+struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
+                                int *failure)
+{
+       wpa_hexdump(MSG_DEBUG, "TNC: SoH TLV", soh_tlv, soh_tlv_len);
+       *failure = 0;
+
+       /* TODO: return MS-SoH Response TLV */
+
+       return NULL;
+}
diff --git a/src/eap_server/tncs.h b/src/eap_server/tncs.h
new file mode 100644 (file)
index 0000000..18a3a1f
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * EAP-TNC - TNCS (IF-IMV, IF-TNCCS, and IF-TNCCS-SOH)
+ * Copyright (c) 2007-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 TNCS_H
+#define TNCS_H
+
+struct tncs_data;
+
+struct tncs_data * tncs_init(void);
+void tncs_deinit(struct tncs_data *tncs);
+void tncs_init_connection(struct tncs_data *tncs);
+size_t tncs_total_send_len(struct tncs_data *tncs);
+u8 * tncs_copy_send_buf(struct tncs_data *tncs, u8 *pos);
+char * tncs_if_tnccs_start(struct tncs_data *tncs);
+char * tncs_if_tnccs_end(void);
+
+enum tncs_process_res {
+       TNCCS_PROCESS_ERROR = -1,
+       TNCCS_PROCESS_OK_NO_RECOMMENDATION = 0,
+       TNCCS_RECOMMENDATION_ERROR,
+       TNCCS_RECOMMENDATION_ALLOW,
+       TNCCS_RECOMMENDATION_NONE,
+       TNCCS_RECOMMENDATION_ISOLATE,
+       TNCCS_RECOMMENDATION_NO_ACCESS,
+       TNCCS_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+enum tncs_process_res tncs_process_if_tnccs(struct tncs_data *tncs,
+                                           const u8 *msg, size_t len);
+
+int tncs_global_init(void);
+void tncs_global_deinit(void);
+
+struct wpabuf * tncs_build_soh_request(void);
+struct wpabuf * tncs_process_soh(const u8 *soh_tlv, size_t soh_tlv_len,
+                                int *failure);
+
+#endif /* TNCS_H */
diff --git a/src/eapol_auth/Makefile b/src/eapol_auth/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/eapol_auth/eapol_auth_dump.c b/src/eapol_auth/eapol_auth_dump.c
new file mode 100644 (file)
index 0000000..a0f0e8d
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - State dump
+ * 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"
+
+#include "common.h"
+#include "eap_server/eap.h"
+#include "eapol_auth_sm.h"
+#include "eapol_auth_sm_i.h"
+
+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
+}
diff --git a/src/eapol_auth/eapol_auth_sm.c b/src/eapol_auth/eapol_auth_sm.c
new file mode 100644 (file)
index 0000000..a1976e8
--- /dev/null
@@ -0,0 +1,1139 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * 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"
+
+#include "common.h"
+#include "eloop.h"
+#include "state_machine.h"
+#include "common/eapol_common.h"
+#include "eap_common/eap_defs.h"
+#include "eap_common/eap_common.h"
+#include "eap_server/eap.h"
+#include "eapol_auth_sm.h"
+#include "eapol_auth_sm_i.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->eapol->conf.ctx, sm->sta, 1)
+#define setPortUnauthorized() \
+sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, 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->eapol->conf.ctx, sm->sta)
+#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, 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_initialize(struct eapol_state_machine *sm);
+
+
+static void eapol_auth_logger(struct eapol_authenticator *eapol,
+                             const u8 *addr, eapol_logger_level level,
+                             const char *txt)
+{
+       if (eapol->cb.logger == NULL)
+               return;
+       eapol->cb.logger(eapol->conf.ctx, addr, level, txt);
+}
+
+
+static void eapol_auth_vlogger(struct eapol_authenticator *eapol,
+                              const u8 *addr, eapol_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->eapol->conf.ctx, 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->eapol->conf.ctx, 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->eapol->conf.ctx, 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_server_get_name(0, 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_server_get_name(0, sm->eap_type_supp));
+       }
+       sm->eapol->cb.finished(sm->eapol->conf.ctx, 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 (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE)
+               extra = " (PMKSA cache)";
+       eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO,
+                          "authenticated - EAP type: %d (%s)%s",
+                          sm->eap_type_authsrv,
+                          eap_server_get_name(0, sm->eap_type_authsrv),
+                          extra);
+       sm->eapol->cb.finished(sm->eapol->conf.ctx, 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;
+       sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
+                                 EAPOL_AUTH_REAUTHENTICATE);
+}
+
+
+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 && !(sm->flags & EAPOL_SM_USES_WPA))
+                       SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT);
+               break;
+       case AUTH_KEY_TX_KEY_TRANSMIT:
+               if (!sm->keyTxEnabled || !sm->keyRun)
+                       SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT);
+               else if (sm->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 flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx)
+{
+       struct eapol_state_machine *sm;
+       struct eap_config eap_conf;
+
+       if (eapol == NULL)
+               return NULL;
+
+       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);
+       sm->flags = flags;
+
+       sm->eapol = eapol;
+       sm->sta = sta_ctx;
+
+       /* 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 &&
+           (eapol->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.msg_ctx = eapol->conf.msg_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 = assoc_wps_ie;
+       eap_conf.peer_addr = addr;
+       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.ctx, 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->eapol->conf.ctx, sm->sta,
+                               wpabuf_head(sm->eap_if->aaaEapRespData),
+                               wpabuf_len(sm->eap_if->aaaEapRespData));
+               }
+       }
+
+       if (eapol_sm_sta_entry_alive(eapol, addr))
+               sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta,
+                                         EAPOL_AUTH_SM_CHANGE);
+}
+
+
+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);
+}
+
+
+static 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);
+}
+
+
+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->eapol->conf.ctx, 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 =
+{
+       eapol_sm_get_eap_user,
+       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->ctx = src->ctx;
+       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->msg_ctx = src->msg_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;
+       }
+
+       if (conf->individual_wep_key_len > 0) {
+               /* use key0 in individual key and key1 in broadcast key */
+               eapol->default_wep_key_idx = 1;
+       }
+
+       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;
+       eapol->cb.eapol_event = cb->eapol_event;
+
+       return eapol;
+}
+
+
+void eapol_auth_deinit(struct eapol_authenticator *eapol)
+{
+       if (eapol == NULL)
+               return;
+
+       eapol_auth_conf_free(&eapol->conf);
+       os_free(eapol->default_wep_key);
+       os_free(eapol);
+}
diff --git a/src/eapol_auth/eapol_auth_sm.h b/src/eapol_auth/eapol_auth_sm.h
new file mode 100644 (file)
index 0000000..ef943ad
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine
+ * 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.
+ */
+
+#ifndef EAPOL_AUTH_SM_H
+#define EAPOL_AUTH_SM_H
+
+#define EAPOL_SM_PREAUTH BIT(0)
+#define EAPOL_SM_WAIT_START BIT(1)
+#define EAPOL_SM_USES_WPA BIT(2)
+#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3)
+
+struct eapol_auth_config {
+       int eap_reauth_period;
+       int wpa;
+       int individual_wep_key_len;
+       int eap_server;
+       void *ssl_ctx;
+       void *msg_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;
+
+       /* Opaque context pointer to owner data for callback functions */
+       void *ctx;
+};
+
+struct eap_user;
+
+typedef enum {
+       EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING
+} eapol_logger_level;
+
+enum eapol_event {
+       EAPOL_AUTH_SM_CHANGE,
+       EAPOL_AUTH_REAUTHENTICATE
+};
+
+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);
+       void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type);
+};
+
+
+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 flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx);
+void eapol_auth_free(struct eapol_state_machine *sm);
+void eapol_auth_step(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_AUTH_SM_H */
diff --git a/src/eapol_auth/eapol_auth_sm_i.h b/src/eapol_auth/eapol_auth_sm_i.h
new file mode 100644 (file)
index 0000000..1000da4
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions)
+ * 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.
+ */
+
+#ifndef EAPOL_AUTH_SM_I_H
+#define EAPOL_AUTH_SM_I_H
+
+#include "common/defs.h"
+#include "radius/radius.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 eapol_authenticator - Global EAPOL authenticator data
+ */
+struct eapol_authenticator {
+       struct eapol_auth_config conf;
+       struct eapol_auth_cb cb;
+
+       u8 *default_wep_key;
+       u8 default_wep_key_idx;
+};
+
+
+/**
+ * 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 */
+       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;
+
+       void *sta; /* station context pointer to use in callbacks */
+};
+
+#endif /* EAPOL_AUTH_SM_I_H */
diff --git a/src/eapol_supp/Makefile b/src/eapol_supp/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
new file mode 100644 (file)
index 0000000..77cd564
--- /dev/null
@@ -0,0 +1,1898 @@
+/*
+ * EAPOL supplicant state machines
+ * 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 "state_machine.h"
+#include "wpabuf.h"
+#include "eloop.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "common/eapol_common.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp_sm.h"
+
+#define STATE_MACHINE_DATA struct eapol_sm
+#define STATE_MACHINE_DEBUG_PREFIX "EAPOL"
+
+
+/* IEEE 802.1X-2004 - Supplicant - EAPOL state machines */
+
+/**
+ * struct eapol_sm - Internal data for EAPOL state machines
+ */
+struct eapol_sm {
+       /* Timers */
+       unsigned int authWhile;
+       unsigned int heldWhile;
+       unsigned int startWhen;
+       unsigned int idleWhile; /* for EAP state machine */
+       int timer_tick_enabled;
+
+       /* Global variables */
+       Boolean eapFail;
+       Boolean eapolEap;
+       Boolean eapSuccess;
+       Boolean initialize;
+       Boolean keyDone;
+       Boolean keyRun;
+       PortControl portControl;
+       Boolean portEnabled;
+       PortStatus suppPortStatus;  /* dot1xSuppControlledPortStatus */
+       Boolean portValid;
+       Boolean suppAbort;
+       Boolean suppFail;
+       Boolean suppStart;
+       Boolean suppSuccess;
+       Boolean suppTimeout;
+
+       /* Supplicant PAE state machine */
+       enum {
+               SUPP_PAE_UNKNOWN = 0,
+               SUPP_PAE_DISCONNECTED = 1,
+               SUPP_PAE_LOGOFF = 2,
+               SUPP_PAE_CONNECTING = 3,
+               SUPP_PAE_AUTHENTICATING = 4,
+               SUPP_PAE_AUTHENTICATED = 5,
+               /* unused(6) */
+               SUPP_PAE_HELD = 7,
+               SUPP_PAE_RESTART = 8,
+               SUPP_PAE_S_FORCE_AUTH = 9,
+               SUPP_PAE_S_FORCE_UNAUTH = 10
+       } SUPP_PAE_state; /* dot1xSuppPaeState */
+       /* Variables */
+       Boolean userLogoff;
+       Boolean logoffSent;
+       unsigned int startCount;
+       Boolean eapRestart;
+       PortControl sPortMode;
+       /* Constants */
+       unsigned int heldPeriod; /* dot1xSuppHeldPeriod */
+       unsigned int startPeriod; /* dot1xSuppStartPeriod */
+       unsigned int maxStart; /* dot1xSuppMaxStart */
+
+       /* Key Receive state machine */
+       enum {
+               KEY_RX_UNKNOWN = 0,
+               KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE
+       } KEY_RX_state;
+       /* Variables */
+       Boolean rxKey;
+
+       /* Supplicant Backend state machine */
+       enum {
+               SUPP_BE_UNKNOWN = 0,
+               SUPP_BE_INITIALIZE = 1,
+               SUPP_BE_IDLE = 2,
+               SUPP_BE_REQUEST = 3,
+               SUPP_BE_RECEIVE = 4,
+               SUPP_BE_RESPONSE = 5,
+               SUPP_BE_FAIL = 6,
+               SUPP_BE_TIMEOUT = 7, 
+               SUPP_BE_SUCCESS = 8
+       } SUPP_BE_state; /* dot1xSuppBackendPaeState */
+       /* Variables */
+       Boolean eapNoResp;
+       Boolean eapReq;
+       Boolean eapResp;
+       /* Constants */
+       unsigned int authPeriod; /* dot1xSuppAuthPeriod */
+
+       /* Statistics */
+       unsigned int dot1xSuppEapolFramesRx;
+       unsigned int dot1xSuppEapolFramesTx;
+       unsigned int dot1xSuppEapolStartFramesTx;
+       unsigned int dot1xSuppEapolLogoffFramesTx;
+       unsigned int dot1xSuppEapolRespFramesTx;
+       unsigned int dot1xSuppEapolReqIdFramesRx;
+       unsigned int dot1xSuppEapolReqFramesRx;
+       unsigned int dot1xSuppInvalidEapolFramesRx;
+       unsigned int dot1xSuppEapLengthErrorFramesRx;
+       unsigned int dot1xSuppLastEapolFrameVersion;
+       unsigned char dot1xSuppLastEapolFrameSource[6];
+
+       /* Miscellaneous variables (not defined in IEEE 802.1X-2004) */
+       Boolean changed;
+       struct eap_sm *eap;
+       struct eap_peer_config *config;
+       Boolean initial_req;
+       u8 *last_rx_key;
+       size_t last_rx_key_len;
+       struct wpabuf *eapReqData; /* for EAP */
+       Boolean altAccept; /* for EAP */
+       Boolean altReject; /* for EAP */
+       Boolean replay_counter_valid;
+       u8 last_replay_counter[16];
+       struct eapol_config conf;
+       struct eapol_ctx *ctx;
+       enum { EAPOL_CB_IN_PROGRESS = 0, EAPOL_CB_SUCCESS, EAPOL_CB_FAILURE }
+               cb_status;
+       Boolean cached_pmk;
+
+       Boolean unicast_key_received, broadcast_key_received;
+};
+
+
+#define IEEE8021X_REPLAY_COUNTER_LEN 8
+#define IEEE8021X_KEY_SIGN_LEN 16
+#define IEEE8021X_KEY_IV_LEN 16
+
+#define IEEE8021X_KEY_INDEX_FLAG 0x80
+#define IEEE8021X_KEY_INDEX_MASK 0x03
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct ieee802_1x_eapol_key {
+       u8 type;
+       /* Note: key_length is unaligned */
+       u8 key_length[2];
+       /* does not repeat within the life of the keying material used to
+        * encrypt the Key field; 64-bit NTP timestamp MAY be used here */
+       u8 replay_counter[IEEE8021X_REPLAY_COUNTER_LEN];
+       u8 key_iv[IEEE8021X_KEY_IV_LEN]; /* cryptographically random number */
+       u8 key_index; /* key flag in the most significant bit:
+                      * 0 = broadcast (default key),
+                      * 1 = unicast (key mapping key); key index is in the
+                      * 7 least significant bits */
+       /* HMAC-MD5 message integrity check computed with MS-MPPE-Send-Key as
+        * the key */
+       u8 key_signature[IEEE8021X_KEY_SIGN_LEN];
+
+       /* followed by key: if packet body length = 44 + key length, then the
+        * key field (of key_length bytes) contains the key in encrypted form;
+        * if packet body length = 44, key field is absent and key_length
+        * represents the number of least significant octets from
+        * MS-MPPE-Send-Key attribute to be used as the keying material;
+        * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm);
+static void eapol_sm_txStart(struct eapol_sm *sm);
+static void eapol_sm_processKey(struct eapol_sm *sm);
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm);
+static void eapol_sm_abortSupp(struct eapol_sm *sm);
+static void eapol_sm_abort_cached(struct eapol_sm *sm);
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx);
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm);
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm);
+
+
+/* Port Timers state machine - implemented as a function that will be called
+ * once a second as a registered event loop timeout */
+static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
+{
+       struct eapol_sm *sm = timeout_ctx;
+
+       if (sm->authWhile > 0) {
+               sm->authWhile--;
+               if (sm->authWhile == 0)
+                       wpa_printf(MSG_DEBUG, "EAPOL: authWhile --> 0");
+       }
+       if (sm->heldWhile > 0) {
+               sm->heldWhile--;
+               if (sm->heldWhile == 0)
+                       wpa_printf(MSG_DEBUG, "EAPOL: heldWhile --> 0");
+       }
+       if (sm->startWhen > 0) {
+               sm->startWhen--;
+               if (sm->startWhen == 0)
+                       wpa_printf(MSG_DEBUG, "EAPOL: startWhen --> 0");
+       }
+       if (sm->idleWhile > 0) {
+               sm->idleWhile--;
+               if (sm->idleWhile == 0)
+                       wpa_printf(MSG_DEBUG, "EAPOL: idleWhile --> 0");
+       }
+
+       if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
+               eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
+                                      sm);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
+               sm->timer_tick_enabled = 0;
+       }
+       eapol_sm_step(sm);
+}
+
+
+static void eapol_enable_timer_tick(struct eapol_sm *sm)
+{
+       if (sm->timer_tick_enabled)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
+       sm->timer_tick_enabled = 1;
+       eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+       eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+}
+
+
+SM_STATE(SUPP_PAE, LOGOFF)
+{
+       SM_ENTRY(SUPP_PAE, LOGOFF);
+       eapol_sm_txLogoff(sm);
+       sm->logoffSent = TRUE;
+       sm->suppPortStatus = Unauthorized;
+       eapol_sm_set_port_unauthorized(sm);
+}
+
+
+SM_STATE(SUPP_PAE, DISCONNECTED)
+{
+       SM_ENTRY(SUPP_PAE, DISCONNECTED);
+       sm->sPortMode = Auto;
+       sm->startCount = 0;
+       sm->logoffSent = FALSE;
+       sm->suppPortStatus = Unauthorized;
+       eapol_sm_set_port_unauthorized(sm);
+       sm->suppAbort = TRUE;
+
+       sm->unicast_key_received = FALSE;
+       sm->broadcast_key_received = FALSE;
+}
+
+
+SM_STATE(SUPP_PAE, CONNECTING)
+{
+       int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
+       SM_ENTRY(SUPP_PAE, CONNECTING);
+       if (send_start) {
+               sm->startWhen = sm->startPeriod;
+               sm->startCount++;
+       } else {
+               /*
+                * Do not send EAPOL-Start immediately since in most cases,
+                * Authenticator is going to start authentication immediately
+                * after association and an extra EAPOL-Start is just going to
+                * delay authentication. Use a short timeout to send the first
+                * EAPOL-Start if Authenticator does not start authentication.
+                */
+#ifdef CONFIG_WPS
+               /* Reduce latency on starting WPS negotiation. */
+               sm->startWhen = 1;
+#else /* CONFIG_WPS */
+               sm->startWhen = 3;
+#endif /* CONFIG_WPS */
+       }
+       eapol_enable_timer_tick(sm);
+       sm->eapolEap = FALSE;
+       if (send_start)
+               eapol_sm_txStart(sm);
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATING)
+{
+       SM_ENTRY(SUPP_PAE, AUTHENTICATING);
+       sm->startCount = 0;
+       sm->suppSuccess = FALSE;
+       sm->suppFail = FALSE;
+       sm->suppTimeout = FALSE;
+       sm->keyRun = FALSE;
+       sm->keyDone = FALSE;
+       sm->suppStart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, HELD)
+{
+       SM_ENTRY(SUPP_PAE, HELD);
+       sm->heldWhile = sm->heldPeriod;
+       eapol_enable_timer_tick(sm);
+       sm->suppPortStatus = Unauthorized;
+       eapol_sm_set_port_unauthorized(sm);
+       sm->cb_status = EAPOL_CB_FAILURE;
+}
+
+
+SM_STATE(SUPP_PAE, AUTHENTICATED)
+{
+       SM_ENTRY(SUPP_PAE, AUTHENTICATED);
+       sm->suppPortStatus = Authorized;
+       eapol_sm_set_port_authorized(sm);
+       sm->cb_status = EAPOL_CB_SUCCESS;
+}
+
+
+SM_STATE(SUPP_PAE, RESTART)
+{
+       SM_ENTRY(SUPP_PAE, RESTART);
+       sm->eapRestart = TRUE;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_AUTH)
+{
+       SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
+       sm->suppPortStatus = Authorized;
+       eapol_sm_set_port_authorized(sm);
+       sm->sPortMode = ForceAuthorized;
+}
+
+
+SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
+{
+       SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
+       sm->suppPortStatus = Unauthorized;
+       eapol_sm_set_port_unauthorized(sm);
+       sm->sPortMode = ForceUnauthorized;
+       eapol_sm_txLogoff(sm);
+}
+
+
+SM_STEP(SUPP_PAE)
+{
+       if ((sm->userLogoff && !sm->logoffSent) &&
+           !(sm->initialize || !sm->portEnabled))
+               SM_ENTER_GLOBAL(SUPP_PAE, LOGOFF);
+       else if (((sm->portControl == Auto) &&
+                 (sm->sPortMode != sm->portControl)) ||
+                sm->initialize || !sm->portEnabled)
+               SM_ENTER_GLOBAL(SUPP_PAE, DISCONNECTED);
+       else if ((sm->portControl == ForceAuthorized) &&
+                (sm->sPortMode != sm->portControl) &&
+                !(sm->initialize || !sm->portEnabled))
+               SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_AUTH);
+       else if ((sm->portControl == ForceUnauthorized) &&
+                (sm->sPortMode != sm->portControl) &&
+                !(sm->initialize || !sm->portEnabled))
+               SM_ENTER_GLOBAL(SUPP_PAE, S_FORCE_UNAUTH);
+       else switch (sm->SUPP_PAE_state) {
+       case SUPP_PAE_UNKNOWN:
+               break;
+       case SUPP_PAE_LOGOFF:
+               if (!sm->userLogoff)
+                       SM_ENTER(SUPP_PAE, DISCONNECTED);
+               break;
+       case SUPP_PAE_DISCONNECTED:
+               SM_ENTER(SUPP_PAE, CONNECTING);
+               break;
+       case SUPP_PAE_CONNECTING:
+               if (sm->startWhen == 0 && sm->startCount < sm->maxStart)
+                       SM_ENTER(SUPP_PAE, CONNECTING);
+               else if (sm->startWhen == 0 &&
+                        sm->startCount >= sm->maxStart &&
+                        sm->portValid)
+                       SM_ENTER(SUPP_PAE, AUTHENTICATED);
+               else if (sm->eapSuccess || sm->eapFail)
+                       SM_ENTER(SUPP_PAE, AUTHENTICATING);
+               else if (sm->eapolEap)
+                       SM_ENTER(SUPP_PAE, RESTART);
+               else if (sm->startWhen == 0 &&
+                        sm->startCount >= sm->maxStart &&
+                        !sm->portValid)
+                       SM_ENTER(SUPP_PAE, HELD);
+               break;
+       case SUPP_PAE_AUTHENTICATING:
+               if (sm->eapSuccess && !sm->portValid &&
+                   sm->conf.accept_802_1x_keys &&
+                   sm->conf.required_keys == 0) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: IEEE 802.1X for "
+                                  "plaintext connection; no EAPOL-Key frames "
+                                  "required");
+                       sm->portValid = TRUE;
+                       if (sm->ctx->eapol_done_cb)
+                               sm->ctx->eapol_done_cb(sm->ctx->ctx);
+               }
+               if (sm->eapSuccess && sm->portValid)
+                       SM_ENTER(SUPP_PAE, AUTHENTICATED);
+               else if (sm->eapFail || (sm->keyDone && !sm->portValid))
+                       SM_ENTER(SUPP_PAE, HELD);
+               else if (sm->suppTimeout)
+                       SM_ENTER(SUPP_PAE, CONNECTING);
+               break;
+       case SUPP_PAE_HELD:
+               if (sm->heldWhile == 0)
+                       SM_ENTER(SUPP_PAE, CONNECTING);
+               else if (sm->eapolEap)
+                       SM_ENTER(SUPP_PAE, RESTART);
+               break;
+       case SUPP_PAE_AUTHENTICATED:
+               if (sm->eapolEap && sm->portValid)
+                       SM_ENTER(SUPP_PAE, RESTART);
+               else if (!sm->portValid)
+                       SM_ENTER(SUPP_PAE, DISCONNECTED);
+               break;
+       case SUPP_PAE_RESTART:
+               if (!sm->eapRestart)
+                       SM_ENTER(SUPP_PAE, AUTHENTICATING);
+               break;
+       case SUPP_PAE_S_FORCE_AUTH:
+               break;
+       case SUPP_PAE_S_FORCE_UNAUTH:
+               break;
+       }
+}
+
+
+SM_STATE(KEY_RX, NO_KEY_RECEIVE)
+{
+       SM_ENTRY(KEY_RX, NO_KEY_RECEIVE);
+}
+
+
+SM_STATE(KEY_RX, KEY_RECEIVE)
+{
+       SM_ENTRY(KEY_RX, KEY_RECEIVE);
+       eapol_sm_processKey(sm);
+       sm->rxKey = FALSE;
+}
+
+
+SM_STEP(KEY_RX)
+{
+       if (sm->initialize || !sm->portEnabled)
+               SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE);
+       switch (sm->KEY_RX_state) {
+       case KEY_RX_UNKNOWN:
+               break;
+       case KEY_RX_NO_KEY_RECEIVE:
+               if (sm->rxKey)
+                       SM_ENTER(KEY_RX, KEY_RECEIVE);
+               break;
+       case KEY_RX_KEY_RECEIVE:
+               if (sm->rxKey)
+                       SM_ENTER(KEY_RX, KEY_RECEIVE);
+               break;
+       }
+}
+
+
+SM_STATE(SUPP_BE, REQUEST)
+{
+       SM_ENTRY(SUPP_BE, REQUEST);
+       sm->authWhile = 0;
+       sm->eapReq = TRUE;
+       eapol_sm_getSuppRsp(sm);
+}
+
+
+SM_STATE(SUPP_BE, RESPONSE)
+{
+       SM_ENTRY(SUPP_BE, RESPONSE);
+       eapol_sm_txSuppRsp(sm);
+       sm->eapResp = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, SUCCESS)
+{
+       SM_ENTRY(SUPP_BE, SUCCESS);
+       sm->keyRun = TRUE;
+       sm->suppSuccess = TRUE;
+
+       if (eap_key_available(sm->eap)) {
+               /* New key received - clear IEEE 802.1X EAPOL-Key replay
+                * counter */
+               sm->replay_counter_valid = FALSE;
+       }
+}
+
+
+SM_STATE(SUPP_BE, FAIL)
+{
+       SM_ENTRY(SUPP_BE, FAIL);
+       sm->suppFail = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, TIMEOUT)
+{
+       SM_ENTRY(SUPP_BE, TIMEOUT);
+       sm->suppTimeout = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, IDLE)
+{
+       SM_ENTRY(SUPP_BE, IDLE);
+       sm->suppStart = FALSE;
+       sm->initial_req = TRUE;
+}
+
+
+SM_STATE(SUPP_BE, INITIALIZE)
+{
+       SM_ENTRY(SUPP_BE, INITIALIZE);
+       eapol_sm_abortSupp(sm);
+       sm->suppAbort = FALSE;
+}
+
+
+SM_STATE(SUPP_BE, RECEIVE)
+{
+       SM_ENTRY(SUPP_BE, RECEIVE);
+       sm->authWhile = sm->authPeriod;
+       eapol_enable_timer_tick(sm);
+       sm->eapolEap = FALSE;
+       sm->eapNoResp = FALSE;
+       sm->initial_req = FALSE;
+}
+
+
+SM_STEP(SUPP_BE)
+{
+       if (sm->initialize || sm->suppAbort)
+               SM_ENTER_GLOBAL(SUPP_BE, INITIALIZE);
+       else switch (sm->SUPP_BE_state) {
+       case SUPP_BE_UNKNOWN:
+               break;
+       case SUPP_BE_REQUEST:
+               /*
+                * IEEE Std 802.1X-2004 has transitions from REQUEST to FAIL
+                * and SUCCESS based on eapFail and eapSuccess, respectively.
+                * However, IEEE Std 802.1X-2004 is also specifying that
+                * eapNoResp should be set in conjuction with eapSuccess and
+                * eapFail which would mean that more than one of the
+                * transitions here would be activated at the same time.
+                * Skipping RESPONSE and/or RECEIVE states in these cases can
+                * cause problems and the direct transitions to do not seem
+                * correct. Because of this, the conditions for these
+                * transitions are verified only after eapNoResp. They are
+                * unlikely to be used since eapNoResp should always be set if
+                * either of eapSuccess or eapFail is set.
+                */
+               if (sm->eapResp && sm->eapNoResp) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: SUPP_BE REQUEST: both "
+                                  "eapResp and eapNoResp set?!");
+               }
+               if (sm->eapResp)
+                       SM_ENTER(SUPP_BE, RESPONSE);
+               else if (sm->eapNoResp)
+                       SM_ENTER(SUPP_BE, RECEIVE);
+               else if (sm->eapFail)
+                       SM_ENTER(SUPP_BE, FAIL);
+               else if (sm->eapSuccess)
+                       SM_ENTER(SUPP_BE, SUCCESS);
+               break;
+       case SUPP_BE_RESPONSE:
+               SM_ENTER(SUPP_BE, RECEIVE);
+               break;
+       case SUPP_BE_SUCCESS:
+               SM_ENTER(SUPP_BE, IDLE);
+               break;
+       case SUPP_BE_FAIL:
+               SM_ENTER(SUPP_BE, IDLE);
+               break;
+       case SUPP_BE_TIMEOUT:
+               SM_ENTER(SUPP_BE, IDLE);
+               break;
+       case SUPP_BE_IDLE:
+               if (sm->eapFail && sm->suppStart)
+                       SM_ENTER(SUPP_BE, FAIL);
+               else if (sm->eapolEap && sm->suppStart)
+                       SM_ENTER(SUPP_BE, REQUEST);
+               else if (sm->eapSuccess && sm->suppStart)
+                       SM_ENTER(SUPP_BE, SUCCESS);
+               break;
+       case SUPP_BE_INITIALIZE:
+               SM_ENTER(SUPP_BE, IDLE);
+               break;
+       case SUPP_BE_RECEIVE:
+               if (sm->eapolEap)
+                       SM_ENTER(SUPP_BE, REQUEST);
+               else if (sm->eapFail)
+                       SM_ENTER(SUPP_BE, FAIL);
+               else if (sm->authWhile == 0)
+                       SM_ENTER(SUPP_BE, TIMEOUT);
+               else if (sm->eapSuccess)
+                       SM_ENTER(SUPP_BE, SUCCESS);
+               break;
+       }
+}
+
+
+static void eapol_sm_txLogoff(struct eapol_sm *sm)
+{
+       wpa_printf(MSG_DEBUG, "EAPOL: txLogoff");
+       sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+                           IEEE802_1X_TYPE_EAPOL_LOGOFF, (u8 *) "", 0);
+       sm->dot1xSuppEapolLogoffFramesTx++;
+       sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_txStart(struct eapol_sm *sm)
+{
+       wpa_printf(MSG_DEBUG, "EAPOL: txStart");
+       sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+                           IEEE802_1X_TYPE_EAPOL_START, (u8 *) "", 0);
+       sm->dot1xSuppEapolStartFramesTx++;
+       sm->dot1xSuppEapolFramesTx++;
+}
+
+
+#define IEEE8021X_ENCR_KEY_LEN 32
+#define IEEE8021X_SIGN_KEY_LEN 32
+
+struct eap_key_data {
+       u8 encr_key[IEEE8021X_ENCR_KEY_LEN];
+       u8 sign_key[IEEE8021X_SIGN_KEY_LEN];
+};
+
+
+static void eapol_sm_processKey(struct eapol_sm *sm)
+{
+       struct ieee802_1x_hdr *hdr;
+       struct ieee802_1x_eapol_key *key;
+       struct eap_key_data keydata;
+       u8 orig_key_sign[IEEE8021X_KEY_SIGN_LEN], datakey[32];
+       u8 ekey[IEEE8021X_KEY_IV_LEN + IEEE8021X_ENCR_KEY_LEN];
+       int key_len, res, sign_key_len, encr_key_len;
+       u16 rx_key_length;
+
+       wpa_printf(MSG_DEBUG, "EAPOL: processKey");
+       if (sm->last_rx_key == NULL)
+               return;
+
+       if (!sm->conf.accept_802_1x_keys) {
+               wpa_printf(MSG_WARNING, "EAPOL: Received IEEE 802.1X EAPOL-Key"
+                          " even though this was not accepted - "
+                          "ignoring this packet");
+               return;
+       }
+
+       hdr = (struct ieee802_1x_hdr *) sm->last_rx_key;
+       key = (struct ieee802_1x_eapol_key *) (hdr + 1);
+       if (sizeof(*hdr) + be_to_host16(hdr->length) > sm->last_rx_key_len) {
+               wpa_printf(MSG_WARNING, "EAPOL: Too short EAPOL-Key frame");
+               return;
+       }
+       rx_key_length = WPA_GET_BE16(key->key_length);
+       wpa_printf(MSG_DEBUG, "EAPOL: RX IEEE 802.1X ver=%d type=%d len=%d "
+                  "EAPOL-Key: type=%d key_length=%d key_index=0x%x",
+                  hdr->version, hdr->type, be_to_host16(hdr->length),
+                  key->type, rx_key_length, key->key_index);
+
+       eapol_sm_notify_lower_layer_success(sm, 1);
+       sign_key_len = IEEE8021X_SIGN_KEY_LEN;
+       encr_key_len = IEEE8021X_ENCR_KEY_LEN;
+       res = eapol_sm_get_key(sm, (u8 *) &keydata, sizeof(keydata));
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "EAPOL: Could not get master key for "
+                          "decrypting EAPOL-Key keys");
+               return;
+       }
+       if (res == 16) {
+               /* LEAP derives only 16 bytes of keying material. */
+               res = eapol_sm_get_key(sm, (u8 *) &keydata, 16);
+               if (res) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: Could not get LEAP "
+                                  "master key for decrypting EAPOL-Key keys");
+                       return;
+               }
+               sign_key_len = 16;
+               encr_key_len = 16;
+               os_memcpy(keydata.sign_key, keydata.encr_key, 16);
+       } else if (res) {
+               wpa_printf(MSG_DEBUG, "EAPOL: Could not get enough master key "
+                          "data for decrypting EAPOL-Key keys (res=%d)", res);
+               return;
+       }
+
+       /* The key replay_counter must increase when same master key */
+       if (sm->replay_counter_valid &&
+           os_memcmp(sm->last_replay_counter, key->replay_counter,
+                     IEEE8021X_REPLAY_COUNTER_LEN) >= 0) {
+               wpa_printf(MSG_WARNING, "EAPOL: EAPOL-Key replay counter did "
+                          "not increase - ignoring key");
+               wpa_hexdump(MSG_DEBUG, "EAPOL: last replay counter",
+                           sm->last_replay_counter,
+                           IEEE8021X_REPLAY_COUNTER_LEN);
+               wpa_hexdump(MSG_DEBUG, "EAPOL: received replay counter",
+                           key->replay_counter, IEEE8021X_REPLAY_COUNTER_LEN);
+               return;
+       }
+
+       /* Verify key signature (HMAC-MD5) */
+       os_memcpy(orig_key_sign, key->key_signature, IEEE8021X_KEY_SIGN_LEN);
+       os_memset(key->key_signature, 0, IEEE8021X_KEY_SIGN_LEN);
+       hmac_md5(keydata.sign_key, sign_key_len,
+                sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
+                key->key_signature);
+       if (os_memcmp(orig_key_sign, key->key_signature,
+                     IEEE8021X_KEY_SIGN_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
+                          "EAPOL-Key packet");
+               os_memcpy(key->key_signature, orig_key_sign,
+                         IEEE8021X_KEY_SIGN_LEN);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "EAPOL: EAPOL-Key key signature verified");
+
+       key_len = be_to_host16(hdr->length) - sizeof(*key);
+       if (key_len > 32 || rx_key_length > 32) {
+               wpa_printf(MSG_WARNING, "EAPOL: Too long key data length %d",
+                          key_len ? key_len : rx_key_length);
+               return;
+       }
+       if (key_len == rx_key_length) {
+               os_memcpy(ekey, key->key_iv, IEEE8021X_KEY_IV_LEN);
+               os_memcpy(ekey + IEEE8021X_KEY_IV_LEN, keydata.encr_key,
+                         encr_key_len);
+               os_memcpy(datakey, key + 1, key_len);
+               rc4_skip(ekey, IEEE8021X_KEY_IV_LEN + encr_key_len, 0,
+                        datakey, key_len);
+               wpa_hexdump_key(MSG_DEBUG, "EAPOL: Decrypted(RC4) key",
+                               datakey, key_len);
+       } else if (key_len == 0) {
+               /*
+                * IEEE 802.1X-2004 specifies that least significant Key Length
+                * octets from MS-MPPE-Send-Key are used as the key if the key
+                * data is not present. This seems to be meaning the beginning
+                * of the MS-MPPE-Send-Key. In addition, MS-MPPE-Send-Key in
+                * Supplicant corresponds to MS-MPPE-Recv-Key in Authenticator.
+                * Anyway, taking the beginning of the keying material from EAP
+                * seems to interoperate with Authenticators.
+                */
+               key_len = rx_key_length;
+               os_memcpy(datakey, keydata.encr_key, key_len);
+               wpa_hexdump_key(MSG_DEBUG, "EAPOL: using part of EAP keying "
+                               "material data encryption key",
+                               datakey, key_len);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAPOL: Invalid key data length %d "
+                          "(key_length=%d)", key_len, rx_key_length);
+               return;
+       }
+
+       sm->replay_counter_valid = TRUE;
+       os_memcpy(sm->last_replay_counter, key->replay_counter,
+                 IEEE8021X_REPLAY_COUNTER_LEN);
+
+       wpa_printf(MSG_DEBUG, "EAPOL: Setting dynamic WEP key: %s keyidx %d "
+                  "len %d",
+                  key->key_index & IEEE8021X_KEY_INDEX_FLAG ?
+                  "unicast" : "broadcast",
+                  key->key_index & IEEE8021X_KEY_INDEX_MASK, key_len);
+
+       if (sm->ctx->set_wep_key &&
+           sm->ctx->set_wep_key(sm->ctx->ctx,
+                                key->key_index & IEEE8021X_KEY_INDEX_FLAG,
+                                key->key_index & IEEE8021X_KEY_INDEX_MASK,
+                                datakey, key_len) < 0) {
+               wpa_printf(MSG_WARNING, "EAPOL: Failed to set WEP key to the "
+                          " driver.");
+       } else {
+               if (key->key_index & IEEE8021X_KEY_INDEX_FLAG)
+                       sm->unicast_key_received = TRUE;
+               else
+                       sm->broadcast_key_received = TRUE;
+
+               if ((sm->unicast_key_received ||
+                    !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_UNICAST)) &&
+                   (sm->broadcast_key_received ||
+                    !(sm->conf.required_keys & EAPOL_REQUIRE_KEY_BROADCAST)))
+               {
+                       wpa_printf(MSG_DEBUG, "EAPOL: all required EAPOL-Key "
+                                  "frames received");
+                       sm->portValid = TRUE;
+                       if (sm->ctx->eapol_done_cb)
+                               sm->ctx->eapol_done_cb(sm->ctx->ctx);
+               }
+       }
+}
+
+
+static void eapol_sm_getSuppRsp(struct eapol_sm *sm)
+{
+       wpa_printf(MSG_DEBUG, "EAPOL: getSuppRsp");
+       /* EAP layer processing; no special code is needed, since Supplicant
+        * Backend state machine is waiting for eapNoResp or eapResp to be set
+        * and these are only set in the EAP state machine when the processing
+        * has finished. */
+}
+
+
+static void eapol_sm_txSuppRsp(struct eapol_sm *sm)
+{
+       struct wpabuf *resp;
+
+       wpa_printf(MSG_DEBUG, "EAPOL: txSuppRsp");
+       resp = eap_get_eapRespData(sm->eap);
+       if (resp == NULL) {
+               wpa_printf(MSG_WARNING, "EAPOL: txSuppRsp - EAP response data "
+                          "not available");
+               return;
+       }
+
+       /* Send EAP-Packet from the EAP layer to the Authenticator */
+       sm->ctx->eapol_send(sm->ctx->eapol_send_ctx,
+                           IEEE802_1X_TYPE_EAP_PACKET, wpabuf_head(resp),
+                           wpabuf_len(resp));
+
+       /* eapRespData is not used anymore, so free it here */
+       wpabuf_free(resp);
+
+       if (sm->initial_req)
+               sm->dot1xSuppEapolReqIdFramesRx++;
+       else
+               sm->dot1xSuppEapolReqFramesRx++;
+       sm->dot1xSuppEapolRespFramesTx++;
+       sm->dot1xSuppEapolFramesTx++;
+}
+
+
+static void eapol_sm_abortSupp(struct eapol_sm *sm)
+{
+       /* release system resources that may have been allocated for the
+        * authentication session */
+       os_free(sm->last_rx_key);
+       sm->last_rx_key = NULL;
+       wpabuf_free(sm->eapReqData);
+       sm->eapReqData = NULL;
+       eap_sm_abort(sm->eap);
+}
+
+
+static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       eapol_sm_step(timeout_ctx);
+}
+
+
+static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
+{
+       if (sm->ctx->port_cb)
+               sm->ctx->port_cb(sm->ctx->ctx, 1);
+}
+
+
+static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
+{
+       if (sm->ctx->port_cb)
+               sm->ctx->port_cb(sm->ctx->ctx, 0);
+}
+
+
+/**
+ * eapol_sm_step - EAPOL state machine step function
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function is called to notify the state machine about changed external
+ * variables. It will step through the EAPOL state machines in loop to process
+ * all triggered state changes.
+ */
+void eapol_sm_step(struct eapol_sm *sm)
+{
+       int i;
+
+       /* In theory, it should be ok to run this in loop until !changed.
+        * However, it is better to use a limit on number of iterations to
+        * allow events (e.g., SIGTERM) to stop the program cleanly if the
+        * state machine were to generate a busy loop. */
+       for (i = 0; i < 100; i++) {
+               sm->changed = FALSE;
+               SM_STEP_RUN(SUPP_PAE);
+               SM_STEP_RUN(KEY_RX);
+               SM_STEP_RUN(SUPP_BE);
+               if (eap_peer_sm_step(sm->eap))
+                       sm->changed = TRUE;
+               if (!sm->changed)
+                       break;
+       }
+
+       if (sm->changed) {
+               /* restart EAPOL state machine step from timeout call in order
+                * to allow other events to be processed. */
+               eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+               eloop_register_timeout(0, 0, eapol_sm_step_timeout, NULL, sm);
+       }
+
+       if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
+               int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+               sm->cb_status = EAPOL_CB_IN_PROGRESS;
+               sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
+       }
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static const char *eapol_supp_pae_state(int state)
+{
+       switch (state) {
+       case SUPP_PAE_LOGOFF:
+               return "LOGOFF";
+       case SUPP_PAE_DISCONNECTED:
+               return "DISCONNECTED";
+       case SUPP_PAE_CONNECTING:
+               return "CONNECTING";
+       case SUPP_PAE_AUTHENTICATING:
+               return "AUTHENTICATING";
+       case SUPP_PAE_HELD:
+               return "HELD";
+       case SUPP_PAE_AUTHENTICATED:
+               return "AUTHENTICATED";
+       case SUPP_PAE_RESTART:
+               return "RESTART";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+static const char *eapol_supp_be_state(int state)
+{
+       switch (state) {
+       case SUPP_BE_REQUEST:
+               return "REQUEST";
+       case SUPP_BE_RESPONSE:
+               return "RESPONSE";
+       case SUPP_BE_SUCCESS:
+               return "SUCCESS";
+       case SUPP_BE_FAIL:
+               return "FAIL";
+       case SUPP_BE_TIMEOUT:
+               return "TIMEOUT";
+       case SUPP_BE_IDLE:
+               return "IDLE";
+       case SUPP_BE_INITIALIZE:
+               return "INITIALIZE";
+       case SUPP_BE_RECEIVE:
+               return "RECEIVE";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+static const char * eapol_port_status(PortStatus status)
+{
+       if (status == Authorized)
+               return "Authorized";
+       else
+               return "Unauthorized";
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static const char * eapol_port_control(PortControl ctrl)
+{
+       switch (ctrl) {
+       case Auto:
+               return "Auto";
+       case ForceUnauthorized:
+               return "ForceUnauthorized";
+       case ForceAuthorized:
+               return "ForceAuthorized";
+       default:
+               return "Unknown";
+       }
+}
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+/**
+ * eapol_sm_configure - Set EAPOL variables
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @heldPeriod: dot1xSuppHeldPeriod
+ * @authPeriod: dot1xSuppAuthPeriod
+ * @startPeriod: dot1xSuppStartPeriod
+ * @maxStart: dot1xSuppMaxStart
+ *
+ * Set configurable EAPOL state machine variables. Each variable can be set to
+ * the given value or ignored if set to -1 (to set only some of the variables).
+ */
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+                       int startPeriod, int maxStart)
+{
+       if (sm == NULL)
+               return;
+       if (heldPeriod >= 0)
+               sm->heldPeriod = heldPeriod;
+       if (authPeriod >= 0)
+               sm->authPeriod = authPeriod;
+       if (startPeriod >= 0)
+               sm->startPeriod = startPeriod;
+       if (maxStart >= 0)
+               sm->maxStart = maxStart;
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * eapol_sm_get_status - Get EAPOL state machine status
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for status information. This function fills in a
+ * text area with current status information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, status information will be truncated
+ * to fit the buffer.
+ */
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+                       int verbose)
+{
+       int len, ret;
+       if (sm == NULL)
+               return 0;
+
+       len = os_snprintf(buf, buflen,
+                         "Supplicant PAE state=%s\n"
+                         "suppPortStatus=%s\n",
+                         eapol_supp_pae_state(sm->SUPP_PAE_state),
+                         eapol_port_status(sm->suppPortStatus));
+       if (len < 0 || (size_t) len >= buflen)
+               return 0;
+
+       if (verbose) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "heldPeriod=%u\n"
+                                 "authPeriod=%u\n"
+                                 "startPeriod=%u\n"
+                                 "maxStart=%u\n"
+                                 "portControl=%s\n"
+                                 "Supplicant Backend state=%s\n",
+                                 sm->heldPeriod,
+                                 sm->authPeriod,
+                                 sm->startPeriod,
+                                 sm->maxStart,
+                                 eapol_port_control(sm->portControl),
+                                 eapol_supp_be_state(sm->SUPP_BE_state));
+               if (ret < 0 || (size_t) ret >= buflen - len)
+                       return len;
+               len += ret;
+       }
+
+       len += eap_sm_get_status(sm->eap, buf + len, buflen - len, verbose);
+
+       return len;
+}
+
+
+/**
+ * eapol_sm_get_mib - Get EAPOL state machine MIBs
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @buf: Buffer for MIB information
+ * @buflen: Maximum buffer length
+ * Returns: Number of bytes written to buf.
+ *
+ * Query EAPOL state machine for MIB information. This function fills in a
+ * text area with current MIB information from the EAPOL state machine. If
+ * the buffer (buf) is not large enough, MIB information will be truncated to
+ * fit the buffer.
+ */
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
+{
+       size_t len;
+       int ret;
+
+       if (sm == NULL)
+               return 0;
+       ret = os_snprintf(buf, buflen,
+                         "dot1xSuppPaeState=%d\n"
+                         "dot1xSuppHeldPeriod=%u\n"
+                         "dot1xSuppAuthPeriod=%u\n"
+                         "dot1xSuppStartPeriod=%u\n"
+                         "dot1xSuppMaxStart=%u\n"
+                         "dot1xSuppSuppControlledPortStatus=%s\n"
+                         "dot1xSuppBackendPaeState=%d\n",
+                         sm->SUPP_PAE_state,
+                         sm->heldPeriod,
+                         sm->authPeriod,
+                         sm->startPeriod,
+                         sm->maxStart,
+                         sm->suppPortStatus == Authorized ?
+                         "Authorized" : "Unauthorized",
+                         sm->SUPP_BE_state);
+
+       if (ret < 0 || (size_t) ret >= buflen)
+               return 0;
+       len = ret;
+
+       ret = os_snprintf(buf + len, buflen - len,
+                         "dot1xSuppEapolFramesRx=%u\n"
+                         "dot1xSuppEapolFramesTx=%u\n"
+                         "dot1xSuppEapolStartFramesTx=%u\n"
+                         "dot1xSuppEapolLogoffFramesTx=%u\n"
+                         "dot1xSuppEapolRespFramesTx=%u\n"
+                         "dot1xSuppEapolReqIdFramesRx=%u\n"
+                         "dot1xSuppEapolReqFramesRx=%u\n"
+                         "dot1xSuppInvalidEapolFramesRx=%u\n"
+                         "dot1xSuppEapLengthErrorFramesRx=%u\n"
+                         "dot1xSuppLastEapolFrameVersion=%u\n"
+                         "dot1xSuppLastEapolFrameSource=" MACSTR "\n",
+                         sm->dot1xSuppEapolFramesRx,
+                         sm->dot1xSuppEapolFramesTx,
+                         sm->dot1xSuppEapolStartFramesTx,
+                         sm->dot1xSuppEapolLogoffFramesTx,
+                         sm->dot1xSuppEapolRespFramesTx,
+                         sm->dot1xSuppEapolReqIdFramesRx,
+                         sm->dot1xSuppEapolReqFramesRx,
+                         sm->dot1xSuppInvalidEapolFramesRx,
+                         sm->dot1xSuppEapLengthErrorFramesRx,
+                         sm->dot1xSuppLastEapolFrameVersion,
+                         MAC2STR(sm->dot1xSuppLastEapolFrameSource));
+
+       if (ret < 0 || (size_t) ret >= buflen - len)
+               return len;
+       len += ret;
+
+       return len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * eapol_sm_rx_eapol - Process received EAPOL frames
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @src: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = EAPOL frame processed, 0 = not for EAPOL state machine,
+ * -1 failure
+ */
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+                     size_t len)
+{
+       const struct ieee802_1x_hdr *hdr;
+       const struct ieee802_1x_eapol_key *key;
+       int data_len;
+       int res = 1;
+       size_t plen;
+
+       if (sm == NULL)
+               return 0;
+       sm->dot1xSuppEapolFramesRx++;
+       if (len < sizeof(*hdr)) {
+               sm->dot1xSuppInvalidEapolFramesRx++;
+               return 0;
+       }
+       hdr = (const struct ieee802_1x_hdr *) buf;
+       sm->dot1xSuppLastEapolFrameVersion = hdr->version;
+       os_memcpy(sm->dot1xSuppLastEapolFrameSource, src, ETH_ALEN);
+       if (hdr->version < EAPOL_VERSION) {
+               /* TODO: backwards compatibility */
+       }
+       plen = be_to_host16(hdr->length);
+       if (plen > len - sizeof(*hdr)) {
+               sm->dot1xSuppEapLengthErrorFramesRx++;
+               return 0;
+       }
+#ifdef CONFIG_WPS
+       if (sm->conf.workaround &&
+           plen < len - sizeof(*hdr) &&
+           hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
+           len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
+               const struct eap_hdr *ehdr =
+                       (const struct eap_hdr *) (hdr + 1);
+               u16 elen;
+
+               elen = be_to_host16(ehdr->length);
+               if (elen > plen && elen <= len - sizeof(*hdr)) {
+                       /*
+                        * Buffalo WHR-G125 Ver.1.47 seems to send EAP-WPS
+                        * packets with too short EAPOL header length field
+                        * (14 octets). This is fixed in firmware Ver.1.49.
+                        * As a workaround, fix the EAPOL header based on the
+                        * correct length in the EAP packet.
+                        */
+                       wpa_printf(MSG_DEBUG, "EAPOL: Workaround - fix EAPOL "
+                                  "payload length based on EAP header: "
+                                  "%d -> %d", (int) plen, elen);
+                       plen = elen;
+               }
+       }
+#endif /* CONFIG_WPS */
+       data_len = plen + sizeof(*hdr);
+
+       switch (hdr->type) {
+       case IEEE802_1X_TYPE_EAP_PACKET:
+               if (sm->cached_pmk) {
+                       /* Trying to use PMKSA caching, but Authenticator did
+                        * not seem to have a matching entry. Need to restart
+                        * EAPOL state machines.
+                        */
+                       eapol_sm_abort_cached(sm);
+               }
+               wpabuf_free(sm->eapReqData);
+               sm->eapReqData = wpabuf_alloc_copy(hdr + 1, plen);
+               if (sm->eapReqData) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: Received EAP-Packet "
+                                  "frame");
+                       sm->eapolEap = TRUE;
+                       eapol_sm_step(sm);
+               }
+               break;
+       case IEEE802_1X_TYPE_EAPOL_KEY:
+               if (plen < sizeof(*key)) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: Too short EAPOL-Key "
+                                  "frame received");
+                       break;
+               }
+               key = (const struct ieee802_1x_eapol_key *) (hdr + 1);
+               if (key->type == EAPOL_KEY_TYPE_WPA ||
+                   key->type == EAPOL_KEY_TYPE_RSN) {
+                       /* WPA Supplicant takes care of this frame. */
+                       wpa_printf(MSG_DEBUG, "EAPOL: Ignoring WPA EAPOL-Key "
+                                  "frame in EAPOL state machines");
+                       res = 0;
+                       break;
+               }
+               if (key->type != EAPOL_KEY_TYPE_RC4) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: Ignored unknown "
+                                  "EAPOL-Key type %d", key->type);
+                       break;
+               }
+               os_free(sm->last_rx_key);
+               sm->last_rx_key = os_malloc(data_len);
+               if (sm->last_rx_key) {
+                       wpa_printf(MSG_DEBUG, "EAPOL: Received EAPOL-Key "
+                                  "frame");
+                       os_memcpy(sm->last_rx_key, buf, data_len);
+                       sm->last_rx_key_len = data_len;
+                       sm->rxKey = TRUE;
+                       eapol_sm_step(sm);
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
+                          hdr->type);
+               sm->dot1xSuppInvalidEapolFramesRx++;
+               break;
+       }
+
+       return res;
+}
+
+
+/**
+ * eapol_sm_notify_tx_eapol_key - Notification about transmitted EAPOL packet
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machine about transmitted EAPOL packet from an external
+ * component, e.g., WPA. This will update the statistics.
+ */
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+       if (sm)
+               sm->dot1xSuppEapolFramesTx++;
+}
+
+
+/**
+ * eapol_sm_notify_portEnabled - Notification about portEnabled change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @enabled: New portEnabled value
+ *
+ * Notify EAPOL state machine about new portEnabled value.
+ */
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+                  "portEnabled=%d", enabled);
+       sm->portEnabled = enabled;
+       eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_portValid - Notification about portValid change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @valid: New portValid value
+ *
+ * Notify EAPOL state machine about new portValid value.
+ */
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+                  "portValid=%d", valid);
+       sm->portValid = valid;
+       eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_success - Notification of external EAP success trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @success: %TRUE = set success, %FALSE = clear success
+ *
+ * Notify the EAPOL state machine that external event has forced EAP state to
+ * success (success = %TRUE). This can be cleared by setting success = %FALSE.
+ *
+ * This function is called to update EAP state when WPA-PSK key handshake has
+ * been completed successfully since WPA-PSK does not use EAP state machine.
+ */
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+                  "EAP success=%d", success);
+       sm->eapSuccess = success;
+       sm->altAccept = success;
+       if (success)
+               eap_notify_success(sm->eap);
+       eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_eap_fail - Notification of external EAP failure trigger
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @fail: %TRUE = set failure, %FALSE = clear failure
+ *
+ * Notify EAPOL state machine that external event has forced EAP state to
+ * failure (fail = %TRUE). This can be cleared by setting fail = %FALSE.
+ */
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+                  "EAP fail=%d", fail);
+       sm->eapFail = fail;
+       sm->altReject = fail;
+       eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_config - Notification of EAPOL configuration change
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @config: Pointer to current network EAP configuration
+ * @conf: Pointer to EAPOL configuration data
+ *
+ * Notify EAPOL state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed. conf will be copied to local EAPOL/EAP configuration
+ * data. If conf is %NULL, this part of the configuration change will be
+ * skipped.
+ */
+void eapol_sm_notify_config(struct eapol_sm *sm,
+                           struct eap_peer_config *config,
+                           const struct eapol_config *conf)
+{
+       if (sm == NULL)
+               return;
+
+       sm->config = config;
+
+       if (conf == NULL)
+               return;
+
+       sm->conf.accept_802_1x_keys = conf->accept_802_1x_keys;
+       sm->conf.required_keys = conf->required_keys;
+       sm->conf.fast_reauth = conf->fast_reauth;
+       sm->conf.workaround = conf->workaround;
+       if (sm->eap) {
+               eap_set_fast_reauth(sm->eap, conf->fast_reauth);
+               eap_set_workaround(sm->eap, conf->workaround);
+               eap_set_force_disabled(sm->eap, conf->eap_disabled);
+       }
+}
+
+
+/**
+ * eapol_sm_get_key - Get master session key (MSK) from EAP
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @key: Pointer for key buffer
+ * @len: Number of bytes to copy to key
+ * Returns: 0 on success (len of key available), maximum available key len
+ * (>0) if key is available but it is shorter than len, or -1 on failure.
+ *
+ * Fetch EAP keying material (MSK, eapKeyData) from EAP state machine. The key
+ * is available only after a successful authentication.
+ */
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+       const u8 *eap_key;
+       size_t eap_len;
+
+       if (sm == NULL || !eap_key_available(sm->eap)) {
+               wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
+               return -1;
+       }
+       eap_key = eap_get_eapKeyData(sm->eap, &eap_len);
+       if (eap_key == NULL) {
+               wpa_printf(MSG_DEBUG, "EAPOL: Failed to get eapKeyData");
+               return -1;
+       }
+       if (len > eap_len) {
+               wpa_printf(MSG_DEBUG, "EAPOL: Requested key length (%lu) not "
+                          "available (len=%lu)",
+                          (unsigned long) len, (unsigned long) eap_len);
+               return eap_len;
+       }
+       os_memcpy(key, eap_key, len);
+       wpa_printf(MSG_DEBUG, "EAPOL: Successfully fetched key (len=%lu)",
+                  (unsigned long) len);
+       return 0;
+}
+
+
+/**
+ * eapol_sm_notify_logoff - Notification of logon/logoff commands
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @logoff: Whether command was logoff
+ *
+ * Notify EAPOL state machines that user requested logon/logoff.
+ */
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+       if (sm) {
+               sm->userLogoff = logoff;
+               eapol_sm_step(sm);
+       }
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of successful PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that PMKSA caching was successful. This is used
+ * to move EAPOL and EAP state machines into authenticated/successful state.
+ */
+void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL");
+       sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED;
+       sm->suppPortStatus = Authorized;
+       eapol_sm_set_port_authorized(sm);
+       sm->portValid = TRUE;
+       eap_notify_success(sm->eap);
+       eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @attempt: Whether PMKSA caching is tried
+ *
+ * Notify EAPOL state machines whether PMKSA caching is used.
+ */
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+{
+       if (sm == NULL)
+               return;
+       if (attempt) {
+               wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+               sm->cached_pmk = TRUE;
+       } else {
+               wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
+               sm->cached_pmk = FALSE;
+       }
+}
+
+
+static void eapol_sm_abort_cached(struct eapol_sm *sm)
+{
+       wpa_printf(MSG_DEBUG, "RSN: Authenticator did not accept PMKID, "
+                  "doing full EAP authentication");
+       if (sm == NULL)
+               return;
+       sm->cached_pmk = FALSE;
+       sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
+       sm->suppPortStatus = Unauthorized;
+       eapol_sm_set_port_unauthorized(sm);
+
+       /* Make sure we do not start sending EAPOL-Start frames first, but
+        * instead move to RESTART state to start EAPOL authentication. */
+       sm->startWhen = 3;
+       eapol_enable_timer_tick(sm);
+
+       if (sm->ctx->aborted_cached)
+               sm->ctx->aborted_cached(sm->ctx->ctx);
+}
+
+
+/**
+ * eapol_sm_register_scard_ctx - Notification of smart card context
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @ctx: Context data for smart card operations
+ *
+ * Notify EAPOL state machines of context data for smart card operations. This
+ * context data will be used as a parameter for scard_*() functions.
+ */
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx)
+{
+       if (sm) {
+               sm->ctx->scard_ctx = ctx;
+               eap_register_scard_ctx(sm->eap, ctx);
+       }
+}
+
+
+/**
+ * eapol_sm_notify_portControl - Notification of portControl changes
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @portControl: New value for portControl variable
+ *
+ * Notify EAPOL state machines that portControl variable has changed.
+ */
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl)
+{
+       if (sm == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
+                  "portControl=%s", eapol_port_control(portControl));
+       sm->portControl = portControl;
+       eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_attached - Notification of attached monitor
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a monitor was attached to the control
+ * interface to trigger re-sending of pending requests for user input.
+ */
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       eap_sm_notify_ctrl_attached(sm->eap);
+}
+
+
+/**
+ * eapol_sm_notify_ctrl_response - Notification of received user input
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Notify EAPOL state machines that a control response, i.e., user
+ * input, was received in order to trigger retrying of a pending EAP request.
+ */
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       if (sm->eapReqData && !sm->eapReq) {
+               wpa_printf(MSG_DEBUG, "EAPOL: received control response (user "
+                          "input) notification - retrying pending EAP "
+                          "Request");
+               sm->eapolEap = TRUE;
+               sm->eapReq = TRUE;
+               eapol_sm_step(sm);
+       }
+}
+
+
+/**
+ * eapol_sm_request_reauth - Request reauthentication
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * This function can be used to request EAPOL reauthentication, e.g., when the
+ * current PMKSA entry is nearing expiration.
+ */
+void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+       if (sm == NULL || sm->SUPP_PAE_state != SUPP_PAE_AUTHENTICATED)
+               return;
+       eapol_sm_txStart(sm);
+}
+
+
+/**
+ * eapol_sm_notify_lower_layer_success - Notification of lower layer success
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @in_eapol_sm: Whether the caller is already running inside EAPOL state
+ * machine loop (eapol_sm_step())
+ *
+ * Notify EAPOL (and EAP) state machines that a lower layer has detected a
+ * successful authentication. This is used to recover from dropped EAP-Success
+ * messages.
+ */
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm)
+{
+       if (sm == NULL)
+               return;
+       eap_notify_lower_layer_success(sm->eap);
+       if (!in_eapol_sm)
+               eapol_sm_step(sm);
+}
+
+
+/**
+ * eapol_sm_invalidate_cached_session - Mark cached EAP session data invalid
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+       if (sm)
+               eap_invalidate_cached_session(sm->eap);
+}
+
+
+static struct eap_peer_config * eapol_sm_get_config(void *ctx)
+{
+       struct eapol_sm *sm = ctx;
+       return sm ? sm->config : NULL;
+}
+
+
+static struct wpabuf * eapol_sm_get_eapReqData(void *ctx)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm == NULL || sm->eapReqData == NULL)
+               return NULL;
+
+       return sm->eapReqData;
+}
+
+
+static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm == NULL)
+               return FALSE;
+       switch (variable) {
+       case EAPOL_eapSuccess:
+               return sm->eapSuccess;
+       case EAPOL_eapRestart:
+               return sm->eapRestart;
+       case EAPOL_eapFail:
+               return sm->eapFail;
+       case EAPOL_eapResp:
+               return sm->eapResp;
+       case EAPOL_eapNoResp:
+               return sm->eapNoResp;
+       case EAPOL_eapReq:
+               return sm->eapReq;
+       case EAPOL_portEnabled:
+               return sm->portEnabled;
+       case EAPOL_altAccept:
+               return sm->altAccept;
+       case EAPOL_altReject:
+               return sm->altReject;
+       }
+       return FALSE;
+}
+
+
+static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
+                             Boolean value)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm == NULL)
+               return;
+       switch (variable) {
+       case EAPOL_eapSuccess:
+               sm->eapSuccess = value;
+               break;
+       case EAPOL_eapRestart:
+               sm->eapRestart = value;
+               break;
+       case EAPOL_eapFail:
+               sm->eapFail = value;
+               break;
+       case EAPOL_eapResp:
+               sm->eapResp = value;
+               break;
+       case EAPOL_eapNoResp:
+               sm->eapNoResp = value;
+               break;
+       case EAPOL_eapReq:
+               sm->eapReq = value;
+               break;
+       case EAPOL_portEnabled:
+               sm->portEnabled = value;
+               break;
+       case EAPOL_altAccept:
+               sm->altAccept = value;
+               break;
+       case EAPOL_altReject:
+               sm->altReject = value;
+               break;
+       }
+}
+
+
+static unsigned int eapol_sm_get_int(void *ctx, enum eapol_int_var variable)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm == NULL)
+               return 0;
+       switch (variable) {
+       case EAPOL_idleWhile:
+               return sm->idleWhile;
+       }
+       return 0;
+}
+
+
+static void eapol_sm_set_int(void *ctx, enum eapol_int_var variable,
+                            unsigned int value)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm == NULL)
+               return;
+       switch (variable) {
+       case EAPOL_idleWhile:
+               sm->idleWhile = value;
+               eapol_enable_timer_tick(sm);
+               break;
+       }
+}
+
+
+static void eapol_sm_set_config_blob(void *ctx, struct wpa_config_blob *blob)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       struct eapol_sm *sm = ctx;
+       if (sm && sm->ctx && sm->ctx->set_config_blob)
+               sm->ctx->set_config_blob(sm->ctx->ctx, blob);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static const struct wpa_config_blob *
+eapol_sm_get_config_blob(void *ctx, const char *name)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       struct eapol_sm *sm = ctx;
+       if (sm && sm->ctx && sm->ctx->get_config_blob)
+               return sm->ctx->get_config_blob(sm->ctx->ctx, name);
+       else
+               return NULL;
+#else /* CONFIG_NO_CONFIG_BLOBS */
+       return NULL;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
+static void eapol_sm_notify_pending(void *ctx)
+{
+       struct eapol_sm *sm = ctx;
+       if (sm == NULL)
+               return;
+       if (sm->eapReqData && !sm->eapReq) {
+               wpa_printf(MSG_DEBUG, "EAPOL: received notification from EAP "
+                          "state machine - retrying pending EAP Request");
+               sm->eapolEap = TRUE;
+               sm->eapReq = TRUE;
+               eapol_sm_step(sm);
+       }
+}
+
+
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_sm_eap_param_needed(void *ctx, const char *field,
+                                     const char *txt)
+{
+       struct eapol_sm *sm = ctx;
+       wpa_printf(MSG_DEBUG, "EAPOL: EAP parameter needed");
+       if (sm->ctx->eap_param_needed)
+               sm->ctx->eap_param_needed(sm->ctx->ctx, field, txt);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_sm_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+static struct eapol_callbacks eapol_cb =
+{
+       eapol_sm_get_config,
+       eapol_sm_get_bool,
+       eapol_sm_set_bool,
+       eapol_sm_get_int,
+       eapol_sm_set_int,
+       eapol_sm_get_eapReqData,
+       eapol_sm_set_config_blob,
+       eapol_sm_get_config_blob,
+       eapol_sm_notify_pending,
+       eapol_sm_eap_param_needed
+};
+
+
+/**
+ * eapol_sm_init - Initialize EAPOL state machine
+ * @ctx: Pointer to EAPOL context data; this needs to be an allocated buffer
+ * and EAPOL state machine will free it in eapol_sm_deinit()
+ * Returns: Pointer to the allocated EAPOL state machine or %NULL on failure
+ *
+ * Allocate and initialize an EAPOL state machine.
+ */
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+       struct eapol_sm *sm;
+       struct eap_config conf;
+       sm = os_zalloc(sizeof(*sm));
+       if (sm == NULL)
+               return NULL;
+       sm->ctx = ctx;
+
+       sm->portControl = Auto;
+
+       /* Supplicant PAE state machine */
+       sm->heldPeriod = 60;
+       sm->startPeriod = 30;
+       sm->maxStart = 3;
+
+       /* Supplicant Backend state machine */
+       sm->authPeriod = 30;
+
+       os_memset(&conf, 0, sizeof(conf));
+       conf.opensc_engine_path = ctx->opensc_engine_path;
+       conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
+       conf.pkcs11_module_path = ctx->pkcs11_module_path;
+       conf.wps = ctx->wps;
+
+       sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf);
+       if (sm->eap == NULL) {
+               os_free(sm);
+               return NULL;
+       }
+
+       /* Initialize EAPOL state machines */
+       sm->initialize = TRUE;
+       eapol_sm_step(sm);
+       sm->initialize = FALSE;
+       eapol_sm_step(sm);
+
+       sm->timer_tick_enabled = 1;
+       eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+
+       return sm;
+}
+
+
+/**
+ * eapol_sm_deinit - Deinitialize EAPOL state machine
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ *
+ * Deinitialize and free EAPOL state machine.
+ */
+void eapol_sm_deinit(struct eapol_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       eloop_cancel_timeout(eapol_sm_step_timeout, NULL, sm);
+       eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
+       eap_peer_sm_deinit(sm->eap);
+       os_free(sm->last_rx_key);
+       wpabuf_free(sm->eapReqData);
+       os_free(sm->ctx);
+       os_free(sm);
+}
diff --git a/src/eapol_supp/eapol_supp_sm.h b/src/eapol_supp/eapol_supp_sm.h
new file mode 100644 (file)
index 0000000..1d2a32b
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ * EAPOL supplicant state machines
+ * 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 EAPOL_SUPP_SM_H
+#define EAPOL_SUPP_SM_H
+
+#include "common/defs.h"
+
+typedef enum { Unauthorized, Authorized } PortStatus;
+typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl;
+
+/**
+ * struct eapol_config - Per network configuration for EAPOL state machines
+ */
+struct eapol_config {
+       /**
+        * accept_802_1x_keys - Accept IEEE 802.1X (non-WPA) EAPOL-Key frames
+        *
+        * This variable should be set to 1 when using EAPOL state machines
+        * with non-WPA security policy to generate dynamic WEP keys. When
+        * using WPA, this should be set to 0 so that WPA state machine can
+        * process the EAPOL-Key frames.
+        */
+       int accept_802_1x_keys;
+
+#define EAPOL_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_REQUIRE_KEY_BROADCAST BIT(1)
+       /**
+        * required_keys - Which EAPOL-Key packets are required
+        *
+        * This variable determines which EAPOL-Key packets are required before
+        * marking connection authenticated. This is a bit field of
+        * EAPOL_REQUIRE_KEY_UNICAST and EAPOL_REQUIRE_KEY_BROADCAST flags.
+        */
+       int required_keys;
+
+       /**
+        * fast_reauth - Whether fast EAP reauthentication is enabled
+        */
+       int fast_reauth;
+
+       /**
+        * workaround - Whether EAP workarounds are enabled
+        */
+       unsigned int workaround;
+
+       /**
+        * eap_disabled - Whether EAP is disabled
+        */
+       int eap_disabled;
+};
+
+struct eapol_sm;
+struct wpa_config_blob;
+
+/**
+ * struct eapol_ctx - Global (for all networks) EAPOL state machine context
+ */
+struct eapol_ctx {
+       /**
+        * ctx - Pointer to arbitrary upper level context
+        */
+       void *ctx;
+
+       /**
+        * preauth - IEEE 802.11i/RSN pre-authentication
+        *
+        * This EAPOL state machine is used for IEEE 802.11i/RSN
+        * pre-authentication
+        */
+       int preauth;
+
+       /**
+        * cb - Function to be called when EAPOL negotiation has been completed
+        * @eapol: Pointer to EAPOL state machine data
+        * @success: Whether the authentication was completed successfully
+        * @ctx: Pointer to context data (cb_ctx)
+        *
+        * This optional callback function will be called when the EAPOL
+        * authentication has been completed. This allows the owner of the
+        * EAPOL state machine to process the key and terminate the EAPOL state
+        * machine. Currently, this is used only in RSN pre-authentication.
+        */
+       void (*cb)(struct eapol_sm *eapol, int success, void *ctx);
+
+       /**
+        * cb_ctx - Callback context for cb()
+        */
+       void *cb_ctx;
+
+       /**
+        * msg_ctx - Callback context for wpa_msg() calls
+        */
+       void *msg_ctx;
+
+       /**
+        * scard_ctx - Callback context for PC/SC scard_*() function calls
+        *
+        * This context can be updated with eapol_sm_register_scard_ctx().
+        */
+       void *scard_ctx;
+
+       /**
+        * eapol_send_ctx - Callback context for eapol_send() calls
+        */
+       void *eapol_send_ctx;
+
+       /**
+        * eapol_done_cb - Function to be called at successful completion
+        * @ctx: Callback context (ctx)
+        *
+        * This function is called at the successful completion of EAPOL
+        * authentication. If dynamic WEP keys are used, this is called only
+        * after all the expected keys have been received.
+        */
+       void (*eapol_done_cb)(void *ctx);
+
+       /**
+        * eapol_send - Send EAPOL packets
+        * @ctx: Callback context (eapol_send_ctx)
+        * @type: EAPOL type (IEEE802_1X_TYPE_*)
+        * @buf: Pointer to EAPOL payload
+        * @len: Length of the EAPOL payload
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*eapol_send)(void *ctx, int type, const u8 *buf, size_t len);
+
+       /**
+        * set_wep_key - Configure WEP keys
+        * @ctx: Callback context (ctx)
+        * @unicast: Non-zero = unicast, 0 = multicast/broadcast key
+        * @keyidx: Key index (0..3)
+        * @key: WEP key
+        * @keylen: Length of the WEP key
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_wep_key)(void *ctx, int unicast, int keyidx,
+                          const u8 *key, size_t keylen);
+
+       /**
+        * set_config_blob - Set or add a named configuration blob
+        * @ctx: Callback context (ctx)
+        * @blob: New value for the blob
+        *
+        * Adds a new configuration blob or replaces the current value of an
+        * existing blob.
+        */
+       void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+
+       /**
+        * get_config_blob - Get a named configuration blob
+        * @ctx: Callback context (ctx)
+        * @name: Name of the blob
+        * Returns: Pointer to blob data or %NULL if not found
+        */
+       const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+                                                         const char *name);
+
+       /**
+        * aborted_cached - Notify that cached PMK attempt was aborted
+        * @ctx: Callback context (ctx)
+        */
+       void (*aborted_cached)(void *ctx);
+
+       /**
+        * opensc_engine_path - Path to the OpenSSL engine for opensc
+        *
+        * This is an OpenSSL specific configuration option for loading OpenSC
+        * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+        */
+       const char *opensc_engine_path;
+
+       /**
+        * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+        *
+        * This is an OpenSSL specific configuration option for loading PKCS#11
+        * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+        */
+       const char *pkcs11_engine_path;
+
+       /**
+        * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+        *
+        * This is an OpenSSL specific configuration option for configuring
+        * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+        * module is not loaded.
+        */
+       const char *pkcs11_module_path;
+
+       /**
+        * wps - WPS context data
+        *
+        * This is only used by EAP-WSC and can be left %NULL if not available.
+        */
+       struct wps_context *wps;
+
+       /**
+        * eap_param_needed - Notify that EAP parameter is needed
+        * @ctx: Callback context (ctx)
+        * @field: Field name (e.g., "IDENTITY")
+        * @txt: User readable text describing the required parameter
+        */
+       void (*eap_param_needed)(void *ctx, const char *field,
+                                const char *txt);
+
+       /**
+        * port_cb - Set port authorized/unauthorized callback (optional)
+        * @ctx: Callback context (ctx)
+        * @authorized: Whether the supplicant port is now in authorized state
+        */
+       void (*port_cb)(void *ctx, int authorized);
+};
+
+
+struct eap_peer_config;
+
+#ifdef IEEE8021X_EAPOL
+struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx);
+void eapol_sm_deinit(struct eapol_sm *sm);
+void eapol_sm_step(struct eapol_sm *sm);
+int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
+                       int verbose);
+int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen);
+void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod, int authPeriod,
+                       int startPeriod, int maxStart);
+int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
+                     size_t len);
+void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm);
+void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled);
+void eapol_sm_notify_portValid(struct eapol_sm *sm, Boolean valid);
+void eapol_sm_notify_eap_success(struct eapol_sm *sm, Boolean success);
+void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail);
+void eapol_sm_notify_config(struct eapol_sm *sm,
+                           struct eap_peer_config *config,
+                           const struct eapol_config *conf);
+int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
+void eapol_sm_notify_cached(struct eapol_sm *sm);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
+void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
+void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
+void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
+void eapol_sm_notify_ctrl_response(struct eapol_sm *sm);
+void eapol_sm_request_reauth(struct eapol_sm *sm);
+void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm, int in_eapol_sm);
+void eapol_sm_invalidate_cached_session(struct eapol_sm *sm);
+#else /* IEEE8021X_EAPOL */
+static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
+{
+       free(ctx);
+       return (struct eapol_sm *) 1;
+}
+static inline void eapol_sm_deinit(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_step(struct eapol_sm *sm)
+{
+}
+static inline int eapol_sm_get_status(struct eapol_sm *sm, char *buf,
+                                     size_t buflen, int verbose)
+{
+       return 0;
+}
+static inline int eapol_sm_get_mib(struct eapol_sm *sm, char *buf,
+                                  size_t buflen)
+{
+       return 0;
+}
+static inline void eapol_sm_configure(struct eapol_sm *sm, int heldPeriod,
+                                     int authPeriod, int startPeriod,
+                                     int maxStart)
+{
+}
+static inline int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src,
+                                   const u8 *buf, size_t len)
+{
+       return 0;
+}
+static inline void eapol_sm_notify_tx_eapol_key(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_portEnabled(struct eapol_sm *sm,
+                                              Boolean enabled)
+{
+}
+static inline void eapol_sm_notify_portValid(struct eapol_sm *sm,
+                                            Boolean valid)
+{
+}
+static inline void eapol_sm_notify_eap_success(struct eapol_sm *sm,
+                                              Boolean success)
+{
+}
+static inline void eapol_sm_notify_eap_fail(struct eapol_sm *sm, Boolean fail)
+{
+}
+static inline void eapol_sm_notify_config(struct eapol_sm *sm,
+                                         struct eap_peer_config *config,
+                                         struct eapol_config *conf)
+{
+}
+static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
+{
+       return -1;
+}
+static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
+{
+}
+static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
+{
+}
+#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0)
+#define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
+static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
+                                              PortControl portControl)
+{
+}
+static inline void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_ctrl_response(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_request_reauth(struct eapol_sm *sm)
+{
+}
+static inline void eapol_sm_notify_lower_layer_success(struct eapol_sm *sm,
+                                                      int in_eapol_sm)
+{
+}
+static inline void eapol_sm_invalidate_cached_session(struct eapol_sm *sm)
+{
+}
+#endif /* IEEE8021X_EAPOL */
+
+#endif /* EAPOL_SUPP_SM_H */
diff --git a/src/l2_packet/Makefile b/src/l2_packet/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/l2_packet/l2_packet.h b/src/l2_packet/l2_packet.h
new file mode 100644 (file)
index 0000000..c7b5014
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * WPA Supplicant - Layer2 packet interface definition
+ * 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.
+ *
+ * This file defines an interface for layer 2 (link layer) packet sending and
+ * receiving. l2_packet_linux.c is one implementation for such a layer 2
+ * implementation using Linux packet sockets and l2_packet_pcap.c another one
+ * using libpcap and libdnet. When porting %wpa_supplicant to other operating
+ * systems, a new l2_packet implementation may need to be added.
+ */
+
+#ifndef L2_PACKET_H
+#define L2_PACKET_H
+
+/**
+ * struct l2_packet_data - Internal l2_packet data structure
+ *
+ * This structure is used by the l2_packet implementation to store its private
+ * data. Other files use a pointer to this data when calling the l2_packet
+ * functions, but the contents of this structure should not be used directly
+ * outside l2_packet implementation.
+ */
+struct l2_packet_data;
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct l2_ethhdr {
+       u8 h_dest[ETH_ALEN];
+       u8 h_source[ETH_ALEN];
+       be16 h_proto;
+} STRUCT_PACKED;
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+/**
+ * l2_packet_init - Initialize l2_packet interface
+ * @ifname: Interface name
+ * @own_addr: Optional own MAC address if available from driver interface or
+ *     %NULL if not available
+ * @protocol: Ethernet protocol number in host byte order
+ * @rx_callback: Callback function that will be called for each received packet
+ * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback()
+ * @l2_hdr: 1 = include layer 2 header, 0 = do not include header
+ * Returns: Pointer to internal data or %NULL on failure
+ *
+ * rx_callback function will be called with src_addr pointing to the source
+ * address (MAC address) of the the packet. If l2_hdr is set to 0, buf
+ * points to len bytes of the payload after the layer 2 header and similarly,
+ * TX buffers start with payload. This behavior can be changed by setting
+ * l2_hdr=1 to include the layer 2 header in the data buffer.
+ */
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr);
+
+/**
+ * l2_packet_deinit - Deinitialize l2_packet interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ */
+void l2_packet_deinit(struct l2_packet_data *l2);
+
+/**
+ * l2_packet_get_own_addr - Get own layer 2 address
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @addr: Buffer for the own address (6 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr);
+
+/**
+ * l2_packet_send - Send a packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @dst_addr: Destination address for the packet (only used if l2_hdr == 0)
+ * @proto: Protocol/ethertype for the packet in host byte order (only used if
+ * l2_hdr == 0)
+ * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was
+ * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet
+ * is included.
+ * @len: Length of the buffer (including l2 header only if l2_hdr == 1)
+ * Returns: >=0 on success, <0 on failure
+ */
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len);
+
+/**
+ * l2_packet_get_ip_addr - Get the current IP address from the interface
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @buf: Buffer for the IP address in text format
+ * @len: Maximum buffer length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to get the current IP address from the interface
+ * bound to the l2_packet. This is mainly for status information and the IP
+ * address will be stored as an ASCII string. This function is not essential
+ * for %wpa_supplicant operation, so full implementation is not required.
+ * l2_packet implementation will need to define the function, but it can return
+ * -1 if the IP address information is not available.
+ */
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len);
+
+
+/**
+ * l2_packet_notify_auth_start - Notify l2_packet about start of authentication
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ *
+ * This function is called when authentication is expected to start, e.g., when
+ * association has been completed, in order to prepare l2_packet implementation
+ * for EAPOL frames. This function is used mainly if the l2_packet code needs
+ * to do polling in which case it can increasing polling frequency. This can
+ * also be an empty function if the l2_packet implementation does not benefit
+ * from knowing about the starting authentication.
+ */
+void l2_packet_notify_auth_start(struct l2_packet_data *l2);
+
+#endif /* L2_PACKET_H */
diff --git a/src/l2_packet/l2_packet_freebsd.c b/src/l2_packet/l2_packet_freebsd.c
new file mode 100644 (file)
index 0000000..009e02c
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with FreeBSD
+ * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005, Sam Leffler <sam@errno.com>
+ *
+ * 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"
+#if defined(__APPLE__) || defined(__GLIBC__)
+#include <net/bpf.h>
+#endif /* __APPLE__ */
+#include <pcap.h>
+
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+       pcap_t *pcap;
+       char ifname[100];
+       u8 own_addr[ETH_ALEN];
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+                    * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       if (!l2->l2_hdr) {
+               int ret;
+               struct l2_ethhdr *eth = os_malloc(sizeof(*eth) + len);
+               if (eth == NULL)
+                       return -1;
+               os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+               os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+               eth->h_proto = htons(proto);
+               os_memcpy(eth + 1, buf, len);
+               ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
+               os_free(eth);
+               return ret;
+       } else
+               return pcap_inject(l2->pcap, buf, len);
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       pcap_t *pcap = sock_ctx;
+       struct pcap_pkthdr hdr;
+       const u_char *packet;
+       struct l2_ethhdr *ethhdr;
+       unsigned char *buf;
+       size_t len;
+
+       packet = pcap_next(pcap, &hdr);
+
+       if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+               return;
+
+       ethhdr = (struct l2_ethhdr *) packet;
+       if (l2->l2_hdr) {
+               buf = (unsigned char *) ethhdr;
+               len = hdr.caplen;
+       } else {
+               buf = (unsigned char *) (ethhdr + 1);
+               len = hdr.caplen - sizeof(*ethhdr);
+       }
+       l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+                                 unsigned short protocol)
+{
+       bpf_u_int32 pcap_maskp, pcap_netp;
+       char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+       struct bpf_program pcap_fp;
+
+       pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+       l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+       if (l2->pcap == NULL) {
+               fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+               fprintf(stderr, "ifname='%s'\n", l2->ifname);
+               return -1;
+       }
+       if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+           pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+               fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+                       pcap_geterr(l2->pcap));
+               return -1;
+       }
+       os_snprintf(pcap_filter, sizeof(pcap_filter),
+                   "not ether src " MACSTR " and "
+                   "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+                   "ether proto 0x%x",
+                   MAC2STR(l2->own_addr), /* do not receive own packets */
+                   MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+                   protocol);
+       if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+               fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+               return -1;
+       }
+
+       if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+               fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+               return -1;
+       }
+
+       pcap_freecode(&pcap_fp);
+       /*
+        * When libpcap uses BPF we must enable "immediate mode" to
+        * receive frames right away; otherwise the system may
+        * buffer them for us.
+        */
+       {
+               unsigned int on = 1;
+               if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+                       fprintf(stderr, "%s: cannot enable immediate mode on "
+                               "interface %s: %s\n",
+                               __func__, l2->ifname, strerror(errno));
+                       /* XXX should we fail? */
+               }
+       }
+
+       eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+                                l2_packet_receive, l2, l2->pcap);
+
+       return 0;
+}
+
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+       struct if_msghdr *ifm;
+       struct sockaddr_dl *sdl;
+       u_char *p, *buf;
+       size_t len;
+       int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+       if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+               return -1;
+       if ((buf = os_malloc(len)) == NULL)
+               return -1;
+       if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+               os_free(buf);
+               return -1;
+       }
+       for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+               ifm = (struct if_msghdr *)p;
+               sdl = (struct sockaddr_dl *)(ifm + 1);
+               if (ifm->ifm_type != RTM_IFINFO ||
+                   (ifm->ifm_addrs & RTA_IFP) == 0)
+                       continue;
+               if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+                   os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+                       continue;
+               os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+               break;
+       }
+       os_free(buf);
+
+       if (p >= buf + len) {
+               errno = ESRCH;
+               return -1;
+       }
+       return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+       l2->l2_hdr = l2_hdr;
+
+       if (eth_get(l2->ifname, l2->own_addr) < 0) {
+               fprintf(stderr, "Failed to get link-level address for "
+                       "interface '%s'.\n", l2->ifname);
+               os_free(l2);
+               return NULL;
+       }
+
+       if (l2_packet_init_libpcap(l2, protocol)) {
+               os_free(l2);
+               return NULL;
+       }
+
+       return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 != NULL) {
+               if (l2->pcap) {
+                       eloop_unregister_read_sock(
+                               pcap_get_selectable_fd(l2->pcap));
+                       pcap_close(l2->pcap);
+               }
+               os_free(l2);
+       }
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       pcap_if_t *devs, *dev;
+       struct pcap_addr *addr;
+       struct sockaddr_in *saddr;
+       int found = 0;
+       char err[PCAP_ERRBUF_SIZE + 1];
+
+       if (pcap_findalldevs(&devs, err) < 0) {
+               wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+               return -1;
+       }
+
+       for (dev = devs; dev && !found; dev = dev->next) {
+               if (os_strcmp(dev->name, l2->ifname) != 0)
+                       continue;
+
+               addr = dev->addresses;
+               while (addr) {
+                       saddr = (struct sockaddr_in *) addr->addr;
+                       if (saddr && saddr->sin_family == AF_INET) {
+                               os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+                                          len);
+                               found = 1;
+                               break;
+                       }
+                       addr = addr->next;
+               }
+       }
+
+       pcap_freealldevs(devs);
+
+       return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_linux.c b/src/l2_packet/l2_packet_linux.c
new file mode 100644 (file)
index 0000000..48d1bde
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Linux packet sockets
+ * 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.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <netpacket/packet.h>
+#include <net/if.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+       int fd; /* packet socket for EAPOL frames */
+       char ifname[IFNAMSIZ + 1];
+       int ifindex;
+       u8 own_addr[ETH_ALEN];
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+                    * buffers */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       int ret;
+       if (l2 == NULL)
+               return -1;
+       if (l2->l2_hdr) {
+               ret = send(l2->fd, buf, len, 0);
+               if (ret < 0)
+                       perror("l2_packet_send - send");
+       } else {
+               struct sockaddr_ll ll;
+               os_memset(&ll, 0, sizeof(ll));
+               ll.sll_family = AF_PACKET;
+               ll.sll_ifindex = l2->ifindex;
+               ll.sll_protocol = htons(proto);
+               ll.sll_halen = ETH_ALEN;
+               os_memcpy(ll.sll_addr, dst_addr, ETH_ALEN);
+               ret = sendto(l2->fd, buf, len, 0, (struct sockaddr *) &ll,
+                            sizeof(ll));
+               if (ret < 0)
+                       perror("l2_packet_send - sendto");
+       }
+       return ret;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       u8 buf[2300];
+       int res;
+       struct sockaddr_ll ll;
+       socklen_t fromlen;
+
+       os_memset(&ll, 0, sizeof(ll));
+       fromlen = sizeof(ll);
+       res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+                      &fromlen);
+       if (res < 0) {
+               perror("l2_packet_receive - recvfrom");
+               return;
+       }
+
+       l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+       struct ifreq ifr;
+       struct sockaddr_ll ll;
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+       l2->l2_hdr = l2_hdr;
+
+       l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+                       htons(protocol));
+       if (l2->fd < 0) {
+               perror("socket(PF_PACKET)");
+               os_free(l2);
+               return NULL;
+       }
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
+       if (ioctl(l2->fd, SIOCGIFINDEX, &ifr) < 0) {
+               perror("ioctl[SIOCGIFINDEX]");
+               close(l2->fd);
+               os_free(l2);
+               return NULL;
+       }
+       l2->ifindex = ifr.ifr_ifindex;
+
+       os_memset(&ll, 0, sizeof(ll));
+       ll.sll_family = PF_PACKET;
+       ll.sll_ifindex = ifr.ifr_ifindex;
+       ll.sll_protocol = htons(protocol);
+       if (bind(l2->fd, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+               perror("bind[PF_PACKET]");
+               close(l2->fd);
+               os_free(l2);
+               return NULL;
+       }
+
+       if (ioctl(l2->fd, SIOCGIFHWADDR, &ifr) < 0) {
+               perror("ioctl[SIOCGIFHWADDR]");
+               close(l2->fd);
+               os_free(l2);
+               return NULL;
+       }
+       os_memcpy(l2->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
+
+       eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+       return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 == NULL)
+               return;
+
+       if (l2->fd >= 0) {
+               eloop_unregister_read_sock(l2->fd);
+               close(l2->fd);
+       }
+               
+       os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       int s;
+       struct ifreq ifr;
+       struct sockaddr_in *saddr;
+       size_t res;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, l2->ifname, sizeof(ifr.ifr_name));
+       if (ioctl(s, SIOCGIFADDR, &ifr) < 0) {
+               if (errno != EADDRNOTAVAIL)
+                       perror("ioctl[SIOCGIFADDR]");
+               close(s);
+               return -1;
+       }
+       close(s);
+       saddr = aliasing_hide_typecast(&ifr.ifr_addr, struct sockaddr_in);
+       if (saddr->sin_family != AF_INET)
+               return -1;
+       res = os_strlcpy(buf, inet_ntoa(saddr->sin_addr), len);
+       if (res >= len)
+               return -1;
+       return 0;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_ndis.c b/src/l2_packet/l2_packet_ndis.c
new file mode 100644 (file)
index 0000000..6ce29aa
--- /dev/null
@@ -0,0 +1,522 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with Microsoft NDISUIO
+ * Copyright (c) 2003-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.
+ *
+ * This implementation requires Windows specific event loop implementation,
+ * i.e., eloop_win.c. In addition, the NDISUIO connection is shared with
+ * driver_ndis.c, so only that driver interface can be used and
+ * CONFIG_USE_NDISUIO must be defined.
+ *
+ * WinXP version of the code uses overlapped I/O and a single threaded design
+ * with callback functions from I/O code. WinCE version uses a separate RX
+ * thread that blocks on ReadFile() whenever the media status is connected.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+#include <ntddndis.h>
+
+#ifdef _WIN32_WCE
+#include <winioctl.h>
+#include <nuiouser.h>
+#endif /* _WIN32_WCE */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+#ifndef _WIN32_WCE
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+       CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+#define IOCTL_NDISUIO_SET_ETHER_TYPE \
+       _NDISUIO_CTL_CODE(0x202, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+#endif /* _WIN32_WCE */
+
+/* From driver_ndis.c to shared the handle to NDISUIO */
+HANDLE driver_ndis_get_ndisuio_handle(void);
+
+/*
+ * NDISUIO supports filtering of only one ethertype at the time, so we must
+ * fake support for two (EAPOL and RSN pre-auth) by switching to pre-auth
+ * whenever wpa_supplicant is trying to pre-authenticate and then switching
+ * back to EAPOL when pre-authentication has been completed.
+ */
+
+struct l2_packet_data;
+
+struct l2_packet_ndisuio_global {
+       int refcount;
+       unsigned short first_proto;
+       struct l2_packet_data *l2[2];
+#ifdef _WIN32_WCE
+       HANDLE rx_thread;
+       HANDLE stop_request;
+       HANDLE ready_for_read;
+       HANDLE rx_processed;
+#endif /* _WIN32_WCE */
+};
+
+static struct l2_packet_ndisuio_global *l2_ndisuio_global = NULL;
+
+struct l2_packet_data {
+       char ifname[100];
+       u8 own_addr[ETH_ALEN];
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+                    * rx_callback and l2_packet_send() */
+       HANDLE rx_avail;
+#ifndef _WIN32_WCE
+       OVERLAPPED rx_overlapped;
+#endif /* _WIN32_WCE */
+       u8 rx_buf[1514];
+       DWORD rx_written;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       BOOL res;
+       DWORD written;
+       struct l2_ethhdr *eth;
+#ifndef _WIN32_WCE
+       OVERLAPPED overlapped;
+#endif /* _WIN32_WCE */
+       OVERLAPPED *o;
+
+       if (l2 == NULL)
+               return -1;
+
+#ifdef _WIN32_WCE
+       o = NULL;
+#else /* _WIN32_WCE */
+       os_memset(&overlapped, 0, sizeof(overlapped));
+       o = &overlapped;
+#endif /* _WIN32_WCE */
+
+       if (l2->l2_hdr) {
+               res = WriteFile(driver_ndis_get_ndisuio_handle(), buf, len,
+                               &written, o);
+       } else {
+               size_t mlen = sizeof(*eth) + len;
+               eth = os_malloc(mlen);
+               if (eth == NULL)
+                       return -1;
+
+               os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+               os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+               eth->h_proto = htons(proto);
+               os_memcpy(eth + 1, buf, len);
+               res = WriteFile(driver_ndis_get_ndisuio_handle(), eth, mlen,
+                               &written, o);
+               os_free(eth);
+       }
+
+       if (!res) {
+               DWORD err = GetLastError();
+#ifndef _WIN32_WCE
+               if (err == ERROR_IO_PENDING) {
+                       wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending "
+                                  "write to complete");
+                       res = GetOverlappedResult(
+                               driver_ndis_get_ndisuio_handle(), &overlapped,
+                               &written, TRUE);
+                       if (!res) {
+                               wpa_printf(MSG_DEBUG, "L2(NDISUIO): "
+                                          "GetOverlappedResult failed: %d",
+                                          (int) GetLastError());
+                               return -1;
+                       }
+                       return 0;
+               }
+#endif /* _WIN32_WCE */
+               wpa_printf(MSG_DEBUG, "L2(NDISUIO): WriteFile failed: %d",
+                          (int) GetLastError());
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void l2_packet_callback(struct l2_packet_data *l2);
+
+#ifdef _WIN32_WCE
+static void l2_packet_rx_thread_try_read(struct l2_packet_data *l2)
+{
+       HANDLE handles[2];
+
+       wpa_printf(MSG_MSGDUMP, "l2_packet_rx_thread: -> ReadFile");
+       if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+                     sizeof(l2->rx_buf), &l2->rx_written, NULL)) {
+               DWORD err = GetLastError();
+               wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: ReadFile failed: "
+                          "%d", (int) err);
+               /*
+                * ReadFile on NDISUIO/WinCE returns ERROR_DEVICE_NOT_CONNECTED
+                * error whenever the connection is not up. Yield the thread to
+                * avoid triggering a busy loop. Connection event should stop
+                * us from looping for long, but we need to allow enough CPU
+                * for the main thread to process the media disconnection.
+                */
+               Sleep(100);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Read %d byte packet",
+                  (int) l2->rx_written);
+
+       /*
+        * Notify the main thread about the availability of a frame and wait
+        * for the frame to be processed.
+        */
+       SetEvent(l2->rx_avail);
+       handles[0] = l2_ndisuio_global->stop_request;
+       handles[1] = l2_ndisuio_global->rx_processed;
+       WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+       ResetEvent(l2_ndisuio_global->rx_processed);
+}
+
+
+static DWORD WINAPI l2_packet_rx_thread(LPVOID arg)
+{
+       struct l2_packet_data *l2 = arg;
+       DWORD res;
+       HANDLE handles[2];
+       int run = 1;
+
+       wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread started");
+       handles[0] = l2_ndisuio_global->stop_request;
+       handles[1] = l2_ndisuio_global->ready_for_read;
+
+       /*
+        * Unfortunately, NDISUIO on WinCE does not seem to support waiting
+        * on the handle. There do not seem to be anything else that we could
+        * wait for either. If one were to modify NDISUIO to set a named event
+        * whenever packets are available, this event could be used here to
+        * avoid having to poll for new packets or we could even move to use a
+        * single threaded design.
+        *
+        * In addition, NDISUIO on WinCE is returning
+        * ERROR_DEVICE_NOT_CONNECTED whenever ReadFile() is attempted while
+        * the adapter is not in connected state. For now, we are just using a
+        * local event to allow ReadFile calls only after having received NDIS
+        * media connect event. This event could be easily converted to handle
+        * another event if the protocol driver is replaced with somewhat more
+        * useful design.
+        */
+
+       while (l2_ndisuio_global && run) {
+               res = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+               switch (res) {
+               case WAIT_OBJECT_0:
+                       wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: Received "
+                                  "request to stop RX thread");
+                       run = 0;
+                       break;
+               case WAIT_OBJECT_0 + 1:
+                       l2_packet_rx_thread_try_read(l2);
+                       break;
+               case WAIT_FAILED:
+               default:
+                       wpa_printf(MSG_DEBUG, "l2_packet_rx_thread: "
+                                  "WaitForMultipleObjects failed: %d",
+                                  (int) GetLastError());
+                       run = 0;
+                       break;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread stopped");
+
+       return 0;
+}
+#else /* _WIN32_WCE */
+static int l2_ndisuio_start_read(struct l2_packet_data *l2, int recursive)
+{
+       os_memset(&l2->rx_overlapped, 0, sizeof(l2->rx_overlapped));
+       l2->rx_overlapped.hEvent = l2->rx_avail;
+       if (!ReadFile(driver_ndis_get_ndisuio_handle(), l2->rx_buf,
+                     sizeof(l2->rx_buf), &l2->rx_written, &l2->rx_overlapped))
+       {
+               DWORD err = GetLastError();
+               if (err != ERROR_IO_PENDING) {
+                       wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile failed: "
+                                  "%d", (int) err);
+                       return -1;
+               }
+               /*
+                * Once read is completed, l2_packet_rx_event() will be
+                * called.
+                */
+       } else {
+               wpa_printf(MSG_DEBUG, "L2(NDISUIO): ReadFile returned data "
+                          "without wait for completion");
+               if (!recursive)
+                       l2_packet_callback(l2);
+       }
+
+       return 0;
+}
+#endif /* _WIN32_WCE */
+
+
+static void l2_packet_callback(struct l2_packet_data *l2)
+{
+       const u8 *rx_buf, *rx_src;
+       size_t rx_len;
+       struct l2_ethhdr *ethhdr = (struct l2_ethhdr *) l2->rx_buf;
+
+       wpa_printf(MSG_DEBUG, "L2(NDISUIO): Read %d bytes",
+                  (int) l2->rx_written);
+
+       if (l2->l2_hdr || l2->rx_written < sizeof(*ethhdr)) {
+               rx_buf = (u8 *) ethhdr;
+               rx_len = l2->rx_written;
+       } else {
+               rx_buf = (u8 *) (ethhdr + 1);
+               rx_len = l2->rx_written - sizeof(*ethhdr);
+       }
+       rx_src = ethhdr->h_source;
+
+       l2->rx_callback(l2->rx_callback_ctx, rx_src, rx_buf, rx_len);
+#ifndef _WIN32_WCE
+       l2_ndisuio_start_read(l2, 1);
+#endif /* _WIN32_WCE */
+}
+
+
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+       struct l2_packet_data *l2 = eloop_data;
+
+       if (l2_ndisuio_global)
+               l2 = l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1];
+
+       ResetEvent(l2->rx_avail);
+
+#ifndef _WIN32_WCE
+       if (!GetOverlappedResult(driver_ndis_get_ndisuio_handle(),
+                                &l2->rx_overlapped, &l2->rx_written, FALSE)) {
+               wpa_printf(MSG_DEBUG, "L2(NDISUIO): GetOverlappedResult "
+                          "failed: %d", (int) GetLastError());
+               return;
+       }
+#endif /* _WIN32_WCE */
+
+       l2_packet_callback(l2);
+
+#ifdef _WIN32_WCE
+       SetEvent(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+}
+
+
+static int l2_ndisuio_set_ether_type(unsigned short protocol)
+{
+       USHORT proto = htons(protocol);
+       DWORD written;
+
+       if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+                            IOCTL_NDISUIO_SET_ETHER_TYPE, &proto,
+                            sizeof(proto), NULL, 0, &written, NULL)) {
+               wpa_printf(MSG_ERROR, "L2(NDISUIO): "
+                          "IOCTL_NDISUIO_SET_ETHER_TYPE failed: %d",
+                          (int) GetLastError());
+               return -1;
+       }
+
+       return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+
+       if (l2_ndisuio_global == NULL) {
+               l2_ndisuio_global = os_zalloc(sizeof(*l2_ndisuio_global));
+               if (l2_ndisuio_global == NULL)
+                       return NULL;
+               l2_ndisuio_global->first_proto = protocol;
+       }
+       if (l2_ndisuio_global->refcount >= 2) {
+               wpa_printf(MSG_ERROR, "L2(NDISUIO): Not more than two "
+                          "simultaneous connections allowed");
+               return NULL;
+       }
+       l2_ndisuio_global->refcount++;
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       l2_ndisuio_global->l2[l2_ndisuio_global->refcount - 1] = l2;
+
+       os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+       l2->l2_hdr = l2_hdr;
+
+       if (own_addr)
+               os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+       if (l2_ndisuio_set_ether_type(protocol) < 0) {
+               os_free(l2);
+               return NULL;
+       }
+
+       if (l2_ndisuio_global->refcount > 1) {
+               wpa_printf(MSG_DEBUG, "L2(NDISUIO): Temporarily setting "
+                          "filtering ethertype to %04x", protocol);
+               if (l2_ndisuio_global->l2[0])
+                       l2->rx_avail = l2_ndisuio_global->l2[0]->rx_avail;
+               return l2;
+       }
+
+       l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (l2->rx_avail == NULL) {
+               os_free(l2);
+               return NULL;
+       }
+
+       eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+                            l2_packet_rx_event, l2, NULL);
+
+#ifdef _WIN32_WCE
+       l2_ndisuio_global->stop_request = CreateEvent(NULL, TRUE, FALSE, NULL);
+       /*
+        * This event is being set based on media connect/disconnect
+        * notifications in driver_ndis.c.
+        */
+       l2_ndisuio_global->ready_for_read =
+               CreateEvent(NULL, TRUE, FALSE, TEXT("WpaSupplicantConnected"));
+       l2_ndisuio_global->rx_processed = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (l2_ndisuio_global->stop_request == NULL ||
+           l2_ndisuio_global->ready_for_read == NULL ||
+           l2_ndisuio_global->rx_processed == NULL) {
+               if (l2_ndisuio_global->stop_request) {
+                       CloseHandle(l2_ndisuio_global->stop_request);
+                       l2_ndisuio_global->stop_request = NULL;
+               }
+               if (l2_ndisuio_global->ready_for_read) {
+                       CloseHandle(l2_ndisuio_global->ready_for_read);
+                       l2_ndisuio_global->ready_for_read = NULL;
+               }
+               if (l2_ndisuio_global->rx_processed) {
+                       CloseHandle(l2_ndisuio_global->rx_processed);
+                       l2_ndisuio_global->rx_processed = NULL;
+               }
+               eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+               os_free(l2);
+               return NULL;
+       }
+
+       l2_ndisuio_global->rx_thread = CreateThread(NULL, 0,
+                                                   l2_packet_rx_thread, l2, 0,
+                                                   NULL);
+       if (l2_ndisuio_global->rx_thread == NULL) {
+               wpa_printf(MSG_INFO, "L2(NDISUIO): Failed to create RX "
+                          "thread: %d", (int) GetLastError());
+               eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+               CloseHandle(l2_ndisuio_global->stop_request);
+               l2_ndisuio_global->stop_request = NULL;
+               os_free(l2);
+               return NULL;
+       }
+#else /* _WIN32_WCE */
+       l2_ndisuio_start_read(l2, 0);
+#endif /* _WIN32_WCE */
+
+       return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 == NULL)
+               return;
+
+       if (l2_ndisuio_global) {
+               l2_ndisuio_global->refcount--;
+               l2_ndisuio_global->l2[l2_ndisuio_global->refcount] = NULL;
+               if (l2_ndisuio_global->refcount) {
+                       wpa_printf(MSG_DEBUG, "L2(NDISUIO): restore filtering "
+                                  "ethertype to %04x",
+                                  l2_ndisuio_global->first_proto);
+                       l2_ndisuio_set_ether_type(
+                               l2_ndisuio_global->first_proto);
+                       return;
+               }
+
+#ifdef _WIN32_WCE
+               wpa_printf(MSG_DEBUG, "L2(NDISUIO): Waiting for RX thread to "
+                          "stop");
+               SetEvent(l2_ndisuio_global->stop_request);
+               /*
+                * Cancel pending ReadFile() in the RX thread (if we were still
+                * connected at this point).
+                */
+               if (!DeviceIoControl(driver_ndis_get_ndisuio_handle(),
+                                    IOCTL_CANCEL_READ, NULL, 0, NULL, 0, NULL,
+                                    NULL)) {
+                       wpa_printf(MSG_DEBUG, "L2(NDISUIO): IOCTL_CANCEL_READ "
+                                  "failed: %d", (int) GetLastError());
+                       /* RX thread will exit blocking ReadFile once NDISUIO
+                        * notices that the adapter is disconnected. */
+               }
+               WaitForSingleObject(l2_ndisuio_global->rx_thread, INFINITE);
+               wpa_printf(MSG_DEBUG, "L2(NDISUIO): RX thread exited");
+               CloseHandle(l2_ndisuio_global->rx_thread);
+               CloseHandle(l2_ndisuio_global->stop_request);
+               CloseHandle(l2_ndisuio_global->ready_for_read);
+               CloseHandle(l2_ndisuio_global->rx_processed);
+#endif /* _WIN32_WCE */
+
+               os_free(l2_ndisuio_global);
+               l2_ndisuio_global = NULL;
+       }
+
+#ifndef _WIN32_WCE
+       CancelIo(driver_ndis_get_ndisuio_handle());
+#endif /* _WIN32_WCE */
+
+       eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+       CloseHandle(l2->rx_avail);
+       os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
diff --git a/src/l2_packet/l2_packet_none.c b/src/l2_packet/l2_packet_none.c
new file mode 100644 (file)
index 0000000..5e3f6e9
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * WPA Supplicant - Layer2 packet handling example with dummy functions
+ * 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.
+ *
+ * This file can be used as a starting point for layer2 packet implementation.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+struct l2_packet_data {
+       char ifname[17];
+       u8 own_addr[ETH_ALEN];
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+                    * buffers */
+       int fd;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       if (l2 == NULL)
+               return -1;
+
+       /*
+        * TODO: Send frame (may need different implementation depending on
+        * whether l2->l2_hdr is set).
+        */
+
+       return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       u8 buf[2300];
+       int res;
+
+       /* TODO: receive frame (e.g., recv() using sock */
+       buf[0] = 0;
+       res = 0;
+
+       l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */,
+                       buf, res);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+       l2->l2_hdr = l2_hdr;
+
+       /*
+        * TODO: open connection for receiving frames
+        */
+       l2->fd = -1;
+       eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+       return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 == NULL)
+               return;
+
+       if (l2->fd >= 0) {
+               eloop_unregister_read_sock(l2->fd);
+               /* TODO: close connection */
+       }
+               
+       os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       /* TODO: get interface IP address */
+       return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+       /* This function can be left empty */
+}
diff --git a/src/l2_packet/l2_packet_pcap.c b/src/l2_packet/l2_packet_pcap.c
new file mode 100644 (file)
index 0000000..8156e29
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with libpcap/libdnet and WinPcap
+ * Copyright (c) 2003-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.
+ */
+
+#include "includes.h"
+#ifndef CONFIG_NATIVE_WINDOWS
+#include <sys/ioctl.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <pcap.h>
+#ifndef CONFIG_WINPCAP
+#include <dnet.h>
+#endif /* CONFIG_WINPCAP */
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+       pcap_t *pcap;
+#ifdef CONFIG_WINPCAP
+       unsigned int num_fast_poll;
+#else /* CONFIG_WINPCAP */
+       eth_t *eth;
+#endif /* CONFIG_WINPCAP */
+       char ifname[100];
+       u8 own_addr[ETH_ALEN];
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls
+                       * to rx_callback */
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static int l2_packet_init_libdnet(struct l2_packet_data *l2)
+{
+       eth_addr_t own_addr;
+
+       l2->eth = eth_open(l2->ifname);
+       if (!l2->eth) {
+               printf("Failed to open interface '%s'.\n", l2->ifname);
+               perror("eth_open");
+               return -1;
+       }
+
+       if (eth_get(l2->eth, &own_addr) < 0) {
+               printf("Failed to get own hw address from interface '%s'.\n",
+                      l2->ifname);
+               perror("eth_get");
+               eth_close(l2->eth);
+               l2->eth = NULL;
+               return -1;
+       }
+       os_memcpy(l2->own_addr, own_addr.data, ETH_ALEN);
+
+       return 0;
+}
+#endif /* CONFIG_WINPCAP */
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       int ret;
+       struct l2_ethhdr *eth;
+
+       if (l2 == NULL)
+               return -1;
+
+       if (l2->l2_hdr) {
+#ifdef CONFIG_WINPCAP
+               ret = pcap_sendpacket(l2->pcap, buf, len);
+#else /* CONFIG_WINPCAP */
+               ret = eth_send(l2->eth, buf, len);
+#endif /* CONFIG_WINPCAP */
+       } else {
+               size_t mlen = sizeof(*eth) + len;
+               eth = os_malloc(mlen);
+               if (eth == NULL)
+                       return -1;
+
+               os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+               os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+               eth->h_proto = htons(proto);
+               os_memcpy(eth + 1, buf, len);
+
+#ifdef CONFIG_WINPCAP
+               ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+#else /* CONFIG_WINPCAP */
+               ret = eth_send(l2->eth, (u8 *) eth, mlen);
+#endif /* CONFIG_WINPCAP */
+
+               os_free(eth);
+       }
+
+       return ret;
+}
+
+
+#ifndef CONFIG_WINPCAP
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       pcap_t *pcap = sock_ctx;
+       struct pcap_pkthdr hdr;
+       const u_char *packet;
+       struct l2_ethhdr *ethhdr;
+       unsigned char *buf;
+       size_t len;
+
+       packet = pcap_next(pcap, &hdr);
+
+       if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+               return;
+
+       ethhdr = (struct l2_ethhdr *) packet;
+       if (l2->l2_hdr) {
+               buf = (unsigned char *) ethhdr;
+               len = hdr.caplen;
+       } else {
+               buf = (unsigned char *) (ethhdr + 1);
+               len = hdr.caplen - sizeof(*ethhdr);
+       }
+       l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+#ifdef CONFIG_WINPCAP
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+                                const u_char *pkt_data)
+{
+       struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+       struct l2_ethhdr *ethhdr;
+       unsigned char *buf;
+       size_t len;
+
+       if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+               return;
+
+       ethhdr = (struct l2_ethhdr *) pkt_data;
+       if (l2->l2_hdr) {
+               buf = (unsigned char *) ethhdr;
+               len = hdr->caplen;
+       } else {
+               buf = (unsigned char *) (ethhdr + 1);
+               len = hdr->caplen - sizeof(*ethhdr);
+       }
+       l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+       /*
+        * Use shorter poll interval for 3 seconds to reduce latency during key
+        * handshake.
+        */
+       l2->num_fast_poll = 3 * 50;
+}
+
+
+static void l2_packet_receive_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       pcap_t *pcap = timeout_ctx;
+       int timeout;
+
+       if (l2->num_fast_poll > 0) {
+               timeout = 20000;
+               l2->num_fast_poll--;
+       } else
+               timeout = 100000;
+
+       /* Register new timeout before calling l2_packet_receive() since
+        * receive handler may free this l2_packet instance (which will
+        * cancel this timeout). */
+       eloop_register_timeout(0, timeout, l2_packet_receive_timeout,
+                              l2, pcap);
+       pcap_dispatch(pcap, 10, l2_packet_receive_cb, (u_char *) l2);
+}
+#endif /* CONFIG_WINPCAP */
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+                                 unsigned short protocol)
+{
+       bpf_u_int32 pcap_maskp, pcap_netp;
+       char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+       struct bpf_program pcap_fp;
+
+#ifdef CONFIG_WINPCAP
+       char ifname[128];
+       os_snprintf(ifname, sizeof(ifname), "\\Device\\NPF_%s", l2->ifname);
+       pcap_lookupnet(ifname, &pcap_netp, &pcap_maskp, pcap_err);
+       l2->pcap = pcap_open_live(ifname, 2500, 0, 10, pcap_err);
+       if (l2->pcap == NULL) {
+               fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+               fprintf(stderr, "ifname='%s'\n", ifname);
+               return -1;
+       }
+       if (pcap_setnonblock(l2->pcap, 1, pcap_err) < 0)
+               fprintf(stderr, "pcap_setnonblock: %s\n",
+                       pcap_geterr(l2->pcap));
+#else /* CONFIG_WINPCAP */
+       pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+       l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+       if (l2->pcap == NULL) {
+               fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+               fprintf(stderr, "ifname='%s'\n", l2->ifname);
+               return -1;
+       }
+       if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+           pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+               fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+                       pcap_geterr(l2->pcap));
+               return -1;
+       }
+#endif /* CONFIG_WINPCAP */
+       os_snprintf(pcap_filter, sizeof(pcap_filter),
+                   "not ether src " MACSTR " and "
+                   "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+                   "ether proto 0x%x",
+                   MAC2STR(l2->own_addr), /* do not receive own packets */
+                   MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+                   protocol);
+       if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+               fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+               return -1;
+       }
+
+       if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+               fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+               return -1;
+       }
+
+       pcap_freecode(&pcap_fp);
+#ifdef BIOCIMMEDIATE
+       /*
+        * When libpcap uses BPF we must enable "immediate mode" to
+        * receive frames right away; otherwise the system may
+        * buffer them for us.
+        */
+       {
+               unsigned int on = 1;
+               if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+                       fprintf(stderr, "%s: cannot enable immediate mode on "
+                               "interface %s: %s\n",
+                               __func__, l2->ifname, strerror(errno));
+                       /* XXX should we fail? */
+               }
+       }
+#endif /* BIOCIMMEDIATE */
+
+#ifdef CONFIG_WINPCAP
+       eloop_register_timeout(0, 100000, l2_packet_receive_timeout,
+                              l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+       eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+                                l2_packet_receive, l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+
+       return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+       l2->l2_hdr = l2_hdr;
+
+#ifdef CONFIG_WINPCAP
+       if (own_addr)
+               os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+#else /* CONFIG_WINPCAP */
+       if (l2_packet_init_libdnet(l2))
+               return NULL;
+#endif /* CONFIG_WINPCAP */
+
+       if (l2_packet_init_libpcap(l2, protocol)) {
+#ifndef CONFIG_WINPCAP
+               eth_close(l2->eth);
+#endif /* CONFIG_WINPCAP */
+               os_free(l2);
+               return NULL;
+       }
+
+       return l2;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 == NULL)
+               return;
+
+#ifdef CONFIG_WINPCAP
+       eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+#else /* CONFIG_WINPCAP */
+       if (l2->eth)
+               eth_close(l2->eth);
+       eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap));
+#endif /* CONFIG_WINPCAP */
+       if (l2->pcap)
+               pcap_close(l2->pcap);
+       os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       pcap_if_t *devs, *dev;
+       struct pcap_addr *addr;
+       struct sockaddr_in *saddr;
+       int found = 0;
+       char err[PCAP_ERRBUF_SIZE + 1];
+
+       if (pcap_findalldevs(&devs, err) < 0) {
+               wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+               return -1;
+       }
+
+       for (dev = devs; dev && !found; dev = dev->next) {
+               if (os_strcmp(dev->name, l2->ifname) != 0)
+                       continue;
+
+               addr = dev->addresses;
+               while (addr) {
+                       saddr = (struct sockaddr_in *) addr->addr;
+                       if (saddr && saddr->sin_family == AF_INET) {
+                               os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+                                          len);
+                               found = 1;
+                               break;
+                       }
+                       addr = addr->next;
+               }
+       }
+
+       pcap_freealldevs(devs);
+
+       return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+#ifdef CONFIG_WINPCAP
+       /*
+        * Use shorter poll interval for 3 seconds to reduce latency during key
+        * handshake.
+        */
+       l2->num_fast_poll = 3 * 50;
+       eloop_cancel_timeout(l2_packet_receive_timeout, l2, l2->pcap);
+       eloop_register_timeout(0, 10000, l2_packet_receive_timeout,
+                              l2, l2->pcap);
+#endif /* CONFIG_WINPCAP */
+}
diff --git a/src/l2_packet/l2_packet_privsep.c b/src/l2_packet/l2_packet_privsep.c
new file mode 100644 (file)
index 0000000..79d2968
--- /dev/null
@@ -0,0 +1,267 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with privilege separation
+ * 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"
+#include <sys/un.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+#include "common/privsep_commands.h"
+
+
+struct l2_packet_data {
+       int fd; /* UNIX domain socket for privsep access */
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       u8 own_addr[ETH_ALEN];
+       char *own_socket_path;
+       struct sockaddr_un priv_addr;
+};
+
+
+static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
+                       const void *data, size_t data_len)
+{
+       struct msghdr msg;
+       struct iovec io[2];
+
+       io[0].iov_base = &cmd;
+       io[0].iov_len = sizeof(cmd);
+       io[1].iov_base = (u8 *) data;
+       io[1].iov_len = data_len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = data ? 2 : 1;
+       msg.msg_name = &l2->priv_addr;
+       msg.msg_namelen = sizeof(l2->priv_addr);
+
+       if (sendmsg(l2->fd, &msg, 0) < 0) {
+               perror("L2: sendmsg(cmd)");
+               return -1;
+       }
+
+       return 0;
+}
+
+                            
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       struct msghdr msg;
+       struct iovec io[4];
+       int cmd = PRIVSEP_CMD_L2_SEND;
+
+       io[0].iov_base = &cmd;
+       io[0].iov_len = sizeof(cmd);
+       io[1].iov_base = &dst_addr;
+       io[1].iov_len = ETH_ALEN;
+       io[2].iov_base = &proto;
+       io[2].iov_len = 2;
+       io[3].iov_base = (u8 *) buf;
+       io[3].iov_len = len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 4;
+       msg.msg_name = &l2->priv_addr;
+       msg.msg_namelen = sizeof(l2->priv_addr);
+
+       if (sendmsg(l2->fd, &msg, 0) < 0) {
+               perror("L2: sendmsg(packet_send)");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       u8 buf[2300];
+       int res;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+
+       os_memset(&from, 0, sizeof(from));
+       res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+                      &fromlen);
+       if (res < 0) {
+               perror("l2_packet_receive - recvfrom");
+               return;
+       }
+       if (res < ETH_ALEN) {
+               wpa_printf(MSG_DEBUG, "L2: Too show packet received");
+               return;
+       }
+
+       if (from.sun_family != AF_UNIX ||
+           os_strncmp(from.sun_path, l2->priv_addr.sun_path,
+                      sizeof(from.sun_path)) != 0) {
+               wpa_printf(MSG_DEBUG, "L2: Received message from unexpected "
+                          "source");
+               return;
+       }
+
+       l2->rx_callback(l2->rx_callback_ctx, buf, buf + ETH_ALEN,
+                       res - ETH_ALEN);
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+       char *own_dir = "/tmp";
+       char *priv_dir = "/var/run/wpa_priv";
+       size_t len;
+       static unsigned int counter = 0;
+       struct sockaddr_un addr;
+       fd_set rfds;
+       struct timeval tv;
+       int res;
+       u8 reply[ETH_ALEN + 1];
+       int reg_cmd[2];
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+
+       len = os_strlen(own_dir) + 50;
+       l2->own_socket_path = os_malloc(len);
+       if (l2->own_socket_path == NULL) {
+               os_free(l2);
+               return NULL;
+       }
+       os_snprintf(l2->own_socket_path, len, "%s/wpa_privsep-l2-%d-%d",
+                   own_dir, getpid(), counter++);
+
+       l2->priv_addr.sun_family = AF_UNIX;
+       os_snprintf(l2->priv_addr.sun_path, sizeof(l2->priv_addr.sun_path),
+                   "%s/%s", priv_dir, ifname);
+
+       l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (l2->fd < 0) {
+               perror("socket(PF_UNIX)");
+               os_free(l2->own_socket_path);
+               l2->own_socket_path = NULL;
+               os_free(l2);
+               return NULL;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
+       if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind(PF_UNIX)");
+               goto fail;
+       }
+
+       reg_cmd[0] = protocol;
+       reg_cmd[1] = l2_hdr;
+       if (wpa_priv_cmd(l2, PRIVSEP_CMD_L2_REGISTER, reg_cmd, sizeof(reg_cmd))
+           < 0) {
+               wpa_printf(MSG_ERROR, "L2: Failed to register with wpa_priv");
+               goto fail;
+       }
+
+       FD_ZERO(&rfds);
+       FD_SET(l2->fd, &rfds);
+       tv.tv_sec = 5;
+       tv.tv_usec = 0;
+       res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
+       if (res < 0 && errno != EINTR) {
+               perror("select");
+               goto fail;
+       }
+
+       if (FD_ISSET(l2->fd, &rfds)) {
+               res = recv(l2->fd, reply, sizeof(reply), 0);
+               if (res < 0) {
+                       perror("recv");
+                       goto fail;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "L2: Timeout while waiting for "
+                          "registration reply");
+               goto fail;
+       }
+
+       if (res != ETH_ALEN) {
+               wpa_printf(MSG_DEBUG, "L2: Unexpected registration reply "
+                          "(len=%d)", res);
+       }
+       os_memcpy(l2->own_addr, reply, ETH_ALEN);
+
+       eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+
+       return l2;
+
+fail:
+       close(l2->fd);
+       l2->fd = -1;
+       unlink(l2->own_socket_path);
+       os_free(l2->own_socket_path);
+       l2->own_socket_path = NULL;
+       os_free(l2);
+       return NULL;
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 == NULL)
+               return;
+
+       if (l2->fd >= 0) {
+               wpa_priv_cmd(l2, PRIVSEP_CMD_L2_UNREGISTER, NULL, 0);
+               eloop_unregister_read_sock(l2->fd);
+               close(l2->fd);
+       }
+
+       if (l2->own_socket_path) {
+               unlink(l2->own_socket_path);
+               os_free(l2->own_socket_path);
+       }
+               
+       os_free(l2);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       /* TODO */
+       return -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+       wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
+}
diff --git a/src/l2_packet/l2_packet_winpcap.c b/src/l2_packet/l2_packet_winpcap.c
new file mode 100644 (file)
index 0000000..f76b386
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * WPA Supplicant - Layer2 packet handling with WinPcap RX thread
+ * Copyright (c) 2003-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.
+ *
+ * This l2_packet implementation is explicitly for WinPcap and Windows events.
+ * l2_packet_pcap.c has support for WinPcap, but it requires polling to receive
+ * frames which means relatively long latency for EAPOL RX processing. The
+ * implementation here uses a separate thread to allow WinPcap to be receiving
+ * all the time to reduce latency for EAPOL receiving from about 100 ms to 3 ms
+ * when comparing l2_packet_pcap.c to l2_packet_winpcap.c. Extra sleep of 50 ms
+ * is added in to receive thread whenever no EAPOL frames has been received for
+ * a while. Whenever an EAPOL handshake is expected, this sleep is removed.
+ *
+ * The RX thread receives a frame and signals main thread through Windows event
+ * about the availability of a new frame. Processing the received frame is
+ * synchronized with pair of Windows events so that no extra buffer or queuing
+ * mechanism is needed. This implementation requires Windows specific event
+ * loop implementation, i.e., eloop_win.c.
+ *
+ * WinPcap has pcap_getevent() that could, in theory at least, be used to
+ * implement this kind of waiting with a simpler single-thread design. However,
+ * that event handle is not really signaled immediately when receiving each
+ * frame, so it does not really work for this kind of use.
+ */
+
+#include "includes.h"
+#include <pcap.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+/*
+ * Number of pcap_dispatch() iterations to do without extra wait after each
+ * received EAPOL packet or authentication notification. This is used to reduce
+ * latency for EAPOL receive.
+ */
+static const size_t no_wait_count = 750;
+
+struct l2_packet_data {
+       pcap_t *pcap;
+       unsigned int num_fast_poll;
+       char ifname[100];
+       u8 own_addr[ETH_ALEN];
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len);
+       void *rx_callback_ctx;
+       int l2_hdr; /* whether to include layer 2 (Ethernet) header in calls to
+                    * rx_callback and l2_packet_send() */
+       int running;
+       HANDLE rx_avail, rx_done, rx_thread, rx_thread_done, rx_notify;
+       u8 *rx_buf, *rx_src;
+       size_t rx_len;
+       size_t rx_no_wait;
+};
+
+
+int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+       os_memcpy(addr, l2->own_addr, ETH_ALEN);
+       return 0;
+}
+
+
+int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
+                  const u8 *buf, size_t len)
+{
+       int ret;
+       struct l2_ethhdr *eth;
+
+       if (l2 == NULL)
+               return -1;
+
+       if (l2->l2_hdr) {
+               ret = pcap_sendpacket(l2->pcap, buf, len);
+       } else {
+               size_t mlen = sizeof(*eth) + len;
+               eth = os_malloc(mlen);
+               if (eth == NULL)
+                       return -1;
+
+               os_memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+               os_memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+               eth->h_proto = htons(proto);
+               os_memcpy(eth + 1, buf, len);
+               ret = pcap_sendpacket(l2->pcap, (u8 *) eth, mlen);
+               os_free(eth);
+       }
+
+       return ret;
+}
+
+
+/* pcap_dispatch() callback for the RX thread */
+static void l2_packet_receive_cb(u_char *user, const struct pcap_pkthdr *hdr,
+                                const u_char *pkt_data)
+{
+       struct l2_packet_data *l2 = (struct l2_packet_data *) user;
+       struct l2_ethhdr *ethhdr;
+
+       if (pkt_data == NULL || hdr->caplen < sizeof(*ethhdr))
+               return;
+
+       ethhdr = (struct l2_ethhdr *) pkt_data;
+       if (l2->l2_hdr) {
+               l2->rx_buf = (u8 *) ethhdr;
+               l2->rx_len = hdr->caplen;
+       } else {
+               l2->rx_buf = (u8 *) (ethhdr + 1);
+               l2->rx_len = hdr->caplen - sizeof(*ethhdr);
+       }
+       l2->rx_src = ethhdr->h_source;
+       SetEvent(l2->rx_avail);
+       WaitForSingleObject(l2->rx_done, INFINITE);
+       ResetEvent(l2->rx_done);
+       l2->rx_no_wait = no_wait_count;
+}
+
+
+/* main RX loop that is running in a separate thread */
+static DWORD WINAPI l2_packet_receive_thread(LPVOID arg)
+{
+       struct l2_packet_data *l2 = arg;
+
+       while (l2->running) {
+               pcap_dispatch(l2->pcap, 1, l2_packet_receive_cb,
+                             (u_char *) l2);
+               if (l2->rx_no_wait > 0)
+                       l2->rx_no_wait--;
+               if (WaitForSingleObject(l2->rx_notify,
+                                       l2->rx_no_wait ? 0 : 50) ==
+                   WAIT_OBJECT_0) {
+                       l2->rx_no_wait = no_wait_count;
+                       ResetEvent(l2->rx_notify);
+               }
+       }
+       SetEvent(l2->rx_thread_done);
+       ExitThread(0);
+       return 0;
+}
+
+
+/* main thread RX event handler */
+static void l2_packet_rx_event(void *eloop_data, void *user_data)
+{
+       struct l2_packet_data *l2 = eloop_data;
+       l2->rx_callback(l2->rx_callback_ctx, l2->rx_src, l2->rx_buf,
+                       l2->rx_len);
+       ResetEvent(l2->rx_avail);
+       SetEvent(l2->rx_done);
+}
+
+
+static int l2_packet_init_libpcap(struct l2_packet_data *l2,
+                                 unsigned short protocol)
+{
+       bpf_u_int32 pcap_maskp, pcap_netp;
+       char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+       struct bpf_program pcap_fp;
+
+       pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+       l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 1, pcap_err);
+       if (l2->pcap == NULL) {
+               fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+               fprintf(stderr, "ifname='%s'\n", l2->ifname);
+               return -1;
+       }
+       os_snprintf(pcap_filter, sizeof(pcap_filter),
+                   "not ether src " MACSTR " and "
+                   "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+                   "ether proto 0x%x",
+                   MAC2STR(l2->own_addr), /* do not receive own packets */
+                   MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+                   protocol);
+       if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+               fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+               return -1;
+       }
+
+       if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+               fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+               return -1;
+       }
+
+       pcap_freecode(&pcap_fp);
+
+       return 0;
+}
+
+
+struct l2_packet_data * l2_packet_init(
+       const char *ifname, const u8 *own_addr, unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+       DWORD thread_id;
+
+       l2 = os_zalloc(sizeof(struct l2_packet_data));
+       if (l2 == NULL)
+               return NULL;
+       if (os_strncmp(ifname, "\\Device\\NPF_", 12) == 0)
+               os_strlcpy(l2->ifname, ifname, sizeof(l2->ifname));
+       else
+               os_snprintf(l2->ifname, sizeof(l2->ifname), "\\Device\\NPF_%s",
+                           ifname);
+       l2->rx_callback = rx_callback;
+       l2->rx_callback_ctx = rx_callback_ctx;
+       l2->l2_hdr = l2_hdr;
+
+       if (own_addr)
+               os_memcpy(l2->own_addr, own_addr, ETH_ALEN);
+
+       if (l2_packet_init_libpcap(l2, protocol)) {
+               os_free(l2);
+               return NULL;
+       }
+
+       l2->rx_avail = CreateEvent(NULL, TRUE, FALSE, NULL);
+       l2->rx_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+       l2->rx_notify = CreateEvent(NULL, TRUE, FALSE, NULL);
+       if (l2->rx_avail == NULL || l2->rx_done == NULL ||
+           l2->rx_notify == NULL) {
+               CloseHandle(l2->rx_avail);
+               CloseHandle(l2->rx_done);
+               CloseHandle(l2->rx_notify);
+               pcap_close(l2->pcap);
+               os_free(l2);
+               return NULL;
+       }
+
+       eloop_register_event(l2->rx_avail, sizeof(l2->rx_avail),
+                            l2_packet_rx_event, l2, NULL);
+
+       l2->running = 1;
+       l2->rx_thread = CreateThread(NULL, 0, l2_packet_receive_thread, l2, 0,
+                                    &thread_id);
+
+       return l2;
+}
+
+
+static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+
+       if (l2->rx_thread_done &&
+           WaitForSingleObject(l2->rx_thread_done, 2000) != WAIT_OBJECT_0) {
+               wpa_printf(MSG_DEBUG, "l2_packet_winpcap: RX thread did not "
+                          "exit - kill it\n");
+               TerminateThread(l2->rx_thread, 0);
+       }
+       CloseHandle(l2->rx_thread_done);
+       CloseHandle(l2->rx_thread);
+       if (l2->pcap)
+               pcap_close(l2->pcap);
+       eloop_unregister_event(l2->rx_avail, sizeof(l2->rx_avail));
+       CloseHandle(l2->rx_avail);
+       CloseHandle(l2->rx_done);
+       CloseHandle(l2->rx_notify);
+       os_free(l2);
+}
+
+
+void l2_packet_deinit(struct l2_packet_data *l2)
+{
+       if (l2 == NULL)
+               return;
+
+       l2->rx_thread_done = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+       l2->running = 0;
+       pcap_breakloop(l2->pcap);
+
+       /*
+        * RX thread may be waiting in l2_packet_receive_cb() for l2->rx_done
+        * event and this event is set in l2_packet_rx_event(). However,
+        * l2_packet_deinit() may end up being called from l2->rx_callback(),
+        * so we need to return from here and complete deinitialization in
+        * a registered timeout to avoid having to forcefully kill the RX
+        * thread.
+        */
+       eloop_register_timeout(0, 0, l2_packet_deinit_timeout, l2, NULL);
+}
+
+
+int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+       pcap_if_t *devs, *dev;
+       struct pcap_addr *addr;
+       struct sockaddr_in *saddr;
+       int found = 0;
+       char err[PCAP_ERRBUF_SIZE + 1];
+
+       if (pcap_findalldevs(&devs, err) < 0) {
+               wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+               return -1;
+       }
+
+       for (dev = devs; dev && !found; dev = dev->next) {
+               if (os_strcmp(dev->name, l2->ifname) != 0)
+                       continue;
+
+               addr = dev->addresses;
+               while (addr) {
+                       saddr = (struct sockaddr_in *) addr->addr;
+                       if (saddr && saddr->sin_family == AF_INET) {
+                               os_strlcpy(buf, inet_ntoa(saddr->sin_addr),
+                                          len);
+                               found = 1;
+                               break;
+                       }
+                       addr = addr->next;
+               }
+       }
+
+       pcap_freealldevs(devs);
+
+       return found ? 0 : -1;
+}
+
+
+void l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+       if (l2)
+               SetEvent(l2->rx_notify);
+}
diff --git a/src/lib.rules b/src/lib.rules
new file mode 100644 (file)
index 0000000..b260d25
--- /dev/null
@@ -0,0 +1,21 @@
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I.. -I../utils
+
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+%.o: %.c
+       $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+       @$(E) "  CC " $<
diff --git a/src/radius/.gitignore b/src/radius/.gitignore
new file mode 100644 (file)
index 0000000..a89a1f9
--- /dev/null
@@ -0,0 +1 @@
+libradius.a
diff --git a/src/radius/Makefile b/src/radius/Makefile
new file mode 100644 (file)
index 0000000..b199be8
--- /dev/null
@@ -0,0 +1,22 @@
+all: libradius.a
+
+clean:
+       rm -f *~ *.o *.d libradius.a
+
+install:
+       @echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_IPV6
+
+LIB_OBJS= \
+       radius.o \
+       radius_client.o \
+       radius_server.o
+
+libradius.a: $(LIB_OBJS)
+       $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/radius/radius.c b/src/radius/radius.c
new file mode 100644 (file)
index 0000000..70754ef
--- /dev/null
@@ -0,0 +1,1317 @@
+/*
+ * RADIUS message processing
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/wpabuf.h"
+#include "crypto/md5.h"
+#include "crypto/crypto.h"
+#include "radius.h"
+
+
+/**
+ * struct radius_msg - RADIUS message structure for new and parsed messages
+ */
+struct radius_msg {
+       /**
+        * buf - Allocated buffer for RADIUS message
+        */
+       struct wpabuf *buf;
+
+       /**
+        * hdr - Pointer to the RADIUS header in buf
+        */
+       struct radius_hdr *hdr;
+
+       /**
+        * attr_pos - Array of indexes to attributes
+        *
+        * The values are number of bytes from buf to the beginning of
+        * struct radius_attr_hdr.
+        */
+       size_t *attr_pos;
+
+       /**
+        * attr_size - Total size of the attribute pointer array
+        */
+       size_t attr_size;
+
+       /**
+        * attr_used - Total number of attributes in the array
+        */
+       size_t attr_used;
+};
+
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg)
+{
+       return msg->hdr;
+}
+
+
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg)
+{
+       return msg->buf;
+}
+
+
+static struct radius_attr_hdr *
+radius_get_attr_hdr(struct radius_msg *msg, int idx)
+{
+       return (struct radius_attr_hdr *)
+               (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]);
+}
+
+
+static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier)
+{
+       msg->hdr->code = code;
+       msg->hdr->identifier = identifier;
+}
+
+
+static int radius_msg_initialize(struct radius_msg *msg)
+{
+       msg->attr_pos =
+               os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos));
+       if (msg->attr_pos == NULL)
+               return -1;
+
+       msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT;
+       msg->attr_used = 0;
+
+       return 0;
+}
+
+
+/**
+ * radius_msg_new - Create a new RADIUS message
+ * @code: Code for RADIUS header
+ * @identifier: Identifier for RADIUS header
+ * Returns: Context for RADIUS message or %NULL on failure
+ *
+ * The caller is responsible for freeing the returned data with
+ * radius_msg_free().
+ */
+struct radius_msg * radius_msg_new(u8 code, u8 identifier)
+{
+       struct radius_msg *msg;
+
+       msg = os_zalloc(sizeof(*msg));
+       if (msg == NULL)
+               return NULL;
+
+       msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE);
+       if (msg->buf == NULL || radius_msg_initialize(msg)) {
+               radius_msg_free(msg);
+               return NULL;
+       }
+       msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr));
+
+       radius_msg_set_hdr(msg, code, identifier);
+
+       return msg;
+}
+
+
+/**
+ * radius_msg_free - Free a RADIUS message
+ * @msg: RADIUS message from radius_msg_new() or radius_msg_parse()
+ */
+void radius_msg_free(struct radius_msg *msg)
+{
+       if (msg == NULL)
+               return;
+
+       wpabuf_free(msg->buf);
+       os_free(msg->attr_pos);
+       os_free(msg);
+}
+
+
+static const char *radius_code_string(u8 code)
+{
+       switch (code) {
+       case RADIUS_CODE_ACCESS_REQUEST: return "Access-Request";
+       case RADIUS_CODE_ACCESS_ACCEPT: return "Access-Accept";
+       case RADIUS_CODE_ACCESS_REJECT: return "Access-Reject";
+       case RADIUS_CODE_ACCOUNTING_REQUEST: return "Accounting-Request";
+       case RADIUS_CODE_ACCOUNTING_RESPONSE: return "Accounting-Response";
+       case RADIUS_CODE_ACCESS_CHALLENGE: return "Access-Challenge";
+       case RADIUS_CODE_STATUS_SERVER: return "Status-Server";
+       case RADIUS_CODE_STATUS_CLIENT: return "Status-Client";
+       case RADIUS_CODE_RESERVED: return "Reserved";
+       default: return "?Unknown?";
+       }
+}
+
+
+struct radius_attr_type {
+       u8 type;
+       char *name;
+       enum {
+               RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP,
+               RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6
+       } data_type;
+};
+
+static struct radius_attr_type radius_attrs[] =
+{
+       { RADIUS_ATTR_USER_NAME, "User-Name", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_USER_PASSWORD, "User-Password", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_NAS_IP_ADDRESS, "NAS-IP-Address", RADIUS_ATTR_IP },
+       { RADIUS_ATTR_NAS_PORT, "NAS-Port", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_FRAMED_MTU, "Framed-MTU", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_REPLY_MESSAGE, "Reply-Message", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_STATE, "State", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_CLASS, "Class", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_VENDOR_SPECIFIC, "Vendor-Specific", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_SESSION_TIMEOUT, "Session-Timeout", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_IDLE_TIMEOUT, "Idle-Timeout", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_TERMINATION_ACTION, "Termination-Action",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_CALLED_STATION_ID, "Called-Station-Id",
+         RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_CALLING_STATION_ID, "Calling-Station-Id",
+         RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_NAS_IDENTIFIER, "NAS-Identifier", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_PROXY_STATE, "Proxy-State", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_ACCT_STATUS_TYPE, "Acct-Status-Type",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_DELAY_TIME, "Acct-Delay-Time", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_INPUT_OCTETS, "Acct-Input-Octets",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_OUTPUT_OCTETS, "Acct-Output-Octets",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_SESSION_ID, "Acct-Session-Id", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_ACCT_AUTHENTIC, "Acct-Authentic", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_SESSION_TIME, "Acct-Session-Time",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_INPUT_PACKETS, "Acct-Input-Packets",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_OUTPUT_PACKETS, "Acct-Output-Packets",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_TERMINATE_CAUSE, "Acct-Terminate-Cause",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_MULTI_SESSION_ID, "Acct-Multi-Session-Id",
+         RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_ACCT_LINK_COUNT, "Acct-Link-Count", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, "Acct-Input-Gigawords", 
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, "Acct-Output-Gigawords",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator",
+         RADIUS_ATTR_UNDIST },
+       { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargable-User-Identity",
+         RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
+};
+#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+
+
+static struct radius_attr_type *radius_get_attr_type(u8 type)
+{
+       size_t i;
+
+       for (i = 0; i < RADIUS_ATTRS; i++) {
+               if (type == radius_attrs[i].type)
+                       return &radius_attrs[i];
+       }
+
+       return NULL;
+}
+
+
+static void print_char(char c)
+{
+       if (c >= 32 && c < 127)
+               printf("%c", c);
+       else
+               printf("<%02x>", c);
+}
+
+
+static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
+{
+       struct radius_attr_type *attr;
+       int i, len;
+       unsigned char *pos;
+
+       attr = radius_get_attr_type(hdr->type);
+
+       printf("   Attribute %d (%s) length=%d\n",
+              hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+
+       if (attr == NULL)
+               return;
+
+       len = hdr->length - sizeof(struct radius_attr_hdr);
+       pos = (unsigned char *) (hdr + 1);
+
+       switch (attr->data_type) {
+       case RADIUS_ATTR_TEXT:
+               printf("      Value: '");
+               for (i = 0; i < len; i++)
+                       print_char(pos[i]);
+               printf("'\n");
+               break;
+
+       case RADIUS_ATTR_IP:
+               if (len == 4) {
+                       struct in_addr addr;
+                       os_memcpy(&addr, pos, 4);
+                       printf("      Value: %s\n", inet_ntoa(addr));
+               } else
+                       printf("      Invalid IP address length %d\n", len);
+               break;
+
+#ifdef CONFIG_IPV6
+       case RADIUS_ATTR_IPV6:
+               if (len == 16) {
+                       char buf[128];
+                       const char *atxt;
+                       struct in6_addr *addr = (struct in6_addr *) pos;
+                       atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+                       printf("      Value: %s\n", atxt ? atxt : "?");
+               } else
+                       printf("      Invalid IPv6 address length %d\n", len);
+               break;
+#endif /* CONFIG_IPV6 */
+
+       case RADIUS_ATTR_HEXDUMP:
+       case RADIUS_ATTR_UNDIST:
+               printf("      Value:");
+               for (i = 0; i < len; i++)
+                       printf(" %02x", pos[i]);
+               printf("\n");
+               break;
+
+       case RADIUS_ATTR_INT32:
+               if (len == 4)
+                       printf("      Value: %u\n", WPA_GET_BE32(pos));
+               else
+                       printf("      Invalid INT32 length %d\n", len);
+               break;
+
+       default:
+               break;
+       }
+}
+
+
+void radius_msg_dump(struct radius_msg *msg)
+{
+       size_t i;
+
+       printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
+              msg->hdr->code, radius_code_string(msg->hdr->code),
+              msg->hdr->identifier, ntohs(msg->hdr->length));
+
+       for (i = 0; i < msg->attr_used; i++) {
+               struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+               radius_msg_dump_attr(attr);
+       }
+}
+
+
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+                     size_t secret_len)
+{
+       if (secret) {
+               u8 auth[MD5_MAC_LEN];
+               struct radius_attr_hdr *attr;
+
+               os_memset(auth, 0, MD5_MAC_LEN);
+               attr = radius_msg_add_attr(msg,
+                                          RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+                                          auth, MD5_MAC_LEN);
+               if (attr == NULL) {
+                       wpa_printf(MSG_WARNING, "RADIUS: Could not add "
+                                  "Message-Authenticator");
+                       return -1;
+               }
+               msg->hdr->length = htons(wpabuf_len(msg->buf));
+               hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+                        wpabuf_len(msg->buf), (u8 *) (attr + 1));
+       } else
+               msg->hdr->length = htons(wpabuf_len(msg->buf));
+
+       if (wpabuf_len(msg->buf) > 0xffff) {
+               wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+                          (unsigned long) wpabuf_len(msg->buf));
+               return -1;
+       }
+       return 0;
+}
+
+
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+                         size_t secret_len, const u8 *req_authenticator)
+{
+       u8 auth[MD5_MAC_LEN];
+       struct radius_attr_hdr *attr;
+       const u8 *addr[4];
+       size_t len[4];
+
+       os_memset(auth, 0, MD5_MAC_LEN);
+       attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
+                                  auth, MD5_MAC_LEN);
+       if (attr == NULL) {
+               printf("WARNING: Could not add Message-Authenticator\n");
+               return -1;
+       }
+       msg->hdr->length = htons(wpabuf_len(msg->buf));
+       os_memcpy(msg->hdr->authenticator, req_authenticator,
+                 sizeof(msg->hdr->authenticator));
+       hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+                wpabuf_len(msg->buf), (u8 *) (attr + 1));
+
+       /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+       addr[0] = (u8 *) msg->hdr;
+       len[0] = 1 + 1 + 2;
+       addr[1] = req_authenticator;
+       len[1] = MD5_MAC_LEN;
+       addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
+       len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+       addr[3] = secret;
+       len[3] = secret_len;
+       md5_vector(4, addr, len, msg->hdr->authenticator);
+
+       if (wpabuf_len(msg->buf) > 0xffff) {
+               wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)",
+                          (unsigned long) wpabuf_len(msg->buf));
+               return -1;
+       }
+       return 0;
+}
+
+
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+                           size_t secret_len)
+{
+       const u8 *addr[2];
+       size_t len[2];
+
+       msg->hdr->length = htons(wpabuf_len(msg->buf));
+       os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN);
+       addr[0] = wpabuf_head(msg->buf);
+       len[0] = wpabuf_len(msg->buf);
+       addr[1] = secret;
+       len[1] = secret_len;
+       md5_vector(2, addr, len, msg->hdr->authenticator);
+
+       if (wpabuf_len(msg->buf) > 0xffff) {
+               wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
+                          (unsigned long) wpabuf_len(msg->buf));
+       }
+}
+
+
+static int radius_msg_add_attr_to_array(struct radius_msg *msg,
+                                       struct radius_attr_hdr *attr)
+{
+       if (msg->attr_used >= msg->attr_size) {
+               size_t *nattr_pos;
+               int nlen = msg->attr_size * 2;
+
+               nattr_pos = os_realloc(msg->attr_pos,
+                                      nlen * sizeof(*msg->attr_pos));
+               if (nattr_pos == NULL)
+                       return -1;
+
+               msg->attr_pos = nattr_pos;
+               msg->attr_size = nlen;
+       }
+
+       msg->attr_pos[msg->attr_used++] =
+               (unsigned char *) attr - wpabuf_head_u8(msg->buf);
+
+       return 0;
+}
+
+
+struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
+                                           const u8 *data, size_t data_len)
+{
+       size_t buf_needed;
+       struct radius_attr_hdr *attr;
+
+       if (data_len > RADIUS_MAX_ATTR_LEN) {
+               printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+                      (unsigned long) data_len);
+               return NULL;
+       }
+
+       buf_needed = sizeof(*attr) + data_len;
+
+       if (wpabuf_tailroom(msg->buf) < buf_needed) {
+               /* allocate more space for message buffer */
+               if (wpabuf_resize(&msg->buf, buf_needed) < 0)
+                       return NULL;
+               msg->hdr = wpabuf_mhead(msg->buf);
+       }
+
+       attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr));
+       attr->type = type;
+       attr->length = sizeof(*attr) + data_len;
+       wpabuf_put_data(msg->buf, data, data_len);
+
+       if (radius_msg_add_attr_to_array(msg, attr))
+               return NULL;
+
+       return attr;
+}
+
+
+/**
+ * radius_msg_parse - Parse a RADIUS message
+ * @data: RADIUS message to be parsed
+ * @len: Length of data buffer in octets
+ * Returns: Parsed RADIUS message or %NULL on failure
+ *
+ * This parses a RADIUS message and makes a copy of its data. The caller is
+ * responsible for freeing the returned data with radius_msg_free().
+ */
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len)
+{
+       struct radius_msg *msg;
+       struct radius_hdr *hdr;
+       struct radius_attr_hdr *attr;
+       size_t msg_len;
+       unsigned char *pos, *end;
+
+       if (data == NULL || len < sizeof(*hdr))
+               return NULL;
+
+       hdr = (struct radius_hdr *) data;
+
+       msg_len = ntohs(hdr->length);
+       if (msg_len < sizeof(*hdr) || msg_len > len) {
+               wpa_printf(MSG_INFO, "RADIUS: Invalid message length");
+               return NULL;
+       }
+
+       if (msg_len < len) {
+               wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after "
+                          "RADIUS message", (unsigned long) len - msg_len);
+       }
+
+       msg = os_zalloc(sizeof(*msg));
+       if (msg == NULL)
+               return NULL;
+
+       msg->buf = wpabuf_alloc_copy(data, msg_len);
+       if (msg->buf == NULL || radius_msg_initialize(msg)) {
+               radius_msg_free(msg);
+               return NULL;
+       }
+       msg->hdr = wpabuf_mhead(msg->buf);
+
+       /* parse attributes */
+       pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr);
+       end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf);
+       while (pos < end) {
+               if ((size_t) (end - pos) < sizeof(*attr))
+                       goto fail;
+
+               attr = (struct radius_attr_hdr *) pos;
+
+               if (pos + attr->length > end || attr->length < sizeof(*attr))
+                       goto fail;
+
+               /* TODO: check that attr->length is suitable for attr->type */
+
+               if (radius_msg_add_attr_to_array(msg, attr))
+                       goto fail;
+
+               pos += attr->length;
+       }
+
+       return msg;
+
+ fail:
+       radius_msg_free(msg);
+       return NULL;
+}
+
+
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len)
+{
+       const u8 *pos = data;
+       size_t left = data_len;
+
+       while (left > 0) {
+               int len;
+               if (left > RADIUS_MAX_ATTR_LEN)
+                       len = RADIUS_MAX_ATTR_LEN;
+               else
+                       len = left;
+
+               if (!radius_msg_add_attr(msg, RADIUS_ATTR_EAP_MESSAGE,
+                                        pos, len))
+                       return 0;
+
+               pos += len;
+               left -= len;
+       }
+
+       return 1;
+}
+
+
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len)
+{
+       u8 *eap, *pos;
+       size_t len, i;
+       struct radius_attr_hdr *attr;
+
+       if (msg == NULL)
+               return NULL;
+
+       len = 0;
+       for (i = 0; i < msg->attr_used; i++) {
+               attr = radius_get_attr_hdr(msg, i);
+               if (attr->type == RADIUS_ATTR_EAP_MESSAGE)
+                       len += attr->length - sizeof(struct radius_attr_hdr);
+       }
+
+       if (len == 0)
+               return NULL;
+
+       eap = os_malloc(len);
+       if (eap == NULL)
+               return NULL;
+
+       pos = eap;
+       for (i = 0; i < msg->attr_used; i++) {
+               attr = radius_get_attr_hdr(msg, i);
+               if (attr->type == RADIUS_ATTR_EAP_MESSAGE) {
+                       int flen = attr->length - sizeof(*attr);
+                       os_memcpy(pos, attr + 1, flen);
+                       pos += flen;
+               }
+       }
+
+       if (eap_len)
+               *eap_len = len;
+
+       return eap;
+}
+
+
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+                              size_t secret_len, const u8 *req_auth)
+{
+       u8 auth[MD5_MAC_LEN], orig[MD5_MAC_LEN];
+       u8 orig_authenticator[16];
+       struct radius_attr_hdr *attr = NULL, *tmp;
+       size_t i;
+
+       for (i = 0; i < msg->attr_used; i++) {
+               tmp = radius_get_attr_hdr(msg, i);
+               if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
+                       if (attr != NULL) {
+                               printf("Multiple Message-Authenticator "
+                                      "attributes in RADIUS message\n");
+                               return 1;
+                       }
+                       attr = tmp;
+               }
+       }
+
+       if (attr == NULL) {
+               printf("No Message-Authenticator attribute found\n");
+               return 1;
+       }
+
+       os_memcpy(orig, attr + 1, MD5_MAC_LEN);
+       os_memset(attr + 1, 0, MD5_MAC_LEN);
+       if (req_auth) {
+               os_memcpy(orig_authenticator, msg->hdr->authenticator,
+                         sizeof(orig_authenticator));
+               os_memcpy(msg->hdr->authenticator, req_auth,
+                         sizeof(msg->hdr->authenticator));
+       }
+       hmac_md5(secret, secret_len, wpabuf_head(msg->buf),
+                wpabuf_len(msg->buf), auth);
+       os_memcpy(attr + 1, orig, MD5_MAC_LEN);
+       if (req_auth) {
+               os_memcpy(msg->hdr->authenticator, orig_authenticator,
+                         sizeof(orig_authenticator));
+       }
+
+       if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
+               printf("Invalid Message-Authenticator!\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+                     size_t secret_len, struct radius_msg *sent_msg, int auth)
+{
+       const u8 *addr[4];
+       size_t len[4];
+       u8 hash[MD5_MAC_LEN];
+
+       if (sent_msg == NULL) {
+               printf("No matching Access-Request message found\n");
+               return 1;
+       }
+
+       if (auth &&
+           radius_msg_verify_msg_auth(msg, secret, secret_len,
+                                      sent_msg->hdr->authenticator)) {
+               return 1;
+       }
+
+       /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */
+       addr[0] = (u8 *) msg->hdr;
+       len[0] = 1 + 1 + 2;
+       addr[1] = sent_msg->hdr->authenticator;
+       len[1] = MD5_MAC_LEN;
+       addr[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr);
+       len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr);
+       addr[3] = secret;
+       len[3] = secret_len;
+       md5_vector(4, addr, len, hash);
+       if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+               printf("Response Authenticator invalid!\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+                        u8 type)
+{
+       struct radius_attr_hdr *attr;
+       size_t i;
+       int count = 0;
+
+       for (i = 0; i < src->attr_used; i++) {
+               attr = radius_get_attr_hdr(src, i);
+               if (attr->type == type) {
+                       if (!radius_msg_add_attr(dst, type, (u8 *) (attr + 1),
+                                                attr->length - sizeof(*attr)))
+                               return -1;
+                       count++;
+               }
+       }
+
+       return count;
+}
+
+
+/* Create Request Authenticator. The value should be unique over the lifetime
+ * of the shared secret between authenticator and authentication server.
+ * Use one-way MD5 hash calculated from current timestamp and some data given
+ * by the caller. */
+void radius_msg_make_authenticator(struct radius_msg *msg,
+                                  const u8 *data, size_t len)
+{
+       struct os_time tv;
+       long int l;
+       const u8 *addr[3];
+       size_t elen[3];
+
+       os_get_time(&tv);
+       l = os_random();
+       addr[0] = (u8 *) &tv;
+       elen[0] = sizeof(tv);
+       addr[1] = data;
+       elen[1] = len;
+       addr[2] = (u8 *) &l;
+       elen[2] = sizeof(l);
+       md5_vector(3, addr, elen, msg->hdr->authenticator);
+}
+
+
+/* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message.
+ * Returns the Attribute payload and sets alen to indicate the length of the
+ * payload if a vendor attribute with subtype is found, otherwise returns NULL.
+ * The returned payload is allocated with os_malloc() and caller must free it
+ * by calling os_free().
+ */
+static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
+                                     u8 subtype, size_t *alen)
+{
+       u8 *data, *pos;
+       size_t i, len;
+
+       if (msg == NULL)
+               return NULL;
+
+       for (i = 0; i < msg->attr_used; i++) {
+               struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+               size_t left;
+               u32 vendor_id;
+               struct radius_attr_vendor *vhdr;
+
+               if (attr->type != RADIUS_ATTR_VENDOR_SPECIFIC)
+                       continue;
+
+               left = attr->length - sizeof(*attr);
+               if (left < 4)
+                       continue;
+
+               pos = (u8 *) (attr + 1);
+
+               os_memcpy(&vendor_id, pos, 4);
+               pos += 4;
+               left -= 4;
+
+               if (ntohl(vendor_id) != vendor)
+                       continue;
+
+               while (left >= sizeof(*vhdr)) {
+                       vhdr = (struct radius_attr_vendor *) pos;
+                       if (vhdr->vendor_length > left ||
+                           vhdr->vendor_length < sizeof(*vhdr)) {
+                               left = 0;
+                               break;
+                       }
+                       if (vhdr->vendor_type != subtype) {
+                               pos += vhdr->vendor_length;
+                               left -= vhdr->vendor_length;
+                               continue;
+                       }
+
+                       len = vhdr->vendor_length - sizeof(*vhdr);
+                       data = os_malloc(len);
+                       if (data == NULL)
+                               return NULL;
+                       os_memcpy(data, pos + sizeof(*vhdr), len);
+                       if (alen)
+                               *alen = len;
+                       return data;
+               }
+       }
+
+       return NULL;
+}
+
+
+static u8 * decrypt_ms_key(const u8 *key, size_t len,
+                          const u8 *req_authenticator,
+                          const u8 *secret, size_t secret_len, size_t *reslen)
+{
+       u8 *plain, *ppos, *res;
+       const u8 *pos;
+       size_t left, plen;
+       u8 hash[MD5_MAC_LEN];
+       int i, first = 1;
+       const u8 *addr[3];
+       size_t elen[3];
+
+       /* key: 16-bit salt followed by encrypted key info */
+
+       if (len < 2 + 16)
+               return NULL;
+
+       pos = key + 2;
+       left = len - 2;
+       if (left % 16) {
+               printf("Invalid ms key len %lu\n", (unsigned long) left);
+               return NULL;
+       }
+
+       plen = left;
+       ppos = plain = os_malloc(plen);
+       if (plain == NULL)
+               return NULL;
+       plain[0] = 0;
+
+       while (left > 0) {
+               /* b(1) = MD5(Secret + Request-Authenticator + Salt)
+                * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+
+               addr[0] = secret;
+               elen[0] = secret_len;
+               if (first) {
+                       addr[1] = req_authenticator;
+                       elen[1] = MD5_MAC_LEN;
+                       addr[2] = key;
+                       elen[2] = 2; /* Salt */
+               } else {
+                       addr[1] = pos - MD5_MAC_LEN;
+                       elen[1] = MD5_MAC_LEN;
+               }
+               md5_vector(first ? 3 : 2, addr, elen, hash);
+               first = 0;
+
+               for (i = 0; i < MD5_MAC_LEN; i++)
+                       *ppos++ = *pos++ ^ hash[i];
+               left -= MD5_MAC_LEN;
+       }
+
+       if (plain[0] == 0 || plain[0] > plen - 1) {
+               printf("Failed to decrypt MPPE key\n");
+               os_free(plain);
+               return NULL;
+       }
+
+       res = os_malloc(plain[0]);
+       if (res == NULL) {
+               os_free(plain);
+               return NULL;
+       }
+       os_memcpy(res, plain + 1, plain[0]);
+       if (reslen)
+               *reslen = plain[0];
+       os_free(plain);
+       return res;
+}
+
+
+static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt,
+                          const u8 *req_authenticator,
+                          const u8 *secret, size_t secret_len,
+                          u8 *ebuf, size_t *elen)
+{
+       int i, len, first = 1;
+       u8 hash[MD5_MAC_LEN], saltbuf[2], *pos;
+       const u8 *addr[3];
+       size_t _len[3];
+
+       WPA_PUT_BE16(saltbuf, salt);
+
+       len = 1 + key_len;
+       if (len & 0x0f) {
+               len = (len & 0xf0) + 16;
+       }
+       os_memset(ebuf, 0, len);
+       ebuf[0] = key_len;
+       os_memcpy(ebuf + 1, key, key_len);
+
+       *elen = len;
+
+       pos = ebuf;
+       while (len > 0) {
+               /* b(1) = MD5(Secret + Request-Authenticator + Salt)
+                * b(i) = MD5(Secret + c(i - 1)) for i > 1 */
+               addr[0] = secret;
+               _len[0] = secret_len;
+               if (first) {
+                       addr[1] = req_authenticator;
+                       _len[1] = MD5_MAC_LEN;
+                       addr[2] = saltbuf;
+                       _len[2] = sizeof(saltbuf);
+               } else {
+                       addr[1] = pos - MD5_MAC_LEN;
+                       _len[1] = MD5_MAC_LEN;
+               }
+               md5_vector(first ? 3 : 2, addr, _len, hash);
+               first = 0;
+
+               for (i = 0; i < MD5_MAC_LEN; i++)
+                       *pos++ ^= hash[i];
+
+               len -= MD5_MAC_LEN;
+       }
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+                      const u8 *secret, size_t secret_len)
+{
+       u8 *key;
+       size_t keylen;
+       struct radius_ms_mppe_keys *keys;
+
+       if (msg == NULL || sent_msg == NULL)
+               return NULL;
+
+       keys = os_zalloc(sizeof(*keys));
+       if (keys == NULL)
+               return NULL;
+
+       key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+                                        RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY,
+                                        &keylen);
+       if (key) {
+               keys->send = decrypt_ms_key(key, keylen,
+                                           sent_msg->hdr->authenticator,
+                                           secret, secret_len,
+                                           &keys->send_len);
+               os_free(key);
+       }
+
+       key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT,
+                                        RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY,
+                                        &keylen);
+       if (key) {
+               keys->recv = decrypt_ms_key(key, keylen,
+                                           sent_msg->hdr->authenticator,
+                                           secret, secret_len,
+                                           &keys->recv_len);
+               os_free(key);
+       }
+
+       return keys;
+}
+
+
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+                         const u8 *secret, size_t secret_len)
+{
+       u8 *key;
+       size_t keylen;
+       struct radius_ms_mppe_keys *keys;
+
+       if (msg == NULL || sent_msg == NULL)
+               return NULL;
+
+       keys = os_zalloc(sizeof(*keys));
+       if (keys == NULL)
+               return NULL;
+
+       key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO,
+                                        RADIUS_CISCO_AV_PAIR, &keylen);
+       if (key && keylen == 51 &&
+           os_memcmp(key, "leap:session-key=", 17) == 0) {
+               keys->recv = decrypt_ms_key(key + 17, keylen - 17,
+                                           sent_msg->hdr->authenticator,
+                                           secret, secret_len,
+                                           &keys->recv_len);
+       }
+       os_free(key);
+
+       return keys;
+}
+
+
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+                            const u8 *req_authenticator,
+                            const u8 *secret, size_t secret_len,
+                            const u8 *send_key, size_t send_key_len,
+                            const u8 *recv_key, size_t recv_key_len)
+{
+       struct radius_attr_hdr *attr;
+       u32 vendor_id = htonl(RADIUS_VENDOR_ID_MICROSOFT);
+       u8 *buf;
+       struct radius_attr_vendor *vhdr;
+       u8 *pos;
+       size_t elen;
+       int hlen;
+       u16 salt;
+
+       hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2;
+
+       /* MS-MPPE-Send-Key */
+       buf = os_malloc(hlen + send_key_len + 16);
+       if (buf == NULL) {
+               return 0;
+       }
+       pos = buf;
+       os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+       pos += sizeof(vendor_id);
+       vhdr = (struct radius_attr_vendor *) pos;
+       vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY;
+       pos = (u8 *) (vhdr + 1);
+       salt = os_random() | 0x8000;
+       WPA_PUT_BE16(pos, salt);
+       pos += 2;
+       encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret,
+                      secret_len, pos, &elen);
+       vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+       attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+                                  buf, hlen + elen);
+       os_free(buf);
+       if (attr == NULL) {
+               return 0;
+       }
+
+       /* MS-MPPE-Recv-Key */
+       buf = os_malloc(hlen + send_key_len + 16);
+       if (buf == NULL) {
+               return 0;
+       }
+       pos = buf;
+       os_memcpy(pos, &vendor_id, sizeof(vendor_id));
+       pos += sizeof(vendor_id);
+       vhdr = (struct radius_attr_vendor *) pos;
+       vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY;
+       pos = (u8 *) (vhdr + 1);
+       salt ^= 1;
+       WPA_PUT_BE16(pos, salt);
+       pos += 2;
+       encrypt_ms_key(recv_key, recv_key_len, salt, req_authenticator, secret,
+                      secret_len, pos, &elen);
+       vhdr->vendor_length = hlen + elen - sizeof(vendor_id);
+
+       attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+                                  buf, hlen + elen);
+       os_free(buf);
+       if (attr == NULL) {
+               return 0;
+       }
+
+       return 1;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+                                 const u8 *data, size_t data_len,
+                                 const u8 *secret, size_t secret_len)
+{
+       u8 buf[128];
+       int padlen, i;
+       size_t buf_len, pos;
+       const u8 *addr[2];
+       size_t len[2];
+       u8 hash[16];
+
+       if (data_len > 128)
+               return NULL;
+
+       os_memcpy(buf, data, data_len);
+       buf_len = data_len;
+
+       padlen = data_len % 16;
+       if (padlen) {
+               padlen = 16 - padlen;
+               os_memset(buf + data_len, 0, padlen);
+               buf_len += padlen;
+       }
+
+       addr[0] = secret;
+       len[0] = secret_len;
+       addr[1] = msg->hdr->authenticator;
+       len[1] = 16;
+       md5_vector(2, addr, len, hash);
+
+       for (i = 0; i < 16; i++)
+               buf[i] ^= hash[i];
+       pos = 16;
+
+       while (pos < buf_len) {
+               addr[0] = secret;
+               len[0] = secret_len;
+               addr[1] = &buf[pos - 16];
+               len[1] = 16;
+               md5_vector(2, addr, len, hash);
+
+               for (i = 0; i < 16; i++)
+                       buf[pos + i] ^= hash[i];
+
+               pos += 16;
+       }
+
+       return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
+                                  buf, buf_len);
+}
+
+
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len)
+{
+       struct radius_attr_hdr *attr = NULL, *tmp;
+       size_t i, dlen;
+
+       for (i = 0; i < msg->attr_used; i++) {
+               tmp = radius_get_attr_hdr(msg, i);
+               if (tmp->type == type) {
+                       attr = tmp;
+                       break;
+               }
+       }
+
+       if (!attr)
+               return -1;
+
+       dlen = attr->length - sizeof(*attr);
+       if (buf)
+               os_memcpy(buf, (attr + 1), dlen > len ? len : dlen);
+       return dlen;
+}
+
+
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+                           size_t *len, const u8 *start)
+{
+       size_t i;
+       struct radius_attr_hdr *attr = NULL, *tmp;
+
+       for (i = 0; i < msg->attr_used; i++) {
+               tmp = radius_get_attr_hdr(msg, i);
+               if (tmp->type == type &&
+                   (start == NULL || (u8 *) tmp > start)) {
+                       attr = tmp;
+                       break;
+               }
+       }
+
+       if (!attr)
+               return -1;
+
+       *buf = (u8 *) (attr + 1);
+       *len = attr->length - sizeof(*attr);
+       return 0;
+}
+
+
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len)
+{
+       size_t i;
+       int count;
+
+       for (count = 0, i = 0; i < msg->attr_used; i++) {
+               struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
+               if (attr->type == type &&
+                   attr->length >= sizeof(struct radius_attr_hdr) + min_len)
+                       count++;
+       }
+
+       return count;
+}
+
+
+struct radius_tunnel_attrs {
+       int tag_used;
+       int type; /* Tunnel-Type */
+       int medium_type; /* Tunnel-Medium-Type */
+       int vlanid;
+};
+
+
+/**
+ * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information
+ * @msg: RADIUS message
+ * Returns: VLAN ID for the first tunnel configuration of -1 if none is found
+ */
+int radius_msg_get_vlanid(struct radius_msg *msg)
+{
+       struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun;
+       size_t i;
+       struct radius_attr_hdr *attr = NULL;
+       const u8 *data;
+       char buf[10];
+       size_t dlen;
+
+       os_memset(&tunnel, 0, sizeof(tunnel));
+
+       for (i = 0; i < msg->attr_used; i++) {
+               attr = radius_get_attr_hdr(msg, i);
+               data = (const u8 *) (attr + 1);
+               dlen = attr->length - sizeof(*attr);
+               if (attr->length < 3)
+                       continue;
+               if (data[0] >= RADIUS_TUNNEL_TAGS)
+                       tun = &tunnel[0];
+               else
+                       tun = &tunnel[data[0]];
+
+               switch (attr->type) {
+               case RADIUS_ATTR_TUNNEL_TYPE:
+                       if (attr->length != 6)
+                               break;
+                       tun->tag_used++;
+                       tun->type = WPA_GET_BE24(data + 1);
+                       break;
+               case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
+                       if (attr->length != 6)
+                               break;
+                       tun->tag_used++;
+                       tun->medium_type = WPA_GET_BE24(data + 1);
+                       break;
+               case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
+                       if (data[0] < RADIUS_TUNNEL_TAGS) {
+                               data++;
+                               dlen--;
+                       }
+                       if (dlen >= sizeof(buf))
+                               break;
+                       os_memcpy(buf, data, dlen);
+                       buf[dlen] = '\0';
+                       tun->tag_used++;
+                       tun->vlanid = atoi(buf);
+                       break;
+               }
+       }
+
+       for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) {
+               tun = &tunnel[i];
+               if (tun->tag_used &&
+                   tun->type == RADIUS_TUNNEL_TYPE_VLAN &&
+                   tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 &&
+                   tun->vlanid > 0)
+                       return tun->vlanid;
+       }
+
+       return -1;
+}
+
+
+void radius_free_class(struct radius_class_data *c)
+{
+       size_t i;
+       if (c == NULL)
+               return;
+       for (i = 0; i < c->count; i++)
+               os_free(c->attr[i].data);
+       os_free(c->attr);
+       c->attr = NULL;
+       c->count = 0;
+}
+
+
+int radius_copy_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;
+}
diff --git a/src/radius/radius.h b/src/radius/radius.h
new file mode 100644 (file)
index 0000000..a3cdac0
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * RADIUS message processing
+ * 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.
+ */
+
+#ifndef RADIUS_H
+#define RADIUS_H
+
+/* RFC 2865 - RADIUS */
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+struct radius_hdr {
+       u8 code;
+       u8 identifier;
+       u16 length; /* including this header */
+       u8 authenticator[16];
+       /* followed by length-20 octets of attributes */
+} STRUCT_PACKED;
+
+enum { RADIUS_CODE_ACCESS_REQUEST = 1,
+       RADIUS_CODE_ACCESS_ACCEPT = 2,
+       RADIUS_CODE_ACCESS_REJECT = 3,
+       RADIUS_CODE_ACCOUNTING_REQUEST = 4,
+       RADIUS_CODE_ACCOUNTING_RESPONSE = 5,
+       RADIUS_CODE_ACCESS_CHALLENGE = 11,
+       RADIUS_CODE_STATUS_SERVER = 12,
+       RADIUS_CODE_STATUS_CLIENT = 13,
+       RADIUS_CODE_RESERVED = 255
+};
+
+struct radius_attr_hdr {
+       u8 type;
+       u8 length; /* including this header */
+       /* followed by length-2 octets of attribute value */
+} STRUCT_PACKED;
+
+#define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr))
+
+enum { RADIUS_ATTR_USER_NAME = 1,
+       RADIUS_ATTR_USER_PASSWORD = 2,
+       RADIUS_ATTR_NAS_IP_ADDRESS = 4,
+       RADIUS_ATTR_NAS_PORT = 5,
+       RADIUS_ATTR_FRAMED_MTU = 12,
+       RADIUS_ATTR_REPLY_MESSAGE = 18,
+       RADIUS_ATTR_STATE = 24,
+       RADIUS_ATTR_CLASS = 25,
+       RADIUS_ATTR_VENDOR_SPECIFIC = 26,
+       RADIUS_ATTR_SESSION_TIMEOUT = 27,
+       RADIUS_ATTR_IDLE_TIMEOUT = 28,
+       RADIUS_ATTR_TERMINATION_ACTION = 29,
+       RADIUS_ATTR_CALLED_STATION_ID = 30,
+       RADIUS_ATTR_CALLING_STATION_ID = 31,
+       RADIUS_ATTR_NAS_IDENTIFIER = 32,
+       RADIUS_ATTR_PROXY_STATE = 33,
+       RADIUS_ATTR_ACCT_STATUS_TYPE = 40,
+       RADIUS_ATTR_ACCT_DELAY_TIME = 41,
+       RADIUS_ATTR_ACCT_INPUT_OCTETS = 42,
+       RADIUS_ATTR_ACCT_OUTPUT_OCTETS = 43,
+       RADIUS_ATTR_ACCT_SESSION_ID = 44,
+       RADIUS_ATTR_ACCT_AUTHENTIC = 45,
+       RADIUS_ATTR_ACCT_SESSION_TIME = 46,
+       RADIUS_ATTR_ACCT_INPUT_PACKETS = 47,
+       RADIUS_ATTR_ACCT_OUTPUT_PACKETS = 48,
+       RADIUS_ATTR_ACCT_TERMINATE_CAUSE = 49,
+       RADIUS_ATTR_ACCT_MULTI_SESSION_ID = 50,
+       RADIUS_ATTR_ACCT_LINK_COUNT = 51,
+       RADIUS_ATTR_ACCT_INPUT_GIGAWORDS = 52,
+       RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53,
+       RADIUS_ATTR_EVENT_TIMESTAMP = 55,
+       RADIUS_ATTR_NAS_PORT_TYPE = 61,
+       RADIUS_ATTR_TUNNEL_TYPE = 64,
+       RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65,
+       RADIUS_ATTR_CONNECT_INFO = 77,
+       RADIUS_ATTR_EAP_MESSAGE = 79,
+       RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80,
+       RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81,
+       RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
+       RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
+       RADIUS_ATTR_NAS_IPV6_ADDRESS = 95
+};
+
+
+/* Termination-Action */
+#define RADIUS_TERMINATION_ACTION_DEFAULT 0
+#define RADIUS_TERMINATION_ACTION_RADIUS_REQUEST 1
+
+/* NAS-Port-Type */
+#define RADIUS_NAS_PORT_TYPE_IEEE_802_11 19
+
+/* Acct-Status-Type */
+#define RADIUS_ACCT_STATUS_TYPE_START 1
+#define RADIUS_ACCT_STATUS_TYPE_STOP 2
+#define RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE 3
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON 7
+#define RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF 8
+
+/* Acct-Authentic */
+#define RADIUS_ACCT_AUTHENTIC_RADIUS 1
+#define RADIUS_ACCT_AUTHENTIC_LOCAL 2
+#define RADIUS_ACCT_AUTHENTIC_REMOTE 3
+
+/* Acct-Terminate-Cause */
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST 1
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_CARRIER 2
+#define RADIUS_ACCT_TERMINATE_CAUSE_LOST_SERVICE 3
+#define RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT 4
+#define RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT 5
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_RESET 6
+#define RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT 7
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_ERROR 8
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_ERROR 9
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REQUEST 10
+#define RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT 11
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_UNNEEDED 12
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_PREEMPTED 13
+#define RADIUS_ACCT_TERMINATE_CAUSE_PORT_SUSPENDED 14
+#define RADIUS_ACCT_TERMINATE_CAUSE_SERVICE_UNAVAILABLE 15
+#define RADIUS_ACCT_TERMINATE_CAUSE_CALLBACK 16
+#define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17
+#define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18
+
+#define RADIUS_TUNNEL_TAGS 32
+
+/* Tunnel-Type */
+#define RADIUS_TUNNEL_TYPE_PPTP 1
+#define RADIUS_TUNNEL_TYPE_L2TP 3
+#define RADIUS_TUNNEL_TYPE_IPIP 7
+#define RADIUS_TUNNEL_TYPE_GRE 10
+#define RADIUS_TUNNEL_TYPE_VLAN 13
+
+/* Tunnel-Medium-Type */
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1
+#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2
+#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6
+
+
+struct radius_attr_vendor {
+       u8 vendor_type;
+       u8 vendor_length;
+} STRUCT_PACKED;
+
+#define RADIUS_VENDOR_ID_CISCO 9
+#define RADIUS_CISCO_AV_PAIR 1
+
+/* RFC 2548 - Microsoft Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_MICROSOFT 311
+
+enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
+       RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
+};
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+struct radius_ms_mppe_keys {
+       u8 *send;
+       size_t send_len;
+       u8 *recv;
+       size_t recv_len;
+};
+
+
+struct radius_msg;
+
+/* Default size to be allocated for new RADIUS messages */
+#define RADIUS_DEFAULT_MSG_SIZE 1024
+
+/* Default size to be allocated for attribute array */
+#define RADIUS_DEFAULT_ATTR_COUNT 16
+
+
+/* MAC address ASCII format for IEEE 802.1X use
+ * (draft-congdon-radius-8021x-20.txt) */
+#define RADIUS_802_1X_ADDR_FORMAT "%02X-%02X-%02X-%02X-%02X-%02X"
+/* MAC address ASCII format for non-802.1X use */
+#define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg);
+struct wpabuf * radius_msg_get_buf(struct radius_msg *msg);
+struct radius_msg * radius_msg_new(u8 code, u8 identifier);
+void radius_msg_free(struct radius_msg *msg);
+void radius_msg_dump(struct radius_msg *msg);
+int radius_msg_finish(struct radius_msg *msg, const u8 *secret,
+                     size_t secret_len);
+int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
+                         size_t secret_len, const u8 *req_authenticator);
+void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
+                           size_t secret_len);
+struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type,
+                                            const u8 *data, size_t data_len);
+struct radius_msg * radius_msg_parse(const u8 *data, size_t len);
+int radius_msg_add_eap(struct radius_msg *msg, const u8 *data,
+                      size_t data_len);
+u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len);
+int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
+                     size_t secret_len, struct radius_msg *sent_msg,
+                     int auth);
+int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
+                              size_t secret_len, const u8 *req_auth);
+int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src,
+                        u8 type);
+void radius_msg_make_authenticator(struct radius_msg *msg,
+                                  const u8 *data, size_t len);
+struct radius_ms_mppe_keys *
+radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+                      const u8 *secret, size_t secret_len);
+struct radius_ms_mppe_keys *
+radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
+                         const u8 *secret, size_t secret_len);
+int radius_msg_add_mppe_keys(struct radius_msg *msg,
+                            const u8 *req_authenticator,
+                            const u8 *secret, size_t secret_len,
+                            const u8 *send_key, size_t send_key_len,
+                            const u8 *recv_key, size_t recv_key_len);
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+                                 const u8 *data, size_t data_len,
+                                 const u8 *secret, size_t secret_len);
+int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len);
+int radius_msg_get_vlanid(struct radius_msg *msg);
+
+static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type,
+                                           u32 value)
+{
+       u32 val = htonl(value);
+       return radius_msg_add_attr(msg, type, (u8 *) &val, 4) != NULL;
+}
+
+static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type,
+                                           u32 *value)
+{
+       u32 val;
+       int res;
+       res = radius_msg_get_attr(msg, type, (u8 *) &val, 4);
+       if (res != 4)
+               return -1;
+
+       *value = ntohl(val);
+       return 0;
+}
+int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf,
+                           size_t *len, const u8 *start);
+int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len);
+
+
+struct radius_attr_data {
+       u8 *data;
+       size_t len;
+};
+
+struct radius_class_data {
+       struct radius_attr_data *attr;
+       size_t count;
+};
+
+void radius_free_class(struct radius_class_data *c);
+int radius_copy_class(struct radius_class_data *dst,
+                     const struct radius_class_data *src);
+
+#endif /* RADIUS_H */
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
new file mode 100644 (file)
index 0000000..171af29
--- /dev/null
@@ -0,0 +1,1491 @@
+/*
+ * RADIUS client
+ * 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"
+
+#include "common.h"
+#include "radius.h"
+#include "radius_client.h"
+#include "eloop.h"
+
+/* Defaults for RADIUS retransmit values (exponential backoff) */
+
+/**
+ * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds
+ */
+#define RADIUS_CLIENT_FIRST_WAIT 3
+
+/**
+ * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds
+ */
+#define RADIUS_CLIENT_MAX_WAIT 120
+
+/**
+ * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
+ *
+ * Maximum number of retransmit attempts before the entry is removed from
+ * retransmit list.
+ */
+#define RADIUS_CLIENT_MAX_RETRIES 10
+
+/**
+ * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
+ *
+ * Maximum number of entries in retransmit list (oldest entries will be
+ * removed, if this limit is exceeded).
+ */
+#define RADIUS_CLIENT_MAX_ENTRIES 30
+
+/**
+ * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point
+ *
+ * The number of failed retry attempts after which the RADIUS server will be
+ * changed (if one of more backup servers are configured).
+ */
+#define RADIUS_CLIENT_NUM_FAILOVER 4
+
+
+/**
+ * struct radius_rx_handler - RADIUS client RX handler
+ *
+ * This data structure is used internally inside the RADIUS client module to
+ * store registered RX handlers. These handlers are registered by calls to
+ * radius_client_register() and unregistered when the RADIUS client is
+ * deinitialized with a call to radius_client_deinit().
+ */
+struct radius_rx_handler {
+       /**
+        * handler - Received RADIUS message handler
+        */
+       RadiusRxResult (*handler)(struct radius_msg *msg,
+                                 struct radius_msg *req,
+                                 const u8 *shared_secret,
+                                 size_t shared_secret_len,
+                                 void *data);
+
+       /**
+        * data - Context data for the handler
+        */
+       void *data;
+};
+
+
+/**
+ * struct radius_msg_list - RADIUS client message retransmit list
+ *
+ * This data structure is used internally inside the RADIUS client module to
+ * store pending RADIUS requests that may still need to be retransmitted.
+ */
+struct radius_msg_list {
+       /**
+        * addr - STA/client address
+        *
+        * This is used to find RADIUS messages for the same STA.
+        */
+       u8 addr[ETH_ALEN];
+
+       /**
+        * msg - RADIUS message
+        */
+       struct radius_msg *msg;
+
+       /**
+        * msg_type - Message type
+        */
+       RadiusType msg_type;
+
+       /**
+        * first_try - Time of the first transmission attempt
+        */
+       os_time_t first_try;
+
+       /**
+        * next_try - Time for the next transmission attempt
+        */
+       os_time_t next_try;
+
+       /**
+        * attempts - Number of transmission attempts
+        */
+       int attempts;
+
+       /**
+        * next_wait - Next retransmission wait time in seconds
+        */
+       int next_wait;
+
+       /**
+        * last_attempt - Time of the last transmission attempt
+        */
+       struct os_time last_attempt;
+
+       /**
+        * shared_secret - Shared secret with the target RADIUS server
+        */
+       const u8 *shared_secret;
+
+       /**
+        * shared_secret_len - shared_secret length in octets
+        */
+       size_t shared_secret_len;
+
+       /* TODO: server config with failover to backup server(s) */
+
+       /**
+        * next - Next message in the list
+        */
+       struct radius_msg_list *next;
+};
+
+
+/**
+ * struct radius_client_data - Internal RADIUS client data
+ *
+ * This data structure is used internally inside the RADIUS client module.
+ * External users allocate this by calling radius_client_init() and free it by
+ * calling radius_client_deinit(). The pointer to this opaque data is used in
+ * calls to other functions as an identifier for the RADIUS client instance.
+ */
+struct radius_client_data {
+       /**
+        * ctx - Context pointer for hostapd_logger() callbacks
+        */
+       void *ctx;
+
+       /**
+        * conf - RADIUS client configuration (list of RADIUS servers to use)
+        */
+       struct hostapd_radius_servers *conf;
+
+       /**
+        * auth_serv_sock - IPv4 socket for RADIUS authentication messages
+        */
+       int auth_serv_sock;
+
+       /**
+        * acct_serv_sock - IPv4 socket for RADIUS accounting messages
+        */
+       int acct_serv_sock;
+
+       /**
+        * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages
+        */
+       int auth_serv_sock6;
+
+       /**
+        * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages
+        */
+       int acct_serv_sock6;
+
+       /**
+        * auth_sock - Currently used socket for RADIUS authentication server
+        */
+       int auth_sock;
+
+       /**
+        * acct_sock - Currently used socket for RADIUS accounting server
+        */
+       int acct_sock;
+
+       /**
+        * auth_handlers - Authentication message handlers
+        */
+       struct radius_rx_handler *auth_handlers;
+
+       /**
+        * num_auth_handlers - Number of handlers in auth_handlers
+        */
+       size_t num_auth_handlers;
+
+       /**
+        * acct_handlers - Accounting message handlers
+        */
+       struct radius_rx_handler *acct_handlers;
+
+       /**
+        * num_acct_handlers - Number of handlers in acct_handlers
+        */
+       size_t num_acct_handlers;
+
+       /**
+        * msgs - Pending outgoing RADIUS messages
+        */
+       struct radius_msg_list *msgs;
+
+       /**
+        * num_msgs - Number of pending messages in the msgs list
+        */
+       size_t num_msgs;
+
+       /**
+        * next_radius_identifier - Next RADIUS message identifier to use
+        */
+       u8 next_radius_identifier;
+};
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+                    struct hostapd_radius_server *nserv,
+                    struct hostapd_radius_server *oserv,
+                    int sock, int sock6, int auth);
+static int radius_client_init_acct(struct radius_client_data *radius);
+static int radius_client_init_auth(struct radius_client_data *radius);
+
+
+static void radius_client_msg_free(struct radius_msg_list *req)
+{
+       radius_msg_free(req->msg);
+       os_free(req);
+}
+
+
+/**
+ * radius_client_register - Register a RADIUS client RX handler
+ * @radius: RADIUS client context from radius_client_init()
+ * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT)
+ * @handler: Handler for received RADIUS messages
+ * @data: Context pointer for handler callbacks
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to register a handler for processing received RADIUS
+ * authentication and accounting messages. The handler() callback function will
+ * be called whenever a RADIUS message is received from the active server.
+ *
+ * There can be multiple registered RADIUS message handlers. The handlers will
+ * be called in order until one of them indicates that it has processed or
+ * queued the message.
+ */
+int radius_client_register(struct radius_client_data *radius,
+                          RadiusType msg_type,
+                          RadiusRxResult (*handler)(struct radius_msg *msg,
+                                                    struct radius_msg *req,
+                                                    const u8 *shared_secret,
+                                                    size_t shared_secret_len,
+                                                    void *data),
+                          void *data)
+{
+       struct radius_rx_handler **handlers, *newh;
+       size_t *num;
+
+       if (msg_type == RADIUS_ACCT) {
+               handlers = &radius->acct_handlers;
+               num = &radius->num_acct_handlers;
+       } else {
+               handlers = &radius->auth_handlers;
+               num = &radius->num_auth_handlers;
+       }
+
+       newh = os_realloc(*handlers,
+                         (*num + 1) * sizeof(struct radius_rx_handler));
+       if (newh == NULL)
+               return -1;
+
+       newh[*num].handler = handler;
+       newh[*num].data = data;
+       (*num)++;
+       *handlers = newh;
+
+       return 0;
+}
+
+
+static void radius_client_handle_send_error(struct radius_client_data *radius,
+                                           int s, RadiusType msg_type)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+       int _errno = errno;
+       perror("send[RADIUS]");
+       if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
+           _errno == EBADF) {
+               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                              HOSTAPD_LEVEL_INFO,
+                              "Send failed - maybe interface status changed -"
+                              " try to connect again");
+               eloop_unregister_read_sock(s);
+               close(s);
+               if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
+                       radius_client_init_acct(radius);
+               else
+                       radius_client_init_auth(radius);
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+static int radius_client_retransmit(struct radius_client_data *radius,
+                                   struct radius_msg_list *entry,
+                                   os_time_t now)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       int s;
+       struct wpabuf *buf;
+
+       if (entry->msg_type == RADIUS_ACCT ||
+           entry->msg_type == RADIUS_ACCT_INTERIM) {
+               s = radius->acct_sock;
+               if (entry->attempts == 0)
+                       conf->acct_server->requests++;
+               else {
+                       conf->acct_server->timeouts++;
+                       conf->acct_server->retransmissions++;
+               }
+       } else {
+               s = radius->auth_sock;
+               if (entry->attempts == 0)
+                       conf->auth_server->requests++;
+               else {
+                       conf->auth_server->timeouts++;
+                       conf->auth_server->retransmissions++;
+               }
+       }
+
+       /* retransmit; remove entry if too many attempts */
+       entry->attempts++;
+       hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
+                      radius_msg_get_hdr(entry->msg)->identifier);
+
+       os_get_time(&entry->last_attempt);
+       buf = radius_msg_get_buf(entry->msg);
+       if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
+               radius_client_handle_send_error(radius, s, entry->msg_type);
+
+       entry->next_try = now + entry->next_wait;
+       entry->next_wait *= 2;
+       if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
+               entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
+       if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
+               printf("Removing un-ACKed RADIUS message due to too many "
+                      "failed retransmit attempts\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
+{
+       struct radius_client_data *radius = eloop_ctx;
+       struct hostapd_radius_servers *conf = radius->conf;
+       struct os_time now;
+       os_time_t first;
+       struct radius_msg_list *entry, *prev, *tmp;
+       int auth_failover = 0, acct_failover = 0;
+       char abuf[50];
+
+       entry = radius->msgs;
+       if (!entry)
+               return;
+
+       os_get_time(&now);
+       first = 0;
+
+       prev = NULL;
+       while (entry) {
+               if (now.sec >= entry->next_try &&
+                   radius_client_retransmit(radius, entry, now.sec)) {
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               radius->msgs = entry->next;
+
+                       tmp = entry;
+                       entry = entry->next;
+                       radius_client_msg_free(tmp);
+                       radius->num_msgs--;
+                       continue;
+               }
+
+               if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+                       if (entry->msg_type == RADIUS_ACCT ||
+                           entry->msg_type == RADIUS_ACCT_INTERIM)
+                               acct_failover++;
+                       else
+                               auth_failover++;
+               }
+
+               if (first == 0 || entry->next_try < first)
+                       first = entry->next_try;
+
+               prev = entry;
+               entry = entry->next;
+       }
+
+       if (radius->msgs) {
+               if (first < now.sec)
+                       first = now.sec;
+               eloop_register_timeout(first - now.sec, 0,
+                                      radius_client_timer, radius, NULL);
+               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                              HOSTAPD_LEVEL_DEBUG, "Next RADIUS client "
+                              "retransmit in %ld seconds",
+                              (long int) (first - now.sec));
+       }
+
+       if (auth_failover && conf->num_auth_servers > 1) {
+               struct hostapd_radius_server *next, *old;
+               old = conf->auth_server;
+               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                              HOSTAPD_LEVEL_NOTICE,
+                              "No response from Authentication server "
+                              "%s:%d - failover",
+                              hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+                              old->port);
+
+               for (entry = radius->msgs; entry; entry = entry->next) {
+                       if (entry->msg_type == RADIUS_AUTH)
+                               old->timeouts++;
+               }
+
+               next = old + 1;
+               if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
+                       next = conf->auth_servers;
+               conf->auth_server = next;
+               radius_change_server(radius, next, old,
+                                    radius->auth_serv_sock,
+                                    radius->auth_serv_sock6, 1);
+       }
+
+       if (acct_failover && conf->num_acct_servers > 1) {
+               struct hostapd_radius_server *next, *old;
+               old = conf->acct_server;
+               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                              HOSTAPD_LEVEL_NOTICE,
+                              "No response from Accounting server "
+                              "%s:%d - failover",
+                              hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+                              old->port);
+
+               for (entry = radius->msgs; entry; entry = entry->next) {
+                       if (entry->msg_type == RADIUS_ACCT ||
+                           entry->msg_type == RADIUS_ACCT_INTERIM)
+                               old->timeouts++;
+               }
+
+               next = old + 1;
+               if (next > &conf->acct_servers[conf->num_acct_servers - 1])
+                       next = conf->acct_servers;
+               conf->acct_server = next;
+               radius_change_server(radius, next, old,
+                                    radius->acct_serv_sock,
+                                    radius->acct_serv_sock6, 0);
+       }
+}
+
+
+static void radius_client_update_timeout(struct radius_client_data *radius)
+{
+       struct os_time now;
+       os_time_t first;
+       struct radius_msg_list *entry;
+
+       eloop_cancel_timeout(radius_client_timer, radius, NULL);
+
+       if (radius->msgs == NULL) {
+               return;
+       }
+
+       first = 0;
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if (first == 0 || entry->next_try < first)
+                       first = entry->next_try;
+       }
+
+       os_get_time(&now);
+       if (first < now.sec)
+               first = now.sec;
+       eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
+                              NULL);
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in"
+                      " %ld seconds\n", (long int) (first - now.sec));
+}
+
+
+static void radius_client_list_add(struct radius_client_data *radius,
+                                  struct radius_msg *msg,
+                                  RadiusType msg_type,
+                                  const u8 *shared_secret,
+                                  size_t shared_secret_len, const u8 *addr)
+{
+       struct radius_msg_list *entry, *prev;
+
+       if (eloop_terminated()) {
+               /* No point in adding entries to retransmit queue since event
+                * loop has already been terminated. */
+               radius_msg_free(msg);
+               return;
+       }
+
+       entry = os_zalloc(sizeof(*entry));
+       if (entry == NULL) {
+               printf("Failed to add RADIUS packet into retransmit list\n");
+               radius_msg_free(msg);
+               return;
+       }
+
+       if (addr)
+               os_memcpy(entry->addr, addr, ETH_ALEN);
+       entry->msg = msg;
+       entry->msg_type = msg_type;
+       entry->shared_secret = shared_secret;
+       entry->shared_secret_len = shared_secret_len;
+       os_get_time(&entry->last_attempt);
+       entry->first_try = entry->last_attempt.sec;
+       entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
+       entry->attempts = 1;
+       entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+       entry->next = radius->msgs;
+       radius->msgs = entry;
+       radius_client_update_timeout(radius);
+
+       if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
+               printf("Removing the oldest un-ACKed RADIUS packet due to "
+                      "retransmit list limits.\n");
+               prev = NULL;
+               while (entry->next) {
+                       prev = entry;
+                       entry = entry->next;
+               }
+               if (prev) {
+                       prev->next = NULL;
+                       radius_client_msg_free(entry);
+               }
+       } else
+               radius->num_msgs++;
+}
+
+
+static void radius_client_list_del(struct radius_client_data *radius,
+                                  RadiusType msg_type, const u8 *addr)
+{
+       struct radius_msg_list *entry, *prev, *tmp;
+
+       if (addr == NULL)
+               return;
+
+       entry = radius->msgs;
+       prev = NULL;
+       while (entry) {
+               if (entry->msg_type == msg_type &&
+                   os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               radius->msgs = entry->next;
+                       tmp = entry;
+                       entry = entry->next;
+                       hostapd_logger(radius->ctx, addr,
+                                      HOSTAPD_MODULE_RADIUS,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Removing matching RADIUS message");
+                       radius_client_msg_free(tmp);
+                       radius->num_msgs--;
+                       continue;
+               }
+               prev = entry;
+               entry = entry->next;
+       }
+}
+
+
+/**
+ * radius_client_send - Send a RADIUS request
+ * @radius: RADIUS client context from radius_client_init()
+ * @msg: RADIUS message to be sent
+ * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM)
+ * @addr: MAC address of the device related to this message or %NULL
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or
+ * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference
+ * between accounting and interim accounting messages is that the interim
+ * message will override any pending interim accounting updates while a new
+ * accounting message does not remove any pending messages.
+ *
+ * The message is added on the retransmission queue and will be retransmitted
+ * automatically until a response is received or maximum number of retries
+ * (RADIUS_CLIENT_MAX_RETRIES) is reached.
+ *
+ * The related device MAC address can be used to identify pending messages that
+ * can be removed with radius_client_flush_auth() or with interim accounting
+ * updates.
+ */
+int radius_client_send(struct radius_client_data *radius,
+                      struct radius_msg *msg, RadiusType msg_type,
+                      const u8 *addr)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       const u8 *shared_secret;
+       size_t shared_secret_len;
+       char *name;
+       int s, res;
+       struct wpabuf *buf;
+
+       if (msg_type == RADIUS_ACCT_INTERIM) {
+               /* Remove any pending interim acct update for the same STA. */
+               radius_client_list_del(radius, msg_type, addr);
+       }
+
+       if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
+               if (conf->acct_server == NULL) {
+                       hostapd_logger(radius->ctx, NULL,
+                                      HOSTAPD_MODULE_RADIUS,
+                                      HOSTAPD_LEVEL_INFO,
+                                      "No accounting server configured");
+                       return -1;
+               }
+               shared_secret = conf->acct_server->shared_secret;
+               shared_secret_len = conf->acct_server->shared_secret_len;
+               radius_msg_finish_acct(msg, shared_secret, shared_secret_len);
+               name = "accounting";
+               s = radius->acct_sock;
+               conf->acct_server->requests++;
+       } else {
+               if (conf->auth_server == NULL) {
+                       hostapd_logger(radius->ctx, NULL,
+                                      HOSTAPD_MODULE_RADIUS,
+                                      HOSTAPD_LEVEL_INFO,
+                                      "No authentication server configured");
+                       return -1;
+               }
+               shared_secret = conf->auth_server->shared_secret;
+               shared_secret_len = conf->auth_server->shared_secret_len;
+               radius_msg_finish(msg, shared_secret, shared_secret_len);
+               name = "authentication";
+               s = radius->auth_sock;
+               conf->auth_server->requests++;
+       }
+
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s "
+                      "server", name);
+       if (conf->msg_dumps)
+               radius_msg_dump(msg);
+
+       buf = radius_msg_get_buf(msg);
+       res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0);
+       if (res < 0)
+               radius_client_handle_send_error(radius, s, msg_type);
+
+       radius_client_list_add(radius, msg, msg_type, shared_secret,
+                              shared_secret_len, addr);
+
+       return res;
+}
+
+
+static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct radius_client_data *radius = eloop_ctx;
+       struct hostapd_radius_servers *conf = radius->conf;
+       RadiusType msg_type = (RadiusType) sock_ctx;
+       int len, roundtrip;
+       unsigned char buf[3000];
+       struct radius_msg *msg;
+       struct radius_hdr *hdr;
+       struct radius_rx_handler *handlers;
+       size_t num_handlers, i;
+       struct radius_msg_list *req, *prev_req;
+       struct os_time now;
+       struct hostapd_radius_server *rconf;
+       int invalid_authenticator = 0;
+
+       if (msg_type == RADIUS_ACCT) {
+               handlers = radius->acct_handlers;
+               num_handlers = radius->num_acct_handlers;
+               rconf = conf->acct_server;
+       } else {
+               handlers = radius->auth_handlers;
+               num_handlers = radius->num_auth_handlers;
+               rconf = conf->auth_server;
+       }
+
+       len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
+       if (len < 0) {
+               perror("recv[RADIUS]");
+               return;
+       }
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
+                      "server", len);
+       if (len == sizeof(buf)) {
+               printf("Possibly too long UDP frame for our buffer - "
+                      "dropping it\n");
+               return;
+       }
+
+       msg = radius_msg_parse(buf, len);
+       if (msg == NULL) {
+               printf("Parsing incoming RADIUS frame failed\n");
+               rconf->malformed_responses++;
+               return;
+       }
+       hdr = radius_msg_get_hdr(msg);
+
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG, "Received RADIUS message");
+       if (conf->msg_dumps)
+               radius_msg_dump(msg);
+
+       switch (hdr->code) {
+       case RADIUS_CODE_ACCESS_ACCEPT:
+               rconf->access_accepts++;
+               break;
+       case RADIUS_CODE_ACCESS_REJECT:
+               rconf->access_rejects++;
+               break;
+       case RADIUS_CODE_ACCESS_CHALLENGE:
+               rconf->access_challenges++;
+               break;
+       case RADIUS_CODE_ACCOUNTING_RESPONSE:
+               rconf->responses++;
+               break;
+       }
+
+       prev_req = NULL;
+       req = radius->msgs;
+       while (req) {
+               /* TODO: also match by src addr:port of the packet when using
+                * alternative RADIUS servers (?) */
+               if ((req->msg_type == msg_type ||
+                    (req->msg_type == RADIUS_ACCT_INTERIM &&
+                     msg_type == RADIUS_ACCT)) &&
+                   radius_msg_get_hdr(req->msg)->identifier ==
+                   hdr->identifier)
+                       break;
+
+               prev_req = req;
+               req = req->next;
+       }
+
+       if (req == NULL) {
+               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "No matching RADIUS request found (type=%d "
+                              "id=%d) - dropping packet",
+                              msg_type, hdr->identifier);
+               goto fail;
+       }
+
+       os_get_time(&now);
+       roundtrip = (now.sec - req->last_attempt.sec) * 100 +
+               (now.usec - req->last_attempt.usec) / 10000;
+       hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG,
+                      "Received RADIUS packet matched with a pending "
+                      "request, round trip time %d.%02d sec",
+                      roundtrip / 100, roundtrip % 100);
+       rconf->round_trip_time = roundtrip;
+
+       /* Remove ACKed RADIUS packet from retransmit list */
+       if (prev_req)
+               prev_req->next = req->next;
+       else
+               radius->msgs = req->next;
+       radius->num_msgs--;
+
+       for (i = 0; i < num_handlers; i++) {
+               RadiusRxResult res;
+               res = handlers[i].handler(msg, req->msg, req->shared_secret,
+                                         req->shared_secret_len,
+                                         handlers[i].data);
+               switch (res) {
+               case RADIUS_RX_PROCESSED:
+                       radius_msg_free(msg);
+                       /* continue */
+               case RADIUS_RX_QUEUED:
+                       radius_client_msg_free(req);
+                       return;
+               case RADIUS_RX_INVALID_AUTHENTICATOR:
+                       invalid_authenticator++;
+                       /* continue */
+               case RADIUS_RX_UNKNOWN:
+                       /* continue with next handler */
+                       break;
+               }
+       }
+
+       if (invalid_authenticator)
+               rconf->bad_authenticators++;
+       else
+               rconf->unknown_types++;
+       hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found "
+                      "(type=%d code=%d id=%d)%s - dropping packet",
+                      msg_type, hdr->code, hdr->identifier,
+                      invalid_authenticator ? " [INVALID AUTHENTICATOR]" :
+                      "");
+       radius_client_msg_free(req);
+
+ fail:
+       radius_msg_free(msg);
+}
+
+
+/**
+ * radius_client_get_id - Get an identifier for a new RADIUS message
+ * @radius: RADIUS client context from radius_client_init()
+ * Returns: Allocated identifier
+ *
+ * This function is used to fetch a unique (among pending requests) identifier
+ * for a new RADIUS message.
+ */
+u8 radius_client_get_id(struct radius_client_data *radius)
+{
+       struct radius_msg_list *entry, *prev, *_remove;
+       u8 id = radius->next_radius_identifier++;
+
+       /* remove entries with matching id from retransmit list to avoid
+        * using new reply from the RADIUS server with an old request */
+       entry = radius->msgs;
+       prev = NULL;
+       while (entry) {
+               if (radius_msg_get_hdr(entry->msg)->identifier == id) {
+                       hostapd_logger(radius->ctx, entry->addr,
+                                      HOSTAPD_MODULE_RADIUS,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Removing pending RADIUS message, "
+                                      "since its id (%d) is reused", id);
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               radius->msgs = entry->next;
+                       _remove = entry;
+               } else {
+                       _remove = NULL;
+                       prev = entry;
+               }
+               entry = entry->next;
+
+               if (_remove)
+                       radius_client_msg_free(_remove);
+       }
+
+       return id;
+}
+
+
+/**
+ * radius_client_flush - Flush all pending RADIUS client messages
+ * @radius: RADIUS client context from radius_client_init()
+ * @only_auth: Whether only authentication messages are removed
+ */
+void radius_client_flush(struct radius_client_data *radius, int only_auth)
+{
+       struct radius_msg_list *entry, *prev, *tmp;
+
+       if (!radius)
+               return;
+
+       prev = NULL;
+       entry = radius->msgs;
+
+       while (entry) {
+               if (!only_auth || entry->msg_type == RADIUS_AUTH) {
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               radius->msgs = entry->next;
+
+                       tmp = entry;
+                       entry = entry->next;
+                       radius_client_msg_free(tmp);
+                       radius->num_msgs--;
+               } else {
+                       prev = entry;
+                       entry = entry->next;
+               }
+       }
+
+       if (radius->msgs == NULL)
+               eloop_cancel_timeout(radius_client_timer, radius, NULL);
+}
+
+
+static void radius_client_update_acct_msgs(struct radius_client_data *radius,
+                                          const u8 *shared_secret,
+                                          size_t shared_secret_len)
+{
+       struct radius_msg_list *entry;
+
+       if (!radius)
+               return;
+
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if (entry->msg_type == RADIUS_ACCT) {
+                       entry->shared_secret = shared_secret;
+                       entry->shared_secret_len = shared_secret_len;
+                       radius_msg_finish_acct(entry->msg, shared_secret,
+                                              shared_secret_len);
+               }
+       }
+}
+
+
+static int
+radius_change_server(struct radius_client_data *radius,
+                    struct hostapd_radius_server *nserv,
+                    struct hostapd_radius_server *oserv,
+                    int sock, int sock6, int auth)
+{
+       struct sockaddr_in serv, claddr;
+#ifdef CONFIG_IPV6
+       struct sockaddr_in6 serv6, claddr6;
+#endif /* CONFIG_IPV6 */
+       struct sockaddr *addr, *cl_addr;
+       socklen_t addrlen, claddrlen;
+       char abuf[50];
+       int sel_sock;
+       struct radius_msg_list *entry;
+       struct hostapd_radius_servers *conf = radius->conf;
+
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_INFO,
+                      "%s server %s:%d",
+                      auth ? "Authentication" : "Accounting",
+                      hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
+                      nserv->port);
+
+       if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
+           os_memcmp(nserv->shared_secret, oserv->shared_secret,
+                     nserv->shared_secret_len) != 0) {
+               /* Pending RADIUS packets used different shared secret, so
+                * they need to be modified. Update accounting message
+                * authenticators here. Authentication messages are removed
+                * since they would require more changes and the new RADIUS
+                * server may not be prepared to receive them anyway due to
+                * missing state information. Client will likely retry
+                * authentication, so this should not be an issue. */
+               if (auth)
+                       radius_client_flush(radius, 1);
+               else {
+                       radius_client_update_acct_msgs(
+                               radius, nserv->shared_secret,
+                               nserv->shared_secret_len);
+               }
+       }
+
+       /* Reset retry counters for the new server */
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if ((auth && entry->msg_type != RADIUS_AUTH) ||
+                   (!auth && entry->msg_type != RADIUS_ACCT))
+                       continue;
+               entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
+               entry->attempts = 0;
+               entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+       }
+
+       if (radius->msgs) {
+               eloop_cancel_timeout(radius_client_timer, radius, NULL);
+               eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0,
+                                      radius_client_timer, radius, NULL);
+       }
+
+       switch (nserv->addr.af) {
+       case AF_INET:
+               os_memset(&serv, 0, sizeof(serv));
+               serv.sin_family = AF_INET;
+               serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr;
+               serv.sin_port = htons(nserv->port);
+               addr = (struct sockaddr *) &serv;
+               addrlen = sizeof(serv);
+               sel_sock = sock;
+               break;
+#ifdef CONFIG_IPV6
+       case AF_INET6:
+               os_memset(&serv6, 0, sizeof(serv6));
+               serv6.sin6_family = AF_INET6;
+               os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6,
+                         sizeof(struct in6_addr));
+               serv6.sin6_port = htons(nserv->port);
+               addr = (struct sockaddr *) &serv6;
+               addrlen = sizeof(serv6);
+               sel_sock = sock6;
+               break;
+#endif /* CONFIG_IPV6 */
+       default:
+               return -1;
+       }
+
+       if (conf->force_client_addr) {
+               switch (conf->client_addr.af) {
+               case AF_INET:
+                       os_memset(&claddr, 0, sizeof(claddr));
+                       claddr.sin_family = AF_INET;
+                       claddr.sin_addr.s_addr = conf->client_addr.u.v4.s_addr;
+                       claddr.sin_port = htons(0);
+                       cl_addr = (struct sockaddr *) &claddr;
+                       claddrlen = sizeof(claddr);
+                       break;
+#ifdef CONFIG_IPV6
+               case AF_INET6:
+                       os_memset(&claddr6, 0, sizeof(claddr6));
+                       claddr6.sin6_family = AF_INET6;
+                       os_memcpy(&claddr6.sin6_addr, &conf->client_addr.u.v6,
+                                 sizeof(struct in6_addr));
+                       claddr6.sin6_port = htons(0);
+                       cl_addr = (struct sockaddr *) &claddr6;
+                       claddrlen = sizeof(claddr6);
+                       break;
+#endif /* CONFIG_IPV6 */
+               default:
+                       return -1;
+               }
+
+               if (bind(sel_sock, cl_addr, claddrlen) < 0) {
+                       perror("bind[radius]");
+                       return -1;
+               }
+       }
+
+       if (connect(sel_sock, addr, addrlen) < 0) {
+               perror("connect[radius]");
+               return -1;
+       }
+
+#ifndef CONFIG_NATIVE_WINDOWS
+       switch (nserv->addr.af) {
+       case AF_INET:
+               claddrlen = sizeof(claddr);
+               getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
+               wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+                          inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
+               break;
+#ifdef CONFIG_IPV6
+       case AF_INET6: {
+               claddrlen = sizeof(claddr6);
+               getsockname(sel_sock, (struct sockaddr *) &claddr6,
+                           &claddrlen);
+               wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+                          inet_ntop(AF_INET6, &claddr6.sin6_addr,
+                                    abuf, sizeof(abuf)),
+                          ntohs(claddr6.sin6_port));
+               break;
+       }
+#endif /* CONFIG_IPV6 */
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       if (auth)
+               radius->auth_sock = sel_sock;
+       else
+               radius->acct_sock = sel_sock;
+
+       return 0;
+}
+
+
+static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
+{
+       struct radius_client_data *radius = eloop_ctx;
+       struct hostapd_radius_servers *conf = radius->conf;
+       struct hostapd_radius_server *oserv;
+
+       if (radius->auth_sock >= 0 && conf->auth_servers &&
+           conf->auth_server != conf->auth_servers) {
+               oserv = conf->auth_server;
+               conf->auth_server = conf->auth_servers;
+               radius_change_server(radius, conf->auth_server, oserv,
+                                    radius->auth_serv_sock,
+                                    radius->auth_serv_sock6, 1);
+       }
+
+       if (radius->acct_sock >= 0 && conf->acct_servers &&
+           conf->acct_server != conf->acct_servers) {
+               oserv = conf->acct_server;
+               conf->acct_server = conf->acct_servers;
+               radius_change_server(radius, conf->acct_server, oserv,
+                                    radius->acct_serv_sock,
+                                    radius->acct_serv_sock6, 0);
+       }
+
+       if (conf->retry_primary_interval)
+               eloop_register_timeout(conf->retry_primary_interval, 0,
+                                      radius_retry_primary_timer, radius,
+                                      NULL);
+}
+
+
+static int radius_client_disable_pmtu_discovery(int s)
+{
+       int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+       /* Turn off Path MTU discovery on IPv4/UDP sockets. */
+       int action = IP_PMTUDISC_DONT;
+       r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+                      sizeof(action));
+       if (r == -1)
+               wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
+                          "%s", strerror(errno));
+#endif
+       return r;
+}
+
+
+static int radius_client_init_auth(struct radius_client_data *radius)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       int ok = 0;
+
+       radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (radius->auth_serv_sock < 0)
+               perror("socket[PF_INET,SOCK_DGRAM]");
+       else {
+               radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
+               ok++;
+       }
+
+#ifdef CONFIG_IPV6
+       radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
+       if (radius->auth_serv_sock6 < 0)
+               perror("socket[PF_INET6,SOCK_DGRAM]");
+       else
+               ok++;
+#endif /* CONFIG_IPV6 */
+
+       if (ok == 0)
+               return -1;
+
+       radius_change_server(radius, conf->auth_server, NULL,
+                            radius->auth_serv_sock, radius->auth_serv_sock6,
+                            1);
+
+       if (radius->auth_serv_sock >= 0 &&
+           eloop_register_read_sock(radius->auth_serv_sock,
+                                    radius_client_receive, radius,
+                                    (void *) RADIUS_AUTH)) {
+               printf("Could not register read socket for authentication "
+                      "server\n");
+               return -1;
+       }
+
+#ifdef CONFIG_IPV6
+       if (radius->auth_serv_sock6 >= 0 &&
+           eloop_register_read_sock(radius->auth_serv_sock6,
+                                    radius_client_receive, radius,
+                                    (void *) RADIUS_AUTH)) {
+               printf("Could not register read socket for authentication "
+                      "server\n");
+               return -1;
+       }
+#endif /* CONFIG_IPV6 */
+
+       return 0;
+}
+
+
+static int radius_client_init_acct(struct radius_client_data *radius)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       int ok = 0;
+
+       radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (radius->acct_serv_sock < 0)
+               perror("socket[PF_INET,SOCK_DGRAM]");
+       else {
+               radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
+               ok++;
+       }
+
+#ifdef CONFIG_IPV6
+       radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
+       if (radius->acct_serv_sock6 < 0)
+               perror("socket[PF_INET6,SOCK_DGRAM]");
+       else
+               ok++;
+#endif /* CONFIG_IPV6 */
+
+       if (ok == 0)
+               return -1;
+
+       radius_change_server(radius, conf->acct_server, NULL,
+                            radius->acct_serv_sock, radius->acct_serv_sock6,
+                            0);
+
+       if (radius->acct_serv_sock >= 0 &&
+           eloop_register_read_sock(radius->acct_serv_sock,
+                                    radius_client_receive, radius,
+                                    (void *) RADIUS_ACCT)) {
+               printf("Could not register read socket for accounting "
+                      "server\n");
+               return -1;
+       }
+
+#ifdef CONFIG_IPV6
+       if (radius->acct_serv_sock6 >= 0 &&
+           eloop_register_read_sock(radius->acct_serv_sock6,
+                                    radius_client_receive, radius,
+                                    (void *) RADIUS_ACCT)) {
+               printf("Could not register read socket for accounting "
+                      "server\n");
+               return -1;
+       }
+#endif /* CONFIG_IPV6 */
+
+       return 0;
+}
+
+
+/**
+ * radius_client_init - Initialize RADIUS client
+ * @ctx: Callback context to be used in hostapd_logger() calls
+ * @conf: RADIUS client configuration (RADIUS servers)
+ * Returns: Pointer to private RADIUS client context or %NULL on failure
+ *
+ * The caller is responsible for keeping the configuration data available for
+ * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is
+ * called for the returned context pointer.
+ */
+struct radius_client_data *
+radius_client_init(void *ctx, struct hostapd_radius_servers *conf)
+{
+       struct radius_client_data *radius;
+
+       radius = os_zalloc(sizeof(struct radius_client_data));
+       if (radius == NULL)
+               return NULL;
+
+       radius->ctx = ctx;
+       radius->conf = conf;
+       radius->auth_serv_sock = radius->acct_serv_sock =
+               radius->auth_serv_sock6 = radius->acct_serv_sock6 =
+               radius->auth_sock = radius->acct_sock = -1;
+
+       if (conf->auth_server && radius_client_init_auth(radius)) {
+               radius_client_deinit(radius);
+               return NULL;
+       }
+
+       if (conf->acct_server && radius_client_init_acct(radius)) {
+               radius_client_deinit(radius);
+               return NULL;
+       }
+
+       if (conf->retry_primary_interval)
+               eloop_register_timeout(conf->retry_primary_interval, 0,
+                                      radius_retry_primary_timer, radius,
+                                      NULL);
+
+       return radius;
+}
+
+
+/**
+ * radius_client_deinit - Deinitialize RADIUS client
+ * @radius: RADIUS client context from radius_client_init()
+ */
+void radius_client_deinit(struct radius_client_data *radius)
+{
+       if (!radius)
+               return;
+
+       if (radius->auth_serv_sock >= 0)
+               eloop_unregister_read_sock(radius->auth_serv_sock);
+       if (radius->acct_serv_sock >= 0)
+               eloop_unregister_read_sock(radius->acct_serv_sock);
+#ifdef CONFIG_IPV6
+       if (radius->auth_serv_sock6 >= 0)
+               eloop_unregister_read_sock(radius->auth_serv_sock6);
+       if (radius->acct_serv_sock6 >= 0)
+               eloop_unregister_read_sock(radius->acct_serv_sock6);
+#endif /* CONFIG_IPV6 */
+
+       eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
+
+       radius_client_flush(radius, 0);
+       os_free(radius->auth_handlers);
+       os_free(radius->acct_handlers);
+       os_free(radius);
+}
+
+
+/**
+ * radius_client_flush_auth - Flush pending RADIUS messages for an address
+ * @radius: RADIUS client context from radius_client_init()
+ * @addr: MAC address of the related device
+ *
+ * This function can be used to remove pending RADIUS authentication messages
+ * that are related to a specific device. The addr parameter is matched with
+ * the one used in radius_client_send() call that was used to transmit the
+ * authentication request.
+ */
+void radius_client_flush_auth(struct radius_client_data *radius,
+                             const u8 *addr)
+{
+       struct radius_msg_list *entry, *prev, *tmp;
+
+       prev = NULL;
+       entry = radius->msgs;
+       while (entry) {
+               if (entry->msg_type == RADIUS_AUTH &&
+                   os_memcmp(entry->addr, addr, ETH_ALEN) == 0) {
+                       hostapd_logger(radius->ctx, addr,
+                                      HOSTAPD_MODULE_RADIUS,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Removing pending RADIUS authentication"
+                                      " message for removed client");
+
+                       if (prev)
+                               prev->next = entry->next;
+                       else
+                               radius->msgs = entry->next;
+
+                       tmp = entry;
+                       entry = entry->next;
+                       radius_client_msg_free(tmp);
+                       radius->num_msgs--;
+                       continue;
+               }
+
+               prev = entry;
+               entry = entry->next;
+       }
+}
+
+
+static int radius_client_dump_auth_server(char *buf, size_t buflen,
+                                         struct hostapd_radius_server *serv,
+                                         struct radius_client_data *cli)
+{
+       int pending = 0;
+       struct radius_msg_list *msg;
+       char abuf[50];
+
+       if (cli) {
+               for (msg = cli->msgs; msg; msg = msg->next) {
+                       if (msg->msg_type == RADIUS_AUTH)
+                               pending++;
+               }
+       }
+
+       return os_snprintf(buf, buflen,
+                          "radiusAuthServerIndex=%d\n"
+                          "radiusAuthServerAddress=%s\n"
+                          "radiusAuthClientServerPortNumber=%d\n"
+                          "radiusAuthClientRoundTripTime=%d\n"
+                          "radiusAuthClientAccessRequests=%u\n"
+                          "radiusAuthClientAccessRetransmissions=%u\n"
+                          "radiusAuthClientAccessAccepts=%u\n"
+                          "radiusAuthClientAccessRejects=%u\n"
+                          "radiusAuthClientAccessChallenges=%u\n"
+                          "radiusAuthClientMalformedAccessResponses=%u\n"
+                          "radiusAuthClientBadAuthenticators=%u\n"
+                          "radiusAuthClientPendingRequests=%u\n"
+                          "radiusAuthClientTimeouts=%u\n"
+                          "radiusAuthClientUnknownTypes=%u\n"
+                          "radiusAuthClientPacketsDropped=%u\n",
+                          serv->index,
+                          hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
+                          serv->port,
+                          serv->round_trip_time,
+                          serv->requests,
+                          serv->retransmissions,
+                          serv->access_accepts,
+                          serv->access_rejects,
+                          serv->access_challenges,
+                          serv->malformed_responses,
+                          serv->bad_authenticators,
+                          pending,
+                          serv->timeouts,
+                          serv->unknown_types,
+                          serv->packets_dropped);
+}
+
+
+static int radius_client_dump_acct_server(char *buf, size_t buflen,
+                                         struct hostapd_radius_server *serv,
+                                         struct radius_client_data *cli)
+{
+       int pending = 0;
+       struct radius_msg_list *msg;
+       char abuf[50];
+
+       if (cli) {
+               for (msg = cli->msgs; msg; msg = msg->next) {
+                       if (msg->msg_type == RADIUS_ACCT ||
+                           msg->msg_type == RADIUS_ACCT_INTERIM)
+                               pending++;
+               }
+       }
+
+       return os_snprintf(buf, buflen,
+                          "radiusAccServerIndex=%d\n"
+                          "radiusAccServerAddress=%s\n"
+                          "radiusAccClientServerPortNumber=%d\n"
+                          "radiusAccClientRoundTripTime=%d\n"
+                          "radiusAccClientRequests=%u\n"
+                          "radiusAccClientRetransmissions=%u\n"
+                          "radiusAccClientResponses=%u\n"
+                          "radiusAccClientMalformedResponses=%u\n"
+                          "radiusAccClientBadAuthenticators=%u\n"
+                          "radiusAccClientPendingRequests=%u\n"
+                          "radiusAccClientTimeouts=%u\n"
+                          "radiusAccClientUnknownTypes=%u\n"
+                          "radiusAccClientPacketsDropped=%u\n",
+                          serv->index,
+                          hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)),
+                          serv->port,
+                          serv->round_trip_time,
+                          serv->requests,
+                          serv->retransmissions,
+                          serv->responses,
+                          serv->malformed_responses,
+                          serv->bad_authenticators,
+                          pending,
+                          serv->timeouts,
+                          serv->unknown_types,
+                          serv->packets_dropped);
+}
+
+
+/**
+ * radius_client_get_mib - Get RADIUS client MIB information
+ * @radius: RADIUS client context from radius_client_init()
+ * @buf: Buffer for returning MIB data in text format
+ * @buflen: Maximum buf length in octets
+ * Returns: Number of octets written into the buffer
+ */
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+                         size_t buflen)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       int i;
+       struct hostapd_radius_server *serv;
+       int count = 0;
+
+       if (conf->auth_servers) {
+               for (i = 0; i < conf->num_auth_servers; i++) {
+                       serv = &conf->auth_servers[i];
+                       count += radius_client_dump_auth_server(
+                               buf + count, buflen - count, serv,
+                               serv == conf->auth_server ?
+                               radius : NULL);
+               }
+       }
+
+       if (conf->acct_servers) {
+               for (i = 0; i < conf->num_acct_servers; i++) {
+                       serv = &conf->acct_servers[i];
+                       count += radius_client_dump_acct_server(
+                               buf + count, buflen - count, serv,
+                               serv == conf->acct_server ?
+                               radius : NULL);
+               }
+       }
+
+       return count;
+}
diff --git a/src/radius/radius_client.h b/src/radius/radius_client.h
new file mode 100644 (file)
index 0000000..644ea23
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * RADIUS client
+ * 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.
+ */
+
+#ifndef RADIUS_CLIENT_H
+#define RADIUS_CLIENT_H
+
+#include "ip_addr.h"
+
+struct radius_msg;
+
+/**
+ * struct hostapd_radius_server - RADIUS server information for RADIUS client
+ *
+ * This structure contains information about a RADIUS server. The values are
+ * mainly for MIB information. The MIB variable prefix (radiusAuth or
+ * radiusAcc) depends on whether this is an authentication or accounting
+ * server.
+ *
+ * radiusAuthClientPendingRequests (or radiusAccClientPendingRequests) is the
+ * number struct radius_client_data::msgs for matching msg_type.
+ */
+struct hostapd_radius_server {
+       /**
+        * addr - radiusAuthServerAddress or radiusAccServerAddress
+        */
+       struct hostapd_ip_addr addr;
+
+       /**
+        * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber
+        */
+       int port;
+
+       /**
+        * shared_secret - Shared secret for authenticating RADIUS messages
+        */
+       u8 *shared_secret;
+
+       /**
+        * shared_secret_len - Length of shared_secret in octets
+        */
+       size_t shared_secret_len;
+
+       /* Dynamic (not from configuration file) MIB data */
+
+       /**
+        * index - radiusAuthServerIndex or radiusAccServerIndex
+        */
+       int index;
+
+       /**
+        * round_trip_time - radiusAuthClientRoundTripTime or radiusAccClientRoundTripTime
+        * Round-trip time in hundredths of a second.
+        */
+       int round_trip_time;
+
+       /**
+        * requests - radiusAuthClientAccessRequests or radiusAccClientRequests
+        */
+       u32 requests;
+
+       /**
+        * retransmissions - radiusAuthClientAccessRetransmissions or radiusAccClientRetransmissions
+        */
+       u32 retransmissions;
+
+       /**
+        * access_accepts - radiusAuthClientAccessAccepts
+        */
+       u32 access_accepts;
+
+       /**
+        * access_rejects - radiusAuthClientAccessRejects
+        */
+       u32 access_rejects;
+
+       /**
+        * access_challenges - radiusAuthClientAccessChallenges
+        */
+       u32 access_challenges;
+
+       /**
+        * responses - radiusAccClientResponses
+        */
+       u32 responses;
+
+       /**
+        * malformed_responses - radiusAuthClientMalformedAccessResponses or radiusAccClientMalformedResponses
+        */
+       u32 malformed_responses;
+
+       /**
+        * bad_authenticators - radiusAuthClientBadAuthenticators or radiusAccClientBadAuthenticators
+        */
+       u32 bad_authenticators;
+
+       /**
+        * timeouts - radiusAuthClientTimeouts or radiusAccClientTimeouts
+        */
+       u32 timeouts;
+
+       /**
+        * unknown_types - radiusAuthClientUnknownTypes or radiusAccClientUnknownTypes
+        */
+       u32 unknown_types;
+
+       /**
+        * packets_dropped - radiusAuthClientPacketsDropped or radiusAccClientPacketsDropped
+        */
+       u32 packets_dropped;
+};
+
+/**
+ * struct hostapd_radius_servers - RADIUS servers for RADIUS client
+ */
+struct hostapd_radius_servers {
+       /**
+        * auth_servers - RADIUS Authentication servers in priority order
+        */
+       struct hostapd_radius_server *auth_servers;
+
+       /**
+        * num_auth_servers - Number of auth_servers entries
+        */
+       int num_auth_servers;
+
+       /**
+        * auth_server - The current Authentication server
+        */
+       struct hostapd_radius_server *auth_server;
+
+       /**
+        * acct_servers - RADIUS Accounting servers in priority order
+        */
+       struct hostapd_radius_server *acct_servers;
+
+       /**
+        * num_acct_servers - Number of acct_servers entries
+        */
+       int num_acct_servers;
+
+       /**
+        * acct_server - The current Accounting server
+        */
+       struct hostapd_radius_server *acct_server;
+
+       /**
+        * retry_primary_interval - Retry interval for trying primary server
+        *
+        * This specifies a retry interval in sexconds for trying to return to
+        * the primary RADIUS server. 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 (non-zero), the primary server
+        * will be retried after the specified number of seconds has passed
+        * even if the current used secondary server is still working.
+        */
+       int retry_primary_interval;
+
+       /**
+        * msg_dumps - Whether RADIUS message details are shown in stdout
+        */
+       int msg_dumps;
+
+       /**
+        * client_addr - Client (local) address to use if force_client_addr
+        */
+       struct hostapd_ip_addr client_addr;
+
+       /**
+        * force_client_addr - Whether to force client (local) address
+        */
+       int force_client_addr;
+};
+
+
+/**
+ * RadiusType - RADIUS server type for RADIUS client
+ */
+typedef enum {
+       /**
+        * RADIUS authentication
+        */
+       RADIUS_AUTH,
+
+       /**
+        * RADIUS_ACCT - RADIUS accounting
+        */
+       RADIUS_ACCT,
+
+       /**
+        * RADIUS_ACCT_INTERIM - RADIUS interim accounting message
+        *
+        * Used only with radius_client_send(). This behaves just like
+        * RADIUS_ACCT, but removes any pending interim RADIUS Accounting
+        * messages for the same STA before sending the new interim update.
+        */
+       RADIUS_ACCT_INTERIM
+} RadiusType;
+
+/**
+ * RadiusRxResult - RADIUS client RX handler result
+ */
+typedef enum {
+       /**
+        * RADIUS_RX_PROCESSED - Message processed
+        *
+        * This stops handler calls and frees the message.
+        */
+       RADIUS_RX_PROCESSED,
+
+       /**
+        * RADIUS_RX_QUEUED - Message has been queued
+        *
+        * This stops handler calls, but does not free the message; the handler
+        * that returned this is responsible for eventually freeing the
+        * message.
+        */
+       RADIUS_RX_QUEUED,
+
+       /**
+        * RADIUS_RX_UNKNOWN - Message is not for this handler
+        */
+       RADIUS_RX_UNKNOWN,
+
+       /**
+        * RADIUS_RX_INVALID_AUTHENTICATOR - Message has invalid Authenticator
+        */
+       RADIUS_RX_INVALID_AUTHENTICATOR
+} RadiusRxResult;
+
+struct radius_client_data;
+
+int radius_client_register(struct radius_client_data *radius,
+                          RadiusType msg_type,
+                          RadiusRxResult (*handler)
+                          (struct radius_msg *msg, struct radius_msg *req,
+                           const u8 *shared_secret, size_t shared_secret_len,
+                           void *data),
+                          void *data);
+int radius_client_send(struct radius_client_data *radius,
+                      struct radius_msg *msg,
+                      RadiusType msg_type, const u8 *addr);
+u8 radius_client_get_id(struct radius_client_data *radius);
+void radius_client_flush(struct radius_client_data *radius, int only_auth);
+struct radius_client_data *
+radius_client_init(void *ctx, struct hostapd_radius_servers *conf);
+void radius_client_deinit(struct radius_client_data *radius);
+void radius_client_flush_auth(struct radius_client_data *radius,
+                             const u8 *addr);
+int radius_client_get_mib(struct radius_client_data *radius, char *buf,
+                         size_t buflen);
+
+#endif /* RADIUS_CLIENT_H */
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
new file mode 100644 (file)
index 0000000..f8780a6
--- /dev/null
@@ -0,0 +1,1518 @@
+/*
+ * RADIUS authentication server
+ * Copyright (c) 2005-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 <net/if.h>
+
+#include "common.h"
+#include "radius.h"
+#include "eloop.h"
+#include "eap_server/eap.h"
+#include "radius_server.h"
+
+/**
+ * RADIUS_SESSION_TIMEOUT - Session timeout in seconds
+ */
+#define RADIUS_SESSION_TIMEOUT 60
+
+/**
+ * RADIUS_MAX_SESSION - Maximum number of active sessions
+ */
+#define RADIUS_MAX_SESSION 100
+
+/**
+ * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages
+ */
+#define RADIUS_MAX_MSG_LEN 3000
+
+static struct eapol_callbacks radius_server_eapol_cb;
+
+struct radius_client;
+struct radius_server_data;
+
+/**
+ * struct radius_server_counters - RADIUS server statistics counters
+ */
+struct radius_server_counters {
+       u32 access_requests;
+       u32 invalid_requests;
+       u32 dup_access_requests;
+       u32 access_accepts;
+       u32 access_rejects;
+       u32 access_challenges;
+       u32 malformed_access_requests;
+       u32 bad_authenticators;
+       u32 packets_dropped;
+       u32 unknown_types;
+};
+
+/**
+ * struct radius_session - Internal RADIUS server data for a session
+ */
+struct radius_session {
+       struct radius_session *next;
+       struct radius_client *client;
+       struct radius_server_data *server;
+       unsigned int sess_id;
+       struct eap_sm *eap;
+       struct eap_eapol_interface *eap_if;
+
+       struct radius_msg *last_msg;
+       char *last_from_addr;
+       int last_from_port;
+       struct sockaddr_storage last_from;
+       socklen_t last_fromlen;
+       u8 last_identifier;
+       struct radius_msg *last_reply;
+       u8 last_authenticator[16];
+};
+
+/**
+ * struct radius_client - Internal RADIUS server data for a client
+ */
+struct radius_client {
+       struct radius_client *next;
+       struct in_addr addr;
+       struct in_addr mask;
+#ifdef CONFIG_IPV6
+       struct in6_addr addr6;
+       struct in6_addr mask6;
+#endif /* CONFIG_IPV6 */
+       char *shared_secret;
+       int shared_secret_len;
+       struct radius_session *sessions;
+       struct radius_server_counters counters;
+};
+
+/**
+ * struct radius_server_data - Internal RADIUS server data
+ */
+struct radius_server_data {
+       /**
+        * auth_sock - Socket for RADIUS authentication messages
+        */
+       int auth_sock;
+
+       /**
+        * clients - List of authorized RADIUS clients
+        */
+       struct radius_client *clients;
+
+       /**
+        * next_sess_id - Next session identifier
+        */
+       unsigned int next_sess_id;
+
+       /**
+        * conf_ctx - Context pointer for callbacks
+        *
+        * This is used as the ctx argument in get_eap_user() calls.
+        */
+       void *conf_ctx;
+
+       /**
+        * num_sess - Number of active sessions
+        */
+       int num_sess;
+
+       /**
+        * eap_sim_db_priv - EAP-SIM/AKA database context
+        *
+        * This is passed to the EAP-SIM/AKA server implementation as a
+        * callback context.
+        */
+       void *eap_sim_db_priv;
+
+       /**
+        * ssl_ctx - TLS context
+        *
+        * This is passed to the EAP server implementation as a callback
+        * context for TLS operations.
+        */
+       void *ssl_ctx;
+
+       /**
+        * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+        *
+        * This parameter is used to set a key for EAP-FAST to encrypt the
+        * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+        * set, must point to a 16-octet key.
+        */
+       u8 *pac_opaque_encr_key;
+
+       /**
+        * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+        *
+        * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+        * is a variable length field, but due to some existing implementations
+        * requiring A-ID to be 16 octets in length, it is recommended to use
+        * that length for the field to provide interoperability with deployed
+        * peer implementations.
+        */
+       u8 *eap_fast_a_id;
+
+       /**
+        * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+        */
+       size_t eap_fast_a_id_len;
+
+       /**
+        * eap_fast_a_id_info - EAP-FAST authority identifier information
+        *
+        * This A-ID-Info contains a user-friendly name for the A-ID. For
+        * example, this could be the enterprise and server names in
+        * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+        * is not used, this can be set to %NULL.
+        */
+       char *eap_fast_a_id_info;
+
+       /**
+        * eap_fast_prov - EAP-FAST provisioning modes
+        *
+        * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+        * 2 = only authenticated provisioning allowed, 3 = both provisioning
+        * modes allowed.
+        */
+       int eap_fast_prov;
+
+       /**
+        * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+        *
+        * This is the hard limit on how long a provisioned PAC-Key can be
+        * used.
+        */
+       int pac_key_lifetime;
+
+       /**
+        * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+        *
+        * This is a soft limit on the PAC-Key. The server will automatically
+        * generate a new PAC-Key when this number of seconds (or fewer) of the
+        * lifetime remains.
+        */
+       int pac_key_refresh_time;
+
+       /**
+        * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+        *
+        * This controls whether the protected success/failure indication
+        * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+        */
+       int eap_sim_aka_result_ind;
+
+       /**
+        * tnc - Trusted Network Connect (TNC)
+        *
+        * This controls whether TNC is enabled and 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.
+        */
+       int tnc;
+
+       /**
+        * wps - Wi-Fi Protected Setup context
+        *
+        * If WPS is used with an external RADIUS server (which is quite
+        * unlikely configuration), this is used to provide a pointer to WPS
+        * context data. Normally, this can be set to %NULL.
+        */
+       struct wps_context *wps;
+
+       /**
+        * ipv6 - Whether to enable IPv6 support in the RADIUS server
+        */
+       int ipv6;
+
+       /**
+        * start_time - Timestamp of server start
+        */
+       struct os_time start_time;
+
+       /**
+        * counters - Statistics counters for server operations
+        *
+        * These counters are the sum over all clients.
+        */
+       struct radius_server_counters counters;
+
+       /**
+        * get_eap_user - Callback for fetching EAP user information
+        * @ctx: Context data from conf_ctx
+        * @identity: User identity
+        * @identity_len: identity buffer length in octets
+        * @phase2: Whether this is for Phase 2 identity
+        * @user: Data structure for filling in the user information
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is used to fetch information from user database. The callback
+        * will fill in information about allowed EAP methods and the user
+        * password. The password field will be an allocated copy of the
+        * password data and RADIUS server will free it after use.
+        */
+       int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+                           int phase2, struct eap_user *user);
+
+       /**
+        * eap_req_id_text - Optional data for EAP-Request/Identity
+        *
+        * This can be used to configure an optional, displayable message that
+        * will be sent in EAP-Request/Identity. This string can contain an
+        * ASCII-0 character (nul) to separate network infromation per RFC
+        * 4284. The actual string length is explicit provided in
+        * eap_req_id_text_len since nul character will not be used as a string
+        * terminator.
+        */
+       char *eap_req_id_text;
+
+       /**
+        * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
+        */
+       size_t eap_req_id_text_len;
+
+       /*
+        * msg_ctx - Context data for wpa_msg() calls
+        */
+       void *msg_ctx;
+};
+
+
+extern int wpa_debug_level;
+
+#define RADIUS_DEBUG(args...) \
+wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
+#define RADIUS_ERROR(args...) \
+wpa_printf(MSG_ERROR, "RADIUS SRV: " args)
+#define RADIUS_DUMP(args...) \
+wpa_hexdump(MSG_MSGDUMP, "RADIUS SRV: " args)
+#define RADIUS_DUMP_ASCII(args...) \
+wpa_hexdump_ascii(MSG_MSGDUMP, "RADIUS SRV: " args)
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
+static void radius_server_session_remove_timeout(void *eloop_ctx,
+                                                void *timeout_ctx);
+
+
+static struct radius_client *
+radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
+                        int ipv6)
+{
+       struct radius_client *client = data->clients;
+
+       while (client) {
+#ifdef CONFIG_IPV6
+               if (ipv6) {
+                       struct in6_addr *addr6;
+                       int i;
+
+                       addr6 = (struct in6_addr *) addr;
+                       for (i = 0; i < 16; i++) {
+                               if ((addr6->s6_addr[i] &
+                                    client->mask6.s6_addr[i]) !=
+                                   (client->addr6.s6_addr[i] &
+                                    client->mask6.s6_addr[i])) {
+                                       i = 17;
+                                       break;
+                               }
+                       }
+                       if (i == 16) {
+                               break;
+                       }
+               }
+#endif /* CONFIG_IPV6 */
+               if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) ==
+                   (addr->s_addr & client->mask.s_addr)) {
+                       break;
+               }
+
+               client = client->next;
+       }
+
+       return client;
+}
+
+
+static struct radius_session *
+radius_server_get_session(struct radius_client *client, unsigned int sess_id)
+{
+       struct radius_session *sess = client->sessions;
+
+       while (sess) {
+               if (sess->sess_id == sess_id) {
+                       break;
+               }
+               sess = sess->next;
+       }
+
+       return sess;
+}
+
+
+static void radius_server_session_free(struct radius_server_data *data,
+                                      struct radius_session *sess)
+{
+       eloop_cancel_timeout(radius_server_session_timeout, data, sess);
+       eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
+       eap_server_sm_deinit(sess->eap);
+       radius_msg_free(sess->last_msg);
+       os_free(sess->last_from_addr);
+       radius_msg_free(sess->last_reply);
+       os_free(sess);
+       data->num_sess--;
+}
+
+
+static void radius_server_session_remove(struct radius_server_data *data,
+                                        struct radius_session *sess)
+{
+       struct radius_client *client = sess->client;
+       struct radius_session *session, *prev;
+
+       eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess);
+
+       prev = NULL;
+       session = client->sessions;
+       while (session) {
+               if (session == sess) {
+                       if (prev == NULL) {
+                               client->sessions = sess->next;
+                       } else {
+                               prev->next = sess->next;
+                       }
+                       radius_server_session_free(data, sess);
+                       break;
+               }
+               prev = session;
+               session = session->next;
+       }
+}
+
+
+static void radius_server_session_remove_timeout(void *eloop_ctx,
+                                                void *timeout_ctx)
+{
+       struct radius_server_data *data = eloop_ctx;
+       struct radius_session *sess = timeout_ctx;
+       RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id);
+       radius_server_session_remove(data, sess);
+}
+
+
+static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct radius_server_data *data = eloop_ctx;
+       struct radius_session *sess = timeout_ctx;
+
+       RADIUS_DEBUG("Timing out authentication session 0x%x", sess->sess_id);
+       radius_server_session_remove(data, sess);
+}
+
+
+static struct radius_session *
+radius_server_new_session(struct radius_server_data *data,
+                         struct radius_client *client)
+{
+       struct radius_session *sess;
+
+       if (data->num_sess >= RADIUS_MAX_SESSION) {
+               RADIUS_DEBUG("Maximum number of existing session - no room "
+                            "for a new session");
+               return NULL;
+       }
+
+       sess = os_zalloc(sizeof(*sess));
+       if (sess == NULL)
+               return NULL;
+
+       sess->server = data;
+       sess->client = client;
+       sess->sess_id = data->next_sess_id++;
+       sess->next = client->sessions;
+       client->sessions = sess;
+       eloop_register_timeout(RADIUS_SESSION_TIMEOUT, 0,
+                              radius_server_session_timeout, data, sess);
+       data->num_sess++;
+       return sess;
+}
+
+
+static struct radius_session *
+radius_server_get_new_session(struct radius_server_data *data,
+                             struct radius_client *client,
+                             struct radius_msg *msg)
+{
+       u8 *user;
+       size_t user_len;
+       int res;
+       struct radius_session *sess;
+       struct eap_config eap_conf;
+
+       RADIUS_DEBUG("Creating a new session");
+
+       user = os_malloc(256);
+       if (user == NULL) {
+               return NULL;
+       }
+       res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
+       if (res < 0 || res > 256) {
+               RADIUS_DEBUG("Could not get User-Name");
+               os_free(user);
+               return NULL;
+       }
+       user_len = res;
+       RADIUS_DUMP_ASCII("User-Name", user, user_len);
+
+       res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL);
+       os_free(user);
+
+       if (res == 0) {
+               RADIUS_DEBUG("Matching user entry found");
+               sess = radius_server_new_session(data, client);
+               if (sess == NULL) {
+                       RADIUS_DEBUG("Failed to create a new session");
+                       return NULL;
+               }
+       } else {
+               RADIUS_DEBUG("User-Name not found from user database");
+               return NULL;
+       }
+
+       os_memset(&eap_conf, 0, sizeof(eap_conf));
+       eap_conf.ssl_ctx = data->ssl_ctx;
+       eap_conf.msg_ctx = data->msg_ctx;
+       eap_conf.eap_sim_db_priv = data->eap_sim_db_priv;
+       eap_conf.backend_auth = TRUE;
+       eap_conf.eap_server = 1;
+       eap_conf.pac_opaque_encr_key = data->pac_opaque_encr_key;
+       eap_conf.eap_fast_a_id = data->eap_fast_a_id;
+       eap_conf.eap_fast_a_id_len = data->eap_fast_a_id_len;
+       eap_conf.eap_fast_a_id_info = data->eap_fast_a_id_info;
+       eap_conf.eap_fast_prov = data->eap_fast_prov;
+       eap_conf.pac_key_lifetime = data->pac_key_lifetime;
+       eap_conf.pac_key_refresh_time = data->pac_key_refresh_time;
+       eap_conf.eap_sim_aka_result_ind = data->eap_sim_aka_result_ind;
+       eap_conf.tnc = data->tnc;
+       eap_conf.wps = data->wps;
+       sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
+                                      &eap_conf);
+       if (sess->eap == NULL) {
+               RADIUS_DEBUG("Failed to initialize EAP state machine for the "
+                            "new session");
+               radius_server_session_free(data, sess);
+               return NULL;
+       }
+       sess->eap_if = eap_get_interface(sess->eap);
+       sess->eap_if->eapRestart = TRUE;
+       sess->eap_if->portEnabled = TRUE;
+
+       RADIUS_DEBUG("New session 0x%x initialized", sess->sess_id);
+
+       return sess;
+}
+
+
+static struct radius_msg *
+radius_server_encapsulate_eap(struct radius_server_data *data,
+                             struct radius_client *client,
+                             struct radius_session *sess,
+                             struct radius_msg *request)
+{
+       struct radius_msg *msg;
+       int code;
+       unsigned int sess_id;
+       struct radius_hdr *hdr = radius_msg_get_hdr(request);
+
+       if (sess->eap_if->eapFail) {
+               sess->eap_if->eapFail = FALSE;
+               code = RADIUS_CODE_ACCESS_REJECT;
+       } else if (sess->eap_if->eapSuccess) {
+               sess->eap_if->eapSuccess = FALSE;
+               code = RADIUS_CODE_ACCESS_ACCEPT;
+       } else {
+               sess->eap_if->eapReq = FALSE;
+               code = RADIUS_CODE_ACCESS_CHALLENGE;
+       }
+
+       msg = radius_msg_new(code, hdr->identifier);
+       if (msg == NULL) {
+               RADIUS_DEBUG("Failed to allocate reply message");
+               return NULL;
+       }
+
+       sess_id = htonl(sess->sess_id);
+       if (code == RADIUS_CODE_ACCESS_CHALLENGE &&
+           !radius_msg_add_attr(msg, RADIUS_ATTR_STATE,
+                                (u8 *) &sess_id, sizeof(sess_id))) {
+               RADIUS_DEBUG("Failed to add State attribute");
+       }
+
+       if (sess->eap_if->eapReqData &&
+           !radius_msg_add_eap(msg, wpabuf_head(sess->eap_if->eapReqData),
+                               wpabuf_len(sess->eap_if->eapReqData))) {
+               RADIUS_DEBUG("Failed to add EAP-Message attribute");
+       }
+
+       if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->eap_if->eapKeyData) {
+               int len;
+               if (sess->eap_if->eapKeyDataLen > 64) {
+                       len = 32;
+               } else {
+                       len = sess->eap_if->eapKeyDataLen / 2;
+               }
+               if (!radius_msg_add_mppe_keys(msg, hdr->authenticator,
+                                             (u8 *) client->shared_secret,
+                                             client->shared_secret_len,
+                                             sess->eap_if->eapKeyData + len,
+                                             len, sess->eap_if->eapKeyData,
+                                             len)) {
+                       RADIUS_DEBUG("Failed to add MPPE key attributes");
+               }
+       }
+
+       if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
+               RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
+               radius_msg_free(msg);
+               return NULL;
+       }
+
+       if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+                                 client->shared_secret_len,
+                                 hdr->authenticator) < 0) {
+               RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+       }
+
+       return msg;
+}
+
+
+static int radius_server_reject(struct radius_server_data *data,
+                               struct radius_client *client,
+                               struct radius_msg *request,
+                               struct sockaddr *from, socklen_t fromlen,
+                               const char *from_addr, int from_port)
+{
+       struct radius_msg *msg;
+       int ret = 0;
+       struct eap_hdr eapfail;
+       struct wpabuf *buf;
+       struct radius_hdr *hdr = radius_msg_get_hdr(request);
+
+       RADIUS_DEBUG("Reject invalid request from %s:%d",
+                    from_addr, from_port);
+
+       msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier);
+       if (msg == NULL) {
+               return -1;
+       }
+
+       os_memset(&eapfail, 0, sizeof(eapfail));
+       eapfail.code = EAP_CODE_FAILURE;
+       eapfail.identifier = 0;
+       eapfail.length = host_to_be16(sizeof(eapfail));
+
+       if (!radius_msg_add_eap(msg, (u8 *) &eapfail, sizeof(eapfail))) {
+               RADIUS_DEBUG("Failed to add EAP-Message attribute");
+       }
+
+       if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
+               RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
+               radius_msg_free(msg);
+               return -1;
+       }
+
+       if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+                                 client->shared_secret_len,
+                                 hdr->authenticator) <
+           0) {
+               RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+       }
+
+       if (wpa_debug_level <= MSG_MSGDUMP) {
+               radius_msg_dump(msg);
+       }
+
+       data->counters.access_rejects++;
+       client->counters.access_rejects++;
+       buf = radius_msg_get_buf(msg);
+       if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
+                  (struct sockaddr *) from, sizeof(*from)) < 0) {
+               perror("sendto[RADIUS SRV]");
+               ret = -1;
+       }
+
+       radius_msg_free(msg);
+
+       return ret;
+}
+
+
+static int radius_server_request(struct radius_server_data *data,
+                                struct radius_msg *msg,
+                                struct sockaddr *from, socklen_t fromlen,
+                                struct radius_client *client,
+                                const char *from_addr, int from_port,
+                                struct radius_session *force_sess)
+{
+       u8 *eap = NULL;
+       size_t eap_len;
+       int res, state_included = 0;
+       u8 statebuf[4];
+       unsigned int state;
+       struct radius_session *sess;
+       struct radius_msg *reply;
+       int is_complete = 0;
+
+       if (force_sess)
+               sess = force_sess;
+       else {
+               res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf,
+                                         sizeof(statebuf));
+               state_included = res >= 0;
+               if (res == sizeof(statebuf)) {
+                       state = WPA_GET_BE32(statebuf);
+                       sess = radius_server_get_session(client, state);
+               } else {
+                       sess = NULL;
+               }
+       }
+
+       if (sess) {
+               RADIUS_DEBUG("Request for session 0x%x", sess->sess_id);
+       } else if (state_included) {
+               RADIUS_DEBUG("State attribute included but no session found");
+               radius_server_reject(data, client, msg, from, fromlen,
+                                    from_addr, from_port);
+               return -1;
+       } else {
+               sess = radius_server_get_new_session(data, client, msg);
+               if (sess == NULL) {
+                       RADIUS_DEBUG("Could not create a new session");
+                       radius_server_reject(data, client, msg, from, fromlen,
+                                            from_addr, from_port);
+                       return -1;
+               }
+       }
+
+       if (sess->last_from_port == from_port &&
+           sess->last_identifier == radius_msg_get_hdr(msg)->identifier &&
+           os_memcmp(sess->last_authenticator,
+                     radius_msg_get_hdr(msg)->authenticator, 16) == 0) {
+               RADIUS_DEBUG("Duplicate message from %s", from_addr);
+               data->counters.dup_access_requests++;
+               client->counters.dup_access_requests++;
+
+               if (sess->last_reply) {
+                       struct wpabuf *buf;
+                       buf = radius_msg_get_buf(sess->last_reply);
+                       res = sendto(data->auth_sock, wpabuf_head(buf),
+                                    wpabuf_len(buf), 0,
+                                    (struct sockaddr *) from, fromlen);
+                       if (res < 0) {
+                               perror("sendto[RADIUS SRV]");
+                       }
+                       return 0;
+               }
+
+               RADIUS_DEBUG("No previous reply available for duplicate "
+                            "message");
+               return -1;
+       }
+                     
+       eap = radius_msg_get_eap(msg, &eap_len);
+       if (eap == NULL) {
+               RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
+                            from_addr);
+               data->counters.packets_dropped++;
+               client->counters.packets_dropped++;
+               return -1;
+       }
+
+       RADIUS_DUMP("Received EAP data", eap, eap_len);
+
+       /* FIX: if Code is Request, Success, or Failure, send Access-Reject;
+        * RFC3579 Sect. 2.6.2.
+        * Include EAP-Response/Nak with no preferred method if
+        * code == request.
+        * If code is not 1-4, discard the packet silently.
+        * Or is this already done by the EAP state machine? */
+
+       wpabuf_free(sess->eap_if->eapRespData);
+       sess->eap_if->eapRespData = wpabuf_alloc_ext_data(eap, eap_len);
+       if (sess->eap_if->eapRespData == NULL)
+               os_free(eap);
+       eap = NULL;
+       sess->eap_if->eapResp = TRUE;
+       eap_server_sm_step(sess->eap);
+
+       if ((sess->eap_if->eapReq || sess->eap_if->eapSuccess ||
+            sess->eap_if->eapFail) && sess->eap_if->eapReqData) {
+               RADIUS_DUMP("EAP data from the state machine",
+                           wpabuf_head(sess->eap_if->eapReqData),
+                           wpabuf_len(sess->eap_if->eapReqData));
+       } else if (sess->eap_if->eapFail) {
+               RADIUS_DEBUG("No EAP data from the state machine, but eapFail "
+                            "set");
+       } else if (eap_sm_method_pending(sess->eap)) {
+               radius_msg_free(sess->last_msg);
+               sess->last_msg = msg;
+               sess->last_from_port = from_port;
+               os_free(sess->last_from_addr);
+               sess->last_from_addr = os_strdup(from_addr);
+               sess->last_fromlen = fromlen;
+               os_memcpy(&sess->last_from, from, fromlen);
+               return -2;
+       } else {
+               RADIUS_DEBUG("No EAP data from the state machine - ignore this"
+                            " Access-Request silently (assuming it was a "
+                            "duplicate)");
+               data->counters.packets_dropped++;
+               client->counters.packets_dropped++;
+               return -1;
+       }
+
+       if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
+               is_complete = 1;
+
+       reply = radius_server_encapsulate_eap(data, client, sess, msg);
+
+       if (reply) {
+               struct wpabuf *buf;
+               struct radius_hdr *hdr;
+
+               RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port);
+               if (wpa_debug_level <= MSG_MSGDUMP) {
+                       radius_msg_dump(reply);
+               }
+
+               switch (radius_msg_get_hdr(reply)->code) {
+               case RADIUS_CODE_ACCESS_ACCEPT:
+                       data->counters.access_accepts++;
+                       client->counters.access_accepts++;
+                       break;
+               case RADIUS_CODE_ACCESS_REJECT:
+                       data->counters.access_rejects++;
+                       client->counters.access_rejects++;
+                       break;
+               case RADIUS_CODE_ACCESS_CHALLENGE:
+                       data->counters.access_challenges++;
+                       client->counters.access_challenges++;
+                       break;
+               }
+               buf = radius_msg_get_buf(reply);
+               res = sendto(data->auth_sock, wpabuf_head(buf),
+                            wpabuf_len(buf), 0,
+                            (struct sockaddr *) from, fromlen);
+               if (res < 0) {
+                       perror("sendto[RADIUS SRV]");
+               }
+               radius_msg_free(sess->last_reply);
+               sess->last_reply = reply;
+               sess->last_from_port = from_port;
+               hdr = radius_msg_get_hdr(msg);
+               sess->last_identifier = hdr->identifier;
+               os_memcpy(sess->last_authenticator, hdr->authenticator, 16);
+       } else {
+               data->counters.packets_dropped++;
+               client->counters.packets_dropped++;
+       }
+
+       if (is_complete) {
+               RADIUS_DEBUG("Removing completed session 0x%x after timeout",
+                            sess->sess_id);
+               eloop_cancel_timeout(radius_server_session_remove_timeout,
+                                    data, sess);
+               eloop_register_timeout(10, 0,
+                                      radius_server_session_remove_timeout,
+                                      data, sess);
+       }
+
+       return 0;
+}
+
+
+static void radius_server_receive_auth(int sock, void *eloop_ctx,
+                                      void *sock_ctx)
+{
+       struct radius_server_data *data = eloop_ctx;
+       u8 *buf = NULL;
+       union {
+               struct sockaddr_storage ss;
+               struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+               struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+       } from;
+       socklen_t fromlen;
+       int len;
+       struct radius_client *client = NULL;
+       struct radius_msg *msg = NULL;
+       char abuf[50];
+       int from_port = 0;
+
+       buf = os_malloc(RADIUS_MAX_MSG_LEN);
+       if (buf == NULL) {
+               goto fail;
+       }
+
+       fromlen = sizeof(from);
+       len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
+                      (struct sockaddr *) &from.ss, &fromlen);
+       if (len < 0) {
+               perror("recvfrom[radius_server]");
+               goto fail;
+       }
+
+#ifdef CONFIG_IPV6
+       if (data->ipv6) {
+               if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
+                             sizeof(abuf)) == NULL)
+                       abuf[0] = '\0';
+               from_port = ntohs(from.sin6.sin6_port);
+               RADIUS_DEBUG("Received %d bytes from %s:%d",
+                            len, abuf, from_port);
+
+               client = radius_server_get_client(data,
+                                                 (struct in_addr *)
+                                                 &from.sin6.sin6_addr, 1);
+       }
+#endif /* CONFIG_IPV6 */
+
+       if (!data->ipv6) {
+               os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+               from_port = ntohs(from.sin.sin_port);
+               RADIUS_DEBUG("Received %d bytes from %s:%d",
+                            len, abuf, from_port);
+
+               client = radius_server_get_client(data, &from.sin.sin_addr, 0);
+       }
+
+       RADIUS_DUMP("Received data", buf, len);
+
+       if (client == NULL) {
+               RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
+               data->counters.invalid_requests++;
+               goto fail;
+       }
+
+       msg = radius_msg_parse(buf, len);
+       if (msg == NULL) {
+               RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+               data->counters.malformed_access_requests++;
+               client->counters.malformed_access_requests++;
+               goto fail;
+       }
+
+       os_free(buf);
+       buf = NULL;
+
+       if (wpa_debug_level <= MSG_MSGDUMP) {
+               radius_msg_dump(msg);
+       }
+
+       if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) {
+               RADIUS_DEBUG("Unexpected RADIUS code %d",
+                            radius_msg_get_hdr(msg)->code);
+               data->counters.unknown_types++;
+               client->counters.unknown_types++;
+               goto fail;
+       }
+
+       data->counters.access_requests++;
+       client->counters.access_requests++;
+
+       if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret,
+                                      client->shared_secret_len, NULL)) {
+               RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf);
+               data->counters.bad_authenticators++;
+               client->counters.bad_authenticators++;
+               goto fail;
+       }
+
+       if (radius_server_request(data, msg, (struct sockaddr *) &from,
+                                 fromlen, client, abuf, from_port, NULL) ==
+           -2)
+               return; /* msg was stored with the session */
+
+fail:
+       radius_msg_free(msg);
+       os_free(buf);
+}
+
+
+static int radius_server_disable_pmtu_discovery(int s)
+{
+       int r = -1;
+#if defined(IP_MTU_DISCOVER) && defined(IP_PMTUDISC_DONT)
+       /* Turn off Path MTU discovery on IPv4/UDP sockets. */
+       int action = IP_PMTUDISC_DONT;
+       r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
+                      sizeof(action));
+       if (r == -1)
+               wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
+                          "%s", strerror(errno));
+#endif
+       return r;
+}
+
+
+static int radius_server_open_socket(int port)
+{
+       int s;
+       struct sockaddr_in addr;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket");
+               return -1;
+       }
+
+       radius_server_disable_pmtu_discovery(s);
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons(port);
+       if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind");
+               close(s);
+               return -1;
+       }
+
+       return s;
+}
+
+
+#ifdef CONFIG_IPV6
+static int radius_server_open_socket6(int port)
+{
+       int s;
+       struct sockaddr_in6 addr;
+
+       s = socket(PF_INET6, SOCK_DGRAM, 0);
+       if (s < 0) {
+               perror("socket[IPv6]");
+               return -1;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin6_family = AF_INET6;
+       os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
+       addr.sin6_port = htons(port);
+       if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind");
+               close(s);
+               return -1;
+       }
+
+       return s;
+}
+#endif /* CONFIG_IPV6 */
+
+
+static void radius_server_free_sessions(struct radius_server_data *data,
+                                       struct radius_session *sessions)
+{
+       struct radius_session *session, *prev;
+
+       session = sessions;
+       while (session) {
+               prev = session;
+               session = session->next;
+               radius_server_session_free(data, prev);
+       }
+}
+
+
+static void radius_server_free_clients(struct radius_server_data *data,
+                                      struct radius_client *clients)
+{
+       struct radius_client *client, *prev;
+
+       client = clients;
+       while (client) {
+               prev = client;
+               client = client->next;
+
+               radius_server_free_sessions(data, prev->sessions);
+               os_free(prev->shared_secret);
+               os_free(prev);
+       }
+}
+
+
+static struct radius_client *
+radius_server_read_clients(const char *client_file, int ipv6)
+{
+       FILE *f;
+       const int buf_size = 1024;
+       char *buf, *pos;
+       struct radius_client *clients, *tail, *entry;
+       int line = 0, mask, failed = 0, i;
+       struct in_addr addr;
+#ifdef CONFIG_IPV6
+       struct in6_addr addr6;
+#endif /* CONFIG_IPV6 */
+       unsigned int val;
+
+       f = fopen(client_file, "r");
+       if (f == NULL) {
+               RADIUS_ERROR("Could not open client file '%s'", client_file);
+               return NULL;
+       }
+
+       buf = os_malloc(buf_size);
+       if (buf == NULL) {
+               fclose(f);
+               return NULL;
+       }
+
+       clients = tail = NULL;
+       while (fgets(buf, buf_size, f)) {
+               /* Configuration file format:
+                * 192.168.1.0/24 secret
+                * 192.168.1.2 secret
+                * fe80::211:22ff:fe33:4455/64 secretipv6
+                */
+               line++;
+               buf[buf_size - 1] = '\0';
+               pos = buf;
+               while (*pos != '\0' && *pos != '\n')
+                       pos++;
+               if (*pos == '\n')
+                       *pos = '\0';
+               if (*buf == '\0' || *buf == '#')
+                       continue;
+
+               pos = buf;
+               while ((*pos >= '0' && *pos <= '9') || *pos == '.' ||
+                      (*pos >= 'a' && *pos <= 'f') || *pos == ':' ||
+                      (*pos >= 'A' && *pos <= 'F')) {
+                       pos++;
+               }
+
+               if (*pos == '\0') {
+                       failed = 1;
+                       break;
+               }
+
+               if (*pos == '/') {
+                       char *end;
+                       *pos++ = '\0';
+                       mask = strtol(pos, &end, 10);
+                       if ((pos == end) ||
+                           (mask < 0 || mask > (ipv6 ? 128 : 32))) {
+                               failed = 1;
+                               break;
+                       }
+                       pos = end;
+               } else {
+                       mask = ipv6 ? 128 : 32;
+                       *pos++ = '\0';
+               }
+
+               if (!ipv6 && inet_aton(buf, &addr) == 0) {
+                       failed = 1;
+                       break;
+               }
+#ifdef CONFIG_IPV6
+               if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) {
+                       if (inet_pton(AF_INET, buf, &addr) <= 0) {
+                               failed = 1;
+                               break;
+                       }
+                       /* Convert IPv4 address to IPv6 */
+                       if (mask <= 32)
+                               mask += (128 - 32);
+                       os_memset(addr6.s6_addr, 0, 10);
+                       addr6.s6_addr[10] = 0xff;
+                       addr6.s6_addr[11] = 0xff;
+                       os_memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr,
+                                 4);
+               }
+#endif /* CONFIG_IPV6 */
+
+               while (*pos == ' ' || *pos == '\t') {
+                       pos++;
+               }
+
+               if (*pos == '\0') {
+                       failed = 1;
+                       break;
+               }
+
+               entry = os_zalloc(sizeof(*entry));
+               if (entry == NULL) {
+                       failed = 1;
+                       break;
+               }
+               entry->shared_secret = os_strdup(pos);
+               if (entry->shared_secret == NULL) {
+                       failed = 1;
+                       os_free(entry);
+                       break;
+               }
+               entry->shared_secret_len = os_strlen(entry->shared_secret);
+               entry->addr.s_addr = addr.s_addr;
+               if (!ipv6) {
+                       val = 0;
+                       for (i = 0; i < mask; i++)
+                               val |= 1 << (31 - i);
+                       entry->mask.s_addr = htonl(val);
+               }
+#ifdef CONFIG_IPV6
+               if (ipv6) {
+                       int offset = mask / 8;
+
+                       os_memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16);
+                       os_memset(entry->mask6.s6_addr, 0xff, offset);
+                       val = 0;
+                       for (i = 0; i < (mask % 8); i++)
+                               val |= 1 << (7 - i);
+                       if (offset < 16)
+                               entry->mask6.s6_addr[offset] = val;
+               }
+#endif /* CONFIG_IPV6 */
+
+               if (tail == NULL) {
+                       clients = tail = entry;
+               } else {
+                       tail->next = entry;
+                       tail = entry;
+               }
+       }
+
+       if (failed) {
+               RADIUS_ERROR("Invalid line %d in '%s'", line, client_file);
+               radius_server_free_clients(NULL, clients);
+               clients = NULL;
+       }
+
+       os_free(buf);
+       fclose(f);
+
+       return clients;
+}
+
+
+/**
+ * radius_server_init - Initialize RADIUS server
+ * @conf: Configuration for the RADIUS server
+ * Returns: Pointer to private RADIUS server context or %NULL on failure
+ *
+ * This initializes a RADIUS server instance and returns a context pointer that
+ * will be used in other calls to the RADIUS server module. The server can be
+ * deinitialize by calling radius_server_deinit().
+ */
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf)
+{
+       struct radius_server_data *data;
+
+#ifndef CONFIG_IPV6
+       if (conf->ipv6) {
+               fprintf(stderr, "RADIUS server compiled without IPv6 "
+                       "support.\n");
+               return NULL;
+       }
+#endif /* CONFIG_IPV6 */
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       os_get_time(&data->start_time);
+       data->conf_ctx = conf->conf_ctx;
+       data->eap_sim_db_priv = conf->eap_sim_db_priv;
+       data->ssl_ctx = conf->ssl_ctx;
+       data->msg_ctx = conf->msg_ctx;
+       data->ipv6 = conf->ipv6;
+       if (conf->pac_opaque_encr_key) {
+               data->pac_opaque_encr_key = os_malloc(16);
+               os_memcpy(data->pac_opaque_encr_key, conf->pac_opaque_encr_key,
+                         16);
+       }
+       if (conf->eap_fast_a_id) {
+               data->eap_fast_a_id = os_malloc(conf->eap_fast_a_id_len);
+               if (data->eap_fast_a_id) {
+                       os_memcpy(data->eap_fast_a_id, conf->eap_fast_a_id,
+                                 conf->eap_fast_a_id_len);
+                       data->eap_fast_a_id_len = conf->eap_fast_a_id_len;
+               }
+       }
+       if (conf->eap_fast_a_id_info)
+               data->eap_fast_a_id_info = os_strdup(conf->eap_fast_a_id_info);
+       data->eap_fast_prov = conf->eap_fast_prov;
+       data->pac_key_lifetime = conf->pac_key_lifetime;
+       data->pac_key_refresh_time = conf->pac_key_refresh_time;
+       data->get_eap_user = conf->get_eap_user;
+       data->eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind;
+       data->tnc = conf->tnc;
+       data->wps = conf->wps;
+       if (conf->eap_req_id_text) {
+               data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
+               if (data->eap_req_id_text) {
+                       os_memcpy(data->eap_req_id_text, conf->eap_req_id_text,
+                                 conf->eap_req_id_text_len);
+                       data->eap_req_id_text_len = conf->eap_req_id_text_len;
+               }
+       }
+
+       data->clients = radius_server_read_clients(conf->client_file,
+                                                  conf->ipv6);
+       if (data->clients == NULL) {
+               printf("No RADIUS clients configured.\n");
+               radius_server_deinit(data);
+               return NULL;
+       }
+
+#ifdef CONFIG_IPV6
+       if (conf->ipv6)
+               data->auth_sock = radius_server_open_socket6(conf->auth_port);
+       else
+#endif /* CONFIG_IPV6 */
+       data->auth_sock = radius_server_open_socket(conf->auth_port);
+       if (data->auth_sock < 0) {
+               printf("Failed to open UDP socket for RADIUS authentication "
+                      "server\n");
+               radius_server_deinit(data);
+               return NULL;
+       }
+       if (eloop_register_read_sock(data->auth_sock,
+                                    radius_server_receive_auth,
+                                    data, NULL)) {
+               radius_server_deinit(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+/**
+ * radius_server_deinit - Deinitialize RADIUS server
+ * @data: RADIUS server context from radius_server_init()
+ */
+void radius_server_deinit(struct radius_server_data *data)
+{
+       if (data == NULL)
+               return;
+
+       if (data->auth_sock >= 0) {
+               eloop_unregister_read_sock(data->auth_sock);
+               close(data->auth_sock);
+       }
+
+       radius_server_free_clients(data, data->clients);
+
+       os_free(data->pac_opaque_encr_key);
+       os_free(data->eap_fast_a_id);
+       os_free(data->eap_fast_a_id_info);
+       os_free(data->eap_req_id_text);
+       os_free(data);
+}
+
+
+/**
+ * radius_server_get_mib - Get RADIUS server MIB information
+ * @data: RADIUS server context from radius_server_init()
+ * @buf: Buffer for returning the MIB data in text format
+ * @buflen: buf length in octets
+ * Returns: Number of octets written into buf
+ */
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+                         size_t buflen)
+{
+       int ret, uptime;
+       unsigned int idx;
+       char *end, *pos;
+       struct os_time now;
+       struct radius_client *cli;
+
+       /* RFC 2619 - RADIUS Authentication Server MIB */
+
+       if (data == NULL || buflen == 0)
+               return 0;
+
+       pos = buf;
+       end = buf + buflen;
+
+       os_get_time(&now);
+       uptime = (now.sec - data->start_time.sec) * 100 +
+               ((now.usec - data->start_time.usec) / 10000) % 100;
+       ret = os_snprintf(pos, end - pos,
+                         "RADIUS-AUTH-SERVER-MIB\n"
+                         "radiusAuthServIdent=hostapd\n"
+                         "radiusAuthServUpTime=%d\n"
+                         "radiusAuthServResetTime=0\n"
+                         "radiusAuthServConfigReset=4\n",
+                         uptime);
+       if (ret < 0 || ret >= end - pos) {
+               *pos = '\0';
+               return pos - buf;
+       }
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "radiusAuthServTotalAccessRequests=%u\n"
+                         "radiusAuthServTotalInvalidRequests=%u\n"
+                         "radiusAuthServTotalDupAccessRequests=%u\n"
+                         "radiusAuthServTotalAccessAccepts=%u\n"
+                         "radiusAuthServTotalAccessRejects=%u\n"
+                         "radiusAuthServTotalAccessChallenges=%u\n"
+                         "radiusAuthServTotalMalformedAccessRequests=%u\n"
+                         "radiusAuthServTotalBadAuthenticators=%u\n"
+                         "radiusAuthServTotalPacketsDropped=%u\n"
+                         "radiusAuthServTotalUnknownTypes=%u\n",
+                         data->counters.access_requests,
+                         data->counters.invalid_requests,
+                         data->counters.dup_access_requests,
+                         data->counters.access_accepts,
+                         data->counters.access_rejects,
+                         data->counters.access_challenges,
+                         data->counters.malformed_access_requests,
+                         data->counters.bad_authenticators,
+                         data->counters.packets_dropped,
+                         data->counters.unknown_types);
+       if (ret < 0 || ret >= end - pos) {
+               *pos = '\0';
+               return pos - buf;
+       }
+       pos += ret;
+
+       for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) {
+               char abuf[50], mbuf[50];
+#ifdef CONFIG_IPV6
+               if (data->ipv6) {
+                       if (inet_ntop(AF_INET6, &cli->addr6, abuf,
+                                     sizeof(abuf)) == NULL)
+                               abuf[0] = '\0';
+                       if (inet_ntop(AF_INET6, &cli->mask6, abuf,
+                                     sizeof(mbuf)) == NULL)
+                               mbuf[0] = '\0';
+               }
+#endif /* CONFIG_IPV6 */
+               if (!data->ipv6) {
+                       os_strlcpy(abuf, inet_ntoa(cli->addr), sizeof(abuf));
+                       os_strlcpy(mbuf, inet_ntoa(cli->mask), sizeof(mbuf));
+               }
+
+               ret = os_snprintf(pos, end - pos,
+                                 "radiusAuthClientIndex=%u\n"
+                                 "radiusAuthClientAddress=%s/%s\n"
+                                 "radiusAuthServAccessRequests=%u\n"
+                                 "radiusAuthServDupAccessRequests=%u\n"
+                                 "radiusAuthServAccessAccepts=%u\n"
+                                 "radiusAuthServAccessRejects=%u\n"
+                                 "radiusAuthServAccessChallenges=%u\n"
+                                 "radiusAuthServMalformedAccessRequests=%u\n"
+                                 "radiusAuthServBadAuthenticators=%u\n"
+                                 "radiusAuthServPacketsDropped=%u\n"
+                                 "radiusAuthServUnknownTypes=%u\n",
+                                 idx,
+                                 abuf, mbuf,
+                                 cli->counters.access_requests,
+                                 cli->counters.dup_access_requests,
+                                 cli->counters.access_accepts,
+                                 cli->counters.access_rejects,
+                                 cli->counters.access_challenges,
+                                 cli->counters.malformed_access_requests,
+                                 cli->counters.bad_authenticators,
+                                 cli->counters.packets_dropped,
+                                 cli->counters.unknown_types);
+               if (ret < 0 || ret >= end - pos) {
+                       *pos = '\0';
+                       return pos - buf;
+               }
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
+static int radius_server_get_eap_user(void *ctx, const u8 *identity,
+                                     size_t identity_len, int phase2,
+                                     struct eap_user *user)
+{
+       struct radius_session *sess = ctx;
+       struct radius_server_data *data = sess->server;
+
+       return data->get_eap_user(data->conf_ctx, identity, identity_len,
+                                 phase2, user);
+}
+
+
+static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
+{
+       struct radius_session *sess = ctx;
+       struct radius_server_data *data = sess->server;
+       *len = data->eap_req_id_text_len;
+       return data->eap_req_id_text;
+}
+
+
+static struct eapol_callbacks radius_server_eapol_cb =
+{
+       .get_eap_user = radius_server_get_eap_user,
+       .get_eap_req_id_text = radius_server_get_eap_req_id_text,
+};
+
+
+/**
+ * radius_server_eap_pending_cb - Pending EAP data notification
+ * @data: RADIUS server context from radius_server_init()
+ * @ctx: Pending EAP context pointer
+ *
+ * This function is used to notify EAP server module that a pending operation
+ * has been completed and processing of the EAP session can proceed.
+ */
+void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
+{
+       struct radius_client *cli;
+       struct radius_session *s, *sess = NULL;
+       struct radius_msg *msg;
+
+       if (data == NULL)
+               return;
+
+       for (cli = data->clients; cli; cli = cli->next) {
+               for (s = cli->sessions; s; s = s->next) {
+                       if (s->eap == ctx && s->last_msg) {
+                               sess = s;
+                               break;
+                       }
+                       if (sess)
+                               break;
+               }
+               if (sess)
+                       break;
+       }
+
+       if (sess == NULL) {
+               RADIUS_DEBUG("No session matched callback ctx");
+               return;
+       }
+
+       msg = sess->last_msg;
+       sess->last_msg = NULL;
+       eap_sm_pending_cb(sess->eap);
+       if (radius_server_request(data, msg,
+                                 (struct sockaddr *) &sess->last_from,
+                                 sess->last_fromlen, cli,
+                                 sess->last_from_addr,
+                                 sess->last_from_port, sess) == -2)
+               return; /* msg was stored with the session */
+
+       radius_msg_free(msg);
+}
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
new file mode 100644 (file)
index 0000000..f9c951d
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * RADIUS authentication server
+ * Copyright (c) 2005-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.
+ */
+
+#ifndef RADIUS_SERVER_H
+#define RADIUS_SERVER_H
+
+struct radius_server_data;
+struct eap_user;
+
+/**
+ * struct radius_server_conf - RADIUS server configuration
+ */
+struct radius_server_conf {
+       /**
+        * auth_port - UDP port to listen to as an authentication server
+        */
+       int auth_port;
+
+       /**
+        * client_file - RADIUS client configuration file
+        *
+        * This file contains the RADIUS clients and the shared secret to be
+        * used with them in a format where each client is on its own line. The
+        * first item on the line is the IPv4 or IPv6 address of the client
+        * with an optional address mask to allow full network to be specified
+        * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white
+        * space (space or tabulator) and the shared secret. Lines starting
+        * with '#' are skipped and can be used as comments.
+        */
+       char *client_file;
+
+       /**
+        * conf_ctx - Context pointer for callbacks
+        *
+        * This is used as the ctx argument in get_eap_user() calls.
+        */
+       void *conf_ctx;
+
+       /**
+        * eap_sim_db_priv - EAP-SIM/AKA database context
+        *
+        * This is passed to the EAP-SIM/AKA server implementation as a
+        * callback context.
+        */
+       void *eap_sim_db_priv;
+
+       /**
+        * ssl_ctx - TLS context
+        *
+        * This is passed to the EAP server implementation as a callback
+        * context for TLS operations.
+        */
+       void *ssl_ctx;
+
+       /**
+        * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST
+        *
+        * This parameter is used to set a key for EAP-FAST to encrypt the
+        * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If
+        * set, must point to a 16-octet key.
+        */
+       u8 *pac_opaque_encr_key;
+
+       /**
+        * eap_fast_a_id - EAP-FAST authority identity (A-ID)
+        *
+        * If EAP-FAST is not used, this can be set to %NULL. In theory, this
+        * is a variable length field, but due to some existing implementations
+        * requiring A-ID to be 16 octets in length, it is recommended to use
+        * that length for the field to provide interoperability with deployed
+        * peer implementations.
+        */
+       u8 *eap_fast_a_id;
+
+       /**
+        * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets
+        */
+       size_t eap_fast_a_id_len;
+
+       /**
+        * eap_fast_a_id_info - EAP-FAST authority identifier information
+        *
+        * This A-ID-Info contains a user-friendly name for the A-ID. For
+        * example, this could be the enterprise and server names in
+        * human-readable format. This field is encoded as UTF-8. If EAP-FAST
+        * is not used, this can be set to %NULL.
+        */
+       char *eap_fast_a_id_info;
+
+       /**
+        * eap_fast_prov - EAP-FAST provisioning modes
+        *
+        * 0 = provisioning disabled, 1 = only anonymous provisioning allowed,
+        * 2 = only authenticated provisioning allowed, 3 = both provisioning
+        * modes allowed.
+        */
+       int eap_fast_prov;
+
+       /**
+        * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds
+        *
+        * This is the hard limit on how long a provisioned PAC-Key can be
+        * used.
+        */
+       int pac_key_lifetime;
+
+       /**
+        * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds
+        *
+        * This is a soft limit on the PAC-Key. The server will automatically
+        * generate a new PAC-Key when this number of seconds (or fewer) of the
+        * lifetime remains.
+        */
+       int pac_key_refresh_time;
+
+       /**
+        * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication
+        *
+        * This controls whether the protected success/failure indication
+        * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA.
+        */
+       int eap_sim_aka_result_ind;
+
+       /**
+        * tnc - Trusted Network Connect (TNC)
+        *
+        * This controls whether TNC is enabled and 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.
+        */
+       int tnc;
+
+       /**
+        * wps - Wi-Fi Protected Setup context
+        *
+        * If WPS is used with an external RADIUS server (which is quite
+        * unlikely configuration), this is used to provide a pointer to WPS
+        * context data. Normally, this can be set to %NULL.
+        */
+       struct wps_context *wps;
+
+       /**
+        * ipv6 - Whether to enable IPv6 support in the RADIUS server
+        */
+       int ipv6;
+
+       /**
+        * get_eap_user - Callback for fetching EAP user information
+        * @ctx: Context data from conf_ctx
+        * @identity: User identity
+        * @identity_len: identity buffer length in octets
+        * @phase2: Whether this is for Phase 2 identity
+        * @user: Data structure for filling in the user information
+        * Returns: 0 on success, -1 on failure
+        *
+        * This is used to fetch information from user database. The callback
+        * will fill in information about allowed EAP methods and the user
+        * password. The password field will be an allocated copy of the
+        * password data and RADIUS server will free it after use.
+        */
+       int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
+                           int phase2, struct eap_user *user);
+
+       /**
+        * eap_req_id_text - Optional data for EAP-Request/Identity
+        *
+        * This can be used to configure an optional, displayable message that
+        * will be sent in EAP-Request/Identity. This string can contain an
+        * ASCII-0 character (nul) to separate network infromation per RFC
+        * 4284. The actual string length is explicit provided in
+        * eap_req_id_text_len since nul character will not be used as a string
+        * terminator.
+        */
+       const char *eap_req_id_text;
+
+       /**
+        * eap_req_id_text_len - Length of eap_req_id_text buffer in octets
+        */
+       size_t eap_req_id_text_len;
+
+       /*
+        * msg_ctx - Context data for wpa_msg() calls
+        */
+       void *msg_ctx;
+};
+
+
+struct radius_server_data *
+radius_server_init(struct radius_server_conf *conf);
+
+void radius_server_deinit(struct radius_server_data *data);
+
+int radius_server_get_mib(struct radius_server_data *data, char *buf,
+                         size_t buflen);
+
+void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx);
+
+#endif /* RADIUS_SERVER_H */
diff --git a/src/rsn_supp/Makefile b/src/rsn_supp/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/rsn_supp/peerkey.c b/src/rsn_supp/peerkey.c
new file mode 100644 (file)
index 0000000..9d60d4a
--- /dev/null
@@ -0,0 +1,1184 @@
+/*
+ * WPA Supplicant - 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"
+
+#ifdef CONFIG_PEERKEY
+
+#include "common.h"
+#include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+
+
+static u8 * wpa_add_ie(u8 *pos, const u8 *ie, size_t ie_len)
+{
+       os_memcpy(pos, ie, ie_len);
+       return pos + ie_len;
+}
+
+
+static u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len)
+{
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       *pos++ = RSN_SELECTOR_LEN + data_len;
+       RSN_SELECTOR_PUT(pos, kde);
+       pos += RSN_SELECTOR_LEN;
+       os_memcpy(pos, data, data_len);
+       pos += data_len;
+       return pos;
+}
+
+
+static void wpa_supplicant_smk_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+#if 0
+       struct wpa_sm *sm = eloop_ctx;
+       struct wpa_peerkey *peerkey = timeout_ctx;
+#endif
+       /* TODO: time out SMK and any STK that was generated using this SMK */
+}
+
+
+static void wpa_supplicant_peerkey_free(struct wpa_sm *sm,
+                                       struct wpa_peerkey *peerkey)
+{
+       eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+       os_free(peerkey);
+}
+
+
+static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
+                                        const u8 *peer,
+                                        u16 mui, u16 error_type, int ver)
+{
+       size_t rlen;
+       struct wpa_eapol_key *err;
+       struct rsn_error_kde error;
+       u8 *rbuf, *pos;
+       size_t kde_len;
+       u16 key_info;
+
+       kde_len = 2 + RSN_SELECTOR_LEN + sizeof(error);
+       if (peer)
+               kde_len += 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+                                 NULL, sizeof(*err) + kde_len, &rlen,
+                                 (void *) &err);
+       if (rbuf == NULL)
+               return -1;
+
+       err->type = EAPOL_KEY_TYPE_RSN;
+       key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+               WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ERROR |
+               WPA_KEY_INFO_REQUEST;
+       WPA_PUT_BE16(err->key_info, key_info);
+       WPA_PUT_BE16(err->key_length, 0);
+       os_memcpy(err->replay_counter, sm->request_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(err->key_data_length, (u16) kde_len);
+       pos = (u8 *) (err + 1);
+
+       if (peer) {
+               /* Peer MAC Address KDE */
+               pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+       }
+
+       /* Error KDE */
+       error.mui = host_to_be16(mui);
+       error.error_type = host_to_be16(error_type);
+       wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error));
+
+       if (peer) {
+               wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error (peer "
+                          MACSTR " mui %d error_type %d)",
+                          MAC2STR(peer), mui, error_type);
+       } else {
+               wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK Error "
+                          "(mui %d error_type %d)", mui, error_type);
+       }
+
+       wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL,
+                          rbuf, rlen, err->key_mic);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
+                                     const unsigned char *src_addr,
+                                     const struct wpa_eapol_key *key,
+                                     int ver, struct wpa_peerkey *peerkey)
+{
+       size_t rlen;
+       struct wpa_eapol_key *reply;
+       u8 *rbuf, *pos;
+       size_t kde_len;
+       u16 key_info;
+
+       /* KDEs: Peer RSN IE, Initiator MAC Address, Initiator Nonce */
+       kde_len = peerkey->rsnie_p_len +
+               2 + RSN_SELECTOR_LEN + ETH_ALEN +
+               2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN;
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+                                 NULL, sizeof(*reply) + kde_len, &rlen,
+                                 (void *) &reply);
+       if (rbuf == NULL)
+               return -1;
+
+       reply->type = EAPOL_KEY_TYPE_RSN;
+       key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+               WPA_KEY_INFO_SECURE;
+       WPA_PUT_BE16(reply->key_info, key_info);
+       WPA_PUT_BE16(reply->key_length, 0);
+       os_memcpy(reply->replay_counter, key->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+
+       os_memcpy(reply->key_nonce, peerkey->pnonce, WPA_NONCE_LEN);
+
+       WPA_PUT_BE16(reply->key_data_length, (u16) kde_len);
+       pos = (u8 *) (reply + 1);
+
+       /* Peer RSN IE */
+       pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+       /* Initiator MAC Address KDE */
+       pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peerkey->addr, ETH_ALEN);
+
+       /* Initiator Nonce */
+       wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
+
+       wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
+       wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL,
+                          rbuf, rlen, reply->key_mic);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m2(
+       struct wpa_sm *sm, const unsigned char *src_addr,
+       const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+       struct wpa_peerkey *peerkey;
+       struct wpa_eapol_ie_parse kde;
+       struct wpa_ie_data ie;
+       int cipher;
+       struct rsn_ie_hdr *hdr;
+       u8 *pos;
+
+       wpa_printf(MSG_DEBUG, "RSN: Received SMK M2");
+
+       if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+               wpa_printf(MSG_INFO, "RSN: SMK handshake not allowed for "
+                          "the current network");
+               return -1;
+       }
+
+       if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+           0) {
+               wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M2");
+               return -1;
+       }
+
+       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 M2");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "RSN: SMK M2 - SMK initiator " MACSTR,
+                  MAC2STR(kde.mac_addr));
+
+       if (kde.rsn_ie_len > PEERKEY_MAX_IE_LEN) {
+               wpa_printf(MSG_INFO, "RSN: Too long Initiator RSN IE in SMK "
+                          "M2");
+               return -1;
+       }
+
+       if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
+               wpa_printf(MSG_INFO, "RSN: Failed to parse RSN IE in SMK M2");
+               return -1;
+       }
+
+       cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
+       if (cipher & WPA_CIPHER_CCMP) {
+               wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
+               cipher = WPA_CIPHER_CCMP;
+       } else if (cipher & WPA_CIPHER_TKIP) {
+               wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
+               cipher = WPA_CIPHER_TKIP;
+       } else {
+               wpa_printf(MSG_INFO, "RSN: No acceptable cipher in SMK M2");
+               wpa_supplicant_send_smk_error(sm, src_addr, kde.mac_addr,
+                                             STK_MUI_SMK, STK_ERR_CPHR_NS,
+                                             ver);
+               return -1;
+       }
+
+       /* TODO: find existing entry and if found, use that instead of adding
+        * a new one; how to handle the case where both ends initiate at the
+        * same time? */
+       peerkey = os_zalloc(sizeof(*peerkey));
+       if (peerkey == NULL)
+               return -1;
+       os_memcpy(peerkey->addr, kde.mac_addr, ETH_ALEN);
+       os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+       os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
+       peerkey->rsnie_i_len = kde.rsn_ie_len;
+       peerkey->cipher = cipher;
+#ifdef CONFIG_IEEE80211W
+       if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                          WPA_KEY_MGMT_PSK_SHA256))
+               peerkey->use_sha256 = 1;
+#endif /* CONFIG_IEEE80211W */
+
+       if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Failed to get random data for PNonce");
+               wpa_supplicant_peerkey_free(sm, peerkey);
+               return -1;
+       }
+
+       hdr = (struct rsn_ie_hdr *) peerkey->rsnie_p;
+       hdr->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(hdr->version, RSN_VERSION);
+       pos = (u8 *) (hdr + 1);
+       /* Group Suite can be anything for SMK RSN IE; receiver will just
+        * ignore it. */
+       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       pos += RSN_SELECTOR_LEN;
+       /* Include only the selected cipher in pairwise cipher suite */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+       if (cipher == WPA_CIPHER_CCMP)
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       else if (cipher == WPA_CIPHER_TKIP)
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+       pos += RSN_SELECTOR_LEN;
+
+       hdr->len = (pos - peerkey->rsnie_p) - 2;
+       peerkey->rsnie_p_len = pos - peerkey->rsnie_p;
+       wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+                   peerkey->rsnie_p, peerkey->rsnie_p_len);
+
+       wpa_supplicant_send_smk_m3(sm, src_addr, key, ver, peerkey);
+
+       peerkey->next = sm->peerkey;
+       sm->peerkey = peerkey;
+
+       return 0;
+}
+
+
+/**
+ * rsn_smkid - Derive SMK identifier
+ * @smk: Station master key (32 bytes)
+ * @pnonce: Peer Nonce
+ * @mac_p: Peer MAC address
+ * @inonce: Initiator Nonce
+ * @mac_i: Initiator MAC address
+ * @use_sha256: Whether to use SHA256-based KDF
+ *
+ * 8.5.1.4 Station to station (STK) key hierarchy
+ * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
+ */
+static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
+                     const u8 *inonce, const u8 *mac_i, u8 *smkid,
+                     int use_sha256)
+{
+       char *title = "SMK Name";
+       const u8 *addr[5];
+       const size_t len[5] = { 8, WPA_NONCE_LEN, ETH_ALEN, WPA_NONCE_LEN,
+                               ETH_ALEN };
+       unsigned char hash[SHA256_MAC_LEN];
+
+       addr[0] = (u8 *) title;
+       addr[1] = pnonce;
+       addr[2] = mac_p;
+       addr[3] = inonce;
+       addr[4] = mac_i;
+
+#ifdef CONFIG_IEEE80211W
+       if (use_sha256)
+               hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
+       else
+#endif /* CONFIG_IEEE80211W */
+               hmac_sha1_vector(smk, PMK_LEN, 5, addr, len, hash);
+       os_memcpy(smkid, hash, PMKID_LEN);
+}
+
+
+static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
+                                          struct wpa_peerkey *peerkey)
+{
+       size_t mlen;
+       struct wpa_eapol_key *msg;
+       u8 *mbuf;
+       size_t kde_len;
+       u16 key_info, ver;
+
+       kde_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
+
+       mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+                                 sizeof(*msg) + kde_len, &mlen,
+                                 (void *) &msg);
+       if (mbuf == NULL)
+               return;
+
+       msg->type = EAPOL_KEY_TYPE_RSN;
+
+       if (peerkey->cipher == WPA_CIPHER_CCMP)
+               ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+       else
+               ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+       key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK;
+       WPA_PUT_BE16(msg->key_info, key_info);
+
+       if (peerkey->cipher == WPA_CIPHER_CCMP)
+               WPA_PUT_BE16(msg->key_length, 16);
+       else
+               WPA_PUT_BE16(msg->key_length, 32);
+
+       os_memcpy(msg->replay_counter, peerkey->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(msg->key_data_length, kde_len);
+       wpa_add_kde((u8 *) (msg + 1), RSN_KEY_DATA_PMKID,
+                   peerkey->smkid, PMKID_LEN);
+
+       if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "RSN: Failed to get random data for INonce (STK)");
+               os_free(mbuf);
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "RSN: INonce for STK 4-Way Handshake",
+                   peerkey->inonce, WPA_NONCE_LEN);
+       os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+       wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
+                  MAC2STR(peerkey->addr));
+       wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
+                          mbuf, mlen, NULL);
+}
+
+
+static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
+                                          struct wpa_peerkey *peerkey)
+{
+       size_t mlen;
+       struct wpa_eapol_key *msg;
+       u8 *mbuf, *pos;
+       size_t kde_len;
+       u16 key_info, ver;
+       be32 lifetime;
+
+       kde_len = peerkey->rsnie_i_len +
+               2 + RSN_SELECTOR_LEN + sizeof(lifetime);
+
+       mbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+                                 sizeof(*msg) + kde_len, &mlen,
+                                 (void *) &msg);
+       if (mbuf == NULL)
+               return;
+
+       msg->type = EAPOL_KEY_TYPE_RSN;
+
+       if (peerkey->cipher == WPA_CIPHER_CCMP)
+               ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+       else
+               ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+       key_info = ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_ACK |
+               WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+       WPA_PUT_BE16(msg->key_info, key_info);
+
+       if (peerkey->cipher == WPA_CIPHER_CCMP)
+               WPA_PUT_BE16(msg->key_length, 16);
+       else
+               WPA_PUT_BE16(msg->key_length, 32);
+
+       os_memcpy(msg->replay_counter, peerkey->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       inc_byte_array(peerkey->replay_counter, WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(msg->key_data_length, kde_len);
+       pos = (u8 *) (msg + 1);
+       pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+       lifetime = host_to_be32(peerkey->lifetime);
+       wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+                   (u8 *) &lifetime, sizeof(lifetime));
+
+       os_memcpy(msg->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+
+       wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
+                  MAC2STR(peerkey->addr));
+       wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr,
+                          ETH_P_EAPOL, mbuf, mlen, msg->key_mic);
+}
+
+
+static int wpa_supplicant_process_smk_m4(struct wpa_peerkey *peerkey,
+                                        struct wpa_eapol_ie_parse *kde)
+{
+       wpa_printf(MSG_DEBUG, "RSN: Received SMK M4 (Initiator " MACSTR ")",
+                  MAC2STR(kde->mac_addr));
+
+       if (os_memcmp(kde->smk + PMK_LEN, peerkey->pnonce, WPA_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_INFO, "RSN: PNonce in SMK KDE does not "
+                          "match with the one used in SMK M3");
+               return -1;
+       }
+
+       if (os_memcmp(kde->nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_INFO, "RSN: INonce in SMK M4 did not "
+                          "match with the one received in SMK M2");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m5(struct wpa_sm *sm,
+                                        const unsigned char *src_addr,
+                                        const struct wpa_eapol_key *key,
+                                        int ver,
+                                        struct wpa_peerkey *peerkey,
+                                        struct wpa_eapol_ie_parse *kde)
+{
+       int cipher;
+       struct wpa_ie_data ie;
+
+       wpa_printf(MSG_DEBUG, "RSN: Received SMK M5 (Peer " MACSTR ")",
+                  MAC2STR(kde->mac_addr));
+       if (kde->rsn_ie == NULL || kde->rsn_ie_len > PEERKEY_MAX_IE_LEN ||
+           wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0) {
+               wpa_printf(MSG_INFO, "RSN: No RSN IE in SMK M5");
+               /* TODO: abort negotiation */
+               return -1;
+       }
+
+       if (os_memcmp(key->key_nonce, peerkey->inonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_INFO, "RSN: Key Nonce in SMK M5 does "
+                          "not match with INonce used in SMK M1");
+               return -1;
+       }
+
+       if (os_memcmp(kde->smk + PMK_LEN, peerkey->inonce, WPA_NONCE_LEN) != 0)
+       {
+               wpa_printf(MSG_INFO, "RSN: INonce in SMK KDE does not "
+                          "match with the one used in SMK M1");
+               return -1;
+       }
+
+       os_memcpy(peerkey->rsnie_p, kde->rsn_ie, kde->rsn_ie_len);
+       peerkey->rsnie_p_len = kde->rsn_ie_len;
+       os_memcpy(peerkey->pnonce, kde->nonce, WPA_NONCE_LEN);
+
+       cipher = ie.pairwise_cipher & sm->allowed_pairwise_cipher;
+       if (cipher & WPA_CIPHER_CCMP) {
+               wpa_printf(MSG_DEBUG, "RSN: Using CCMP for PeerKey");
+               peerkey->cipher = WPA_CIPHER_CCMP;
+       } else if (cipher & WPA_CIPHER_TKIP) {
+               wpa_printf(MSG_DEBUG, "RSN: Using TKIP for PeerKey");
+               peerkey->cipher = WPA_CIPHER_TKIP;
+       } else {
+               wpa_printf(MSG_INFO, "RSN: SMK Peer STA " MACSTR " selected "
+                          "unacceptable cipher", MAC2STR(kde->mac_addr));
+               wpa_supplicant_send_smk_error(sm, src_addr, kde->mac_addr,
+                                             STK_MUI_SMK, STK_ERR_CPHR_NS,
+                                             ver);
+               /* TODO: abort negotiation */
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_process_smk_m45(
+       struct wpa_sm *sm, const unsigned char *src_addr,
+       const struct wpa_eapol_key *key, size_t extra_len, int ver)
+{
+       struct wpa_peerkey *peerkey;
+       struct wpa_eapol_ie_parse kde;
+       u32 lifetime;
+       struct os_time now;
+
+       if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+               wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+                          "the current network");
+               return -1;
+       }
+
+       if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+           0) {
+               wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M4/M5");
+               return -1;
+       }
+
+       if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN ||
+           kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN ||
+           kde.smk == NULL || kde.smk_len < PMK_LEN + WPA_NONCE_LEN ||
+           kde.lifetime == NULL || kde.lifetime_len < 4) {
+               wpa_printf(MSG_INFO, "RSN: No MAC Address, Nonce, SMK, or "
+                          "Lifetime KDE in SMK M4/M5");
+               return -1;
+       }
+
+       for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+               if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) == 0 &&
+                   os_memcmp(peerkey->initiator ? peerkey->inonce :
+                          peerkey->pnonce,
+                          key->key_nonce, WPA_NONCE_LEN) == 0)
+                       break;
+       }
+       if (peerkey == NULL) {
+               wpa_printf(MSG_INFO, "RSN: No matching SMK handshake found "
+                          "for SMK M4/M5: peer " MACSTR,
+                          MAC2STR(kde.mac_addr));
+               return -1;
+       }
+
+       if (peerkey->initiator) {
+               if (wpa_supplicant_process_smk_m5(sm, src_addr, key, ver,
+                                                 peerkey, &kde) < 0)
+                       return -1;
+       } else {
+               if (wpa_supplicant_process_smk_m4(peerkey, &kde) < 0)
+                       return -1;
+       }
+
+       os_memcpy(peerkey->smk, kde.smk, PMK_LEN);
+       peerkey->smk_complete = 1;
+       wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", peerkey->smk, PMK_LEN);
+       lifetime = WPA_GET_BE32(kde.lifetime);
+       wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
+       if (lifetime > 1000000000)
+               lifetime = 1000000000; /* avoid overflowing expiration time */
+       peerkey->lifetime = lifetime;
+       os_get_time(&now);
+       peerkey->expiration = now.sec + lifetime;
+       eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+                              sm, peerkey);
+
+       if (peerkey->initiator) {
+               rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
+                         peerkey->inonce, sm->own_addr, peerkey->smkid,
+                         peerkey->use_sha256);
+               wpa_supplicant_send_stk_1_of_4(sm, peerkey);
+       } else {
+               rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
+                         peerkey->inonce, peerkey->addr, peerkey->smkid,
+                         peerkey->use_sha256);
+       }
+       wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_process_smk_error(
+       struct wpa_sm *sm, const unsigned char *src_addr,
+       const struct wpa_eapol_key *key, size_t extra_len)
+{
+       struct wpa_eapol_ie_parse kde;
+       struct rsn_error_kde error;
+       u8 peer[ETH_ALEN];
+       u16 error_type;
+
+       wpa_printf(MSG_DEBUG, "RSN: Received SMK Error");
+
+       if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
+               wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
+                          "the current network");
+               return -1;
+       }
+
+       if (wpa_supplicant_parse_ies((const u8 *) (key + 1), extra_len, &kde) <
+           0) {
+               wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
+               return -1;
+       }
+
+       if (kde.error == NULL || kde.error_len < sizeof(error)) {
+               wpa_printf(MSG_INFO, "RSN: No Error KDE in SMK Error");
+               return -1;
+       }
+
+       if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN)
+               os_memcpy(peer, kde.mac_addr, ETH_ALEN);
+       else
+               os_memset(peer, 0, ETH_ALEN);
+       os_memcpy(&error, kde.error, sizeof(error));
+       error_type = be_to_host16(error.error_type);
+       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+               "RSN: SMK Error KDE received: MUI %d error_type %d peer "
+               MACSTR,
+               be_to_host16(error.mui), error_type,
+               MAC2STR(peer));
+
+       if (kde.mac_addr &&
+           (error_type == STK_ERR_STA_NR || error_type == STK_ERR_STA_NRSN ||
+            error_type == STK_ERR_CPHR_NS)) {
+               struct wpa_peerkey *peerkey;
+
+               for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+                       if (os_memcmp(peerkey->addr, kde.mac_addr, ETH_ALEN) ==
+                           0)
+                               break;
+               }
+               if (peerkey == NULL) {
+                       wpa_printf(MSG_DEBUG, "RSN: No matching SMK handshake "
+                                  "found for SMK Error");
+                       return -1;
+               }
+               /* TODO: abort SMK/STK handshake and remove all related keys */
+       }
+
+       return 0;
+}
+
+
+static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
+                                             struct wpa_peerkey *peerkey,
+                                             const struct wpa_eapol_key *key,
+                                             u16 ver)
+{
+       struct wpa_eapol_ie_parse ie;
+       const u8 *kde;
+       size_t len, kde_buf_len;
+       struct wpa_ptk *stk;
+       u8 buf[8], *kde_buf, *pos;
+       be32 lifetime;
+
+       wpa_printf(MSG_DEBUG, "RSN: RX message 1 of STK 4-Way Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+       os_memset(&ie, 0, sizeof(ie));
+
+       /* RSN: msg 1/4 should contain SMKID for the selected SMK */
+       kde = (const u8 *) (key + 1);
+       len = WPA_GET_BE16(key->key_data_length);
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len);
+       if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) {
+               wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
+               return;
+       }
+       if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+               wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
+                           ie.pmkid, PMKID_LEN);
+               return;
+       }
+
+       if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "RSN: Failed to get random data for PNonce");
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPA: Renewed PNonce",
+                   peerkey->pnonce, WPA_NONCE_LEN);
+
+       /* Calculate STK which will be stored as a temporary STK until it has
+        * been verified when processing message 3/4. */
+       stk = &peerkey->tstk;
+       wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+                      sm->own_addr, peerkey->addr,
+                      peerkey->pnonce, key->key_nonce,
+                      (u8 *) stk, sizeof(*stk),
+                      peerkey->use_sha256);
+       /* Supplicant: swap tx/rx Mic keys */
+       os_memcpy(buf, stk->u.auth.tx_mic_key, 8);
+       os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8);
+       os_memcpy(stk->u.auth.rx_mic_key, buf, 8);
+       peerkey->tstk_set = 1;
+
+       kde_buf_len = peerkey->rsnie_p_len +
+               2 + RSN_SELECTOR_LEN + sizeof(lifetime) +
+               2 + RSN_SELECTOR_LEN + PMKID_LEN;
+       kde_buf = os_malloc(kde_buf_len);
+       if (kde_buf == NULL)
+               return;
+       pos = kde_buf;
+       pos = wpa_add_ie(pos, peerkey->rsnie_p, peerkey->rsnie_p_len);
+       lifetime = host_to_be32(peerkey->lifetime);
+       pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME,
+                         (u8 *) &lifetime, sizeof(lifetime));
+       wpa_add_kde(pos, RSN_KEY_DATA_PMKID, peerkey->smkid, PMKID_LEN);
+
+       if (wpa_supplicant_send_2_of_4(sm, peerkey->addr, key, ver,
+                                      peerkey->pnonce, kde_buf, kde_buf_len,
+                                      stk)) {
+               os_free(kde_buf);
+               return;
+       }
+       os_free(kde_buf);
+
+       os_memcpy(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
+                                              struct wpa_peerkey *peerkey,
+                                              struct wpa_eapol_ie_parse *kde)
+{
+       u32 lifetime;
+       struct os_time now;
+
+       if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
+               return;
+
+       lifetime = WPA_GET_BE32(kde->lifetime);
+
+       if (lifetime >= peerkey->lifetime) {
+               wpa_printf(MSG_DEBUG, "RSN: Peer used SMK lifetime %u seconds "
+                          "which is larger than or equal to own value %u "
+                          "seconds - ignored", lifetime, peerkey->lifetime);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RSN: Peer used shorter SMK lifetime %u seconds "
+                  "(own was %u seconds) - updated",
+                  lifetime, peerkey->lifetime);
+       peerkey->lifetime = lifetime;
+
+       os_get_time(&now);
+       peerkey->expiration = now.sec + lifetime;
+       eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
+       eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
+                              sm, peerkey);
+}
+
+
+static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
+                                             struct wpa_peerkey *peerkey,
+                                             const struct wpa_eapol_key *key,
+                                             u16 ver)
+{
+       struct wpa_eapol_ie_parse kde;
+       const u8 *keydata;
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+       os_memset(&kde, 0, sizeof(kde));
+
+       /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
+        * from the peer. It may also include Lifetime KDE. */
+       keydata = (const u8 *) (key + 1);
+       len = WPA_GET_BE16(key->key_data_length);
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len);
+       if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 ||
+           kde.pmkid == NULL || kde.rsn_ie == NULL) {
+               wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
+               return;
+       }
+
+       if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+               wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
+                           kde.pmkid, PMKID_LEN);
+               return;
+       }
+
+       if (kde.rsn_ie_len != peerkey->rsnie_p_len ||
+           os_memcmp(kde.rsn_ie, peerkey->rsnie_p, kde.rsn_ie_len) != 0) {
+               wpa_printf(MSG_INFO, "RSN: Peer RSN IE in SMK and STK "
+                          "handshakes did not match");
+               wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in SMK handshake",
+                           peerkey->rsnie_p, peerkey->rsnie_p_len);
+               wpa_hexdump(MSG_DEBUG, "RSN: Peer RSN IE in STK handshake",
+                           kde.rsn_ie, kde.rsn_ie_len);
+               return;
+       }
+
+       wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+       wpa_supplicant_send_stk_3_of_4(sm, peerkey);
+       os_memcpy(peerkey->pnonce, key->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
+                                             struct wpa_peerkey *peerkey,
+                                             const struct wpa_eapol_key *key,
+                                             u16 ver)
+{
+       struct wpa_eapol_ie_parse kde;
+       const u8 *keydata;
+       size_t len, key_len;
+       const u8 *_key;
+       u8 key_buf[32], rsc[6];
+
+       wpa_printf(MSG_DEBUG, "RSN: RX message 3 of STK 4-Way Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+       os_memset(&kde, 0, sizeof(kde));
+
+       /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
+        * Lifetime KDE. */
+       keydata = (const u8 *) (key + 1);
+       len = WPA_GET_BE16(key->key_data_length);
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len);
+       if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) {
+               wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
+                          "STK 3/4");
+               return;
+       }
+
+       if (kde.rsn_ie_len != peerkey->rsnie_i_len ||
+           os_memcmp(kde.rsn_ie, peerkey->rsnie_i, kde.rsn_ie_len) != 0) {
+               wpa_printf(MSG_INFO, "RSN: Initiator RSN IE in SMK and STK "
+                          "handshakes did not match");
+               wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in SMK "
+                           "handshake",
+                           peerkey->rsnie_i, peerkey->rsnie_i_len);
+               wpa_hexdump(MSG_DEBUG, "RSN: Initiator RSN IE in STK "
+                           "handshake",
+                           kde.rsn_ie, kde.rsn_ie_len);
+               return;
+       }
+
+       if (os_memcmp(peerkey->inonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_WARNING, "RSN: INonce from message 1 of STK "
+                          "4-Way Handshake differs from 3 of STK 4-Way "
+                          "Handshake - drop packet (src=" MACSTR ")",
+                          MAC2STR(peerkey->addr));
+               return;
+       }
+
+       wpa_supplicant_update_smk_lifetime(sm, peerkey, &kde);
+
+       if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
+                                      WPA_GET_BE16(key->key_info),
+                                      NULL, 0, &peerkey->stk))
+               return;
+
+       _key = (u8 *) peerkey->stk.tk1;
+       if (peerkey->cipher == WPA_CIPHER_TKIP) {
+               /* Swap Tx/Rx keys for Michael MIC */
+               os_memcpy(key_buf, _key, 16);
+               os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8);
+               os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8);
+               _key = key_buf;
+               key_len = 32;
+       } else
+               key_len = 16;
+
+       os_memset(rsc, 0, 6);
+       if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+                          rsc, sizeof(rsc), _key, key_len) < 0) {
+               wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+                          "driver.");
+               return;
+       }
+}
+
+
+static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
+                                             struct wpa_peerkey *peerkey,
+                                             const struct wpa_eapol_key *key,
+                                             u16 ver)
+{
+       u8 rsc[6];
+
+       wpa_printf(MSG_DEBUG, "RSN: RX message 4 of STK 4-Way Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
+
+       os_memset(rsc, 0, 6);
+       if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
+                          rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1,
+                          peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
+               wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
+                          "driver.");
+               return;
+       }
+}
+
+
+/**
+ * peerkey_verify_eapol_key_mic - Verify PeerKey MIC
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peerkey: Pointer to the PeerKey data for the peer
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @buf: Pointer to the beginning of EAPOL-Key frame
+ * @len: Length of the EAPOL-Key frame
+ * Returns: 0 on success, -1 on failure
+ */
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+                                struct wpa_peerkey *peerkey,
+                                struct wpa_eapol_key *key, u16 ver,
+                                const u8 *buf, size_t len)
+{
+       u8 mic[16];
+       int ok = 0;
+
+       if (peerkey->initiator && !peerkey->stk_set) {
+               wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
+                              sm->own_addr, peerkey->addr,
+                              peerkey->inonce, key->key_nonce,
+                              (u8 *) &peerkey->stk, sizeof(peerkey->stk),
+                              peerkey->use_sha256);
+               peerkey->stk_set = 1;
+       }
+
+       os_memcpy(mic, key->key_mic, 16);
+       if (peerkey->tstk_set) {
+               os_memset(key->key_mic, 0, 16);
+               wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len,
+                                 key->key_mic);
+               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+                       wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+                                  "when using TSTK - ignoring TSTK");
+               } else {
+                       ok = 1;
+                       peerkey->tstk_set = 0;
+                       peerkey->stk_set = 1;
+                       os_memcpy(&peerkey->stk, &peerkey->tstk,
+                                 sizeof(peerkey->stk));
+               }
+       }
+
+       if (!ok && peerkey->stk_set) {
+               os_memset(key->key_mic, 0, 16);
+               wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len,
+                                 key->key_mic);
+               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+                       wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
+                                  "- dropping packet");
+                       return -1;
+               }
+               ok = 1;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_WARNING, "RSN: Could not verify EAPOL-Key MIC "
+                          "- dropping packet");
+               return -1;
+       }
+
+       os_memcpy(peerkey->replay_counter, key->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       peerkey->replay_counter_set = 1;
+       return 0;
+}
+
+
+/**
+ * wpa_sm_stkstart - Send EAPOL-Key Request for STK handshake (STK M1)
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @peer: MAC address of the peer STA
+ * Returns: 0 on success, or -1 on failure
+ *
+ * Send an EAPOL-Key Request to the current authenticator to start STK
+ * handshake with the peer.
+ */
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+       size_t rlen, kde_len;
+       struct wpa_eapol_key *req;
+       int key_info, ver;
+       u8 bssid[ETH_ALEN], *rbuf, *pos, *count_pos;
+       u16 count;
+       struct rsn_ie_hdr *hdr;
+       struct wpa_peerkey *peerkey;
+       struct wpa_ie_data ie;
+
+       if (sm->proto != WPA_PROTO_RSN || !sm->ptk_set || !sm->peerkey_enabled)
+               return -1;
+
+       if (sm->ap_rsn_ie &&
+           wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len, &ie) == 0 &&
+           !(ie.capabilities & WPA_CAPABILITY_PEERKEY_ENABLED)) {
+               wpa_printf(MSG_DEBUG, "RSN: Current AP does not support STK");
+               return -1;
+       }
+
+       if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+               ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+       else
+               ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+       if (wpa_sm_get_bssid(sm, bssid) < 0) {
+               wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+                          "SMK M1");
+               return -1;
+       }
+
+       /* TODO: find existing entry and if found, use that instead of adding
+        * a new one */
+       peerkey = os_zalloc(sizeof(*peerkey));
+       if (peerkey == NULL)
+               return -1;
+       peerkey->initiator = 1;
+       os_memcpy(peerkey->addr, peer, ETH_ALEN);
+#ifdef CONFIG_IEEE80211W
+       if (wpa_key_mgmt_sha256(sm->key_mgmt))
+               peerkey->use_sha256 = 1;
+#endif /* CONFIG_IEEE80211W */
+
+       /* SMK M1:
+        * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
+        *           MIC=MIC, DataKDs=(RSNIE_I, MAC_P KDE))
+        */
+
+       hdr = (struct rsn_ie_hdr *) peerkey->rsnie_i;
+       hdr->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(hdr->version, RSN_VERSION);
+       pos = (u8 *) (hdr + 1);
+       /* Group Suite can be anything for SMK RSN IE; receiver will just
+        * ignore it. */
+       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       pos += RSN_SELECTOR_LEN;
+       count_pos = pos;
+       pos += 2;
+
+       count = 0;
+       if (sm->allowed_pairwise_cipher & WPA_CIPHER_CCMP) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+               pos += RSN_SELECTOR_LEN;
+               count++;
+       }
+       if (sm->allowed_pairwise_cipher & WPA_CIPHER_TKIP) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+               pos += RSN_SELECTOR_LEN;
+               count++;
+       }
+       WPA_PUT_LE16(count_pos, count);
+
+       hdr->len = (pos - peerkey->rsnie_i) - 2;
+       peerkey->rsnie_i_len = pos - peerkey->rsnie_i;
+       wpa_hexdump(MSG_DEBUG, "WPA: RSN IE for SMK handshake",
+                   peerkey->rsnie_i, peerkey->rsnie_i_len);
+
+       kde_len = peerkey->rsnie_i_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN;
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+                                 sizeof(*req) + kde_len, &rlen,
+                                 (void *) &req);
+       if (rbuf == NULL) {
+               wpa_supplicant_peerkey_free(sm, peerkey);
+               return -1;
+       }
+
+       req->type = EAPOL_KEY_TYPE_RSN;
+       key_info = WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
+               WPA_KEY_INFO_SECURE | WPA_KEY_INFO_REQUEST | ver;
+       WPA_PUT_BE16(req->key_info, key_info);
+       WPA_PUT_BE16(req->key_length, 0);
+       os_memcpy(req->replay_counter, sm->request_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+       if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                       "WPA: Failed to get random data for INonce");
+               os_free(rbuf);
+               wpa_supplicant_peerkey_free(sm, peerkey);
+               return -1;
+       }
+       os_memcpy(req->key_nonce, peerkey->inonce, WPA_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPA: INonce for SMK handshake",
+                   req->key_nonce, WPA_NONCE_LEN);
+
+       WPA_PUT_BE16(req->key_data_length, (u16) kde_len);
+       pos = (u8 *) (req + 1);
+
+       /* Initiator RSN IE */
+       pos = wpa_add_ie(pos, peerkey->rsnie_i, peerkey->rsnie_i_len);
+       /* Peer MAC address KDE */
+       wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN);
+
+       wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
+                  MACSTR ")", MAC2STR(peer));
+       wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
+                          rbuf, rlen, req->key_mic);
+
+       peerkey->next = sm->peerkey;
+       sm->peerkey = peerkey;
+
+       return 0;
+}
+
+
+/**
+ * peerkey_deinit - Free PeerKey values
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void peerkey_deinit(struct wpa_sm *sm)
+{
+       struct wpa_peerkey *prev, *peerkey = sm->peerkey;
+       while (peerkey) {
+               prev = peerkey;
+               peerkey = peerkey->next;
+               os_free(prev);
+       }
+}
+
+
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+                          struct wpa_eapol_key *key, u16 key_info, u16 ver)
+{
+       if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
+           (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
+               /* 3/4 STK 4-Way Handshake */
+               wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver);
+       } else if (key_info & WPA_KEY_INFO_ACK) {
+               /* 1/4 STK 4-Way Handshake */
+               wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver);
+       } else if (key_info & WPA_KEY_INFO_SECURE) {
+               /* 4/4 STK 4-Way Handshake */
+               wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
+       } else {
+               /* 2/4 STK 4-Way Handshake */
+               wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver);
+       }
+}
+
+
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+                         struct wpa_eapol_key *key, size_t extra_len,
+                         u16 key_info, u16 ver)
+{
+       if (key_info & WPA_KEY_INFO_ERROR) {
+               /* SMK Error */
+               wpa_supplicant_process_smk_error(sm, src_addr, key, extra_len);
+       } else if (key_info & WPA_KEY_INFO_ACK) {
+               /* SMK M2 */
+               wpa_supplicant_process_smk_m2(sm, src_addr, key, extra_len,
+                                             ver);
+       } else {
+               /* SMK M4 or M5 */
+               wpa_supplicant_process_smk_m45(sm, src_addr, key, extra_len,
+                                              ver);
+       }
+}
+
+#endif /* CONFIG_PEERKEY */
diff --git a/src/rsn_supp/peerkey.h b/src/rsn_supp/peerkey.h
new file mode 100644 (file)
index 0000000..2613127
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * WPA Supplicant - 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.
+ */
+
+#ifndef PEERKEY_H
+#define PEERKEY_H
+
+#define PEERKEY_MAX_IE_LEN 80
+struct wpa_peerkey {
+       struct wpa_peerkey *next;
+       int initiator; /* whether this end was initator for SMK handshake */
+       u8 addr[ETH_ALEN]; /* other end MAC address */
+       u8 inonce[WPA_NONCE_LEN]; /* Initiator Nonce */
+       u8 pnonce[WPA_NONCE_LEN]; /* Peer Nonce */
+       u8 rsnie_i[PEERKEY_MAX_IE_LEN]; /* Initiator RSN IE */
+       size_t rsnie_i_len;
+       u8 rsnie_p[PEERKEY_MAX_IE_LEN]; /* Peer RSN IE */
+       size_t rsnie_p_len;
+       u8 smk[PMK_LEN];
+       int smk_complete;
+       u8 smkid[PMKID_LEN];
+       u32 lifetime;
+       os_time_t expiration;
+       int cipher; /* Selected cipher (WPA_CIPHER_*) */
+       u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+       int replay_counter_set;
+       int use_sha256; /* whether AKMP indicate SHA256-based derivations */
+
+       struct wpa_ptk stk, tstk;
+       int stk_set, tstk_set;
+};
+
+
+#ifdef CONFIG_PEERKEY
+
+int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+                                struct wpa_peerkey *peerkey,
+                                struct wpa_eapol_key *key, u16 ver,
+                                const u8 *buf, size_t len);
+void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+                          struct wpa_eapol_key *key, u16 key_info, u16 ver);
+void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+                         struct wpa_eapol_key *key, size_t extra_len,
+                         u16 key_info, u16 ver);
+void peerkey_deinit(struct wpa_sm *sm);
+
+#else /* CONFIG_PEERKEY */
+
+static inline int
+peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
+                            struct wpa_peerkey *peerkey,
+                            struct wpa_eapol_key *key, u16 ver,
+                            const u8 *buf, size_t len)
+{
+       return -1;
+}
+
+static inline void
+peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
+                     struct wpa_eapol_key *key, u16 key_info, u16 ver)
+{
+}
+
+static inline void
+peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
+                    struct wpa_eapol_key *key, size_t extra_len,
+                    u16 key_info, u16 ver)
+{
+}
+
+static inline void peerkey_deinit(struct wpa_sm *sm)
+{
+}
+
+#endif /* CONFIG_PEERKEY */
+
+#endif /* PEERKEY_H */
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
new file mode 100644 (file)
index 0000000..cac8c83
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * WPA Supplicant - RSN PMKSA cache
+ * 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 "eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "pmksa_cache.h"
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+static const int pmksa_cache_max_entries = 32;
+
+struct rsn_pmksa_cache {
+       struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
+       int pmksa_count; /* number of entries in PMKSA cache */
+       struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
+
+       void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
+                       int replace);
+       void *ctx;
+};
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
+
+
+static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
+{
+       os_free(entry);
+}
+
+
+static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
+                                  struct rsn_pmksa_cache_entry *entry,
+                                  int replace)
+{
+       pmksa->pmksa_count--;
+       pmksa->free_cb(entry, pmksa->ctx, replace);
+       _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->aa));
+               pmksa_cache_free_entry(pmksa, entry, 0);
+       }
+
+       pmksa_cache_set_expiration(pmksa);
+}
+
+
+static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+       struct rsn_pmksa_cache *pmksa = eloop_ctx;
+       pmksa->sm->cur_pmksa = NULL;
+       eapol_sm_request_reauth(pmksa->sm->eapol);
+}
+
+
+static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
+{
+       int sec;
+       struct rsn_pmksa_cache_entry *entry;
+       struct os_time now;
+
+       eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+       eloop_cancel_timeout(pmksa_cache_reauth, 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);
+
+       entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
+               pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
+       if (entry) {
+               sec = pmksa->pmksa->reauth_time - now.sec;
+               if (sec < 0)
+                       sec = 0;
+               eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
+                                      NULL);
+       }
+}
+
+
+/**
+ * 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
+ * @network_ctx: Network configuration context for this PMK
+ * @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 Authenticator,
+ * this entry will be replaced with the new entry. PMKID will be calculated
+ * based on the PMK and the driver interface is notified of the new 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, void *network_ctx, int akmp)
+{
+       struct rsn_pmksa_cache_entry *entry, *pos, *prev;
+       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 + pmksa->sm->dot11RSNAConfigPMKLifetime;
+       entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
+               pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
+       entry->akmp = akmp;
+       os_memcpy(entry->aa, aa, ETH_ALEN);
+       entry->network_ctx = network_ctx;
+
+       /* Replace an old entry for the same Authenticator (if found) with the
+        * new entry */
+       pos = pmksa->pmksa;
+       prev = NULL;
+       while (pos) {
+               if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
+                       if (pos->pmk_len == pmk_len &&
+                           os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
+                           os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
+                           0) {
+                               wpa_printf(MSG_DEBUG, "WPA: reusing previous "
+                                          "PMKSA entry");
+                               os_free(entry);
+                               return pos;
+                       }
+                       if (prev == NULL)
+                               pmksa->pmksa = pos->next;
+                       else
+                               prev->next = pos->next;
+                       if (pos == pmksa->sm->cur_pmksa) {
+                               /* We are about to replace the current PMKSA
+                                * cache entry. This happens when the PMKSA
+                                * caching attempt fails, so we don't want to
+                                * force pmksa_cache_free_entry() to disconnect
+                                * at this point. Let's just make sure the old
+                                * PMKSA cache entry will not be used in the
+                                * future.
+                                */
+                               wpa_printf(MSG_DEBUG, "RSN: replacing current "
+                                          "PMKSA entry");
+                               pmksa->sm->cur_pmksa = NULL;
+                       }
+                       wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
+                                  "the current AP");
+                       pmksa_cache_free_entry(pmksa, pos, 1);
+                       break;
+               }
+               prev = pos;
+               pos = pos->next;
+       }
+
+       if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
+               /* Remove the oldest entry to make room for the new entry */
+               pos = pmksa->pmksa;
+               pmksa->pmksa = pos->next;
+               wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
+                          "entry (for " MACSTR ") to make room for new one",
+                          MAC2STR(pos->aa));
+               wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
+               pmksa_cache_free_entry(pmksa, pos, 0);
+       }
+
+       /* 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;
+               pmksa_cache_set_expiration(pmksa);
+       } else {
+               entry->next = prev->next;
+               prev->next = entry;
+       }
+       pmksa->pmksa_count++;
+       wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
+                  MAC2STR(entry->aa));
+       wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
+
+       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;
+
+       if (pmksa == NULL)
+               return;
+
+       entry = pmksa->pmksa;
+       pmksa->pmksa = NULL;
+       while (entry) {
+               prev = entry;
+               entry = entry->next;
+               os_free(prev);
+       }
+       pmksa_cache_set_expiration(pmksa);
+       os_free(pmksa);
+}
+
+
+/**
+ * pmksa_cache_get - Fetch a PMKSA cache entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @aa: Authenticator 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 *aa, const u8 *pmkid)
+{
+       struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+       while (entry) {
+               if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
+                   (pmkid == NULL ||
+                    os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
+                       return entry;
+               entry = entry->next;
+       }
+       return NULL;
+}
+
+
+/**
+ * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ *
+ * Clear references to old data structures when wpa_supplicant is reconfigured.
+ */
+void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+       struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+       while (entry) {
+               entry->network_ctx = NULL;
+               entry = entry->next;
+       }
+}
+
+
+static struct rsn_pmksa_cache_entry *
+pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
+                       const struct rsn_pmksa_cache_entry *old_entry,
+                       const u8 *aa)
+{
+       struct rsn_pmksa_cache_entry *new_entry;
+
+       new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+                                   aa, pmksa->sm->own_addr,
+                                   old_entry->network_ctx, old_entry->akmp);
+       if (new_entry == NULL)
+               return NULL;
+
+       /* TODO: reorder entries based on expiration time? */
+       new_entry->expiration = old_entry->expiration;
+       new_entry->opportunistic = 1;
+
+       return new_entry;
+}
+
+
+/**
+ * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @network_ctx: Network configuration context
+ * @aa: Authenticator address for the new AP
+ * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
+ *
+ * Try to create a new PMKSA cache entry opportunistically by guessing that the
+ * new AP is sharing the same PMK as another AP that has the same SSID and has
+ * already an entry in PMKSA cache.
+ */
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
+                             const u8 *aa)
+{
+       struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
+
+       if (network_ctx == NULL)
+               return NULL;
+       while (entry) {
+               if (entry->network_ctx == network_ctx) {
+                       entry = pmksa_cache_clone_entry(pmksa, entry, aa);
+                       if (entry) {
+                               wpa_printf(MSG_DEBUG, "RSN: added "
+                                          "opportunistic PMKSA cache entry "
+                                          "for " MACSTR, MAC2STR(aa));
+                       }
+                       return entry;
+               }
+               entry = entry->next;
+       }
+       return NULL;
+}
+
+
+/**
+ * pmksa_cache_get_current - Get the current used PMKSA entry
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
+ */
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
+{
+       if (sm == NULL)
+               return NULL;
+       return sm->cur_pmksa;
+}
+
+
+/**
+ * pmksa_cache_clear_current - Clear the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       sm->cur_pmksa = NULL;
+}
+
+
+/**
+ * pmksa_cache_set_current - Set the current PMKSA entry selection
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmkid: PMKID for selecting PMKSA or %NULL if not used
+ * @bssid: BSSID for PMKSA or %NULL if not used
+ * @network_ctx: Network configuration context
+ * @try_opportunistic: Whether to allow opportunistic PMKSA caching
+ * Returns: 0 if PMKSA was found or -1 if no matching entry was found
+ */
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+                           const u8 *bssid, void *network_ctx,
+                           int try_opportunistic)
+{
+       struct rsn_pmksa_cache *pmksa = sm->pmksa;
+       sm->cur_pmksa = NULL;
+       if (pmkid)
+               sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
+       if (sm->cur_pmksa == NULL && bssid)
+               sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
+       if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
+               sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
+                                                             network_ctx,
+                                                             bssid);
+       if (sm->cur_pmksa) {
+               wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
+                           sm->cur_pmksa->pmkid, PMKID_LEN);
+               return 0;
+       }
+       return -1;
+}
+
+
+/**
+ * pmksa_cache_list - Dump text list of entries in PMKSA cache
+ * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PMKSA cache contents for the ctrl_iface PMKSA command.
+ */
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
+{
+       int i, ret;
+       char *pos = buf;
+       struct rsn_pmksa_cache_entry *entry;
+       struct os_time now;
+
+       os_get_time(&now);
+       ret = os_snprintf(pos, buf + len - pos,
+                         "Index / AA / PMKID / expiration (in seconds) / "
+                         "opportunistic\n");
+       if (ret < 0 || ret >= buf + len - pos)
+               return pos - buf;
+       pos += ret;
+       i = 0;
+       entry = pmksa->pmksa;
+       while (entry) {
+               i++;
+               ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
+                                 i, MAC2STR(entry->aa));
+               if (ret < 0 || ret >= buf + len - pos)
+                       return pos - buf;
+               pos += ret;
+               pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
+                                       PMKID_LEN);
+               ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
+                                 (int) (entry->expiration - now.sec),
+                                 entry->opportunistic);
+               if (ret < 0 || ret >= buf + len - pos)
+                       return pos - buf;
+               pos += ret;
+               entry = entry->next;
+       }
+       return pos - buf;
+}
+
+
+/**
+ * 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
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * 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, int replace),
+                void *ctx, struct wpa_sm *sm)
+{
+       struct rsn_pmksa_cache *pmksa;
+
+       pmksa = os_zalloc(sizeof(*pmksa));
+       if (pmksa) {
+               pmksa->free_cb = free_cb;
+               pmksa->ctx = ctx;
+               pmksa->sm = sm;
+       }
+
+       return pmksa;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
diff --git a/src/rsn_supp/pmksa_cache.h b/src/rsn_supp/pmksa_cache.h
new file mode 100644 (file)
index 0000000..a1447e5
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * wpa_supplicant - WPA2/RSN PMKSA cache functions
+ * Copyright (c) 2003-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;
+       u8 pmkid[PMKID_LEN];
+       u8 pmk[PMK_LEN];
+       size_t pmk_len;
+       os_time_t expiration;
+       int akmp; /* WPA_KEY_MGMT_* */
+       u8 aa[ETH_ALEN];
+
+       os_time_t reauth_time;
+
+       /**
+        * network_ctx - Network configuration context
+        *
+        * This field is only used to match PMKSA cache entries to a specific
+        * network configuration (e.g., a specific SSID and security policy).
+        * This can be a pointer to the configuration entry, but PMKSA caching
+        * code does not dereference the value and this could be any kind of
+        * identifier.
+        */
+       void *network_ctx;
+       int opportunistic;
+};
+
+struct rsn_pmksa_cache;
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+                                void *ctx, int replace),
+                void *ctx, struct wpa_sm *sm);
+void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
+                                              const u8 *aa, const u8 *pmkid);
+int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
+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, void *network_ctx, int akmp);
+void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa);
+struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
+void pmksa_cache_clear_current(struct wpa_sm *sm);
+int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+                           const u8 *bssid, void *network_ctx,
+                           int try_opportunistic);
+struct rsn_pmksa_cache_entry *
+pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa,
+                             void *network_ctx, const u8 *aa);
+
+#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+static inline struct rsn_pmksa_cache *
+pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
+                                void *ctx, int replace),
+                void *ctx, struct wpa_sm *sm)
+{
+       return (void *) -1;
+}
+
+static inline void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid)
+{
+       return NULL;
+}
+
+static inline struct rsn_pmksa_cache_entry *
+pmksa_cache_get_current(struct wpa_sm *sm)
+{
+       return NULL;
+}
+
+static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
+                                  size_t len)
+{
+       return -1;
+}
+
+static inline 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, void *network_ctx, int akmp)
+{
+       return NULL;
+}
+
+static inline void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
+{
+}
+
+static inline void pmksa_cache_clear_current(struct wpa_sm *sm)
+{
+}
+
+static inline int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
+                                         const u8 *bssid,
+                                         void *network_ctx,
+                                         int try_opportunistic)
+{
+       return -1;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+#endif /* PMKSA_CACHE_H */
diff --git a/src/rsn_supp/preauth.c b/src/rsn_supp/preauth.c
new file mode 100644 (file)
index 0000000..6109f5e
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * RSN pre-authentication (supplicant)
+ * Copyright (c) 2003-2010, 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 "wpa.h"
+#include "eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "common/ieee802_11_defs.h"
+
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+#define PMKID_CANDIDATE_PRIO_SCAN 1000
+
+
+struct rsn_pmksa_candidate {
+       struct dl_list list;
+       u8 bssid[ETH_ALEN];
+       int priority;
+};
+
+
+/**
+ * pmksa_candidate_free - Free all entries in PMKSA candidate list
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void pmksa_candidate_free(struct wpa_sm *sm)
+{
+       struct rsn_pmksa_candidate *entry, *n;
+
+       if (sm == NULL)
+               return;
+
+       dl_list_for_each_safe(entry, n, &sm->pmksa_candidates,
+                             struct rsn_pmksa_candidate, list) {
+               dl_list_del(&entry->list);
+               os_free(entry);
+       }
+}
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+                               const u8 *buf, size_t len)
+{
+       struct wpa_sm *sm = ctx;
+
+       wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr));
+       wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len);
+
+       if (sm->preauth_eapol == NULL ||
+           is_zero_ether_addr(sm->preauth_bssid) ||
+           os_memcmp(sm->preauth_bssid, src_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_WARNING, "RSN pre-auth frame received from "
+                          "unexpected source " MACSTR " - dropped",
+                          MAC2STR(src_addr));
+               return;
+       }
+
+       eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len);
+}
+
+
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+                                void *ctx)
+{
+       struct wpa_sm *sm = ctx;
+       u8 pmk[PMK_LEN];
+
+       if (success) {
+               int res, pmk_len;
+               pmk_len = PMK_LEN;
+               res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+               if (res) {
+                       /*
+                        * EAP-LEAP is an exception from other EAP methods: it
+                        * uses only 16-byte PMK.
+                        */
+                       res = eapol_sm_get_key(eapol, pmk, 16);
+                       pmk_len = 16;
+               }
+               if (res == 0) {
+                       wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth",
+                                       pmk, pmk_len);
+                       sm->pmk_len = pmk_len;
+                       pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+                                       sm->preauth_bssid, sm->own_addr,
+                                       sm->network_ctx,
+                                       WPA_KEY_MGMT_IEEE8021X);
+               } else {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "RSN: failed to get master session key from "
+                               "pre-auth EAPOL state machines");
+                       success = 0;
+               }
+       }
+
+       wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+               MACSTR " %s", MAC2STR(sm->preauth_bssid),
+               success ? "completed successfully" : "failed");
+
+       rsn_preauth_deinit(sm);
+       rsn_preauth_candidate_process(sm);
+}
+
+
+static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_sm *sm = eloop_ctx;
+
+       wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
+               MACSTR " timed out", MAC2STR(sm->preauth_bssid));
+       rsn_preauth_deinit(sm);
+       rsn_preauth_candidate_process(sm);
+}
+
+
+static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf,
+                                 size_t len)
+{
+       struct wpa_sm *sm = ctx;
+       u8 *msg;
+       size_t msglen;
+       int res;
+
+       /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+        * extra copy here */
+
+       if (sm->l2_preauth == NULL)
+               return -1;
+
+       msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL);
+       if (msg == NULL)
+               return -1;
+
+       wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen);
+       res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid,
+                            ETH_P_RSN_PREAUTH, msg, msglen);
+       os_free(msg);
+       return res;
+}
+
+
+/**
+ * rsn_preauth_init - Start new RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Authenticator address (BSSID) with which to preauthenticate
+ * @eap_conf: Current EAP configuration
+ * Returns: 0 on success, -1 on another pre-authentication is in progress,
+ * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine
+ * initialization failure, -4 on memory allocation failure
+ *
+ * This function request an RSN pre-authentication with a given destination
+ * address. This is usually called for PMKSA candidates found from scan results
+ * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger
+ * pre-authentication.
+ */
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+                    struct eap_peer_config *eap_conf)
+{
+       struct eapol_config eapol_conf;
+       struct eapol_ctx *ctx;
+
+       if (sm->preauth_eapol)
+               return -1;
+
+       wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG,
+               "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst));
+
+       sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr,
+                                       ETH_P_RSN_PREAUTH,
+                                       rsn_preauth_receive, sm, 0);
+       if (sm->l2_preauth == NULL) {
+               wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet "
+                          "processing for pre-authentication");
+               return -2;
+       }
+
+       if (sm->bridge_ifname) {
+               sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname,
+                                                  sm->own_addr,
+                                                  ETH_P_RSN_PREAUTH,
+                                                  rsn_preauth_receive, sm, 0);
+               if (sm->l2_preauth_br == NULL) {
+                       wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
+                                  "packet processing (bridge) for "
+                                  "pre-authentication");
+                       return -2;
+               }
+       }
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL) {
+               wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
+               return -4;
+       }
+       ctx->ctx = sm->ctx->ctx;
+       ctx->msg_ctx = sm->ctx->ctx;
+       ctx->preauth = 1;
+       ctx->cb = rsn_preauth_eapol_cb;
+       ctx->cb_ctx = sm;
+       ctx->scard_ctx = sm->scard_ctx;
+       ctx->eapol_send = rsn_preauth_eapol_send;
+       ctx->eapol_send_ctx = sm;
+       ctx->set_config_blob = sm->ctx->set_config_blob;
+       ctx->get_config_blob = sm->ctx->get_config_blob;
+
+       sm->preauth_eapol = eapol_sm_init(ctx);
+       if (sm->preauth_eapol == NULL) {
+               os_free(ctx);
+               wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
+                          "state machines for pre-authentication");
+               return -3;
+       }
+       os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+       eapol_conf.accept_802_1x_keys = 0;
+       eapol_conf.required_keys = 0;
+       eapol_conf.fast_reauth = sm->fast_reauth;
+       eapol_conf.workaround = sm->eap_workaround;
+       eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf);
+       /*
+        * Use a shorter startPeriod with preauthentication since the first
+        * preauth EAPOL-Start frame may end up being dropped due to race
+        * condition in the AP between the data receive and key configuration
+        * after the 4-Way Handshake.
+        */
+       eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6);
+       os_memcpy(sm->preauth_bssid, dst, ETH_ALEN);
+
+       eapol_sm_notify_portValid(sm->preauth_eapol, TRUE);
+       /* 802.1X::portControl = Auto */
+       eapol_sm_notify_portEnabled(sm->preauth_eapol, TRUE);
+
+       eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0,
+                              rsn_preauth_timeout, sm, NULL);
+
+       return 0;
+}
+
+
+/**
+ * rsn_preauth_deinit - Abort RSN pre-authentication
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function aborts the current RSN pre-authentication (if one is started)
+ * and frees resources allocated for it.
+ */
+void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+       if (sm == NULL || !sm->preauth_eapol)
+               return;
+
+       eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL);
+       eapol_sm_deinit(sm->preauth_eapol);
+       sm->preauth_eapol = NULL;
+       os_memset(sm->preauth_bssid, 0, ETH_ALEN);
+
+       l2_packet_deinit(sm->l2_preauth);
+       sm->l2_preauth = NULL;
+       if (sm->l2_preauth_br) {
+               l2_packet_deinit(sm->l2_preauth_br);
+               sm->l2_preauth_br = NULL;
+       }
+}
+
+
+/**
+ * rsn_preauth_candidate_process - Process PMKSA candidates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Go through the PMKSA candidates and start pre-authentication if a candidate
+ * without an existing PMKSA cache entry is found. Processed candidates will be
+ * removed from the list.
+ */
+void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+       struct rsn_pmksa_candidate *candidate, *n;
+
+       if (dl_list_empty(&sm->pmksa_candidates))
+               return;
+
+       /* TODO: drop priority for old candidate entries */
+
+       wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate "
+               "list");
+       if (sm->preauth_eapol ||
+           sm->proto != WPA_PROTO_RSN ||
+           wpa_sm_get_state(sm) != WPA_COMPLETED ||
+           (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
+            sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
+                       "state for new pre-authentication");
+               return; /* invalid state for new pre-auth */
+       }
+
+       dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates,
+                             struct rsn_pmksa_candidate, list) {
+               struct rsn_pmksa_cache_entry *p = NULL;
+               p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL);
+               if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 &&
+                   (p == NULL || p->opportunistic)) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA "
+                               "candidate " MACSTR
+                               " selected for pre-authentication",
+                               MAC2STR(candidate->bssid));
+                       dl_list_del(&candidate->list);
+                       rsn_preauth_init(sm, candidate->bssid,
+                                        sm->eap_conf_ctx);
+                       os_free(candidate);
+                       return;
+               }
+               wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate "
+                       MACSTR " does not need pre-authentication anymore",
+                       MAC2STR(candidate->bssid));
+               /* Some drivers (e.g., NDIS) expect to get notified about the
+                * PMKIDs again, so report the existing data now. */
+               if (p) {
+                       wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid);
+               }
+
+               dl_list_del(&candidate->list);
+               os_free(candidate);
+       }
+       wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA "
+               "candidates");
+}
+
+
+/**
+ * pmksa_candidate_add - Add a new PMKSA candidate
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: BSSID (authenticator address) of the candidate
+ * @prio: Priority (the smaller number, the higher priority)
+ * @preauth: Whether the candidate AP advertises support for pre-authentication
+ *
+ * This function is used to add PMKSA candidates for RSN pre-authentication. It
+ * is called from scan result processing and from driver events for PMKSA
+ * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event().
+ */
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+                        int prio, int preauth)
+{
+       struct rsn_pmksa_candidate *cand, *pos;
+
+       if (sm->network_ctx && sm->proactive_key_caching)
+               pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx,
+                                             bssid);
+
+       if (!preauth) {
+               wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without "
+                          "preauth flag");
+               return;
+       }
+
+       /* If BSSID already on candidate list, update the priority of the old
+        * entry. Do not override priority based on normal scan results. */
+       cand = NULL;
+       dl_list_for_each(pos, &sm->pmksa_candidates,
+                        struct rsn_pmksa_candidate, list) {
+               if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) {
+                       cand = pos;
+                       break;
+               }
+       }
+
+       if (cand) {
+               dl_list_del(&cand->list);
+               if (prio < PMKID_CANDIDATE_PRIO_SCAN)
+                       cand->priority = prio;
+       } else {
+               cand = os_zalloc(sizeof(*cand));
+               if (cand == NULL)
+                       return;
+               os_memcpy(cand->bssid, bssid, ETH_ALEN);
+               cand->priority = prio;
+       }
+
+       /* Add candidate to the list; order by increasing priority value. i.e.,
+        * highest priority (smallest value) first. */
+       dl_list_for_each(pos, &sm->pmksa_candidates,
+                        struct rsn_pmksa_candidate, list) {
+               if (cand->priority <= pos->priority) {
+                       dl_list_add(pos->list.prev, &cand->list);
+                       cand = NULL;
+                       break;
+               }
+       }
+       if (cand)
+               dl_list_add_tail(&sm->pmksa_candidates, &cand->list);
+
+       wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache "
+               "candidate " MACSTR " prio %d", MAC2STR(bssid), prio);
+       rsn_preauth_candidate_process(sm);
+}
+
+
+/* TODO: schedule periodic scans if current AP supports preauth */
+
+/**
+ * rsn_preauth_scan_results - Start processing scan results for canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * Returns: 0 if ready to process results or -1 to skip processing
+ *
+ * This functions is used to notify RSN code about start of new scan results
+ * processing. The actual scan results will be provided by calling
+ * rsn_preauth_scan_result() for each BSS if this function returned 0.
+ */
+int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+       if (sm->ssid_len == 0)
+               return -1;
+
+       /*
+        * TODO: is it ok to free all candidates? What about the entries
+        * received from EVENT_PMKID_CANDIDATE?
+        */
+       pmksa_candidate_free(sm);
+
+       return 0;
+}
+
+
+/**
+ * rsn_preauth_scan_result - Processing scan result for PMKSA canditates
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Add all suitable APs (Authenticators) from scan results into PMKSA
+ * candidate list.
+ */
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+                            const u8 *ssid, const u8 *rsn)
+{
+       struct wpa_ie_data ie;
+       struct rsn_pmksa_cache_entry *pmksa;
+
+       if (ssid[1] != sm->ssid_len ||
+           os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0)
+               return; /* Not for the current SSID */
+
+       if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0)
+               return; /* Ignore current AP */
+
+       if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie))
+               return;
+
+       pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL);
+       if (pmksa && (!pmksa->opportunistic ||
+                     !(ie.capabilities & WPA_CAPABILITY_PREAUTH)))
+               return;
+
+       /* Give less priority to candidates found from normal scan results. */
+       pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN,
+                           ie.capabilities & WPA_CAPABILITY_PREAUTH);
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+/**
+ * rsn_preauth_get_status - Get pre-authentication status
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA2 pre-authentication for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+                          int verbose)
+{
+       char *pos = buf, *end = buf + buflen;
+       int res, ret;
+
+       if (sm->preauth_eapol) {
+               ret = os_snprintf(pos, end - pos, "Pre-authentication "
+                                 "EAPOL state machines:\n");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               res = eapol_sm_get_status(sm->preauth_eapol,
+                                         pos, end - pos, verbose);
+               if (res >= 0)
+                       pos += res;
+       }
+
+       return pos - buf;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+/**
+ * rsn_preauth_in_progress - Verify whether pre-authentication is in progress
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+       return sm->preauth_eapol != NULL;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
diff --git a/src/rsn_supp/preauth.h b/src/rsn_supp/preauth.h
new file mode 100644 (file)
index 0000000..f8240ab
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * wpa_supplicant - WPA2/RSN pre-authentication functions
+ * Copyright (c) 2003-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.
+ */
+
+#ifndef PREAUTH_H
+#define PREAUTH_H
+
+struct wpa_scan_results;
+
+#if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
+
+void pmksa_candidate_free(struct wpa_sm *sm);
+int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+                    struct eap_peer_config *eap_conf);
+void rsn_preauth_deinit(struct wpa_sm *sm);
+int rsn_preauth_scan_results(struct wpa_sm *sm);
+void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+                            const u8 *ssid, const u8 *rsn);
+void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
+                        int prio, int preauth);
+void rsn_preauth_candidate_process(struct wpa_sm *sm);
+int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+                          int verbose);
+int rsn_preauth_in_progress(struct wpa_sm *sm);
+
+#else /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+static inline void pmksa_candidate_free(struct wpa_sm *sm)
+{
+}
+
+static inline void rsn_preauth_candidate_process(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
+                                  struct eap_peer_config *eap_conf)
+{
+       return -1;
+}
+
+static inline void rsn_preauth_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline int rsn_preauth_scan_results(struct wpa_sm *sm)
+{
+       return -1;
+}
+
+static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid,
+                                          const u8 *ssid, const u8 *rsn)
+{
+}
+
+static inline void pmksa_candidate_add(struct wpa_sm *sm,
+                                      const u8 *bssid,
+                                      int prio, int preauth)
+{
+}
+
+static inline int rsn_preauth_get_status(struct wpa_sm *sm, char *buf,
+                                        size_t buflen, int verbose)
+{
+       return 0;
+}
+
+static inline int rsn_preauth_in_progress(struct wpa_sm *sm)
+{
+       return 0;
+}
+
+#endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
+
+#endif /* PREAUTH_H */
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
new file mode 100644 (file)
index 0000000..9439f97
--- /dev/null
@@ -0,0 +1,2566 @@
+/*
+ * WPA Supplicant - WPA state machine and EAPOL-Key processing
+ * Copyright (c) 2003-2010, 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 "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa.h"
+#include "eloop.h"
+#include "preauth.h"
+#include "pmksa_cache.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+#include "peerkey.h"
+
+
+/**
+ * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @kck: Key Confirmation Key (KCK, part of PTK)
+ * @ver: Version field from Key Info
+ * @dest: Destination address for the frame
+ * @proto: Ethertype (usually ETH_P_EAPOL)
+ * @msg: EAPOL-Key message
+ * @msg_len: Length of message
+ * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
+ */
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+                       int ver, const u8 *dest, u16 proto,
+                       u8 *msg, size_t msg_len, u8 *key_mic)
+{
+       if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
+               /*
+                * Association event was not yet received; try to fetch
+                * BSSID from the driver.
+                */
+               if (wpa_sm_get_bssid(sm, sm->bssid) < 0) {
+                       wpa_printf(MSG_DEBUG, "WPA: Failed to read BSSID for "
+                                  "EAPOL-Key destination address");
+               } else {
+                       dest = sm->bssid;
+                       wpa_printf(MSG_DEBUG, "WPA: Use BSSID (" MACSTR
+                                  ") as the destination for EAPOL-Key",
+                                  MAC2STR(dest));
+               }
+       }
+       if (key_mic &&
+           wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) {
+               wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key "
+                          "version %d MIC", ver);
+               goto out;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
+       wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
+       eapol_sm_notify_tx_eapol_key(sm->eapol);
+out:
+       os_free(msg);
+}
+
+
+/**
+ * wpa_sm_key_request - Send EAPOL-Key Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @error: Indicate whether this is an Michael MIC error report
+ * @pairwise: 1 = error report for pairwise packet, 0 = for group packet
+ *
+ * Send an EAPOL-Key Request to the current authenticator. This function is
+ * used to request rekeying and it is usually called when a local Michael MIC
+ * failure is detected.
+ */
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
+{
+       size_t rlen;
+       struct wpa_eapol_key *reply;
+       int key_info, ver;
+       u8 bssid[ETH_ALEN], *rbuf;
+
+       if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
+               ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
+       else if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+               ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
+       else
+               ver = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
+
+       if (wpa_sm_get_bssid(sm, bssid) < 0) {
+               wpa_printf(MSG_WARNING, "Failed to read BSSID for EAPOL-Key "
+                          "request");
+               return;
+       }
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+                                 sizeof(*reply), &rlen, (void *) &reply);
+       if (rbuf == NULL)
+               return;
+
+       reply->type = sm->proto == WPA_PROTO_RSN ?
+               EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+       key_info = WPA_KEY_INFO_REQUEST | ver;
+       if (sm->ptk_set)
+               key_info |= WPA_KEY_INFO_MIC;
+       if (error)
+               key_info |= WPA_KEY_INFO_ERROR;
+       if (pairwise)
+               key_info |= WPA_KEY_INFO_KEY_TYPE;
+       WPA_PUT_BE16(reply->key_info, key_info);
+       WPA_PUT_BE16(reply->key_length, 0);
+       os_memcpy(reply->replay_counter, sm->request_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(reply->key_data_length, 0);
+
+       wpa_printf(MSG_INFO, "WPA: Sending EAPOL-Key Request (error=%d "
+                  "pairwise=%d ptk_set=%d len=%lu)",
+                  error, pairwise, sm->ptk_set, (unsigned long) rlen);
+       wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
+                          rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
+                          reply->key_mic : NULL);
+}
+
+
+static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
+                                 const unsigned char *src_addr,
+                                 const u8 *pmkid)
+{
+       int abort_cached = 0;
+
+       if (pmkid && !sm->cur_pmksa) {
+               /* When using drivers that generate RSN IE, wpa_supplicant may
+                * not have enough time to get the association information
+                * event before receiving this 1/4 message, so try to find a
+                * matching PMKSA cache entry here. */
+               sm->cur_pmksa = pmksa_cache_get(sm->pmksa, src_addr, pmkid);
+               if (sm->cur_pmksa) {
+                       wpa_printf(MSG_DEBUG, "RSN: found matching PMKID from "
+                                  "PMKSA cache");
+               } else {
+                       wpa_printf(MSG_DEBUG, "RSN: no matching PMKID found");
+                       abort_cached = 1;
+               }
+       }
+
+       if (pmkid && sm->cur_pmksa &&
+           os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+               wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
+               wpa_sm_set_pmk_from_pmksa(sm);
+               wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
+                               sm->pmk, sm->pmk_len);
+               eapol_sm_notify_cached(sm->eapol);
+#ifdef CONFIG_IEEE80211R
+               sm->xxkey_len = 0;
+#endif /* CONFIG_IEEE80211R */
+       } else if (wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) && sm->eapol) {
+               int res, pmk_len;
+               pmk_len = PMK_LEN;
+               res = eapol_sm_get_key(sm->eapol, sm->pmk, PMK_LEN);
+               if (res) {
+                       /*
+                        * EAP-LEAP is an exception from other EAP methods: it
+                        * uses only 16-byte PMK.
+                        */
+                       res = eapol_sm_get_key(sm->eapol, sm->pmk, 16);
+                       pmk_len = 16;
+               } else {
+#ifdef CONFIG_IEEE80211R
+                       u8 buf[2 * PMK_LEN];
+                       if (eapol_sm_get_key(sm->eapol, buf, 2 * PMK_LEN) == 0)
+                       {
+                               os_memcpy(sm->xxkey, buf + PMK_LEN, PMK_LEN);
+                               sm->xxkey_len = PMK_LEN;
+                               os_memset(buf, 0, sizeof(buf));
+                       }
+#endif /* CONFIG_IEEE80211R */
+               }
+               if (res == 0) {
+                       wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
+                                       "machines", sm->pmk, pmk_len);
+                       sm->pmk_len = pmk_len;
+                       if (sm->proto == WPA_PROTO_RSN) {
+                               pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len,
+                                               src_addr, sm->own_addr,
+                                               sm->network_ctx, sm->key_mgmt);
+                       }
+                       if (!sm->cur_pmksa && pmkid &&
+                           pmksa_cache_get(sm->pmksa, src_addr, pmkid)) {
+                               wpa_printf(MSG_DEBUG, "RSN: the new PMK "
+                                          "matches with the PMKID");
+                               abort_cached = 0;
+                       }
+               } else {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Failed to get master session key from "
+                               "EAPOL state machines");
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Key handshake aborted");
+                       if (sm->cur_pmksa) {
+                               wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA "
+                                          "caching attempt");
+                               sm->cur_pmksa = NULL;
+                               abort_cached = 1;
+                       } else if (!abort_cached) {
+                               return -1;
+                       }
+               }
+       }
+
+       if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) {
+               /* Send EAPOL-Start to trigger full EAP authentication. */
+               u8 *buf;
+               size_t buflen;
+
+               wpa_printf(MSG_DEBUG, "RSN: no PMKSA entry found - trigger "
+                          "full EAP authentication");
+               buf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_START,
+                                        NULL, 0, &buflen, NULL);
+               if (buf) {
+                       wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL,
+                                         buf, buflen);
+                       os_free(buf);
+                       return -2;
+               }
+
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_2_of_4 - Send message 2 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @nonce: Nonce value for the EAPOL-Key frame
+ * @wpa_ie: WPA/RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+                              const struct wpa_eapol_key *key,
+                              int ver, const u8 *nonce,
+                              const u8 *wpa_ie, size_t wpa_ie_len,
+                              struct wpa_ptk *ptk)
+{
+       size_t rlen;
+       struct wpa_eapol_key *reply;
+       u8 *rbuf;
+       u8 *rsn_ie_buf = NULL;
+
+       if (wpa_ie == NULL) {
+               wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot "
+                          "generate msg 2/4");
+               return -1;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+               int res;
+
+               /*
+                * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and
+                * FTIE from (Re)Association Response.
+                */
+               rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN +
+                                      sm->assoc_resp_ies_len);
+               if (rsn_ie_buf == NULL)
+                       return -1;
+               os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len);
+               res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len,
+                                      sm->pmk_r1_name);
+               if (res < 0) {
+                       os_free(rsn_ie_buf);
+                       return -1;
+               }
+               wpa_ie_len += res;
+
+               if (sm->assoc_resp_ies) {
+                       os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies,
+                                 sm->assoc_resp_ies_len);
+                       wpa_ie_len += sm->assoc_resp_ies_len;
+               }
+
+               wpa_ie = rsn_ie_buf;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
+                                 NULL, sizeof(*reply) + wpa_ie_len,
+                                 &rlen, (void *) &reply);
+       if (rbuf == NULL) {
+               os_free(rsn_ie_buf);
+               return -1;
+       }
+
+       reply->type = sm->proto == WPA_PROTO_RSN ?
+               EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+       WPA_PUT_BE16(reply->key_info,
+                    ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
+       if (sm->proto == WPA_PROTO_RSN)
+               WPA_PUT_BE16(reply->key_length, 0);
+       else
+               os_memcpy(reply->key_length, key->key_length, 2);
+       os_memcpy(reply->replay_counter, key->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+       os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+       os_free(rsn_ie_buf);
+
+       os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
+
+       wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
+       wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
+                          rbuf, rlen, reply->key_mic);
+
+       return 0;
+}
+
+
+static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
+                         const struct wpa_eapol_key *key,
+                         struct wpa_ptk *ptk)
+{
+       size_t ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt))
+               return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
+#endif /* CONFIG_IEEE80211R */
+
+       wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+                      sm->own_addr, sm->bssid, sm->snonce, key->key_nonce,
+                      (u8 *) ptk, ptk_len,
+                      wpa_key_mgmt_sha256(sm->key_mgmt));
+       return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
+                                         const unsigned char *src_addr,
+                                         const struct wpa_eapol_key *key,
+                                         u16 ver)
+{
+       struct wpa_eapol_ie_parse ie;
+       struct wpa_ptk *ptk;
+       u8 buf[8];
+       int res;
+
+       if (wpa_sm_get_network_ctx(sm) == NULL) {
+               wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of "
+                          "4).");
+               return;
+       }
+
+       wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+       wpa_printf(MSG_DEBUG, "WPA: RX message 1 of 4-Way Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+       os_memset(&ie, 0, sizeof(ie));
+
+#ifndef CONFIG_NO_WPA2
+       if (sm->proto == WPA_PROTO_RSN) {
+               /* RSN: msg 1/4 should contain PMKID for the selected PMK */
+               const u8 *_buf = (const u8 *) (key + 1);
+               size_t len = WPA_GET_BE16(key->key_data_length);
+               wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
+               wpa_supplicant_parse_ies(_buf, len, &ie);
+               if (ie.pmkid) {
+                       wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
+                                   "Authenticator", ie.pmkid, PMKID_LEN);
+               }
+       }
+#endif /* CONFIG_NO_WPA2 */
+
+       res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid);
+       if (res == -2) {
+               wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - "
+                          "requesting full EAP authentication");
+               return;
+       }
+       if (res)
+               goto failed;
+
+       if (sm->renew_snonce) {
+               if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+                               "WPA: Failed to get random data for SNonce");
+                       goto failed;
+               }
+               sm->renew_snonce = 0;
+               wpa_hexdump(MSG_DEBUG, "WPA: Renewed SNonce",
+                           sm->snonce, WPA_NONCE_LEN);
+       }
+
+       /* Calculate PTK which will be stored as a temporary PTK until it has
+        * been verified when processing message 3/4. */
+       ptk = &sm->tptk;
+       wpa_derive_ptk(sm, src_addr, key, ptk);
+       /* Supplicant: swap tx/rx Mic keys */
+       os_memcpy(buf, ptk->u.auth.tx_mic_key, 8);
+       os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
+       os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+       sm->tptk_set = 1;
+
+       if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
+                                      sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
+                                      ptk))
+               goto failed;
+
+       os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
+       return;
+
+failed:
+       wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_sm *sm = eloop_ctx;
+       rsn_preauth_candidate_process(sm);
+}
+
+
+static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
+                                           const u8 *addr, int secure)
+{
+       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+               "WPA: Key negotiation completed with "
+               MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr),
+               wpa_cipher_txt(sm->pairwise_cipher),
+               wpa_cipher_txt(sm->group_cipher));
+       wpa_sm_cancel_auth_timeout(sm);
+       wpa_sm_set_state(sm, WPA_COMPLETED);
+
+       if (secure) {
+               wpa_sm_mlme_setprotection(
+                       sm, addr, MLME_SETPROTECTION_PROTECT_TYPE_RX_TX,
+                       MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+               eapol_sm_notify_portValid(sm->eapol, TRUE);
+               if (wpa_key_mgmt_wpa_psk(sm->key_mgmt))
+                       eapol_sm_notify_eap_success(sm->eapol, TRUE);
+               /*
+                * Start preauthentication after a short wait to avoid a
+                * possible race condition between the data receive and key
+                * configuration after the 4-Way Handshake. This increases the
+                * likelyhood of the first preauth EAPOL-Start frame getting to
+                * the target AP.
+                */
+               eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+       }
+
+       if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
+               wpa_printf(MSG_DEBUG, "RSN: Authenticator accepted "
+                          "opportunistic PMKSA entry - marking it valid");
+               sm->cur_pmksa->opportunistic = 0;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+               /* Prepare for the next transition */
+               wpa_ft_prepare_auth_request(sm, NULL);
+       }
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+static void wpa_sm_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_sm *sm = eloop_ctx;
+       wpa_printf(MSG_DEBUG, "WPA: Request PTK rekeying");
+       wpa_sm_key_request(sm, 0, 1);
+}
+
+
+static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
+                                     const struct wpa_eapol_key *key)
+{
+       int keylen, rsclen;
+       enum wpa_alg alg;
+       const u8 *key_rsc;
+       u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
+
+       wpa_printf(MSG_DEBUG, "WPA: Installing PTK to the driver.");
+
+       switch (sm->pairwise_cipher) {
+       case WPA_CIPHER_CCMP:
+               alg = WPA_ALG_CCMP;
+               keylen = 16;
+               rsclen = 6;
+               break;
+       case WPA_CIPHER_TKIP:
+               alg = WPA_ALG_TKIP;
+               keylen = 32;
+               rsclen = 6;
+               break;
+       case WPA_CIPHER_NONE:
+               wpa_printf(MSG_DEBUG, "WPA: Pairwise Cipher Suite: "
+                          "NONE - do not use pairwise keys");
+               return 0;
+       default:
+               wpa_printf(MSG_WARNING, "WPA: Unsupported pairwise cipher %d",
+                          sm->pairwise_cipher);
+               return -1;
+       }
+
+       if (sm->proto == WPA_PROTO_RSN) {
+               key_rsc = null_rsc;
+       } else {
+               key_rsc = key->key_rsc;
+               wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, rsclen);
+       }
+
+       if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
+                          (u8 *) sm->ptk.tk1, keylen) < 0) {
+               wpa_printf(MSG_WARNING, "WPA: Failed to set PTK to the "
+                          "driver (alg=%d keylen=%d bssid=" MACSTR ")",
+                          alg, keylen, MAC2STR(sm->bssid));
+               return -1;
+       }
+
+       if (sm->wpa_ptk_rekey) {
+               eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+               eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
+                                      sm, NULL);
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_check_group_cipher(int group_cipher,
+                                            int keylen, int maxkeylen,
+                                            int *key_rsc_len,
+                                            enum wpa_alg *alg)
+{
+       int ret = 0;
+
+       switch (group_cipher) {
+       case WPA_CIPHER_CCMP:
+               if (keylen != 16 || maxkeylen < 16) {
+                       ret = -1;
+                       break;
+               }
+               *key_rsc_len = 6;
+               *alg = WPA_ALG_CCMP;
+               break;
+       case WPA_CIPHER_TKIP:
+               if (keylen != 32 || maxkeylen < 32) {
+                       ret = -1;
+                       break;
+               }
+               *key_rsc_len = 6;
+               *alg = WPA_ALG_TKIP;
+               break;
+       case WPA_CIPHER_WEP104:
+               if (keylen != 13 || maxkeylen < 13) {
+                       ret = -1;
+                       break;
+               }
+               *key_rsc_len = 0;
+               *alg = WPA_ALG_WEP;
+               break;
+       case WPA_CIPHER_WEP40:
+               if (keylen != 5 || maxkeylen < 5) {
+                       ret = -1;
+                       break;
+               }
+               *key_rsc_len = 0;
+               *alg = WPA_ALG_WEP;
+               break;
+       default:
+               wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+                          group_cipher);
+               return -1;
+       }
+
+       if (ret < 0 ) {
+               wpa_printf(MSG_WARNING, "WPA: Unsupported %s Group Cipher key "
+                          "length %d (%d).",
+                          wpa_cipher_txt(group_cipher), keylen, maxkeylen);
+       }
+
+       return ret;
+}
+
+
+struct wpa_gtk_data {
+       enum wpa_alg alg;
+       int tx, key_rsc_len, keyidx;
+       u8 gtk[32];
+       int gtk_len;
+};
+
+
+static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
+                                     const struct wpa_gtk_data *gd,
+                                     const u8 *key_rsc)
+{
+       const u8 *_gtk = gd->gtk;
+       u8 gtk_buf[32];
+
+       wpa_hexdump_key(MSG_DEBUG, "WPA: Group Key", gd->gtk, gd->gtk_len);
+       wpa_printf(MSG_DEBUG, "WPA: Installing GTK to the driver "
+                  "(keyidx=%d tx=%d len=%d).", gd->keyidx, gd->tx,
+                  gd->gtk_len);
+       wpa_hexdump(MSG_DEBUG, "WPA: RSC", key_rsc, gd->key_rsc_len);
+       if (sm->group_cipher == WPA_CIPHER_TKIP) {
+               /* Swap Tx/Rx keys for Michael MIC */
+               os_memcpy(gtk_buf, gd->gtk, 16);
+               os_memcpy(gtk_buf + 16, gd->gtk + 24, 8);
+               os_memcpy(gtk_buf + 24, gd->gtk + 16, 8);
+               _gtk = gtk_buf;
+       }
+       if (sm->pairwise_cipher == WPA_CIPHER_NONE) {
+               if (wpa_sm_set_key(sm, gd->alg,
+                                  (u8 *) "\xff\xff\xff\xff\xff\xff",
+                                  gd->keyidx, 1, key_rsc, gd->key_rsc_len,
+                                  _gtk, gd->gtk_len) < 0) {
+                       wpa_printf(MSG_WARNING, "WPA: Failed to set "
+                                  "GTK to the driver (Group only).");
+                       return -1;
+               }
+       } else if (wpa_sm_set_key(sm, gd->alg,
+                                 (u8 *) "\xff\xff\xff\xff\xff\xff",
+                                 gd->keyidx, gd->tx, key_rsc, gd->key_rsc_len,
+                                 _gtk, gd->gtk_len) < 0) {
+               wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to "
+                          "the driver (alg=%d keylen=%d keyidx=%d)",
+                          gd->alg, gd->gtk_len, gd->keyidx);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_gtk_tx_bit_workaround(const struct wpa_sm *sm,
+                                               int tx)
+{
+       if (tx && sm->pairwise_cipher != WPA_CIPHER_NONE) {
+               /* Ignore Tx bit for GTK if a pairwise key is used. One AP
+                * seemed to set this bit (incorrectly, since Tx is only when
+                * doing Group Key only APs) and without this workaround, the
+                * data connection does not work because wpa_supplicant
+                * configured non-zero keyidx to be used for unicast. */
+               wpa_printf(MSG_INFO, "WPA: Tx bit set for GTK, but pairwise "
+                          "keys are used - ignore Tx bit");
+               return 0;
+       }
+       return tx;
+}
+
+
+static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
+                                      const struct wpa_eapol_key *key,
+                                      const u8 *gtk, size_t gtk_len,
+                                      int key_info)
+{
+#ifndef CONFIG_NO_WPA2
+       struct wpa_gtk_data gd;
+
+       /*
+        * IEEE Std 802.11i-2004 - 8.5.2 EAPOL-Key frames - Figure 43x
+        * GTK KDE format:
+        * KeyID[bits 0-1], Tx [bit 2], Reserved [bits 3-7]
+        * Reserved [bits 0-7]
+        * GTK
+        */
+
+       os_memset(&gd, 0, sizeof(gd));
+       wpa_hexdump_key(MSG_DEBUG, "RSN: received GTK in pairwise handshake",
+                       gtk, gtk_len);
+
+       if (gtk_len < 2 || gtk_len - 2 > sizeof(gd.gtk))
+               return -1;
+
+       gd.keyidx = gtk[0] & 0x3;
+       gd.tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+                                                    !!(gtk[0] & BIT(2)));
+       gtk += 2;
+       gtk_len -= 2;
+
+       os_memcpy(gd.gtk, gtk, gtk_len);
+       gd.gtk_len = gtk_len;
+
+       if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+                                             gtk_len, gtk_len,
+                                             &gd.key_rsc_len, &gd.alg) ||
+           wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
+               wpa_printf(MSG_DEBUG, "RSN: Failed to install GTK");
+               return -1;
+       }
+
+       wpa_supplicant_key_neg_complete(sm, sm->bssid,
+                                       key_info & WPA_KEY_INFO_SECURE);
+       return 0;
+#else /* CONFIG_NO_WPA2 */
+       return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+static int ieee80211w_set_keys(struct wpa_sm *sm,
+                              struct wpa_eapol_ie_parse *ie)
+{
+#ifdef CONFIG_IEEE80211W
+       if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+               return 0;
+
+       if (ie->igtk) {
+               const struct wpa_igtk_kde *igtk;
+               u16 keyidx;
+               if (ie->igtk_len != sizeof(*igtk))
+                       return -1;
+               igtk = (const struct wpa_igtk_kde *) ie->igtk;
+               keyidx = WPA_GET_LE16(igtk->keyid);
+               wpa_printf(MSG_DEBUG, "WPA: IGTK keyid %d "
+                          "pn %02x%02x%02x%02x%02x%02x",
+                          keyidx, MAC2STR(igtk->pn));
+               wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
+                               igtk->igtk, WPA_IGTK_LEN);
+               if (keyidx > 4095) {
+                       wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KeyID %d",
+                                  keyidx);
+                       return -1;
+               }
+               if (wpa_sm_set_key(sm, WPA_ALG_IGTK,
+                                  (u8 *) "\xff\xff\xff\xff\xff\xff",
+                                  keyidx, 0, igtk->pn, sizeof(igtk->pn),
+                                  igtk->igtk, WPA_IGTK_LEN) < 0) {
+                       wpa_printf(MSG_WARNING, "WPA: Failed to configure IGTK"
+                                  " to the driver");
+                       return -1;
+               }
+       }
+
+       return 0;
+#else /* CONFIG_IEEE80211W */
+       return 0;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
+static void wpa_report_ie_mismatch(struct wpa_sm *sm,
+                                  const char *reason, const u8 *src_addr,
+                                  const u8 *wpa_ie, size_t wpa_ie_len,
+                                  const u8 *rsn_ie, size_t rsn_ie_len)
+{
+       wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")",
+               reason, MAC2STR(src_addr));
+
+       if (sm->ap_wpa_ie) {
+               wpa_hexdump(MSG_INFO, "WPA: WPA IE in Beacon/ProbeResp",
+                           sm->ap_wpa_ie, sm->ap_wpa_ie_len);
+       }
+       if (wpa_ie) {
+               if (!sm->ap_wpa_ie) {
+                       wpa_printf(MSG_INFO, "WPA: No WPA IE in "
+                                  "Beacon/ProbeResp");
+               }
+               wpa_hexdump(MSG_INFO, "WPA: WPA IE in 3/4 msg",
+                           wpa_ie, wpa_ie_len);
+       }
+
+       if (sm->ap_rsn_ie) {
+               wpa_hexdump(MSG_INFO, "WPA: RSN IE in Beacon/ProbeResp",
+                           sm->ap_rsn_ie, sm->ap_rsn_ie_len);
+       }
+       if (rsn_ie) {
+               if (!sm->ap_rsn_ie) {
+                       wpa_printf(MSG_INFO, "WPA: No RSN IE in "
+                                  "Beacon/ProbeResp");
+               }
+               wpa_hexdump(MSG_INFO, "WPA: RSN IE in 3/4 msg",
+                           rsn_ie, rsn_ie_len);
+       }
+
+       wpa_sm_disassociate(sm, WLAN_REASON_IE_IN_4WAY_DIFFERS);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+static int ft_validate_mdie(struct wpa_sm *sm,
+                           const unsigned char *src_addr,
+                           struct wpa_eapol_ie_parse *ie,
+                           const u8 *assoc_resp_mdie)
+{
+       struct rsn_mdie *mdie;
+
+       mdie = (struct rsn_mdie *) (ie->mdie + 2);
+       if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) ||
+           os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+                     MOBILITY_DOMAIN_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not "
+                          "match with the current mobility domain");
+               return -1;
+       }
+
+       if (assoc_resp_mdie &&
+           (assoc_resp_mdie[1] != ie->mdie[1] ||
+            os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) {
+               wpa_printf(MSG_DEBUG, "FT: MDIE mismatch");
+               wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4",
+                           ie->mdie, 2 + ie->mdie[1]);
+               wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response",
+                           assoc_resp_mdie, 2 + assoc_resp_mdie[1]);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int ft_validate_ftie(struct wpa_sm *sm,
+                           const unsigned char *src_addr,
+                           struct wpa_eapol_ie_parse *ie,
+                           const u8 *assoc_resp_ftie)
+{
+       if (ie->ftie == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4");
+               return -1;
+       }
+
+       if (assoc_resp_ftie == NULL)
+               return 0;
+
+       if (assoc_resp_ftie[1] != ie->ftie[1] ||
+           os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: FTIE mismatch");
+               wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4",
+                           ie->ftie, 2 + ie->ftie[1]);
+               wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response",
+                           assoc_resp_ftie, 2 + assoc_resp_ftie[1]);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int ft_validate_rsnie(struct wpa_sm *sm,
+                            const unsigned char *src_addr,
+                            struct wpa_eapol_ie_parse *ie)
+{
+       struct wpa_ie_data rsn;
+
+       if (!ie->rsn_ie)
+               return 0;
+
+       /*
+        * Verify that PMKR1Name from EAPOL-Key message 3/4
+        * matches with the value we derived.
+        */
+       if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 ||
+           rsn.num_pmkid != 1 || rsn.pmkid == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in "
+                          "FT 4-way handshake message 3/4");
+               return -1;
+       }
+
+       if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in "
+                          "FT 4-way handshake message 3/4");
+               wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator",
+                           rsn.pmkid, WPA_PMK_NAME_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name",
+                           sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm,
+                                        const unsigned char *src_addr,
+                                        struct wpa_eapol_ie_parse *ie)
+{
+       const u8 *pos, *end, *mdie = NULL, *ftie = NULL;
+
+       if (sm->assoc_resp_ies) {
+               pos = sm->assoc_resp_ies;
+               end = pos + sm->assoc_resp_ies_len;
+               while (pos + 2 < end) {
+                       if (pos + 2 + pos[1] > end)
+                               break;
+                       switch (*pos) {
+                       case WLAN_EID_MOBILITY_DOMAIN:
+                               mdie = pos;
+                               break;
+                       case WLAN_EID_FAST_BSS_TRANSITION:
+                               ftie = pos;
+                               break;
+                       }
+                       pos += 2 + pos[1];
+               }
+       }
+
+       if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 ||
+           ft_validate_ftie(sm, src_addr, ie, ftie) < 0 ||
+           ft_validate_rsnie(sm, src_addr, ie) < 0)
+               return -1;
+
+       return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
+                                     const unsigned char *src_addr,
+                                     struct wpa_eapol_ie_parse *ie)
+{
+       if (sm->ap_wpa_ie == NULL && sm->ap_rsn_ie == NULL) {
+               wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE for this AP known. "
+                          "Trying to get from scan results");
+               if (wpa_sm_get_beacon_ie(sm) < 0) {
+                       wpa_printf(MSG_WARNING, "WPA: Could not find AP from "
+                                  "the scan results");
+               } else {
+                       wpa_printf(MSG_DEBUG, "WPA: Found the current AP from "
+                                  "updated scan results");
+               }
+       }
+
+       if (ie->wpa_ie == NULL && ie->rsn_ie == NULL &&
+           (sm->ap_wpa_ie || sm->ap_rsn_ie)) {
+               wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+                                      "with IE in Beacon/ProbeResp (no IE?)",
+                                      src_addr, ie->wpa_ie, ie->wpa_ie_len,
+                                      ie->rsn_ie, ie->rsn_ie_len);
+               return -1;
+       }
+
+       if ((ie->wpa_ie && sm->ap_wpa_ie &&
+            (ie->wpa_ie_len != sm->ap_wpa_ie_len ||
+             os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) ||
+           (ie->rsn_ie && sm->ap_rsn_ie &&
+            wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt),
+                               sm->ap_rsn_ie, sm->ap_rsn_ie_len,
+                               ie->rsn_ie, ie->rsn_ie_len))) {
+               wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match "
+                                      "with IE in Beacon/ProbeResp",
+                                      src_addr, ie->wpa_ie, ie->wpa_ie_len,
+                                      ie->rsn_ie, ie->rsn_ie_len);
+               return -1;
+       }
+
+       if (sm->proto == WPA_PROTO_WPA &&
+           ie->rsn_ie && sm->ap_rsn_ie == NULL && sm->rsn_enabled) {
+               wpa_report_ie_mismatch(sm, "Possible downgrade attack "
+                                      "detected - RSN was enabled and RSN IE "
+                                      "was in msg 3/4, but not in "
+                                      "Beacon/ProbeResp",
+                                      src_addr, ie->wpa_ie, ie->wpa_ie_len,
+                                      ie->rsn_ie, ie->rsn_ie_len);
+               return -1;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt) &&
+           wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0)
+               return -1;
+#endif /* CONFIG_IEEE80211R */
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_send_4_of_4 - Send message 4 of WPA/RSN 4-Way Handshake
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @dst: Destination address for the frame
+ * @key: Pointer to the EAPOL-Key frame header
+ * @ver: Version bits from EAPOL-Key Key Info
+ * @key_info: Key Info
+ * @kde: KDEs to include the EAPOL-Key frame
+ * @kde_len: Length of KDEs
+ * @ptk: PTK to use for keyed hash and encryption
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
+                              const struct wpa_eapol_key *key,
+                              u16 ver, u16 key_info,
+                              const u8 *kde, size_t kde_len,
+                              struct wpa_ptk *ptk)
+{
+       size_t rlen;
+       struct wpa_eapol_key *reply;
+       u8 *rbuf;
+
+       if (kde)
+               wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len);
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+                                 sizeof(*reply) + kde_len,
+                                 &rlen, (void *) &reply);
+       if (rbuf == NULL)
+               return -1;
+
+       reply->type = sm->proto == WPA_PROTO_RSN ?
+               EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+       key_info &= WPA_KEY_INFO_SECURE;
+       key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
+       WPA_PUT_BE16(reply->key_info, key_info);
+       if (sm->proto == WPA_PROTO_RSN)
+               WPA_PUT_BE16(reply->key_length, 0);
+       else
+               os_memcpy(reply->key_length, key->key_length, 2);
+       os_memcpy(reply->replay_counter, key->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(reply->key_data_length, kde_len);
+       if (kde)
+               os_memcpy(reply + 1, kde, kde_len);
+
+       wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
+       wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
+                          rbuf, rlen, reply->key_mic);
+
+       return 0;
+}
+
+
+static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
+                                         const struct wpa_eapol_key *key,
+                                         u16 ver)
+{
+       u16 key_info, keylen, len;
+       const u8 *pos;
+       struct wpa_eapol_ie_parse ie;
+
+       wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
+       wpa_printf(MSG_DEBUG, "WPA: RX message 3 of 4-Way Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(sm->bssid), ver);
+
+       key_info = WPA_GET_BE16(key->key_info);
+
+       pos = (const u8 *) (key + 1);
+       len = WPA_GET_BE16(key->key_data_length);
+       wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
+       wpa_supplicant_parse_ies(pos, len, &ie);
+       if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+               wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+               goto failed;
+       }
+#ifdef CONFIG_IEEE80211W
+       if (ie.igtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+               wpa_printf(MSG_WARNING, "WPA: IGTK KDE in unencrypted key "
+                          "data");
+               goto failed;
+       }
+
+       if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
+               wpa_printf(MSG_WARNING, "WPA: Invalid IGTK KDE length %lu",
+                          (unsigned long) ie.igtk_len);
+               goto failed;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       if (wpa_supplicant_validate_ie(sm, sm->bssid, &ie) < 0)
+               goto failed;
+
+       if (os_memcmp(sm->anonce, key->key_nonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_WARNING, "WPA: ANonce from message 1 of 4-Way "
+                          "Handshake differs from 3 of 4-Way Handshake - drop"
+                          " packet (src=" MACSTR ")", MAC2STR(sm->bssid));
+               goto failed;
+       }
+
+       keylen = WPA_GET_BE16(key->key_length);
+       switch (sm->pairwise_cipher) {
+       case WPA_CIPHER_CCMP:
+               if (keylen != 16) {
+                       wpa_printf(MSG_WARNING, "WPA: Invalid CCMP key length "
+                                  "%d (src=" MACSTR ")",
+                                  keylen, MAC2STR(sm->bssid));
+                       goto failed;
+               }
+               break;
+       case WPA_CIPHER_TKIP:
+               if (keylen != 32) {
+                       wpa_printf(MSG_WARNING, "WPA: Invalid TKIP key length "
+                                  "%d (src=" MACSTR ")",
+                                  keylen, MAC2STR(sm->bssid));
+                       goto failed;
+               }
+               break;
+       }
+
+       if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
+                                      NULL, 0, &sm->ptk)) {
+               goto failed;
+       }
+
+       /* SNonce was successfully used in msg 3/4, so mark it to be renewed
+        * for the next 4-Way Handshake. If msg 3 is received again, the old
+        * SNonce will still be used to avoid changing PTK. */
+       sm->renew_snonce = 1;
+
+       if (key_info & WPA_KEY_INFO_INSTALL) {
+               if (wpa_supplicant_install_ptk(sm, key))
+                       goto failed;
+       }
+
+       if (key_info & WPA_KEY_INFO_SECURE) {
+               wpa_sm_mlme_setprotection(
+                       sm, sm->bssid, MLME_SETPROTECTION_PROTECT_TYPE_RX,
+                       MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+               eapol_sm_notify_portValid(sm->eapol, TRUE);
+       }
+       wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+       if (ie.gtk &&
+           wpa_supplicant_pairwise_gtk(sm, key,
+                                       ie.gtk, ie.gtk_len, key_info) < 0) {
+               wpa_printf(MSG_INFO, "RSN: Failed to configure GTK");
+               goto failed;
+       }
+
+       if (ieee80211w_set_keys(sm, &ie) < 0) {
+               wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+               goto failed;
+       }
+
+       return;
+
+failed:
+       wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
+                                            const u8 *keydata,
+                                            size_t keydatalen,
+                                            u16 key_info,
+                                            struct wpa_gtk_data *gd)
+{
+       int maxkeylen;
+       struct wpa_eapol_ie_parse ie;
+
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 1/2 key data", keydata, keydatalen);
+       wpa_supplicant_parse_ies(keydata, keydatalen, &ie);
+       if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+               wpa_printf(MSG_WARNING, "WPA: GTK IE in unencrypted key data");
+               return -1;
+       }
+       if (ie.gtk == NULL) {
+               wpa_printf(MSG_INFO, "WPA: No GTK IE in Group Key msg 1/2");
+               return -1;
+       }
+       maxkeylen = gd->gtk_len = ie.gtk_len - 2;
+
+       if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+                                             gd->gtk_len, maxkeylen,
+                                             &gd->key_rsc_len, &gd->alg))
+               return -1;
+
+       wpa_hexdump(MSG_DEBUG, "RSN: received GTK in group key handshake",
+                   ie.gtk, ie.gtk_len);
+       gd->keyidx = ie.gtk[0] & 0x3;
+       gd->tx = wpa_supplicant_gtk_tx_bit_workaround(sm,
+                                                     !!(ie.gtk[0] & BIT(2)));
+       if (ie.gtk_len - 2 > sizeof(gd->gtk)) {
+               wpa_printf(MSG_INFO, "RSN: Too long GTK in GTK IE "
+                          "(len=%lu)", (unsigned long) ie.gtk_len - 2);
+               return -1;
+       }
+       os_memcpy(gd->gtk, ie.gtk + 2, ie.gtk_len - 2);
+
+       if (ieee80211w_set_keys(sm, &ie) < 0)
+               wpa_printf(MSG_INFO, "RSN: Failed to configure IGTK");
+
+       return 0;
+}
+
+
+static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
+                                            const struct wpa_eapol_key *key,
+                                            size_t keydatalen, int key_info,
+                                            size_t extra_len, u16 ver,
+                                            struct wpa_gtk_data *gd)
+{
+       size_t maxkeylen;
+       u8 ek[32];
+
+       gd->gtk_len = WPA_GET_BE16(key->key_length);
+       maxkeylen = keydatalen;
+       if (keydatalen > extra_len) {
+               wpa_printf(MSG_INFO, "WPA: Truncated EAPOL-Key packet:"
+                          " key_data_length=%lu > extra_len=%lu",
+                          (unsigned long) keydatalen,
+                          (unsigned long) extra_len);
+               return -1;
+       }
+       if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+               if (maxkeylen < 8) {
+                       wpa_printf(MSG_INFO, "WPA: Too short maxkeylen (%lu)",
+                                  (unsigned long) maxkeylen);
+                       return -1;
+               }
+               maxkeylen -= 8;
+       }
+
+       if (wpa_supplicant_check_group_cipher(sm->group_cipher,
+                                             gd->gtk_len, maxkeylen,
+                                             &gd->key_rsc_len, &gd->alg))
+               return -1;
+
+       gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+               WPA_KEY_INFO_KEY_INDEX_SHIFT;
+       if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+               os_memcpy(ek, key->key_iv, 16);
+               os_memcpy(ek + 16, sm->ptk.kek, 16);
+               if (keydatalen > sizeof(gd->gtk)) {
+                       wpa_printf(MSG_WARNING, "WPA: RC4 key data "
+                                  "too long (%lu)",
+                                  (unsigned long) keydatalen);
+                       return -1;
+               }
+               os_memcpy(gd->gtk, key + 1, keydatalen);
+               if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
+                       wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+                       return -1;
+               }
+       } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+               if (keydatalen % 8) {
+                       wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP "
+                                  "len %lu", (unsigned long) keydatalen);
+                       return -1;
+               }
+               if (maxkeylen > sizeof(gd->gtk)) {
+                       wpa_printf(MSG_WARNING, "WPA: AES-WRAP key data "
+                                  "too long (keydatalen=%lu maxkeylen=%lu)",
+                                  (unsigned long) keydatalen,
+                                  (unsigned long) maxkeylen);
+                       return -1;
+               }
+               if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
+                              (const u8 *) (key + 1), gd->gtk)) {
+                       wpa_printf(MSG_WARNING, "WPA: AES unwrap "
+                                  "failed - could not decrypt GTK");
+                       return -1;
+               }
+       } else {
+               wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
+                          ver);
+               return -1;
+       }
+       gd->tx = wpa_supplicant_gtk_tx_bit_workaround(
+               sm, !!(key_info & WPA_KEY_INFO_TXRX));
+       return 0;
+}
+
+
+static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
+                                     const struct wpa_eapol_key *key,
+                                     int ver, u16 key_info)
+{
+       size_t rlen;
+       struct wpa_eapol_key *reply;
+       u8 *rbuf;
+
+       rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
+                                 sizeof(*reply), &rlen, (void *) &reply);
+       if (rbuf == NULL)
+               return -1;
+
+       reply->type = sm->proto == WPA_PROTO_RSN ?
+               EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
+       key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
+       key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
+       WPA_PUT_BE16(reply->key_info, key_info);
+       if (sm->proto == WPA_PROTO_RSN)
+               WPA_PUT_BE16(reply->key_length, 0);
+       else
+               os_memcpy(reply->key_length, key->key_length, 2);
+       os_memcpy(reply->replay_counter, key->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+
+       WPA_PUT_BE16(reply->key_data_length, 0);
+
+       wpa_printf(MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
+       wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL,
+                          rbuf, rlen, reply->key_mic);
+
+       return 0;
+}
+
+
+static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
+                                         const unsigned char *src_addr,
+                                         const struct wpa_eapol_key *key,
+                                         int extra_len, u16 ver)
+{
+       u16 key_info, keydatalen;
+       int rekey, ret;
+       struct wpa_gtk_data gd;
+
+       os_memset(&gd, 0, sizeof(gd));
+
+       rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
+       wpa_printf(MSG_DEBUG, "WPA: RX message 1 of Group Key Handshake from "
+                  MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
+
+       key_info = WPA_GET_BE16(key->key_info);
+       keydatalen = WPA_GET_BE16(key->key_data_length);
+
+       if (sm->proto == WPA_PROTO_RSN) {
+               ret = wpa_supplicant_process_1_of_2_rsn(sm,
+                                                       (const u8 *) (key + 1),
+                                                       keydatalen, key_info,
+                                                       &gd);
+       } else {
+               ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen,
+                                                       key_info, extra_len,
+                                                       ver, &gd);
+       }
+
+       wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
+
+       if (ret)
+               goto failed;
+
+       if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
+           wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
+               goto failed;
+
+       if (rekey) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
+                       "completed with " MACSTR " [GTK=%s]",
+                       MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
+               wpa_sm_cancel_auth_timeout(sm);
+               wpa_sm_set_state(sm, WPA_COMPLETED);
+       } else {
+               wpa_supplicant_key_neg_complete(sm, sm->bssid,
+                                               key_info &
+                                               WPA_KEY_INFO_SECURE);
+       }
+       return;
+
+failed:
+       wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+}
+
+
+static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
+                                              struct wpa_eapol_key *key,
+                                              u16 ver,
+                                              const u8 *buf, size_t len)
+{
+       u8 mic[16];
+       int ok = 0;
+
+       os_memcpy(mic, key->key_mic, 16);
+       if (sm->tptk_set) {
+               os_memset(key->key_mic, 0, 16);
+               wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
+                                 key->key_mic);
+               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+                       wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+                                  "when using TPTK - ignoring TPTK");
+               } else {
+                       ok = 1;
+                       sm->tptk_set = 0;
+                       sm->ptk_set = 1;
+                       os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+               }
+       }
+
+       if (!ok && sm->ptk_set) {
+               os_memset(key->key_mic, 0, 16);
+               wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
+                                 key->key_mic);
+               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+                       wpa_printf(MSG_WARNING, "WPA: Invalid EAPOL-Key MIC "
+                                  "- dropping packet");
+                       return -1;
+               }
+               ok = 1;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_WARNING, "WPA: Could not verify EAPOL-Key MIC "
+                          "- dropping packet");
+               return -1;
+       }
+
+       os_memcpy(sm->rx_replay_counter, key->replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       sm->rx_replay_counter_set = 1;
+       return 0;
+}
+
+
+/* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
+static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
+                                          struct wpa_eapol_key *key, u16 ver)
+{
+       u16 keydatalen = WPA_GET_BE16(key->key_data_length);
+
+       wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
+                   (u8 *) (key + 1), keydatalen);
+       if (!sm->ptk_set) {
+               wpa_printf(MSG_WARNING, "WPA: PTK not available, "
+                          "cannot decrypt EAPOL-Key key data.");
+               return -1;
+       }
+
+       /* Decrypt key data here so that this operation does not need
+        * to be implemented separately for each message type. */
+       if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+               u8 ek[32];
+               os_memcpy(ek, key->key_iv, 16);
+               os_memcpy(ek + 16, sm->ptk.kek, 16);
+               if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) {
+                       wpa_printf(MSG_ERROR, "WPA: RC4 failed");
+                       return -1;
+               }
+       } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+                  ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+               u8 *buf;
+               if (keydatalen % 8) {
+                       wpa_printf(MSG_WARNING, "WPA: Unsupported "
+                                  "AES-WRAP len %d", keydatalen);
+                       return -1;
+               }
+               keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+               buf = os_malloc(keydatalen);
+               if (buf == NULL) {
+                       wpa_printf(MSG_WARNING, "WPA: No memory for "
+                                  "AES-UNWRAP buffer");
+                       return -1;
+               }
+               if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
+                              (u8 *) (key + 1), buf)) {
+                       os_free(buf);
+                       wpa_printf(MSG_WARNING, "WPA: AES unwrap failed - "
+                                  "could not decrypt EAPOL-Key key data");
+                       return -1;
+               }
+               os_memcpy(key + 1, buf, keydatalen);
+               os_free(buf);
+               WPA_PUT_BE16(key->key_data_length, keydatalen);
+       } else {
+               wpa_printf(MSG_WARNING, "WPA: Unsupported key_info type %d",
+                          ver);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
+                       (u8 *) (key + 1), keydatalen);
+       return 0;
+}
+
+
+/**
+ * wpa_sm_aborted_cached - Notify WPA that PMKSA caching was aborted
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+       if (sm && sm->cur_pmksa) {
+               wpa_printf(MSG_DEBUG, "RSN: Cancelling PMKSA caching attempt");
+               sm->cur_pmksa = NULL;
+       }
+}
+
+
+static void wpa_eapol_key_dump(const struct wpa_eapol_key *key)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       u16 key_info = WPA_GET_BE16(key->key_info);
+
+       wpa_printf(MSG_DEBUG, "  EAPOL-Key type=%d", key->type);
+       wpa_printf(MSG_DEBUG, "  key_info 0x%x (ver=%d keyidx=%d rsvd=%d %s"
+                  "%s%s%s%s%s%s%s)",
+                  key_info, key_info & WPA_KEY_INFO_TYPE_MASK,
+                  (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+                  WPA_KEY_INFO_KEY_INDEX_SHIFT,
+                  (key_info & (BIT(13) | BIT(14) | BIT(15))) >> 13,
+                  key_info & WPA_KEY_INFO_KEY_TYPE ? "Pairwise" : "Group",
+                  key_info & WPA_KEY_INFO_INSTALL ? " Install" : "",
+                  key_info & WPA_KEY_INFO_ACK ? " Ack" : "",
+                  key_info & WPA_KEY_INFO_MIC ? " MIC" : "",
+                  key_info & WPA_KEY_INFO_SECURE ? " Secure" : "",
+                  key_info & WPA_KEY_INFO_ERROR ? " Error" : "",
+                  key_info & WPA_KEY_INFO_REQUEST ? " Request" : "",
+                  key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
+       wpa_printf(MSG_DEBUG, "  key_length=%u key_data_length=%u",
+                  WPA_GET_BE16(key->key_length),
+                  WPA_GET_BE16(key->key_data_length));
+       wpa_hexdump(MSG_DEBUG, "  replay_counter",
+                   key->replay_counter, WPA_REPLAY_COUNTER_LEN);
+       wpa_hexdump(MSG_DEBUG, "  key_nonce", key->key_nonce, WPA_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "  key_iv", key->key_iv, 16);
+       wpa_hexdump(MSG_DEBUG, "  key_rsc", key->key_rsc, 8);
+       wpa_hexdump(MSG_DEBUG, "  key_id (reserved)", key->key_id, 8);
+       wpa_hexdump(MSG_DEBUG, "  key_mic", key->key_mic, 16);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+/**
+ * wpa_sm_rx_eapol - Process received WPA EAPOL frames
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @src_addr: Source MAC address of the EAPOL packet
+ * @buf: Pointer to the beginning of the EAPOL data (EAPOL header)
+ * @len: Length of the EAPOL frame
+ * Returns: 1 = WPA EAPOL-Key processed, 0 = not a WPA EAPOL-Key, -1 failure
+ *
+ * This function is called for each received EAPOL frame. Other than EAPOL-Key
+ * frames can be skipped if filtering is done elsewhere. wpa_sm_rx_eapol() is
+ * only processing WPA and WPA2 EAPOL-Key frames.
+ *
+ * The received EAPOL-Key packets are validated and valid packets are replied
+ * to. In addition, key material (PTK, GTK) is configured at the end of a
+ * successful key handshake.
+ */
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+                   const u8 *buf, size_t len)
+{
+       size_t plen, data_len, extra_len;
+       struct ieee802_1x_hdr *hdr;
+       struct wpa_eapol_key *key;
+       u16 key_info, ver;
+       u8 *tmp;
+       int ret = -1;
+       struct wpa_peerkey *peerkey = NULL;
+
+#ifdef CONFIG_IEEE80211R
+       sm->ft_completed = 0;
+#endif /* CONFIG_IEEE80211R */
+
+       if (len < sizeof(*hdr) + sizeof(*key)) {
+               wpa_printf(MSG_DEBUG, "WPA: EAPOL frame too short to be a WPA "
+                          "EAPOL-Key (len %lu, expecting at least %lu)",
+                          (unsigned long) len,
+                          (unsigned long) sizeof(*hdr) + sizeof(*key));
+               return 0;
+       }
+
+       tmp = os_malloc(len);
+       if (tmp == NULL)
+               return -1;
+       os_memcpy(tmp, buf, len);
+
+       hdr = (struct ieee802_1x_hdr *) tmp;
+       key = (struct wpa_eapol_key *) (hdr + 1);
+       plen = be_to_host16(hdr->length);
+       data_len = plen + sizeof(*hdr);
+       wpa_printf(MSG_DEBUG, "IEEE 802.1X RX: version=%d type=%d length=%lu",
+                  hdr->version, hdr->type, (unsigned long) plen);
+
+       if (hdr->version < EAPOL_VERSION) {
+               /* TODO: backwards compatibility */
+       }
+       if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+               wpa_printf(MSG_DEBUG, "WPA: EAPOL frame (type %u) discarded, "
+                       "not a Key frame", hdr->type);
+               ret = 0;
+               goto out;
+       }
+       if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+               wpa_printf(MSG_DEBUG, "WPA: EAPOL frame payload size %lu "
+                          "invalid (frame size %lu)",
+                          (unsigned long) plen, (unsigned long) len);
+               ret = 0;
+               goto out;
+       }
+
+       if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
+       {
+               wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key type (%d) unknown, "
+                          "discarded", key->type);
+               ret = 0;
+               goto out;
+       }
+       wpa_eapol_key_dump(key);
+
+       eapol_sm_notify_lower_layer_success(sm->eapol, 0);
+       wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
+       if (data_len < len) {
+               wpa_printf(MSG_DEBUG, "WPA: ignoring %lu bytes after the IEEE "
+                          "802.1X data", (unsigned long) len - data_len);
+       }
+       key_info = WPA_GET_BE16(key->key_info);
+       ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+       if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+#if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
+           ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+#endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+           ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+               wpa_printf(MSG_INFO, "WPA: Unsupported EAPOL-Key descriptor "
+                          "version %d.", ver);
+               goto out;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_key_mgmt_ft(sm->key_mgmt)) {
+               /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
+               if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+                       wpa_printf(MSG_INFO, "FT: AP did not use "
+                                  "AES-128-CMAC.");
+                       goto out;
+               }
+       } else
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
+               if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+                       wpa_printf(MSG_INFO, "WPA: AP did not use the "
+                                  "negotiated AES-128-CMAC.");
+                       goto out;
+               }
+       } else
+#endif /* CONFIG_IEEE80211W */
+       if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+           ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+               wpa_printf(MSG_INFO, "WPA: CCMP is used, but EAPOL-Key "
+                          "descriptor version (%d) is not 2.", ver);
+               if (sm->group_cipher != WPA_CIPHER_CCMP &&
+                   !(key_info & WPA_KEY_INFO_KEY_TYPE)) {
+                       /* Earlier versions of IEEE 802.11i did not explicitly
+                        * require version 2 descriptor for all EAPOL-Key
+                        * packets, so allow group keys to use version 1 if
+                        * CCMP is not used for them. */
+                       wpa_printf(MSG_INFO, "WPA: Backwards compatibility: "
+                                  "allow invalid version for non-CCMP group "
+                                  "keys");
+               } else
+                       goto out;
+       }
+
+#ifdef CONFIG_PEERKEY
+       for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+               if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+                       break;
+       }
+
+       if (!(key_info & WPA_KEY_INFO_SMK_MESSAGE) && peerkey) {
+               if (!peerkey->initiator && peerkey->replay_counter_set &&
+                   os_memcmp(key->replay_counter, peerkey->replay_counter,
+                             WPA_REPLAY_COUNTER_LEN) <= 0) {
+                       wpa_printf(MSG_WARNING, "RSN: EAPOL-Key Replay "
+                                  "Counter did not increase (STK) - dropping "
+                                  "packet");
+                       goto out;
+               } else if (peerkey->initiator) {
+                       u8 _tmp[WPA_REPLAY_COUNTER_LEN];
+                       os_memcpy(_tmp, key->replay_counter,
+                                 WPA_REPLAY_COUNTER_LEN);
+                       inc_byte_array(_tmp, WPA_REPLAY_COUNTER_LEN);
+                       if (os_memcmp(_tmp, peerkey->replay_counter,
+                                     WPA_REPLAY_COUNTER_LEN) != 0) {
+                               wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key Replay "
+                                          "Counter did not match (STK) - "
+                                          "dropping packet");
+                               goto out;
+                       }
+               }
+       }
+
+       if (peerkey && peerkey->initiator && (key_info & WPA_KEY_INFO_ACK)) {
+               wpa_printf(MSG_INFO, "RSN: Ack bit in key_info from STK peer");
+               goto out;
+       }
+#endif /* CONFIG_PEERKEY */
+
+       if (!peerkey && sm->rx_replay_counter_set &&
+           os_memcmp(key->replay_counter, sm->rx_replay_counter,
+                     WPA_REPLAY_COUNTER_LEN) <= 0) {
+               wpa_printf(MSG_WARNING, "WPA: EAPOL-Key Replay Counter did not"
+                          " increase - dropping packet");
+               goto out;
+       }
+
+       if (!(key_info & (WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE))
+#ifdef CONFIG_PEERKEY
+           && (peerkey == NULL || !peerkey->initiator)
+#endif /* CONFIG_PEERKEY */
+               ) {
+               wpa_printf(MSG_INFO, "WPA: No Ack bit in key_info");
+               goto out;
+       }
+
+       if (key_info & WPA_KEY_INFO_REQUEST) {
+               wpa_printf(MSG_INFO, "WPA: EAPOL-Key with Request bit - "
+                          "dropped");
+               goto out;
+       }
+
+       if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
+           wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+               goto out;
+
+#ifdef CONFIG_PEERKEY
+       if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
+           peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len))
+               goto out;
+#endif /* CONFIG_PEERKEY */
+
+       extra_len = data_len - sizeof(*hdr) - sizeof(*key);
+
+       if (WPA_GET_BE16(key->key_data_length) > extra_len) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
+                       "frame - key_data overflow (%d > %lu)",
+                       WPA_GET_BE16(key->key_data_length),
+                       (unsigned long) extra_len);
+               goto out;
+       }
+       extra_len = WPA_GET_BE16(key->key_data_length);
+
+       if (sm->proto == WPA_PROTO_RSN &&
+           (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+               if (wpa_supplicant_decrypt_key_data(sm, key, ver))
+                       goto out;
+               extra_len = WPA_GET_BE16(key->key_data_length);
+       }
+
+       if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+               if (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) {
+                       wpa_printf(MSG_WARNING, "WPA: Ignored EAPOL-Key "
+                                  "(Pairwise) with non-zero key index");
+                       goto out;
+               }
+               if (peerkey) {
+                       /* PeerKey 4-Way Handshake */
+                       peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver);
+               } else if (key_info & WPA_KEY_INFO_MIC) {
+                       /* 3/4 4-Way Handshake */
+                       wpa_supplicant_process_3_of_4(sm, key, ver);
+               } else {
+                       /* 1/4 4-Way Handshake */
+                       wpa_supplicant_process_1_of_4(sm, src_addr, key,
+                                                     ver);
+               }
+       } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
+               /* PeerKey SMK Handshake */
+               peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info,
+                                    ver);
+       } else {
+               if (key_info & WPA_KEY_INFO_MIC) {
+                       /* 1/2 Group Key Handshake */
+                       wpa_supplicant_process_1_of_2(sm, src_addr, key,
+                                                     extra_len, ver);
+               } else {
+                       wpa_printf(MSG_WARNING, "WPA: EAPOL-Key (Group) "
+                                  "without Mic bit - dropped");
+               }
+       }
+
+       ret = 1;
+
+out:
+       os_free(tmp);
+       return ret;
+}
+
+
+#ifdef CONFIG_CTRL_IFACE
+static int wpa_cipher_bits(int cipher)
+{
+       switch (cipher) {
+       case WPA_CIPHER_CCMP:
+               return 128;
+       case WPA_CIPHER_TKIP:
+               return 256;
+       case WPA_CIPHER_WEP104:
+               return 104;
+       case WPA_CIPHER_WEP40:
+               return 40;
+       default:
+               return 0;
+       }
+}
+
+
+static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
+{
+       switch (sm->key_mgmt) {
+       case WPA_KEY_MGMT_IEEE8021X:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
+                       WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+       case WPA_KEY_MGMT_PSK:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X :
+                       WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+       case WPA_KEY_MGMT_FT_IEEE8021X:
+               return RSN_AUTH_KEY_MGMT_FT_802_1X;
+       case WPA_KEY_MGMT_FT_PSK:
+               return RSN_AUTH_KEY_MGMT_FT_PSK;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       case WPA_KEY_MGMT_IEEE8021X_SHA256:
+               return RSN_AUTH_KEY_MGMT_802_1X_SHA256;
+       case WPA_KEY_MGMT_PSK_SHA256:
+               return RSN_AUTH_KEY_MGMT_PSK_SHA256;
+#endif /* CONFIG_IEEE80211W */
+       case WPA_KEY_MGMT_WPA_NONE:
+               return WPA_AUTH_KEY_MGMT_NONE;
+       default:
+               return 0;
+       }
+}
+
+
+static u32 wpa_cipher_suite(struct wpa_sm *sm, int cipher)
+{
+       switch (cipher) {
+       case WPA_CIPHER_CCMP:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
+       case WPA_CIPHER_TKIP:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_CIPHER_SUITE_TKIP : WPA_CIPHER_SUITE_TKIP);
+       case WPA_CIPHER_WEP104:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_CIPHER_SUITE_WEP104 : WPA_CIPHER_SUITE_WEP104);
+       case WPA_CIPHER_WEP40:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_CIPHER_SUITE_WEP40 : WPA_CIPHER_SUITE_WEP40);
+       case WPA_CIPHER_NONE:
+               return (sm->proto == WPA_PROTO_RSN ?
+                       RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+       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
+
+/**
+ * wpa_sm_get_mib - Dump text list of MIB entries
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for the list
+ * @buflen: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used fetch dot11 MIB variables.
+ */
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+       char pmkid_txt[PMKID_LEN * 2 + 1];
+       int rsna, ret;
+       size_t len;
+
+       if (sm->cur_pmksa) {
+               wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt),
+                                sm->cur_pmksa->pmkid, PMKID_LEN);
+       } else
+               pmkid_txt[0] = '\0';
+
+       if ((wpa_key_mgmt_wpa_psk(sm->key_mgmt) ||
+            wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt)) &&
+           sm->proto == WPA_PROTO_RSN)
+               rsna = 1;
+       else
+               rsna = 0;
+
+       ret = os_snprintf(buf, buflen,
+                         "dot11RSNAOptionImplemented=TRUE\n"
+                         "dot11RSNAPreauthenticationImplemented=TRUE\n"
+                         "dot11RSNAEnabled=%s\n"
+                         "dot11RSNAPreauthenticationEnabled=%s\n"
+                         "dot11RSNAConfigVersion=%d\n"
+                         "dot11RSNAConfigPairwiseKeysSupported=5\n"
+                         "dot11RSNAConfigGroupCipherSize=%d\n"
+                         "dot11RSNAConfigPMKLifetime=%d\n"
+                         "dot11RSNAConfigPMKReauthThreshold=%d\n"
+                         "dot11RSNAConfigNumberOfPTKSAReplayCounters=1\n"
+                         "dot11RSNAConfigSATimeout=%d\n",
+                         rsna ? "TRUE" : "FALSE",
+                         rsna ? "TRUE" : "FALSE",
+                         RSN_VERSION,
+                         wpa_cipher_bits(sm->group_cipher),
+                         sm->dot11RSNAConfigPMKLifetime,
+                         sm->dot11RSNAConfigPMKReauthThreshold,
+                         sm->dot11RSNAConfigSATimeout);
+       if (ret < 0 || (size_t) ret >= buflen)
+               return 0;
+       len = ret;
+
+       ret = os_snprintf(
+               buf + len, buflen - len,
+               "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n"
+               "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n"
+               "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n"
+               "dot11RSNAPMKIDUsed=%s\n"
+               "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n"
+               "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n"
+               "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n"
+               "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n"
+               "dot11RSNA4WayHandshakeFailures=%u\n",
+               RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+               RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
+               RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+               pmkid_txt,
+               RSN_SUITE_ARG(wpa_key_mgmt_suite(sm)),
+               RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->pairwise_cipher)),
+               RSN_SUITE_ARG(wpa_cipher_suite(sm, sm->group_cipher)),
+               sm->dot11RSNA4WayHandshakeFailures);
+       if (ret >= 0 && (size_t) ret < buflen)
+               len += ret;
+
+       return (int) len;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static void wpa_sm_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
+                                void *ctx, int replace)
+{
+       struct wpa_sm *sm = ctx;
+
+       if (sm->cur_pmksa == entry ||
+           (sm->pmk_len == entry->pmk_len &&
+            os_memcmp(sm->pmk, entry->pmk, sm->pmk_len) == 0)) {
+               wpa_printf(MSG_DEBUG, "RSN: removed current PMKSA entry");
+               sm->cur_pmksa = NULL;
+
+               if (replace) {
+                       /* A new entry is being added, so no need to
+                        * deauthenticate in this case. This happens when EAP
+                        * authentication is completed again (reauth or failed
+                        * PMKSA caching attempt). */
+                       return;
+               }
+
+               os_memset(sm->pmk, 0, sizeof(sm->pmk));
+               wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
+       }
+}
+
+
+/**
+ * wpa_sm_init - Initialize WPA state machine
+ * @ctx: Context pointer for callbacks; this needs to be an allocated buffer
+ * Returns: Pointer to the allocated WPA state machine data
+ *
+ * This function is used to allocate a new WPA state machine and the returned
+ * value is passed to all WPA state machine calls.
+ */
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+       struct wpa_sm *sm;
+
+       sm = os_zalloc(sizeof(*sm));
+       if (sm == NULL)
+               return NULL;
+       dl_list_init(&sm->pmksa_candidates);
+       sm->renew_snonce = 1;
+       sm->ctx = ctx;
+
+       sm->dot11RSNAConfigPMKLifetime = 43200;
+       sm->dot11RSNAConfigPMKReauthThreshold = 70;
+       sm->dot11RSNAConfigSATimeout = 60;
+
+       sm->pmksa = pmksa_cache_init(wpa_sm_pmksa_free_cb, sm, sm);
+       if (sm->pmksa == NULL) {
+               wpa_printf(MSG_ERROR, "RSN: PMKSA cache initialization "
+                          "failed");
+               os_free(sm);
+               return NULL;
+       }
+
+       return sm;
+}
+
+
+/**
+ * wpa_sm_deinit - Deinitialize WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ */
+void wpa_sm_deinit(struct wpa_sm *sm)
+{
+       if (sm == NULL)
+               return;
+       pmksa_cache_deinit(sm->pmksa);
+       eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+       eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+       os_free(sm->assoc_wpa_ie);
+       os_free(sm->ap_wpa_ie);
+       os_free(sm->ap_rsn_ie);
+       os_free(sm->ctx);
+       peerkey_deinit(sm);
+#ifdef CONFIG_IEEE80211R
+       os_free(sm->assoc_resp_ies);
+#endif /* CONFIG_IEEE80211R */
+       os_free(sm);
+}
+
+
+/**
+ * wpa_sm_notify_assoc - Notify WPA state machine about association
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @bssid: The BSSID of the new association
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was established.
+ */
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+       int clear_ptk = 1;
+
+       if (sm == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "WPA: Association event - clear replay counter");
+       os_memcpy(sm->bssid, bssid, ETH_ALEN);
+       os_memset(sm->rx_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+       sm->rx_replay_counter_set = 0;
+       sm->renew_snonce = 1;
+       if (os_memcmp(sm->preauth_bssid, bssid, ETH_ALEN) == 0)
+               rsn_preauth_deinit(sm);
+
+#ifdef CONFIG_IEEE80211R
+       if (wpa_ft_is_completed(sm)) {
+               /*
+                * Clear portValid to kick EAPOL state machine to re-enter
+                * AUTHENTICATED state to get the EAPOL port Authorized.
+                */
+               eapol_sm_notify_portValid(sm->eapol, FALSE);
+               wpa_supplicant_key_neg_complete(sm, sm->bssid, 1);
+
+               /* Prepare for the next transition */
+               wpa_ft_prepare_auth_request(sm, NULL);
+
+               clear_ptk = 0;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       if (clear_ptk) {
+               /*
+                * IEEE 802.11, 8.4.10: Delete PTK SA on (re)association if
+                * this is not part of a Fast BSS Transition.
+                */
+               wpa_printf(MSG_DEBUG, "WPA: Clear old PTK");
+               sm->ptk_set = 0;
+               sm->tptk_set = 0;
+       }
+}
+
+
+/**
+ * wpa_sm_notify_disassoc - Notify WPA state machine about disassociation
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * This function is called to let WPA state machine know that the connection
+ * was lost. This will abort any existing pre-authentication session.
+ */
+void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+       rsn_preauth_deinit(sm);
+       if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
+               sm->dot11RSNA4WayHandshakeFailures++;
+}
+
+
+/**
+ * wpa_sm_set_pmk - Set PMK
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @pmk: The new PMK
+ * @pmk_len: The length of the new PMK in bytes
+ *
+ * Configure the PMK for WPA state machine.
+ */
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len)
+{
+       if (sm == NULL)
+               return;
+
+       sm->pmk_len = pmk_len;
+       os_memcpy(sm->pmk, pmk, pmk_len);
+
+#ifdef CONFIG_IEEE80211R
+       /* Set XXKey to be PSK for FT key derivation */
+       sm->xxkey_len = pmk_len;
+       os_memcpy(sm->xxkey, pmk, pmk_len);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
+/**
+ * wpa_sm_set_pmk_from_pmksa - Set PMK based on the current PMKSA
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ *
+ * Take the PMK from the current PMKSA into use. If no PMKSA is active, the PMK
+ * will be cleared.
+ */
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+       if (sm == NULL)
+               return;
+
+       if (sm->cur_pmksa) {
+               sm->pmk_len = sm->cur_pmksa->pmk_len;
+               os_memcpy(sm->pmk, sm->cur_pmksa->pmk, sm->pmk_len);
+       } else {
+               sm->pmk_len = PMK_LEN;
+               os_memset(sm->pmk, 0, PMK_LEN);
+       }
+}
+
+
+/**
+ * wpa_sm_set_fast_reauth - Set fast reauthentication (EAP) enabled/disabled
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @fast_reauth: Whether fast reauthentication (EAP) is allowed
+ */
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+       if (sm)
+               sm->fast_reauth = fast_reauth;
+}
+
+
+/**
+ * wpa_sm_set_scard_ctx - Set context pointer for smartcard callbacks
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @scard_ctx: Context pointer for smartcard related callback functions
+ */
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+       if (sm == NULL)
+               return;
+       sm->scard_ctx = scard_ctx;
+       if (sm->preauth_eapol)
+               eapol_sm_register_scard_ctx(sm->preauth_eapol, scard_ctx);
+}
+
+
+/**
+ * wpa_sm_set_config - Notification of current configration change
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @config: Pointer to current network configuration
+ *
+ * Notify WPA state machine that configuration has changed. config will be
+ * stored as a backpointer to network configuration. This can be %NULL to clear
+ * the stored pointed.
+ */
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
+{
+       if (!sm)
+               return;
+
+       if (config) {
+               sm->network_ctx = config->network_ctx;
+               sm->peerkey_enabled = config->peerkey_enabled;
+               sm->allowed_pairwise_cipher = config->allowed_pairwise_cipher;
+               sm->proactive_key_caching = config->proactive_key_caching;
+               sm->eap_workaround = config->eap_workaround;
+               sm->eap_conf_ctx = config->eap_conf_ctx;
+               if (config->ssid) {
+                       os_memcpy(sm->ssid, config->ssid, config->ssid_len);
+                       sm->ssid_len = config->ssid_len;
+               } else
+                       sm->ssid_len = 0;
+               sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+       } else {
+               sm->network_ctx = NULL;
+               sm->peerkey_enabled = 0;
+               sm->allowed_pairwise_cipher = 0;
+               sm->proactive_key_caching = 0;
+               sm->eap_workaround = 0;
+               sm->eap_conf_ctx = NULL;
+               sm->ssid_len = 0;
+               sm->wpa_ptk_rekey = 0;
+       }
+       if (config == NULL || config->network_ctx != sm->network_ctx)
+               pmksa_cache_notify_reconfig(sm->pmksa);
+}
+
+
+/**
+ * wpa_sm_set_own_addr - Set own MAC address
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @addr: Own MAC address
+ */
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+       if (sm)
+               os_memcpy(sm->own_addr, addr, ETH_ALEN);
+}
+
+
+/**
+ * wpa_sm_set_ifname - Set network interface name
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ifname: Interface name
+ * @bridge_ifname: Optional bridge interface name (for pre-auth)
+ */
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+                      const char *bridge_ifname)
+{
+       if (sm) {
+               sm->ifname = ifname;
+               sm->bridge_ifname = bridge_ifname;
+       }
+}
+
+
+/**
+ * wpa_sm_set_eapol - Set EAPOL state machine pointer
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @eapol: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ */
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+       if (sm)
+               sm->eapol = eapol;
+}
+
+
+/**
+ * wpa_sm_set_param - Set WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * @value: Parameter value
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+                    unsigned int value)
+{
+       int ret = 0;
+
+       if (sm == NULL)
+               return -1;
+
+       switch (param) {
+       case RSNA_PMK_LIFETIME:
+               if (value > 0)
+                       sm->dot11RSNAConfigPMKLifetime = value;
+               else
+                       ret = -1;
+               break;
+       case RSNA_PMK_REAUTH_THRESHOLD:
+               if (value > 0 && value <= 100)
+                       sm->dot11RSNAConfigPMKReauthThreshold = value;
+               else
+                       ret = -1;
+               break;
+       case RSNA_SA_TIMEOUT:
+               if (value > 0)
+                       sm->dot11RSNAConfigSATimeout = value;
+               else
+                       ret = -1;
+               break;
+       case WPA_PARAM_PROTO:
+               sm->proto = value;
+               break;
+       case WPA_PARAM_PAIRWISE:
+               sm->pairwise_cipher = value;
+               break;
+       case WPA_PARAM_GROUP:
+               sm->group_cipher = value;
+               break;
+       case WPA_PARAM_KEY_MGMT:
+               sm->key_mgmt = value;
+               break;
+#ifdef CONFIG_IEEE80211W
+       case WPA_PARAM_MGMT_GROUP:
+               sm->mgmt_group_cipher = value;
+               break;
+#endif /* CONFIG_IEEE80211W */
+       case WPA_PARAM_RSN_ENABLED:
+               sm->rsn_enabled = value;
+               break;
+       case WPA_PARAM_MFP:
+               sm->mfp = value;
+               break;
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+
+/**
+ * wpa_sm_get_param - Get WPA state machine parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @param: Parameter field
+ * Returns: Parameter value
+ */
+unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param)
+{
+       if (sm == NULL)
+               return 0;
+
+       switch (param) {
+       case RSNA_PMK_LIFETIME:
+               return sm->dot11RSNAConfigPMKLifetime;
+       case RSNA_PMK_REAUTH_THRESHOLD:
+               return sm->dot11RSNAConfigPMKReauthThreshold;
+       case RSNA_SA_TIMEOUT:
+               return sm->dot11RSNAConfigSATimeout;
+       case WPA_PARAM_PROTO:
+               return sm->proto;
+       case WPA_PARAM_PAIRWISE:
+               return sm->pairwise_cipher;
+       case WPA_PARAM_GROUP:
+               return sm->group_cipher;
+       case WPA_PARAM_KEY_MGMT:
+               return sm->key_mgmt;
+#ifdef CONFIG_IEEE80211W
+       case WPA_PARAM_MGMT_GROUP:
+               return sm->mgmt_group_cipher;
+#endif /* CONFIG_IEEE80211W */
+       case WPA_PARAM_RSN_ENABLED:
+               return sm->rsn_enabled;
+       default:
+               return 0;
+       }
+}
+
+
+/**
+ * wpa_sm_get_status - Get WPA state machine
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @buf: Buffer for status information
+ * @buflen: Maximum buffer length
+ * @verbose: Whether to include verbose status information
+ * Returns: Number of bytes written to buf.
+ *
+ * Query WPA state machine for status information. This function fills in
+ * a text area with current status information. If the buffer (buf) is not
+ * large enough, status information will be truncated to fit the buffer.
+ */
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+                     int verbose)
+{
+       char *pos = buf, *end = buf + buflen;
+       int ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "pairwise_cipher=%s\n"
+                         "group_cipher=%s\n"
+                         "key_mgmt=%s\n",
+                         wpa_cipher_txt(sm->pairwise_cipher),
+                         wpa_cipher_txt(sm->group_cipher),
+                         wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+       return pos - buf;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to buffer for WPA/RSN IE
+ * @wpa_ie_len: Pointer to the length of the wpa_ie buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+                                   size_t *wpa_ie_len)
+{
+       int res;
+
+       if (sm == NULL)
+               return -1;
+
+       res = wpa_gen_wpa_ie(sm, wpa_ie, *wpa_ie_len);
+       if (res < 0)
+               return -1;
+       *wpa_ie_len = res;
+
+       wpa_hexdump(MSG_DEBUG, "WPA: Set own WPA IE default",
+                   wpa_ie, *wpa_ie_len);
+
+       if (sm->assoc_wpa_ie == NULL) {
+               /*
+                * Make a copy of the WPA/RSN IE so that 4-Way Handshake gets
+                * the correct version of the IE even if PMKSA caching is
+                * aborted (which would remove PMKID from IE generation).
+                */
+               sm->assoc_wpa_ie = os_malloc(*wpa_ie_len);
+               if (sm->assoc_wpa_ie == NULL)
+                       return -1;
+
+               os_memcpy(sm->assoc_wpa_ie, wpa_ie, *wpa_ie_len);
+               sm->assoc_wpa_ie_len = *wpa_ie_len;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_sm_set_assoc_wpa_ie - Set own WPA/RSN IE from (Re)AssocReq
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA/RSN IE used in (Re)Association
+ * Request frame. The IE will be used to override the default value generated
+ * with wpa_sm_set_assoc_wpa_ie_default().
+ */
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+       if (sm == NULL)
+               return -1;
+
+       os_free(sm->assoc_wpa_ie);
+       if (ie == NULL || len == 0) {
+               wpa_printf(MSG_DEBUG, "WPA: clearing own WPA/RSN IE");
+               sm->assoc_wpa_ie = NULL;
+               sm->assoc_wpa_ie_len = 0;
+       } else {
+               wpa_hexdump(MSG_DEBUG, "WPA: set own WPA/RSN IE", ie, len);
+               sm->assoc_wpa_ie = os_malloc(len);
+               if (sm->assoc_wpa_ie == NULL)
+                       return -1;
+
+               os_memcpy(sm->assoc_wpa_ie, ie, len);
+               sm->assoc_wpa_ie_len = len;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_sm_set_ap_wpa_ie - Set AP WPA IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the WPA IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+       if (sm == NULL)
+               return -1;
+
+       os_free(sm->ap_wpa_ie);
+       if (ie == NULL || len == 0) {
+               wpa_printf(MSG_DEBUG, "WPA: clearing AP WPA IE");
+               sm->ap_wpa_ie = NULL;
+               sm->ap_wpa_ie_len = 0;
+       } else {
+               wpa_hexdump(MSG_DEBUG, "WPA: set AP WPA IE", ie, len);
+               sm->ap_wpa_ie = os_malloc(len);
+               if (sm->ap_wpa_ie == NULL)
+                       return -1;
+
+               os_memcpy(sm->ap_wpa_ie, ie, len);
+               sm->ap_wpa_ie_len = len;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_sm_set_ap_rsn_ie - Set AP RSN IE from Beacon/ProbeResp
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ie: Pointer to IE data (starting from id)
+ * @len: IE length
+ * Returns: 0 on success, -1 on failure
+ *
+ * Inform WPA state machine about the RSN IE used in Beacon / Probe Response
+ * frame.
+ */
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len)
+{
+       if (sm == NULL)
+               return -1;
+
+       os_free(sm->ap_rsn_ie);
+       if (ie == NULL || len == 0) {
+               wpa_printf(MSG_DEBUG, "WPA: clearing AP RSN IE");
+               sm->ap_rsn_ie = NULL;
+               sm->ap_rsn_ie_len = 0;
+       } else {
+               wpa_hexdump(MSG_DEBUG, "WPA: set AP RSN IE", ie, len);
+               sm->ap_rsn_ie = os_malloc(len);
+               if (sm->ap_rsn_ie == NULL)
+                       return -1;
+
+               os_memcpy(sm->ap_rsn_ie, ie, len);
+               sm->ap_rsn_ie_len = len;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_sm_parse_own_wpa_ie - Parse own WPA/RSN IE
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 if IE is not known, or -2 on parsing failure
+ *
+ * Parse the contents of the own WPA or RSN IE from (Re)AssocReq and write the
+ * parsed data into data.
+ */
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data)
+{
+       if (sm == NULL || sm->assoc_wpa_ie == NULL) {
+               wpa_printf(MSG_DEBUG, "WPA: No WPA/RSN IE available from "
+                          "association info");
+               return -1;
+       }
+       if (wpa_parse_wpa_ie(sm->assoc_wpa_ie, sm->assoc_wpa_ie_len, data))
+               return -2;
+       return 0;
+}
+
+
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len)
+{
+#ifndef CONFIG_NO_WPA2
+       return pmksa_cache_list(sm->pmksa, buf, len);
+#else /* CONFIG_NO_WPA2 */
+       return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+       wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK");
+       sm->ptk_set = 0;
+       sm->tptk_set = 0;
+       os_memset(sm->pmk, 0, sizeof(sm->pmk));
+       os_memset(&sm->ptk, 0, sizeof(sm->ptk));
+       os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+}
+
+
+int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+       if (sm == NULL)
+               return 0;
+       return sm->ptk_set;
+}
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
new file mode 100644 (file)
index 0000000..f1a5554
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * wpa_supplicant - WPA definitions
+ * 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.
+ */
+
+#ifndef WPA_H
+#define WPA_H
+
+#include "common/defs.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+
+struct wpa_sm;
+struct eapol_sm;
+struct wpa_config_blob;
+
+struct wpa_sm_ctx {
+       void *ctx; /* pointer to arbitrary upper level context */
+       void *msg_ctx; /* upper level context for wpa_msg() calls */
+
+       void (*set_state)(void *ctx, enum wpa_states state);
+       enum wpa_states (*get_state)(void *ctx);
+       void (*deauthenticate)(void * ctx, int reason_code); 
+       void (*disassociate)(void *ctx, int reason_code);
+       int (*set_key)(void *ctx, enum wpa_alg alg,
+                      const u8 *addr, int key_idx, int set_tx,
+                      const u8 *seq, size_t seq_len,
+                      const u8 *key, size_t key_len);
+       void * (*get_network_ctx)(void *ctx);
+       int (*get_bssid)(void *ctx, u8 *bssid);
+       int (*ether_send)(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+                         size_t len);
+       int (*get_beacon_ie)(void *ctx);
+       void (*cancel_auth_timeout)(void *ctx);
+       u8 * (*alloc_eapol)(void *ctx, u8 type, const void *data, u16 data_len,
+                           size_t *msg_len, void **data_pos);
+       int (*add_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+       int (*remove_pmkid)(void *ctx, const u8 *bssid, const u8 *pmkid);
+       void (*set_config_blob)(void *ctx, struct wpa_config_blob *blob);
+       const struct wpa_config_blob * (*get_config_blob)(void *ctx,
+                                                         const char *name);
+       int (*mlme_setprotection)(void *ctx, const u8 *addr,
+                                 int protection_type, int key_type);
+       int (*update_ft_ies)(void *ctx, const u8 *md, const u8 *ies,
+                            size_t ies_len);
+       int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap,
+                             const u8 *ies, size_t ies_len);
+       int (*mark_authenticated)(void *ctx, const u8 *target_ap);
+};
+
+
+enum wpa_sm_conf_params {
+       RSNA_PMK_LIFETIME /* dot11RSNAConfigPMKLifetime */,
+       RSNA_PMK_REAUTH_THRESHOLD /* dot11RSNAConfigPMKReauthThreshold */,
+       RSNA_SA_TIMEOUT /* dot11RSNAConfigSATimeout */,
+       WPA_PARAM_PROTO,
+       WPA_PARAM_PAIRWISE,
+       WPA_PARAM_GROUP,
+       WPA_PARAM_KEY_MGMT,
+       WPA_PARAM_MGMT_GROUP,
+       WPA_PARAM_RSN_ENABLED,
+       WPA_PARAM_MFP
+};
+
+struct rsn_supp_config {
+       void *network_ctx;
+       int peerkey_enabled;
+       int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+       int proactive_key_caching;
+       int eap_workaround;
+       void *eap_conf_ctx;
+       const u8 *ssid;
+       size_t ssid_len;
+       int wpa_ptk_rekey;
+};
+
+#ifndef CONFIG_NO_WPA
+
+struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
+void wpa_sm_deinit(struct wpa_sm *sm);
+void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
+void wpa_sm_notify_disassoc(struct wpa_sm *sm);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len);
+void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
+void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
+void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
+void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config);
+void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr);
+void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+                      const char *bridge_ifname);
+void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol);
+int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm, u8 *wpa_ie,
+                                   size_t *wpa_ie_len);
+int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie, size_t len);
+int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
+
+int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
+                    unsigned int value);
+unsigned int wpa_sm_get_param(struct wpa_sm *sm,
+                             enum wpa_sm_conf_params param);
+
+int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
+                     int verbose);
+
+void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
+
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+                    struct wpa_ie_data *data);
+
+void wpa_sm_aborted_cached(struct wpa_sm *sm);
+int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+                   const u8 *buf, size_t len);
+int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data);
+int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len);
+void wpa_sm_drop_sa(struct wpa_sm *sm);
+int wpa_sm_has_ptk(struct wpa_sm *sm);
+
+#else /* CONFIG_NO_WPA */
+
+static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
+{
+       return (struct wpa_sm *) 1;
+}
+
+static inline void wpa_sm_deinit(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
+{
+}
+
+static inline void wpa_sm_notify_disassoc(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk,
+                                 size_t pmk_len)
+{
+}
+
+static inline void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm)
+{
+}
+
+static inline void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth)
+{
+}
+
+static inline void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx)
+{
+}
+
+static inline void wpa_sm_set_config(struct wpa_sm *sm,
+                                    struct rsn_supp_config *config)
+{
+}
+
+static inline void wpa_sm_set_own_addr(struct wpa_sm *sm, const u8 *addr)
+{
+}
+
+static inline void wpa_sm_set_ifname(struct wpa_sm *sm, const char *ifname,
+                                    const char *bridge_ifname)
+{
+}
+
+static inline void wpa_sm_set_eapol(struct wpa_sm *sm, struct eapol_sm *eapol)
+{
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+                                         size_t len)
+{
+       return -1;
+}
+
+static inline int wpa_sm_set_assoc_wpa_ie_default(struct wpa_sm *sm,
+                                                 u8 *wpa_ie,
+                                                 size_t *wpa_ie_len)
+{
+       return -1;
+}
+
+static inline int wpa_sm_set_ap_wpa_ie(struct wpa_sm *sm, const u8 *ie,
+                                      size_t len)
+{
+       return -1;
+}
+
+static inline int wpa_sm_set_ap_rsn_ie(struct wpa_sm *sm, const u8 *ie,
+                                      size_t len)
+{
+       return -1;
+}
+
+static inline int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
+{
+       return 0;
+}
+
+static inline int wpa_sm_set_param(struct wpa_sm *sm,
+                                  enum wpa_sm_conf_params param,
+                                  unsigned int value)
+{
+       return -1;
+}
+
+static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm,
+                                           enum wpa_sm_conf_params param)
+{
+       return 0;
+}
+
+static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
+                                   size_t buflen, int verbose)
+{
+       return 0;
+}
+
+static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
+                                     int pairwise)
+{
+}
+
+static inline int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+                                  struct wpa_ie_data *data)
+{
+       return -1;
+}
+
+static inline void wpa_sm_aborted_cached(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
+                                 const u8 *buf, size_t len)
+{
+       return -1;
+}
+
+static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm,
+                                         struct wpa_ie_data *data)
+{
+       return -1;
+}
+
+static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf,
+                                         size_t len)
+{
+       return -1;
+}
+
+static inline void wpa_sm_drop_sa(struct wpa_sm *sm)
+{
+}
+
+static inline int wpa_sm_has_ptk(struct wpa_sm *sm)
+{
+       return 0;
+}
+
+#endif /* CONFIG_NO_WPA */
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
+#else /* CONFIG_PEERKEY */
+static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
+{
+       return -1;
+}
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len);
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie);
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+                           int ft_action, const u8 *target_ap,
+                           const u8 *ric_ies, size_t ric_ies_len);
+int wpa_ft_is_completed(struct wpa_sm *sm);
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+                                size_t ies_len, const u8 *src_addr);
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+                        const u8 *mdie);
+
+#else /* CONFIG_IEEE80211R */
+
+static inline int
+wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+       return 0;
+}
+
+static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm,
+                                             const u8 *mdie)
+{
+       return 0;
+}
+
+static inline int
+wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+                       int ft_action, const u8 *target_ap)
+{
+       return 0;
+}
+
+static inline int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+       return 0;
+}
+
+static inline int
+wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+                            const u8 *src_addr)
+{
+       return -1;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
new file mode 100644 (file)
index 0000000..23063bc
--- /dev/null
@@ -0,0 +1,1040 @@
+/*
+ * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
+ * Copyright (c) 2006-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 "crypto/aes_wrap.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wpa.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+#ifdef CONFIG_IEEE80211R
+
+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;
+       const u8 *tie;
+       size_t tie_len;
+       const u8 *igtk;
+       size_t igtk_len;
+       const u8 *ric;
+       size_t ric_len;
+};
+
+static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len,
+                           struct wpa_ft_ies *parse);
+
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+                     const struct wpa_eapol_key *key,
+                     struct wpa_ptk *ptk, size_t ptk_len)
+{
+       u8 ptk_name[WPA_PMK_NAME_LEN];
+       const u8 *anonce = key->key_nonce;
+
+       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, sm->ssid,
+                         sm->ssid_len, sm->mobility_domain,
+                         sm->r0kh_id, sm->r0kh_id_len, sm->own_addr,
+                         sm->pmk_r0, sm->pmk_r0_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", sm->pmk_r0, PMK_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name",
+                   sm->pmk_r0_name, WPA_PMK_NAME_LEN);
+       wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+                         sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
+                   WPA_PMK_NAME_LEN);
+       wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+                         sm->bssid, sm->pmk_r1_name,
+                         (u8 *) ptk, ptk_len, ptk_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
+       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+       return 0;
+}
+
+
+/**
+ * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @ies: Association Response IEs or %NULL to clear FT parameters
+ * @ies_len: Length of ies buffer in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
+{
+       struct wpa_ft_ies ft;
+
+       if (sm == NULL)
+               return 0;
+
+       if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0)
+               return -1;
+
+       if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1)
+               return -1;
+
+       if (ft.mdie) {
+               wpa_hexdump(MSG_DEBUG, "FT: Mobility domain",
+                           ft.mdie, MOBILITY_DOMAIN_ID_LEN);
+               os_memcpy(sm->mobility_domain, ft.mdie,
+                         MOBILITY_DOMAIN_ID_LEN);
+               sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN];
+               wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x",
+                          sm->mdie_ft_capab);
+       } else
+               os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN);
+
+       if (ft.r0kh_id) {
+               wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID",
+                           ft.r0kh_id, ft.r0kh_id_len);
+               os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len);
+               sm->r0kh_id_len = ft.r0kh_id_len;
+       } else {
+               /* FIX: When should R0KH-ID be cleared? We need to keep the
+                * old R0KH-ID in order to be able to use this during FT. */
+               /*
+                * os_memset(sm->r0kh_id, 0, FT_R0KH_ID_LEN);
+                * sm->r0kh_id_len = 0;
+                */
+       }
+
+       if (ft.r1kh_id) {
+               wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID",
+                           ft.r1kh_id, FT_R1KH_ID_LEN);
+               os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN);
+       } else
+               os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN);
+
+       os_free(sm->assoc_resp_ies);
+       sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2);
+       if (sm->assoc_resp_ies) {
+               u8 *pos = sm->assoc_resp_ies;
+               if (ft.mdie) {
+                       os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2);
+                       pos += ft.mdie_len + 2;
+               }
+               if (ft.ftie) {
+                       os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2);
+                       pos += ft.ftie_len + 2;
+               }
+               sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies;
+               wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from "
+                           "(Re)Association Response",
+                           sm->assoc_resp_ies, sm->assoc_resp_ies_len);
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @len: Buffer for returning the length of the IEs
+ * @anonce: ANonce or %NULL if not yet available
+ * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
+ * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @target_ap: Target AP address
+ * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
+ * @ric_ies_len: Length of ric_ies buffer in octets
+ * @ap_mdie: Mobility Domain IE from the target AP
+ * Returns: Pointer to buffer with IEs or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free();
+ */
+static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
+                              const u8 *anonce, const u8 *pmk_name,
+                              const u8 *kck, const u8 *target_ap,
+                              const u8 *ric_ies, size_t ric_ies_len,
+                              const u8 *ap_mdie)
+{
+       size_t buf_len;
+       u8 *buf, *pos, *ftie_len, *ftie_pos;
+       struct rsn_mdie *mdie;
+       struct rsn_ftie *ftie;
+       struct rsn_ie_hdr *rsnie;
+       u16 capab;
+
+       sm->ft_completed = 0;
+
+       buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
+               2 + sm->r0kh_id_len + ric_ies_len + 100;
+       buf = os_zalloc(buf_len);
+       if (buf == NULL)
+               return NULL;
+       pos = buf;
+
+       /* RSNIE[PMKR0Name/PMKR1Name] */
+       rsnie = (struct rsn_ie_hdr *) pos;
+       rsnie->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(rsnie->version, RSN_VERSION);
+       pos = (u8 *) (rsnie + 1);
+
+       /* Group Suite Selector */
+       if (sm->group_cipher == WPA_CIPHER_CCMP)
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       else if (sm->group_cipher == WPA_CIPHER_TKIP)
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+       else {
+               wpa_printf(MSG_WARNING, "FT: Invalid group cipher (%d)",
+                          sm->group_cipher);
+               os_free(buf);
+               return NULL;
+       }
+       pos += RSN_SELECTOR_LEN;
+
+       /* Pairwise Suite Count */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+
+       /* Pairwise Suite List */
+       if (sm->pairwise_cipher == WPA_CIPHER_CCMP)
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       else if (sm->pairwise_cipher == WPA_CIPHER_TKIP)
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+       else {
+               wpa_printf(MSG_WARNING, "FT: Invalid pairwise cipher (%d)",
+                          sm->pairwise_cipher);
+               os_free(buf);
+               return NULL;
+       }
+       pos += RSN_SELECTOR_LEN;
+
+       /* Authenticated Key Management Suite Count */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+
+       /* Authenticated Key Management Suite List */
+       if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X)
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+       else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+       else {
+               wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
+                          sm->key_mgmt);
+               os_free(buf);
+               return NULL;
+       }
+       pos += RSN_SELECTOR_LEN;
+
+       /* RSN Capabilities */
+       capab = 0;
+#ifdef CONFIG_IEEE80211W
+       if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
+               capab |= WPA_CAPABILITY_MFPC;
+#endif /* CONFIG_IEEE80211W */
+       WPA_PUT_LE16(pos, capab);
+       pos += 2;
+
+       /* PMKID Count */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+
+       /* PMKID List [PMKR0Name/PMKR1Name] */
+       os_memcpy(pos, pmk_name, WPA_PMK_NAME_LEN);
+       pos += WPA_PMK_NAME_LEN;
+
+#ifdef CONFIG_IEEE80211W
+       if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+               /* Management Group Cipher Suite */
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+               pos += RSN_SELECTOR_LEN;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       rsnie->len = (pos - (u8 *) rsnie) - 2;
+
+       /* MDIE */
+       *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+       *pos++ = sizeof(*mdie);
+       mdie = (struct rsn_mdie *) pos;
+       pos += sizeof(*mdie);
+       os_memcpy(mdie->mobility_domain, sm->mobility_domain,
+                 MOBILITY_DOMAIN_ID_LEN);
+       mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] :
+               sm->mdie_ft_capab;
+
+       /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */
+       ftie_pos = pos;
+       *pos++ = WLAN_EID_FAST_BSS_TRANSITION;
+       ftie_len = pos++;
+       ftie = (struct rsn_ftie *) pos;
+       pos += sizeof(*ftie);
+       os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN);
+       if (anonce)
+               os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN);
+       if (kck) {
+               /* R1KH-ID sub-element in third FT message */
+               *pos++ = FTIE_SUBELEM_R1KH_ID;
+               *pos++ = FT_R1KH_ID_LEN;
+               os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN);
+               pos += FT_R1KH_ID_LEN;
+       }
+       /* R0KH-ID sub-element */
+       *pos++ = FTIE_SUBELEM_R0KH_ID;
+       *pos++ = sm->r0kh_id_len;
+       os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
+       pos += sm->r0kh_id_len;
+       *ftie_len = pos - ftie_len - 1;
+
+       if (ric_ies) {
+               /* RIC Request */
+               os_memcpy(pos, ric_ies, ric_ies_len);
+               pos += ric_ies_len;
+       }
+
+       if (kck) {
+               /*
+                * IEEE Std 802.11r-2008, 11A.8.4
+                * MIC shall be calculated over:
+                * non-AP STA MAC address
+                * Target AP MAC address
+                * Transaction seq number (5 for ReassocReq, 3 otherwise)
+                * RSN IE
+                * MDIE
+                * FTIE (with MIC field set to 0)
+                * RIC-Request (if present)
+                */
+               /* Information element count */
+               ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
+                                                              ric_ies_len);
+               if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5,
+                              ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
+                              ftie_pos, 2 + *ftie_len,
+                              (u8 *) rsnie, 2 + rsnie->len, ric_ies,
+                              ric_ies_len, ftie->mic) < 0) {
+                       wpa_printf(MSG_INFO, "FT: Failed to calculate MIC");
+                       os_free(buf);
+                       return NULL;
+               }
+       }
+
+       *len = pos - buf;
+
+       return buf;
+}
+
+
+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;
+#ifdef CONFIG_IEEE80211W
+               case FTIE_SUBELEM_IGTK:
+                       parse->igtk = pos + 2;
+                       parse->igtk_len = pos[1];
+                       break;
+#endif /* CONFIG_IEEE80211W */
+               }
+
+               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;
+       const struct rsn_ftie *ftie;
+       int prot_ie_count = 0;
+
+       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 (pos[1] < sizeof(*ftie))
+                               return -1;
+                       ftie = (const struct rsn_ftie *) (pos + 2);
+                       prot_ie_count = ftie->mic_control[1];
+                       if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0)
+                               return -1;
+                       break;
+               case WLAN_EID_TIMEOUT_INTERVAL:
+                       parse->tie = pos + 2;
+                       parse->tie_len = pos[1];
+                       break;
+               case WLAN_EID_RIC_DATA:
+                       if (parse->ric == NULL)
+                               parse->ric = pos;
+               }
+
+               pos += 2 + pos[1];
+       }
+
+       if (prot_ie_count == 0)
+               return 0; /* no MIC */
+
+       /*
+        * Check that the protected IE count matches with IEs included in the
+        * frame.
+        */
+       if (parse->rsn)
+               prot_ie_count--;
+       if (parse->mdie)
+               prot_ie_count--;
+       if (parse->ftie)
+               prot_ie_count--;
+       if (parse->tie)
+               prot_ie_count--;
+       if (prot_ie_count < 0) {
+               wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in "
+                          "the protected IE count");
+               return -1;
+       }
+
+       if (prot_ie_count == 0 && parse->ric) {
+               wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not "
+                          "included in protected IE count");
+               return -1;
+       }
+
+       /* Determine the end of the RIC IE(s) */
+       pos = parse->ric;
+       while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end &&
+              prot_ie_count) {
+               prot_ie_count--;
+               pos += 2 + pos[1];
+       }
+       parse->ric_len = pos - parse->ric;
+       if (prot_ie_count) {
+               wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from "
+                          "frame", (int) prot_ie_count);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
+{
+       int keylen;
+       enum wpa_alg alg;
+       u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 };
+
+       wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver.");
+
+       switch (sm->pairwise_cipher) {
+       case WPA_CIPHER_CCMP:
+               alg = WPA_ALG_CCMP;
+               keylen = 16;
+               break;
+       case WPA_CIPHER_TKIP:
+               alg = WPA_ALG_TKIP;
+               keylen = 32;
+               break;
+       default:
+               wpa_printf(MSG_WARNING, "FT: Unsupported pairwise cipher %d",
+                          sm->pairwise_cipher);
+               return -1;
+       }
+
+       if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
+                          sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
+               wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_ft_prepare_auth_request - Generate over-the-air auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @mdie: Target AP MDIE
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
+{
+       u8 *ft_ies;
+       size_t ft_ies_len;
+
+       /* Generate a new SNonce */
+       if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+               wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+               return -1;
+       }
+
+       ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+                                   NULL, sm->bssid, NULL, 0, mdie);
+       if (ft_ies) {
+               wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+                                    ft_ies, ft_ies_len);
+               os_free(ft_ies);
+       }
+
+       return 0;
+}
+
+
+int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
+                           int ft_action, const u8 *target_ap,
+                           const u8 *ric_ies, size_t ric_ies_len)
+{
+       u8 *ft_ies;
+       size_t ft_ies_len, ptk_len;
+       struct wpa_ft_ies parse;
+       struct rsn_mdie *mdie;
+       struct rsn_ftie *ftie;
+       u8 ptk_name[WPA_PMK_NAME_LEN];
+       int ret;
+       const u8 *bssid;
+
+       wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+       wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len);
+
+       if (ft_action) {
+               if (!sm->over_the_ds_in_progress) {
+                       wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+                                  "- drop FT Action Response");
+                       return -1;
+               }
+
+               if (os_memcmp(target_ap, sm->target_ap, ETH_ALEN) != 0) {
+                       wpa_printf(MSG_DEBUG, "FT: No over-the-DS in progress "
+                                  "with this Target AP - drop FT Action "
+                                  "Response");
+                       return -1;
+               }
+       }
+
+       if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+           sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+               wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
+                          "enabled for this connection");
+               return -1;
+       }
+
+       if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+               wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+               return -1;
+       }
+
+       mdie = (struct rsn_mdie *) parse.mdie;
+       if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+           os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+                     MOBILITY_DOMAIN_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+               return -1;
+       }
+
+       ftie = (struct rsn_ftie *) parse.ftie;
+       if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+               return -1;
+       }
+
+       if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+               wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+                           ftie->snonce, WPA_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+                           sm->snonce, WPA_NONCE_LEN);
+               return -1;
+       }
+
+       if (parse.r0kh_id == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+               return -1;
+       }
+
+       if (parse.r0kh_id_len != sm->r0kh_id_len ||
+           os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+                          "the current R0KH-ID");
+               wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+                           parse.r0kh_id, parse.r0kh_id_len);
+               wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+                           sm->r0kh_id, sm->r0kh_id_len);
+               return -1;
+       }
+
+       if (parse.r1kh_id == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+               return -1;
+       }
+
+       if (parse.rsn_pmkid == NULL ||
+           os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) {
+               wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
+                          "RSNIE");
+               return -1;
+       }
+
+       os_memcpy(sm->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN);
+       os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN);
+       wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id,
+                         sm->own_addr, sm->pmk_r1, sm->pmk_r1_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
+       wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name",
+                   sm->pmk_r1_name, WPA_PMK_NAME_LEN);
+
+       bssid = target_ap;
+       ptk_len = sm->pairwise_cipher == WPA_CIPHER_CCMP ? 48 : 64;
+       wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
+                         bssid, sm->pmk_r1_name,
+                         (u8 *) &sm->ptk, ptk_len, ptk_name);
+       wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
+                       (u8 *) &sm->ptk, ptk_len);
+       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+       ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
+                                   sm->pmk_r1_name, sm->ptk.kck, bssid,
+                                   ric_ies, ric_ies_len,
+                                   parse.mdie ? parse.mdie - 2 : NULL);
+       if (ft_ies) {
+               wpa_sm_update_ft_ies(sm, sm->mobility_domain,
+                                    ft_ies, ft_ies_len);
+               os_free(ft_ies);
+       }
+
+       wpa_sm_mark_authenticated(sm, bssid);
+       ret = wpa_ft_install_ptk(sm, bssid);
+       if (ret) {
+               /*
+                * Some drivers do not support key configuration when we are
+                * not associated with the target AP. Work around this by
+                * trying again after the following reassociation gets
+                * completed.
+                */
+               wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to "
+                          "association - try again after reassociation");
+               sm->set_ptk_after_assoc = 1;
+       } else
+               sm->set_ptk_after_assoc = 0;
+
+       sm->ft_completed = 1;
+       if (ft_action) {
+               /*
+                * The caller is expected trigger re-association with the
+                * Target AP.
+                */
+               os_memcpy(sm->bssid, target_ap, ETH_ALEN);
+       }
+
+       return 0;
+}
+
+
+int wpa_ft_is_completed(struct wpa_sm *sm)
+{
+       if (sm == NULL)
+               return 0;
+
+       if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+           sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
+               return 0;
+
+       return sm->ft_completed;
+}
+
+
+static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
+                                     size_t gtk_elem_len)
+{
+       u8 gtk[32];
+       int keyidx;
+       enum wpa_alg alg;
+       size_t gtk_len, keylen, rsc_len;
+
+       if (gtk_elem == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No GTK included in FTIE");
+               return 0;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+                       gtk_elem, gtk_elem_len);
+
+       if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
+           gtk_elem_len - 19 > sizeof(gtk)) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem "
+                          "length %lu", (unsigned long) gtk_elem_len);
+               return -1;
+       }
+       gtk_len = gtk_elem_len - 19;
+       if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) {
+               wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+                          "decrypt GTK");
+               return -1;
+       }
+
+       switch (sm->group_cipher) {
+       case WPA_CIPHER_CCMP:
+               keylen = 16;
+               rsc_len = 6;
+               alg = WPA_ALG_CCMP;
+               break;
+       case WPA_CIPHER_TKIP:
+               keylen = 32;
+               rsc_len = 6;
+               alg = WPA_ALG_TKIP;
+               break;
+       case WPA_CIPHER_WEP104:
+               keylen = 13;
+               rsc_len = 0;
+               alg = WPA_ALG_WEP;
+               break;
+       case WPA_CIPHER_WEP40:
+               keylen = 5;
+               rsc_len = 0;
+               alg = WPA_ALG_WEP;
+               break;
+       default:
+               wpa_printf(MSG_WARNING, "WPA: Unsupported Group Cipher %d",
+                          sm->group_cipher);
+               return -1;
+       }
+
+       if (gtk_len < keylen) {
+               wpa_printf(MSG_DEBUG, "FT: Too short GTK in FTIE");
+               return -1;
+       }
+
+       /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+       keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
+
+       if (gtk_elem[2] != keylen) {
+               wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d "
+                          "negotiated %lu",
+                          gtk_elem[2], (unsigned long) keylen);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+       if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
+                          keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) <
+           0) {
+               wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the "
+                          "driver.");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
+                                      size_t igtk_elem_len)
+{
+       u8 igtk[WPA_IGTK_LEN];
+       u16 keyidx;
+
+       if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+               return 0;
+
+       if (igtk_elem == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No IGTK included in FTIE");
+               return 0;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
+                       igtk_elem, igtk_elem_len);
+
+       if (igtk_elem_len != 2 + 6 + 1 + WPA_IGTK_LEN + 8) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem "
+                          "length %lu", (unsigned long) igtk_elem_len);
+               return -1;
+       }
+       if (igtk_elem[8] != WPA_IGTK_LEN) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid IGTK sub-elem Key Length "
+                          "%d", igtk_elem[8]);
+               return -1;
+       }
+
+       if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) {
+               wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
+                          "decrypt IGTK");
+               return -1;
+       }
+
+       /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+       keyidx = WPA_GET_LE16(igtk_elem);
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk,
+                       WPA_IGTK_LEN);
+       if (wpa_sm_set_key(sm, WPA_ALG_IGTK, (u8 *) "\xff\xff\xff\xff\xff\xff",
+                          keyidx, 0, igtk_elem + 2, 6, igtk, WPA_IGTK_LEN) <
+           0) {
+               wpa_printf(MSG_WARNING, "WPA: Failed to set IGTK to the "
+                          "driver.");
+               return -1;
+       }
+
+       return 0;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
+int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
+                                size_t ies_len, const u8 *src_addr)
+{
+       struct wpa_ft_ies parse;
+       struct rsn_mdie *mdie;
+       struct rsn_ftie *ftie;
+       unsigned int count;
+       u8 mic[16];
+
+       wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
+
+       if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
+           sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+               wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
+                          "enabled for this connection");
+               return -1;
+       }
+
+       if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) {
+               wpa_printf(MSG_DEBUG, "FT: Failed to parse IEs");
+               return -1;
+       }
+
+       mdie = (struct rsn_mdie *) parse.mdie;
+       if (mdie == NULL || parse.mdie_len < sizeof(*mdie) ||
+           os_memcmp(mdie->mobility_domain, sm->mobility_domain,
+                     MOBILITY_DOMAIN_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid MDIE");
+               return -1;
+       }
+
+       ftie = (struct rsn_ftie *) parse.ftie;
+       if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) {
+               wpa_printf(MSG_DEBUG, "FT: Invalid FTIE");
+               return -1;
+       }
+
+       if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE");
+               wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+                           ftie->snonce, WPA_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+                           sm->snonce, WPA_NONCE_LEN);
+               return -1;
+       }
+
+       if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE");
+               wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+                           ftie->anonce, WPA_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+                           sm->anonce, WPA_NONCE_LEN);
+               return -1;
+       }
+
+       if (parse.r0kh_id == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE");
+               return -1;
+       }
+
+       if (parse.r0kh_id_len != sm->r0kh_id_len ||
+           os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
+                          "the current R0KH-ID");
+               wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+                           parse.r0kh_id, parse.r0kh_id_len);
+               wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+                           sm->r0kh_id, sm->r0kh_id_len);
+               return -1;
+       }
+
+       if (parse.r1kh_id == NULL) {
+               wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE");
+               return -1;
+       }
+
+       if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
+                          "ReassocResp");
+               return -1;
+       }
+
+       if (parse.rsn_pmkid == NULL ||
+           os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+               wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
+                          "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
+               return -1;
+       }
+
+       count = 3;
+       if (parse.tie)
+               count++;
+       if (ftie->mic_control[1] != count) {
+               wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC "
+                          "Control: received %u expected %u",
+                          ftie->mic_control[1], count);
+               return -1;
+       }
+
+       if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6,
+                      parse.mdie - 2, parse.mdie_len + 2,
+                      parse.ftie - 2, parse.ftie_len + 2,
+                      parse.rsn - 2, parse.rsn_len + 2,
+                      parse.ric, parse.ric_len,
+                      mic) < 0) {
+               wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC");
+               return -1;
+       }
+
+       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 -1;
+       }
+
+       if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
+               return -1;
+
+#ifdef CONFIG_IEEE80211W
+       if (wpa_ft_process_igtk_subelem(sm, parse.igtk, parse.igtk_len) < 0)
+               return -1;
+#endif /* CONFIG_IEEE80211W */
+
+       if (sm->set_ptk_after_assoc) {
+               wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we "
+                          "are associated");
+               if (wpa_ft_install_ptk(sm, src_addr) < 0)
+                       return -1;
+               sm->set_ptk_after_assoc = 0;
+       }
+
+       if (parse.ric) {
+               wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response",
+                           parse.ric, parse.ric_len);
+               /* TODO: parse response and inform driver about results */
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_ft_start_over_ds - Generate over-the-DS auth request
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @target_ap: Target AP Address
+ * @mdie: Mobility Domain IE from the target AP
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
+                        const u8 *mdie)
+{
+       u8 *ft_ies;
+       size_t ft_ies_len;
+
+       wpa_printf(MSG_DEBUG, "FT: Request over-the-DS with " MACSTR,
+                  MAC2STR(target_ap));
+
+       /* Generate a new SNonce */
+       if (os_get_random(sm->snonce, WPA_NONCE_LEN)) {
+               wpa_printf(MSG_INFO, "FT: Failed to generate a new SNonce");
+               return -1;
+       }
+
+       ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
+                                   NULL, target_ap, NULL, 0, mdie);
+       if (ft_ies) {
+               sm->over_the_ds_in_progress = 1;
+               os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
+               wpa_sm_send_ft_action(sm, 1, target_ap, ft_ies, ft_ies_len);
+               os_free(ft_ies);
+       }
+
+       return 0;
+}
+
+#endif /* CONFIG_IEEE80211R */
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
new file mode 100644 (file)
index 0000000..618c090
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Internal WPA/RSN supplicant state machine definitions
+ * Copyright (c) 2004-2010, 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_I_H
+#define WPA_I_H
+
+#include "utils/list.h"
+
+struct wpa_peerkey;
+struct wpa_eapol_key;
+
+/**
+ * struct wpa_sm - Internal WPA state machine data
+ */
+struct wpa_sm {
+       u8 pmk[PMK_LEN];
+       size_t pmk_len;
+       struct wpa_ptk ptk, tptk;
+       int ptk_set, tptk_set;
+       u8 snonce[WPA_NONCE_LEN];
+       u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
+       int renew_snonce;
+       u8 rx_replay_counter[WPA_REPLAY_COUNTER_LEN];
+       int rx_replay_counter_set;
+       u8 request_counter[WPA_REPLAY_COUNTER_LEN];
+
+       struct eapol_sm *eapol; /* EAPOL state machine from upper level code */
+
+       struct rsn_pmksa_cache *pmksa; /* PMKSA cache */
+       struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */
+       struct dl_list pmksa_candidates;
+
+       struct l2_packet_data *l2_preauth;
+       struct l2_packet_data *l2_preauth_br;
+       u8 preauth_bssid[ETH_ALEN]; /* current RSN pre-auth peer or
+                                    * 00:00:00:00:00:00 if no pre-auth is
+                                    * in progress */
+       struct eapol_sm *preauth_eapol;
+
+       struct wpa_sm_ctx *ctx;
+
+       void *scard_ctx; /* context for smartcard callbacks */
+       int fast_reauth; /* whether EAP fast re-authentication is enabled */
+
+       void *network_ctx;
+       int peerkey_enabled;
+       int allowed_pairwise_cipher; /* bitfield of WPA_CIPHER_* */
+       int proactive_key_caching;
+       int eap_workaround;
+       void *eap_conf_ctx;
+       u8 ssid[32];
+       size_t ssid_len;
+       int wpa_ptk_rekey;
+
+       u8 own_addr[ETH_ALEN];
+       const char *ifname;
+       const char *bridge_ifname;
+       u8 bssid[ETH_ALEN];
+
+       unsigned int dot11RSNAConfigPMKLifetime;
+       unsigned int dot11RSNAConfigPMKReauthThreshold;
+       unsigned int dot11RSNAConfigSATimeout;
+
+       unsigned int dot11RSNA4WayHandshakeFailures;
+
+       /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+       unsigned int proto;
+       unsigned int pairwise_cipher;
+       unsigned int group_cipher;
+       unsigned int key_mgmt;
+       unsigned int mgmt_group_cipher;
+
+       int rsn_enabled; /* Whether RSN is enabled in configuration */
+       int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
+
+       u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
+       size_t assoc_wpa_ie_len;
+       u8 *ap_wpa_ie, *ap_rsn_ie;
+       size_t ap_wpa_ie_len, ap_rsn_ie_len;
+
+#ifdef CONFIG_PEERKEY
+       struct wpa_peerkey *peerkey;
+#endif /* CONFIG_PEERKEY */
+
+#ifdef CONFIG_IEEE80211R
+       u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */
+       size_t xxkey_len;
+       u8 pmk_r0[PMK_LEN];
+       u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+       u8 pmk_r1[PMK_LEN];
+       u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+       u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN];
+       u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+       size_t r0kh_id_len;
+       u8 r1kh_id[FT_R1KH_ID_LEN];
+       int ft_completed;
+       int over_the_ds_in_progress;
+       u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
+       int set_ptk_after_assoc;
+       u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */
+       u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
+       size_t assoc_resp_ies_len;
+#endif /* CONFIG_IEEE80211R */
+};
+
+
+static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state)
+{
+       WPA_ASSERT(sm->ctx->set_state);
+       sm->ctx->set_state(sm->ctx->ctx, state);
+}
+
+static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm)
+{
+       WPA_ASSERT(sm->ctx->get_state);
+       return sm->ctx->get_state(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_deauthenticate(struct wpa_sm *sm, int reason_code)
+{
+       WPA_ASSERT(sm->ctx->deauthenticate);
+       sm->ctx->deauthenticate(sm->ctx->ctx, reason_code);
+}
+
+static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code)
+{
+       WPA_ASSERT(sm->ctx->disassociate);
+       sm->ctx->disassociate(sm->ctx->ctx, reason_code);
+}
+
+static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg,
+                                const u8 *addr, int key_idx, int set_tx,
+                                const u8 *seq, size_t seq_len,
+                                const u8 *key, size_t key_len)
+{
+       WPA_ASSERT(sm->ctx->set_key);
+       return sm->ctx->set_key(sm->ctx->ctx, alg, addr, key_idx, set_tx,
+                               seq, seq_len, key, key_len);
+}
+
+static inline void * wpa_sm_get_network_ctx(struct wpa_sm *sm)
+{
+       WPA_ASSERT(sm->ctx->get_network_ctx);
+       return sm->ctx->get_network_ctx(sm->ctx->ctx);
+}
+
+static inline int wpa_sm_get_bssid(struct wpa_sm *sm, u8 *bssid)
+{
+       WPA_ASSERT(sm->ctx->get_bssid);
+       return sm->ctx->get_bssid(sm->ctx->ctx, bssid);
+}
+
+static inline int wpa_sm_ether_send(struct wpa_sm *sm, const u8 *dest,
+                                   u16 proto, const u8 *buf, size_t len)
+{
+       WPA_ASSERT(sm->ctx->ether_send);
+       return sm->ctx->ether_send(sm->ctx->ctx, dest, proto, buf, len);
+}
+
+static inline int wpa_sm_get_beacon_ie(struct wpa_sm *sm)
+{
+       WPA_ASSERT(sm->ctx->get_beacon_ie);
+       return sm->ctx->get_beacon_ie(sm->ctx->ctx);
+}
+
+static inline void wpa_sm_cancel_auth_timeout(struct wpa_sm *sm)
+{
+       WPA_ASSERT(sm->ctx->cancel_auth_timeout);
+       sm->ctx->cancel_auth_timeout(sm->ctx->ctx);
+}
+
+static inline u8 * wpa_sm_alloc_eapol(struct wpa_sm *sm, u8 type,
+                                     const void *data, u16 data_len,
+                                     size_t *msg_len, void **data_pos)
+{
+       WPA_ASSERT(sm->ctx->alloc_eapol);
+       return sm->ctx->alloc_eapol(sm->ctx->ctx, type, data, data_len,
+                                   msg_len, data_pos);
+}
+
+static inline int wpa_sm_add_pmkid(struct wpa_sm *sm, const u8 *bssid,
+                                  const u8 *pmkid)
+{
+       WPA_ASSERT(sm->ctx->add_pmkid);
+       return sm->ctx->add_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_remove_pmkid(struct wpa_sm *sm, const u8 *bssid,
+                                     const u8 *pmkid)
+{
+       WPA_ASSERT(sm->ctx->remove_pmkid);
+       return sm->ctx->remove_pmkid(sm->ctx->ctx, bssid, pmkid);
+}
+
+static inline int wpa_sm_mlme_setprotection(struct wpa_sm *sm, const u8 *addr,
+                                           int protect_type, int key_type)
+{
+       WPA_ASSERT(sm->ctx->mlme_setprotection);
+       return sm->ctx->mlme_setprotection(sm->ctx->ctx, addr, protect_type,
+                                          key_type);
+}
+
+static inline int wpa_sm_update_ft_ies(struct wpa_sm *sm, const u8 *md,
+                                      const u8 *ies, size_t ies_len)
+{
+       if (sm->ctx->update_ft_ies)
+               return sm->ctx->update_ft_ies(sm->ctx->ctx, md, ies, ies_len);
+       return -1;
+}
+
+static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action,
+                                       const u8 *target_ap,
+                                       const u8 *ies, size_t ies_len)
+{
+       if (sm->ctx->send_ft_action)
+               return sm->ctx->send_ft_action(sm->ctx->ctx, action, target_ap,
+                                              ies, ies_len);
+       return -1;
+}
+
+static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm,
+                                           const u8 *target_ap)
+{
+       if (sm->ctx->mark_authenticated)
+               return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap);
+       return -1;
+}
+
+
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+                       int ver, const u8 *dest, u16 proto,
+                       u8 *msg, size_t msg_len, u8 *key_mic);
+int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
+                              const struct wpa_eapol_key *key,
+                              int ver, const u8 *nonce,
+                              const u8 *wpa_ie, size_t wpa_ie_len,
+                              struct wpa_ptk *ptk);
+int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
+                              const struct wpa_eapol_key *key,
+                              u16 ver, u16 key_info,
+                              const u8 *kde, size_t kde_len,
+                              struct wpa_ptk *ptk);
+
+int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
+                     const struct wpa_eapol_key *key,
+                     struct wpa_ptk *ptk, size_t ptk_len);
+
+#endif /* WPA_I_H */
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
new file mode 100644 (file)
index 0000000..f447223
--- /dev/null
@@ -0,0 +1,581 @@
+/*
+ * wpa_supplicant - WPA/RSN IE and KDE processing
+ * Copyright (c) 2003-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 "wpa.h"
+#include "pmksa_cache.h"
+#include "common/ieee802_11_defs.h"
+#include "wpa_i.h"
+#include "wpa_ie.h"
+
+
+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->proto = WPA_PROTO_WPA;
+       data->pairwise_cipher = WPA_CIPHER_TKIP;
+       data->group_cipher = WPA_CIPHER_TKIP;
+       data->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+       data->capabilities = 0;
+       data->pmkid = NULL;
+       data->num_pmkid = 0;
+       data->mgmt_group_cipher = 0;
+
+       if (wpa_ie_len == 0) {
+               /* No WPA IE - fail silently */
+               return -1;
+       }
+
+       if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) {
+               wpa_printf(MSG_DEBUG, "%s: ie len too short %lu",
+                          __func__, (unsigned long) wpa_ie_len);
+               return -1;
+       }
+
+       hdr = (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) {
+               wpa_printf(MSG_DEBUG, "%s: malformed ie or unknown version",
+                          __func__);
+               return -1;
+       }
+
+       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) {
+               wpa_printf(MSG_DEBUG, "%s: ie length mismatch, %u too much",
+                          __func__, left);
+               return -1;
+       }
+
+       if (left >= 2) {
+               data->pairwise_cipher = 0;
+               count = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+               if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+                       wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
+                                  "count %u left %u", __func__, count, left);
+                       return -1;
+               }
+               for (i = 0; i < count; i++) {
+                       data->pairwise_cipher |= wpa_selector_to_bitfield(pos);
+                       pos += WPA_SELECTOR_LEN;
+                       left -= WPA_SELECTOR_LEN;
+               }
+       } else if (left == 1) {
+               wpa_printf(MSG_DEBUG, "%s: ie too short (for key mgmt)",
+                          __func__);
+               return -1;
+       }
+
+       if (left >= 2) {
+               data->key_mgmt = 0;
+               count = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+               if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+                       wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
+                                  "count %u left %u", __func__, count, left);
+                       return -1;
+               }
+               for (i = 0; i < count; i++) {
+                       data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos);
+                       pos += WPA_SELECTOR_LEN;
+                       left -= WPA_SELECTOR_LEN;
+               }
+       } else if (left == 1) {
+               wpa_printf(MSG_DEBUG, "%s: ie too short (for capabilities)",
+                          __func__);
+               return -1;
+       }
+
+       if (left >= 2) {
+               data->capabilities = WPA_GET_LE16(pos);
+               pos += 2;
+               left -= 2;
+       }
+
+       if (left > 0) {
+               wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
+                          __func__, left);
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpa_parse_wpa_ie - Parse WPA/RSN IE
+ * @wpa_ie: Pointer to WPA or RSN IE
+ * @wpa_ie_len: Length of the WPA/RSN IE
+ * @data: Pointer to data area for parsing results
+ * Returns: 0 on success, -1 on failure
+ *
+ * Parse the contents of WPA or RSN IE and write the parsed data into data.
+ */
+int wpa_parse_wpa_ie(const u8 *wpa_ie, size_t wpa_ie_len,
+                    struct wpa_ie_data *data)
+{
+       if (wpa_ie_len >= 1 && wpa_ie[0] == WLAN_EID_RSN)
+               return wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, data);
+       else
+               return wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, data);
+}
+
+
+static int wpa_gen_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len,
+                             int pairwise_cipher, int group_cipher,
+                             int key_mgmt)
+{
+       u8 *pos;
+       struct wpa_ie_hdr *hdr;
+
+       if (wpa_ie_len < sizeof(*hdr) + WPA_SELECTOR_LEN +
+           2 + WPA_SELECTOR_LEN + 2 + WPA_SELECTOR_LEN)
+               return -1;
+
+       hdr = (struct wpa_ie_hdr *) wpa_ie;
+       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 (group_cipher == WPA_CIPHER_CCMP) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+       } else if (group_cipher == WPA_CIPHER_TKIP) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+       } else if (group_cipher == WPA_CIPHER_WEP104) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104);
+       } else if (group_cipher == WPA_CIPHER_WEP40) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40);
+       } else {
+               wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+                          group_cipher);
+               return -1;
+       }
+       pos += WPA_SELECTOR_LEN;
+
+       *pos++ = 1;
+       *pos++ = 0;
+       if (pairwise_cipher == WPA_CIPHER_CCMP) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
+       } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
+       } else if (pairwise_cipher == WPA_CIPHER_NONE) {
+               RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
+       } else {
+               wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+                          pairwise_cipher);
+               return -1;
+       }
+       pos += WPA_SELECTOR_LEN;
+
+       *pos++ = 1;
+       *pos++ = 0;
+       if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+               RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
+       } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+               RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+       } else if (key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+               RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_NONE);
+       } else {
+               wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+                          key_mgmt);
+               return -1;
+       }
+       pos += WPA_SELECTOR_LEN;
+
+       /* WPA Capabilities; use defaults, so no need to include it */
+
+       hdr->len = (pos - wpa_ie) - 2;
+
+       WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+       return pos - wpa_ie;
+}
+
+
+static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
+                             int pairwise_cipher, int group_cipher,
+                             int key_mgmt, int mgmt_group_cipher,
+                             struct wpa_sm *sm)
+{
+#ifndef CONFIG_NO_WPA2
+       u8 *pos;
+       struct rsn_ie_hdr *hdr;
+       u16 capab;
+
+       if (rsn_ie_len < sizeof(*hdr) + RSN_SELECTOR_LEN +
+           2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN + 2 +
+           (sm->cur_pmksa ? 2 + PMKID_LEN : 0)) {
+               wpa_printf(MSG_DEBUG, "RSN: Too short IE buffer (%lu bytes)",
+                          (unsigned long) rsn_ie_len);
+               return -1;
+       }
+
+       hdr = (struct rsn_ie_hdr *) rsn_ie;
+       hdr->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(hdr->version, RSN_VERSION);
+       pos = (u8 *) (hdr + 1);
+
+       if (group_cipher == WPA_CIPHER_CCMP) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       } else if (group_cipher == WPA_CIPHER_TKIP) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+       } else if (group_cipher == WPA_CIPHER_WEP104) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104);
+       } else if (group_cipher == WPA_CIPHER_WEP40) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40);
+       } else {
+               wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+                          group_cipher);
+               return -1;
+       }
+       pos += RSN_SELECTOR_LEN;
+
+       *pos++ = 1;
+       *pos++ = 0;
+       if (pairwise_cipher == WPA_CIPHER_CCMP) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       } else if (pairwise_cipher == WPA_CIPHER_TKIP) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
+       } else if (pairwise_cipher == WPA_CIPHER_NONE) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
+       } else {
+               wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+                          pairwise_cipher);
+               return -1;
+       }
+       pos += RSN_SELECTOR_LEN;
+
+       *pos++ = 1;
+       *pos++ = 0;
+       if (key_mgmt == WPA_KEY_MGMT_IEEE8021X) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X);
+       } else if (key_mgmt == WPA_KEY_MGMT_PSK) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X);
+#ifdef CONFIG_IEEE80211R
+       } else if (key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
+       } else if (key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SHA256) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256);
+       } else if (key_mgmt == WPA_KEY_MGMT_PSK_SHA256) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256);
+#endif /* CONFIG_IEEE80211W */
+       } else {
+               wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
+                          key_mgmt);
+               return -1;
+       }
+       pos += RSN_SELECTOR_LEN;
+
+       /* RSN Capabilities */
+       capab = 0;
+#ifdef CONFIG_IEEE80211W
+       if (sm->mfp)
+               capab |= WPA_CAPABILITY_MFPC;
+       if (sm->mfp == 2)
+               capab |= WPA_CAPABILITY_MFPR;
+#endif /* CONFIG_IEEE80211W */
+       WPA_PUT_LE16(pos, capab);
+       pos += 2;
+
+       if (sm->cur_pmksa) {
+               /* PMKID Count (2 octets, little endian) */
+               *pos++ = 1;
+               *pos++ = 0;
+               /* PMKID */
+               os_memcpy(pos, sm->cur_pmksa->pmkid, PMKID_LEN);
+               pos += PMKID_LEN;
+       }
+
+#ifdef CONFIG_IEEE80211W
+       if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+               if (!sm->cur_pmksa) {
+                       /* 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 - rsn_ie) - 2;
+
+       WPA_ASSERT((size_t) (pos - rsn_ie) <= rsn_ie_len);
+
+       return pos - rsn_ie;
+#else /* CONFIG_NO_WPA2 */
+       return -1;
+#endif /* CONFIG_NO_WPA2 */
+}
+
+
+/**
+ * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
+ * @sm: Pointer to WPA state machine data from wpa_sm_init()
+ * @wpa_ie: Pointer to memory area for the generated WPA/RSN IE
+ * @wpa_ie_len: Maximum length of the generated WPA/RSN IE
+ * Returns: Length of the generated WPA/RSN IE or -1 on failure
+ */
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
+{
+       if (sm->proto == WPA_PROTO_RSN)
+               return wpa_gen_wpa_ie_rsn(wpa_ie, wpa_ie_len,
+                                         sm->pairwise_cipher,
+                                         sm->group_cipher,
+                                         sm->key_mgmt, sm->mgmt_group_cipher,
+                                         sm);
+       else
+               return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
+                                         sm->pairwise_cipher,
+                                         sm->group_cipher,
+                                         sm->key_mgmt);
+}
+
+
+/**
+ * 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;
+               wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key",
+                           ie->wpa_ie, ie->wpa_ie_len);
+               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;
+               wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key",
+                           pos, pos[1] + 2);
+               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;
+               wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key",
+                               pos, pos[1] + 2);
+               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;
+               wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key",
+                           pos, pos[1] + 2);
+               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;
+               wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key",
+                               pos, pos[1] + 2);
+               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;
+               wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key",
+                           pos, pos[1] + 2);
+               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;
+               wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key",
+                           pos, pos[1] + 2);
+               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;
+               wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key",
+                           pos, pos[1] + 2);
+               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;
+               wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key",
+                               pos, pos[1] + 2);
+               return 0;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_parse_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_supplicant_parse_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;
+                       wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key",
+                                   ie->rsn_ie, ie->rsn_ie_len);
+#ifdef CONFIG_IEEE80211R
+               } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) {
+                       ie->mdie = pos;
+                       ie->mdie_len = pos[1] + 2;
+                       wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key",
+                                   ie->mdie, ie->mdie_len);
+               } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) {
+                       ie->ftie = pos;
+                       ie->ftie_len = pos[1] + 2;
+                       wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key",
+                                   ie->ftie, ie->ftie_len);
+               } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) {
+                       if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) {
+                               ie->reassoc_deadline = pos;
+                               wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline "
+                                           "in EAPOL-Key",
+                                           ie->reassoc_deadline, pos[1] + 2);
+                       } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) {
+                               ie->key_lifetime = pos;
+                               wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime "
+                                           "in EAPOL-Key",
+                                           ie->key_lifetime, pos[1] + 2);
+                       } else {
+                               wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized "
+                                           "EAPOL-Key Key Data IE",
+                                           pos, 2 + pos[1]);
+                       }
+#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;
+}
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
new file mode 100644 (file)
index 0000000..94518d8
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * wpa_supplicant - 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_IE_H
+#define WPA_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;
+       const u8 *ftie;
+       size_t ftie_len;
+       const u8 *reassoc_deadline;
+       const u8 *key_lifetime;
+#endif /* CONFIG_IEEE80211R */
+};
+
+int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
+                            struct wpa_eapol_ie_parse *ie);
+int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len);
+
+#endif /* WPA_IE_H */
diff --git a/src/tls/.gitignore b/src/tls/.gitignore
new file mode 100644 (file)
index 0000000..d43242d
--- /dev/null
@@ -0,0 +1 @@
+libtls.a
diff --git a/src/tls/Makefile b/src/tls/Makefile
new file mode 100644 (file)
index 0000000..a2da096
--- /dev/null
@@ -0,0 +1,37 @@
+all: libtls.a
+
+clean:
+       rm -f *~ *.o *.d libtls.a
+
+install:
+       @echo Nothing to be made.
+
+
+include ../lib.rules
+
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+
+LIB_OBJS= \
+       asn1.o \
+       bignum.o \
+       pkcs1.o \
+       pkcs5.o \
+       pkcs8.o \
+       rsa.o \
+       tlsv1_client.o \
+       tlsv1_client_read.o \
+       tlsv1_client_write.o \
+       tlsv1_common.o \
+       tlsv1_cred.o \
+       tlsv1_record.o \
+       tlsv1_server.o \
+       tlsv1_server_read.o \
+       tlsv1_server_write.o \
+       x509v3.o
+
+
+libtls.a: $(LIB_OBJS)
+       $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
new file mode 100644 (file)
index 0000000..3391245
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "asn1.h"
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
+{
+       const u8 *pos, *end;
+       u8 tmp;
+
+       os_memset(hdr, 0, sizeof(*hdr));
+       pos = buf;
+       end = buf + len;
+
+       hdr->identifier = *pos++;
+       hdr->class = hdr->identifier >> 6;
+       hdr->constructed = !!(hdr->identifier & (1 << 5));
+
+       if ((hdr->identifier & 0x1f) == 0x1f) {
+               hdr->tag = 0;
+               do {
+                       if (pos >= end) {
+                               wpa_printf(MSG_DEBUG, "ASN.1: Identifier "
+                                          "underflow");
+                               return -1;
+                       }
+                       tmp = *pos++;
+                       wpa_printf(MSG_MSGDUMP, "ASN.1: Extended tag data: "
+                                  "0x%02x", tmp);
+                       hdr->tag = (hdr->tag << 7) | (tmp & 0x7f);
+               } while (tmp & 0x80);
+       } else
+               hdr->tag = hdr->identifier & 0x1f;
+
+       tmp = *pos++;
+       if (tmp & 0x80) {
+               if (tmp == 0xff) {
+                       wpa_printf(MSG_DEBUG, "ASN.1: Reserved length "
+                                  "value 0xff used");
+                       return -1;
+               }
+               tmp &= 0x7f; /* number of subsequent octets */
+               hdr->length = 0;
+               if (tmp > 4) {
+                       wpa_printf(MSG_DEBUG, "ASN.1: Too long length field");
+                       return -1;
+               }
+               while (tmp--) {
+                       if (pos >= end) {
+                               wpa_printf(MSG_DEBUG, "ASN.1: Length "
+                                          "underflow");
+                               return -1;
+                       }
+                       hdr->length = (hdr->length << 8) | *pos++;
+               }
+       } else {
+               /* Short form - length 0..127 in one octet */
+               hdr->length = tmp;
+       }
+
+       if (end < pos || hdr->length > (unsigned int) (end - pos)) {
+               wpa_printf(MSG_DEBUG, "ASN.1: Contents underflow");
+               return -1;
+       }
+
+       hdr->payload = pos;
+       return 0;
+}
+
+
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid)
+{
+       const u8 *pos, *end;
+       unsigned long val;
+       u8 tmp;
+
+       os_memset(oid, 0, sizeof(*oid));
+
+       pos = buf;
+       end = buf + len;
+
+       while (pos < end) {
+               val = 0;
+
+               do {
+                       if (pos >= end)
+                               return -1;
+                       tmp = *pos++;
+                       val = (val << 7) | (tmp & 0x7f);
+               } while (tmp & 0x80);
+
+               if (oid->len >= ASN1_MAX_OID_LEN) {
+                       wpa_printf(MSG_DEBUG, "ASN.1: Too long OID value");
+                       return -1;
+               }
+               if (oid->len == 0) {
+                       /*
+                        * The first octet encodes the first two object
+                        * identifier components in (X*40) + Y formula.
+                        * X = 0..2.
+                        */
+                       oid->oid[0] = val / 40;
+                       if (oid->oid[0] > 2)
+                               oid->oid[0] = 2;
+                       oid->oid[1] = val - oid->oid[0] * 40;
+                       oid->len = 2;
+               } else
+                       oid->oid[oid->len++] = val;
+       }
+
+       return 0;
+}
+
+
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+                const u8 **next)
+{
+       struct asn1_hdr hdr;
+
+       if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0)
+               return -1;
+
+       if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) {
+               wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d "
+                          "tag 0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       *next = hdr.payload + hdr.length;
+
+       return asn1_parse_oid(hdr.payload, hdr.length, oid);
+}
+
+
+void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+{
+       char *pos = buf;
+       size_t i;
+       int ret;
+
+       if (len == 0)
+               return;
+
+       buf[0] = '\0';
+
+       for (i = 0; i < oid->len; i++) {
+               ret = os_snprintf(pos, buf + len - pos,
+                                 "%s%lu",
+                                 i == 0 ? "" : ".", oid->oid[i]);
+               if (ret < 0 || ret >= buf + len - pos)
+                       break;
+               pos += ret;
+       }
+       buf[len - 1] = '\0';
+}
+
+
+static u8 rotate_bits(u8 octet)
+{
+       int i;
+       u8 res;
+
+       res = 0;
+       for (i = 0; i < 8; i++) {
+               res <<= 1;
+               if (octet & 1)
+                       res |= 1;
+               octet >>= 1;
+       }
+
+       return res;
+}
+
+
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
+{
+       unsigned long val = 0;
+       const u8 *pos = buf;
+
+       /* BER requires that unused bits are zero, so we can ignore the number
+        * of unused bits */
+       pos++;
+
+       if (len >= 2)
+               val |= rotate_bits(*pos++);
+       if (len >= 3)
+               val |= ((unsigned long) rotate_bits(*pos++)) << 8;
+       if (len >= 4)
+               val |= ((unsigned long) rotate_bits(*pos++)) << 16;
+       if (len >= 5)
+               val |= ((unsigned long) rotate_bits(*pos++)) << 24;
+       if (len >= 6)
+               wpa_printf(MSG_DEBUG, "X509: %s - some bits ignored "
+                          "(BIT STRING length %lu)",
+                          __func__, (unsigned long) len);
+
+       return val;
+}
diff --git a/src/tls/asn1.h b/src/tls/asn1.h
new file mode 100644 (file)
index 0000000..2ff571e
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * ASN.1 DER parsing
+ * Copyright (c) 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 ASN1_H
+#define ASN1_H
+
+#define ASN1_TAG_EOC           0x00 /* not used with DER */
+#define ASN1_TAG_BOOLEAN       0x01
+#define ASN1_TAG_INTEGER       0x02
+#define ASN1_TAG_BITSTRING     0x03
+#define ASN1_TAG_OCTETSTRING   0x04
+#define ASN1_TAG_NULL          0x05
+#define ASN1_TAG_OID           0x06
+#define ASN1_TAG_OBJECT_DESCRIPTOR     0x07 /* not yet parsed */
+#define ASN1_TAG_EXTERNAL      0x08 /* not yet parsed */
+#define ASN1_TAG_REAL          0x09 /* not yet parsed */
+#define ASN1_TAG_ENUMERATED    0x0A /* not yet parsed */
+#define ASN1_TAG_UTF8STRING    0x0C /* not yet parsed */
+#define ANS1_TAG_RELATIVE_OID  0x0D
+#define ASN1_TAG_SEQUENCE      0x10 /* shall be constructed */
+#define ASN1_TAG_SET           0x11
+#define ASN1_TAG_NUMERICSTRING 0x12 /* not yet parsed */
+#define ASN1_TAG_PRINTABLESTRING       0x13
+#define ASN1_TAG_TG1STRING     0x14 /* not yet parsed */
+#define ASN1_TAG_VIDEOTEXSTRING        0x15 /* not yet parsed */
+#define ASN1_TAG_IA5STRING     0x16
+#define ASN1_TAG_UTCTIME       0x17
+#define ASN1_TAG_GENERALIZEDTIME       0x18 /* not yet parsed */
+#define ASN1_TAG_GRAPHICSTRING 0x19 /* not yet parsed */
+#define ASN1_TAG_VISIBLESTRING 0x1A
+#define ASN1_TAG_GENERALSTRING 0x1B /* not yet parsed */
+#define ASN1_TAG_UNIVERSALSTRING       0x1C /* not yet parsed */
+#define ASN1_TAG_BMPSTRING     0x1D /* not yet parsed */
+
+#define ASN1_CLASS_UNIVERSAL           0
+#define ASN1_CLASS_APPLICATION         1
+#define ASN1_CLASS_CONTEXT_SPECIFIC    2
+#define ASN1_CLASS_PRIVATE             3
+
+
+struct asn1_hdr {
+       const u8 *payload;
+       u8 identifier, class, constructed;
+       unsigned int tag, length;
+};
+
+#define ASN1_MAX_OID_LEN 20
+struct asn1_oid {
+       unsigned long oid[ASN1_MAX_OID_LEN];
+       size_t len;
+};
+
+
+int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr);
+int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid);
+int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
+                const u8 **next);
+void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len);
+unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len);
+
+#endif /* ASN1_H */
diff --git a/src/tls/bignum.c b/src/tls/bignum.c
new file mode 100644 (file)
index 0000000..5c0fc62
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Big number math
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "bignum.h"
+
+#ifdef CONFIG_INTERNAL_LIBTOMMATH
+#include "libtommath.c"
+#else /* CONFIG_INTERNAL_LIBTOMMATH */
+#include <tommath.h>
+#endif /* CONFIG_INTERNAL_LIBTOMMATH */
+
+
+/*
+ * The current version is just a wrapper for LibTomMath library, so
+ * struct bignum is just typecast to mp_int.
+ */
+
+/**
+ * bignum_init - Allocate memory for bignum
+ * Returns: Pointer to allocated bignum or %NULL on failure
+ */
+struct bignum * bignum_init(void)
+{
+       struct bignum *n = os_zalloc(sizeof(mp_int));
+       if (n == NULL)
+               return NULL;
+       if (mp_init((mp_int *) n) != MP_OKAY) {
+               os_free(n);
+               n = NULL;
+       }
+       return n;
+}
+
+
+/**
+ * bignum_deinit - Free bignum
+ * @n: Bignum from bignum_init()
+ */
+void bignum_deinit(struct bignum *n)
+{
+       if (n) {
+               mp_clear((mp_int *) n);
+               os_free(n);
+       }
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Get length of bignum as an unsigned binary buffer
+ * @n: Bignum from bignum_init()
+ * Returns: Length of n if written to a binary buffer
+ */
+size_t bignum_get_unsigned_bin_len(struct bignum *n)
+{
+       return mp_unsigned_bin_size((mp_int *) n);
+}
+
+
+/**
+ * bignum_get_unsigned_bin - Set binary buffer to unsigned bignum
+ * @n: Bignum from bignum_init()
+ * @buf: Buffer for the binary number
+ * @len: Length of the buffer, can be %NULL if buffer is known to be long
+ * enough. Set to used buffer length on success if not %NULL.
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len)
+{
+       size_t need = mp_unsigned_bin_size((mp_int *) n);
+       if (len && need > *len) {
+               *len = need;
+               return -1;
+       }
+       if (mp_to_unsigned_bin((mp_int *) n, buf) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       if (len)
+               *len = need;
+       return 0;
+}
+
+
+/**
+ * bignum_set_unsigned_bin - Set bignum based on unsigned binary buffer
+ * @n: Bignum from bignum_init(); to be set to the given value
+ * @buf: Buffer with unsigned binary value
+ * @len: Length of buf in octets
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len)
+{
+       if (mp_read_unsigned_bin((mp_int *) n, (u8 *) buf, len) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_cmp - Signed comparison
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp(const struct bignum *a, const struct bignum *b)
+{
+       return mp_cmp((mp_int *) a, (mp_int *) b);
+}
+
+
+/**
+ * bignum_cmd_d - Compare bignum to standard integer
+ * @a: Bignum from bignum_init()
+ * @b: Small integer
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_cmp_d(const struct bignum *a, unsigned long b)
+{
+       return mp_cmp_d((mp_int *) a, b);
+}
+
+
+/**
+ * bignum_add - c = a + b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a + b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_add(const struct bignum *a, const struct bignum *b,
+              struct bignum *c)
+{
+       if (mp_add((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_sub - c = a - b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a - b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+              struct bignum *c)
+{
+       if (mp_sub((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_mul - c = a * b
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); used to store the result of a * b
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+              struct bignum *c)
+{
+       if (mp_mul((mp_int *) a, (mp_int *) b, (mp_int *) c) != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_mulmod - d = a * b (mod c)
+ * @a: Bignum from bignum_init()
+ * @b: Bignum from bignum_init()
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a * b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+                 const struct bignum *c, struct bignum *d)
+{
+       if (mp_mulmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+           != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * bignum_exptmod - Modular exponentiation: d = a^b (mod c)
+ * @a: Bignum from bignum_init(); base
+ * @b: Bignum from bignum_init(); exponent
+ * @c: Bignum from bignum_init(); modulus
+ * @d: Bignum from bignum_init(); used to store the result of a^b (mod c)
+ * Returns: 0 on success, -1 on failure
+ */
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+                  const struct bignum *c, struct bignum *d)
+{
+       if (mp_exptmod((mp_int *) a, (mp_int *) b, (mp_int *) c, (mp_int *) d)
+           != MP_OKAY) {
+               wpa_printf(MSG_DEBUG, "BIGNUM: %s failed", __func__);
+               return -1;
+       }
+       return 0;
+}
diff --git a/src/tls/bignum.h b/src/tls/bignum.h
new file mode 100644 (file)
index 0000000..f25e267
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Big number math
+ * Copyright (c) 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 BIGNUM_H
+#define BIGNUM_H
+
+struct bignum;
+
+struct bignum * bignum_init(void);
+void bignum_deinit(struct bignum *n);
+size_t bignum_get_unsigned_bin_len(struct bignum *n);
+int bignum_get_unsigned_bin(const struct bignum *n, u8 *buf, size_t *len);
+int bignum_set_unsigned_bin(struct bignum *n, const u8 *buf, size_t len);
+int bignum_cmp(const struct bignum *a, const struct bignum *b);
+int bignum_cmp_d(const struct bignum *a, unsigned long b);
+int bignum_add(const struct bignum *a, const struct bignum *b,
+              struct bignum *c);
+int bignum_sub(const struct bignum *a, const struct bignum *b,
+              struct bignum *c);
+int bignum_mul(const struct bignum *a, const struct bignum *b,
+              struct bignum *c);
+int bignum_mulmod(const struct bignum *a, const struct bignum *b,
+                 const struct bignum *c, struct bignum *d);
+int bignum_exptmod(const struct bignum *a, const struct bignum *b,
+                  const struct bignum *c, struct bignum *d);
+
+#endif /* BIGNUM_H */
diff --git a/src/tls/libtommath.c b/src/tls/libtommath.c
new file mode 100644 (file)
index 0000000..1374264
--- /dev/null
@@ -0,0 +1,3381 @@
+/*
+ * Minimal code for RSA support from LibTomMath 0.41
+ * http://libtom.org/
+ * http://libtom.org/files/ltm-0.41.tar.bz2
+ * This library was released in public domain by Tom St Denis.
+ *
+ * The combination in this file may not use all of the optimized algorithms
+ * from LibTomMath and may be considerable slower than the LibTomMath with its
+ * default settings. The main purpose of having this version here is to make it
+ * easier to build bignum.c wrapper without having to install and build an
+ * external library.
+ *
+ * If CONFIG_INTERNAL_LIBTOMMATH is defined, bignum.c includes this
+ * libtommath.c file instead of using the external LibTomMath library.
+ */
+
+#ifndef CHAR_BIT
+#define CHAR_BIT 8
+#endif
+
+#define BN_MP_INVMOD_C
+#define BN_S_MP_EXPTMOD_C /* Note: #undef in tommath_superclass.h; this would
+                          * require BN_MP_EXPTMOD_FAST_C instead */
+#define BN_S_MP_MUL_DIGS_C
+#define BN_MP_INVMOD_SLOW_C
+#define BN_S_MP_SQR_C
+#define BN_S_MP_MUL_HIGH_DIGS_C /* Note: #undef in tommath_superclass.h; this
+                                * would require other than mp_reduce */
+
+#ifdef LTM_FAST
+
+/* Use faster div at the cost of about 1 kB */
+#define BN_MP_MUL_D_C
+
+/* Include faster exptmod (Montgomery) at the cost of about 2.5 kB in code */
+#define BN_MP_EXPTMOD_FAST_C
+#define BN_MP_MONTGOMERY_SETUP_C
+#define BN_FAST_MP_MONTGOMERY_REDUCE_C
+#define BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+#define BN_MP_MUL_2_C
+
+/* Include faster sqr at the cost of about 0.5 kB in code */
+#define BN_FAST_S_MP_SQR_C
+
+#else /* LTM_FAST */
+
+#define BN_MP_DIV_SMALL
+#define BN_MP_INIT_MULTI_C
+#define BN_MP_CLEAR_MULTI_C
+#define BN_MP_ABS_C
+#endif /* LTM_FAST */
+
+/* Current uses do not require support for negative exponent in exptmod, so we
+ * can save about 1.5 kB in leaving out invmod. */
+#define LTM_NO_NEG_EXP
+
+/* from tommath.h */
+
+#ifndef MIN
+   #define MIN(x,y) ((x)<(y)?(x):(y))
+#endif
+
+#ifndef MAX
+   #define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+#define  OPT_CAST(x)
+
+typedef unsigned long mp_digit;
+typedef u64 mp_word;
+
+#define DIGIT_BIT          28
+#define MP_28BIT
+
+
+#define XMALLOC  os_malloc
+#define XFREE    os_free
+#define XREALLOC os_realloc
+
+
+#define MP_MASK          ((((mp_digit)1)<<((mp_digit)DIGIT_BIT))-((mp_digit)1))
+
+#define MP_LT        -1   /* less than */
+#define MP_EQ         0   /* equal to */
+#define MP_GT         1   /* greater than */
+
+#define MP_ZPOS       0   /* positive integer */
+#define MP_NEG        1   /* negative */
+
+#define MP_OKAY       0   /* ok result */
+#define MP_MEM        -2  /* out of mem */
+#define MP_VAL        -3  /* invalid input */
+
+#define MP_YES        1   /* yes response */
+#define MP_NO         0   /* no response */
+
+typedef int           mp_err;
+
+/* define this to use lower memory usage routines (exptmods mostly) */
+#define MP_LOW_MEM
+
+/* default precision */
+#ifndef MP_PREC
+   #ifndef MP_LOW_MEM
+      #define MP_PREC                 32     /* default digits of precision */
+   #else
+      #define MP_PREC                 8      /* default digits of precision */
+   #endif   
+#endif
+
+/* size of comba arrays, should be at least 2 * 2**(BITS_PER_WORD - BITS_PER_DIGIT*2) */
+#define MP_WARRAY               (1 << (sizeof(mp_word) * CHAR_BIT - 2 * DIGIT_BIT + 1))
+
+/* the infamous mp_int structure */
+typedef struct  {
+    int used, alloc, sign;
+    mp_digit *dp;
+} mp_int;
+
+
+/* ---> Basic Manipulations <--- */
+#define mp_iszero(a) (((a)->used == 0) ? MP_YES : MP_NO)
+#define mp_iseven(a) (((a)->used > 0 && (((a)->dp[0] & 1) == 0)) ? MP_YES : MP_NO)
+#define mp_isodd(a)  (((a)->used > 0 && (((a)->dp[0] & 1) == 1)) ? MP_YES : MP_NO)
+
+
+/* prototypes for copied functions */
+#define s_mp_mul(a, b, c) s_mp_mul_digs(a, b, c, (a)->used + (b)->used + 1)
+static int s_mp_exptmod(mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+static int s_mp_sqr(mp_int * a, mp_int * b);
+static int s_mp_mul_high_digs(mp_int * a, mp_int * b, mp_int * c, int digs);
+
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs);
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...);
+#endif
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...);
+#endif
+static int mp_lshd(mp_int * a, int b);
+static void mp_set(mp_int * a, mp_digit b);
+static void mp_clamp(mp_int * a);
+static void mp_exch(mp_int * a, mp_int * b);
+static void mp_rshd(mp_int * a, int b);
+static void mp_zero(mp_int * a);
+static int mp_mod_2d(mp_int * a, int b, mp_int * c);
+static int mp_div_2d(mp_int * a, int b, mp_int * c, mp_int * d);
+static int mp_init_copy(mp_int * a, mp_int * b);
+static int mp_mul_2d(mp_int * a, int b, mp_int * c);
+#ifndef LTM_NO_NEG_EXP
+static int mp_div_2(mp_int * a, mp_int * b);
+static int mp_invmod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_invmod_slow(mp_int * a, mp_int * b, mp_int * c);
+#endif /* LTM_NO_NEG_EXP */
+static int mp_copy(mp_int * a, mp_int * b);
+static int mp_count_bits(mp_int * a);
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d);
+static int mp_mod(mp_int * a, mp_int * b, mp_int * c);
+static int mp_grow(mp_int * a, int size);
+static int mp_cmp_mag(mp_int * a, mp_int * b);
+#ifdef BN_MP_ABS_C
+static int mp_abs(mp_int * a, mp_int * b);
+#endif
+static int mp_sqr(mp_int * a, mp_int * b);
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d);
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d);
+static int mp_2expt(mp_int * a, int b);
+static int mp_reduce_setup(mp_int * a, mp_int * b);
+static int mp_reduce(mp_int * x, mp_int * m, mp_int * mu);
+static int mp_init_size(mp_int * a, int size);
+#ifdef BN_MP_EXPTMOD_FAST_C
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode);
+#endif /* BN_MP_EXPTMOD_FAST_C */
+#ifdef BN_FAST_S_MP_SQR_C
+static int fast_s_mp_sqr (mp_int * a, mp_int * b);
+#endif /* BN_FAST_S_MP_SQR_C */
+#ifdef BN_MP_MUL_D_C
+static int mp_mul_d (mp_int * a, mp_digit b, mp_int * c);
+#endif /* BN_MP_MUL_D_C */
+
+
+
+/* functions from bn_<func name>.c */
+
+
+/* reverse an array, used for radix code */
+static void bn_reverse (unsigned char *s, int len)
+{
+  int     ix, iy;
+  unsigned char t;
+
+  ix = 0;
+  iy = len - 1;
+  while (ix < iy) {
+    t     = s[ix];
+    s[ix] = s[iy];
+    s[iy] = t;
+    ++ix;
+    --iy;
+  }
+}
+
+
+/* low level addition, based on HAC pp.594, Algorithm 14.7 */
+static int s_mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int *x;
+  int     olduse, res, min, max;
+
+  /* find sizes, we let |a| <= |b| which means we have to sort
+   * them.  "x" will point to the input with the most digits
+   */
+  if (a->used > b->used) {
+    min = b->used;
+    max = a->used;
+    x = a;
+  } else {
+    min = a->used;
+    max = b->used;
+    x = b;
+  }
+
+  /* init result */
+  if (c->alloc < max + 1) {
+    if ((res = mp_grow (c, max + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* get old used digit count and set new one */
+  olduse = c->used;
+  c->used = max + 1;
+
+  {
+    register mp_digit u, *tmpa, *tmpb, *tmpc;
+    register int i;
+
+    /* alias for digit pointers */
+
+    /* first input */
+    tmpa = a->dp;
+
+    /* second input */
+    tmpb = b->dp;
+
+    /* destination */
+    tmpc = c->dp;
+
+    /* zero the carry */
+    u = 0;
+    for (i = 0; i < min; i++) {
+      /* Compute the sum at one digit, T[i] = A[i] + B[i] + U */
+      *tmpc = *tmpa++ + *tmpb++ + u;
+
+      /* U = carry bit of T[i] */
+      u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+      /* take away carry bit from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* now copy higher words if any, that is in A+B 
+     * if A or B has more digits add those in 
+     */
+    if (min != max) {
+      for (; i < max; i++) {
+        /* T[i] = X[i] + U */
+        *tmpc = x->dp[i] + u;
+
+        /* U = carry bit of T[i] */
+        u = *tmpc >> ((mp_digit)DIGIT_BIT);
+
+        /* take away carry bit from T[i] */
+        *tmpc++ &= MP_MASK;
+      }
+    }
+
+    /* add carry */
+    *tmpc++ = u;
+
+    /* clear digits above oldused */
+    for (i = c->used; i < olduse; i++) {
+      *tmpc++ = 0;
+    }
+  }
+
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* low level subtraction (assumes |a| > |b|), HAC pp.595 Algorithm 14.9 */
+static int s_mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     olduse, res, min, max;
+
+  /* find sizes */
+  min = b->used;
+  max = a->used;
+
+  /* init result */
+  if (c->alloc < max) {
+    if ((res = mp_grow (c, max)) != MP_OKAY) {
+      return res;
+    }
+  }
+  olduse = c->used;
+  c->used = max;
+
+  {
+    register mp_digit u, *tmpa, *tmpb, *tmpc;
+    register int i;
+
+    /* alias for digit pointers */
+    tmpa = a->dp;
+    tmpb = b->dp;
+    tmpc = c->dp;
+
+    /* set carry to zero */
+    u = 0;
+    for (i = 0; i < min; i++) {
+      /* T[i] = A[i] - B[i] - U */
+      *tmpc = *tmpa++ - *tmpb++ - u;
+
+      /* U = carry bit of T[i]
+       * Note this saves performing an AND operation since
+       * if a carry does occur it will propagate all the way to the
+       * MSB.  As a result a single shift is enough to get the carry
+       */
+      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+      /* Clear carry from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* now copy higher words if any, e.g. if A has more digits than B  */
+    for (; i < max; i++) {
+      /* T[i] = A[i] - U */
+      *tmpc = *tmpa++ - u;
+
+      /* U = carry bit of T[i] */
+      u = *tmpc >> ((mp_digit)(CHAR_BIT * sizeof (mp_digit) - 1));
+
+      /* Clear carry from T[i] */
+      *tmpc++ &= MP_MASK;
+    }
+
+    /* clear digits above used (since we may not have grown result above) */
+    for (i = c->used; i < olduse; i++) {
+      *tmpc++ = 0;
+    }
+  }
+
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* init a new mp_int */
+static int mp_init (mp_int * a)
+{
+  int i;
+
+  /* allocate memory required and clear it */
+  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * MP_PREC);
+  if (a->dp == NULL) {
+    return MP_MEM;
+  }
+
+  /* set the digits to zero */
+  for (i = 0; i < MP_PREC; i++) {
+      a->dp[i] = 0;
+  }
+
+  /* set the used to zero, allocated digits to the default precision
+   * and sign to positive */
+  a->used  = 0;
+  a->alloc = MP_PREC;
+  a->sign  = MP_ZPOS;
+
+  return MP_OKAY;
+}
+
+
+/* clear one (frees)  */
+static void mp_clear (mp_int * a)
+{
+  int i;
+
+  /* only do anything if a hasn't been freed previously */
+  if (a->dp != NULL) {
+    /* first zero the digits */
+    for (i = 0; i < a->used; i++) {
+        a->dp[i] = 0;
+    }
+
+    /* free ram */
+    XFREE(a->dp);
+
+    /* reset members to make debugging easier */
+    a->dp    = NULL;
+    a->alloc = a->used = 0;
+    a->sign  = MP_ZPOS;
+  }
+}
+
+
+/* high level addition (handles signs) */
+static int mp_add (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     sa, sb, res;
+
+  /* get sign of both inputs */
+  sa = a->sign;
+  sb = b->sign;
+
+  /* handle two cases, not four */
+  if (sa == sb) {
+    /* both positive or both negative */
+    /* add their magnitudes, copy the sign */
+    c->sign = sa;
+    res = s_mp_add (a, b, c);
+  } else {
+    /* one positive, the other negative */
+    /* subtract the one with the greater magnitude from */
+    /* the one of the lesser magnitude.  The result gets */
+    /* the sign of the one with the greater magnitude. */
+    if (mp_cmp_mag (a, b) == MP_LT) {
+      c->sign = sb;
+      res = s_mp_sub (b, a, c);
+    } else {
+      c->sign = sa;
+      res = s_mp_sub (a, b, c);
+    }
+  }
+  return res;
+}
+
+
+/* high level subtraction (handles signs) */
+static int mp_sub (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     sa, sb, res;
+
+  sa = a->sign;
+  sb = b->sign;
+
+  if (sa != sb) {
+    /* subtract a negative from a positive, OR */
+    /* subtract a positive from a negative. */
+    /* In either case, ADD their magnitudes, */
+    /* and use the sign of the first number. */
+    c->sign = sa;
+    res = s_mp_add (a, b, c);
+  } else {
+    /* subtract a positive from a positive, OR */
+    /* subtract a negative from a negative. */
+    /* First, take the difference between their */
+    /* magnitudes, then... */
+    if (mp_cmp_mag (a, b) != MP_LT) {
+      /* Copy the sign from the first */
+      c->sign = sa;
+      /* The first has a larger or equal magnitude */
+      res = s_mp_sub (a, b, c);
+    } else {
+      /* The result has the *opposite* sign from */
+      /* the first number. */
+      c->sign = (sa == MP_ZPOS) ? MP_NEG : MP_ZPOS;
+      /* The second has a larger magnitude */
+      res = s_mp_sub (b, a, c);
+    }
+  }
+  return res;
+}
+
+
+/* high level multiplication (handles sign) */
+static int mp_mul (mp_int * a, mp_int * b, mp_int * c)
+{
+  int     res, neg;
+  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+
+  /* use Toom-Cook? */
+#ifdef BN_MP_TOOM_MUL_C
+  if (MIN (a->used, b->used) >= TOOM_MUL_CUTOFF) {
+    res = mp_toom_mul(a, b, c);
+  } else 
+#endif
+#ifdef BN_MP_KARATSUBA_MUL_C
+  /* use Karatsuba? */
+  if (MIN (a->used, b->used) >= KARATSUBA_MUL_CUTOFF) {
+    res = mp_karatsuba_mul (a, b, c);
+  } else 
+#endif
+  {
+    /* can we use the fast multiplier?
+     *
+     * The fast multiplier can be used if the output will 
+     * have less than MP_WARRAY digits and the number of 
+     * digits won't affect carry propagation
+     */
+#ifdef BN_FAST_S_MP_MUL_DIGS_C
+    int     digs = a->used + b->used + 1;
+
+    if ((digs < MP_WARRAY) &&
+        MIN(a->used, b->used) <= 
+        (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+      res = fast_s_mp_mul_digs (a, b, c, digs);
+    } else 
+#endif
+#ifdef BN_S_MP_MUL_DIGS_C
+      res = s_mp_mul (a, b, c); /* uses s_mp_mul_digs */
+#else
+#error mp_mul could fail
+      res = MP_VAL;
+#endif
+
+  }
+  c->sign = (c->used > 0) ? neg : MP_ZPOS;
+  return res;
+}
+
+
+/* d = a * b (mod c) */
+static int mp_mulmod (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+  int     res;
+  mp_int  t;
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  if ((res = mp_mul (a, b, &t)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+  res = mp_mod (&t, c, d);
+  mp_clear (&t);
+  return res;
+}
+
+
+/* c = a mod b, 0 <= c < b */
+static int mp_mod (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int  t;
+  int     res;
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  if ((res = mp_div (a, b, NULL, &t)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+
+  if (t.sign != b->sign) {
+    res = mp_add (b, &t, c);
+  } else {
+    res = MP_OKAY;
+    mp_exch (&t, c);
+  }
+
+  mp_clear (&t);
+  return res;
+}
+
+
+/* this is a shell function that calls either the normal or Montgomery
+ * exptmod functions.  Originally the call to the montgomery code was
+ * embedded in the normal function but that wasted alot of stack space
+ * for nothing (since 99% of the time the Montgomery code would be called)
+ */
+static int mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y)
+{
+  int dr;
+
+  /* modulus P must be positive */
+  if (P->sign == MP_NEG) {
+     return MP_VAL;
+  }
+
+  /* if exponent X is negative we have to recurse */
+  if (X->sign == MP_NEG) {
+#ifdef LTM_NO_NEG_EXP
+        return MP_VAL;
+#else /* LTM_NO_NEG_EXP */
+#ifdef BN_MP_INVMOD_C
+     mp_int tmpG, tmpX;
+     int err;
+
+     /* first compute 1/G mod P */
+     if ((err = mp_init(&tmpG)) != MP_OKAY) {
+        return err;
+     }
+     if ((err = mp_invmod(G, P, &tmpG)) != MP_OKAY) {
+        mp_clear(&tmpG);
+        return err;
+     }
+
+     /* now get |X| */
+     if ((err = mp_init(&tmpX)) != MP_OKAY) {
+        mp_clear(&tmpG);
+        return err;
+     }
+     if ((err = mp_abs(X, &tmpX)) != MP_OKAY) {
+        mp_clear_multi(&tmpG, &tmpX, NULL);
+        return err;
+     }
+
+     /* and now compute (1/G)**|X| instead of G**X [X < 0] */
+     err = mp_exptmod(&tmpG, &tmpX, P, Y);
+     mp_clear_multi(&tmpG, &tmpX, NULL);
+     return err;
+#else 
+#error mp_exptmod would always fail
+     /* no invmod */
+     return MP_VAL;
+#endif
+#endif /* LTM_NO_NEG_EXP */
+  }
+
+/* modified diminished radix reduction */
+#if defined(BN_MP_REDUCE_IS_2K_L_C) && defined(BN_MP_REDUCE_2K_L_C) && defined(BN_S_MP_EXPTMOD_C)
+  if (mp_reduce_is_2k_l(P) == MP_YES) {
+     return s_mp_exptmod(G, X, P, Y, 1);
+  }
+#endif
+
+#ifdef BN_MP_DR_IS_MODULUS_C
+  /* is it a DR modulus? */
+  dr = mp_dr_is_modulus(P);
+#else
+  /* default to no */
+  dr = 0;
+#endif
+
+#ifdef BN_MP_REDUCE_IS_2K_C
+  /* if not, is it a unrestricted DR modulus? */
+  if (dr == 0) {
+     dr = mp_reduce_is_2k(P) << 1;
+  }
+#endif
+    
+  /* if the modulus is odd or dr != 0 use the montgomery method */
+#ifdef BN_MP_EXPTMOD_FAST_C
+  if (mp_isodd (P) == 1 || dr !=  0) {
+    return mp_exptmod_fast (G, X, P, Y, dr);
+  } else {
+#endif
+#ifdef BN_S_MP_EXPTMOD_C
+    /* otherwise use the generic Barrett reduction technique */
+    return s_mp_exptmod (G, X, P, Y, 0);
+#else
+#error mp_exptmod could fail
+    /* no exptmod for evens */
+    return MP_VAL;
+#endif
+#ifdef BN_MP_EXPTMOD_FAST_C
+  }
+#endif
+}
+
+
+/* compare two ints (signed)*/
+static int mp_cmp (mp_int * a, mp_int * b)
+{
+  /* compare based on sign */
+  if (a->sign != b->sign) {
+     if (a->sign == MP_NEG) {
+        return MP_LT;
+     } else {
+        return MP_GT;
+     }
+  }
+  
+  /* compare digits */
+  if (a->sign == MP_NEG) {
+     /* if negative compare opposite direction */
+     return mp_cmp_mag(b, a);
+  } else {
+     return mp_cmp_mag(a, b);
+  }
+}
+
+
+/* compare a digit */
+static int mp_cmp_d(mp_int * a, mp_digit b)
+{
+  /* compare based on sign */
+  if (a->sign == MP_NEG) {
+    return MP_LT;
+  }
+
+  /* compare based on magnitude */
+  if (a->used > 1) {
+    return MP_GT;
+  }
+
+  /* compare the only digit of a to b */
+  if (a->dp[0] > b) {
+    return MP_GT;
+  } else if (a->dp[0] < b) {
+    return MP_LT;
+  } else {
+    return MP_EQ;
+  }
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod (mp_int * a, mp_int * b, mp_int * c)
+{
+  /* b cannot be negative */
+  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+    return MP_VAL;
+  }
+
+#ifdef BN_FAST_MP_INVMOD_C
+  /* if the modulus is odd we can use a faster routine instead */
+  if (mp_isodd (b) == 1) {
+    return fast_mp_invmod (a, b, c);
+  }
+#endif
+
+#ifdef BN_MP_INVMOD_SLOW_C
+  return mp_invmod_slow(a, b, c);
+#endif
+
+#ifndef BN_FAST_MP_INVMOD_C
+#ifndef BN_MP_INVMOD_SLOW_C
+#error mp_invmod would always fail
+#endif
+#endif
+  return MP_VAL;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* get the size for an unsigned equivalent */
+static int mp_unsigned_bin_size (mp_int * a)
+{
+  int     size = mp_count_bits (a);
+  return (size / 8 + ((size & 7) != 0 ? 1 : 0));
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* hac 14.61, pp608 */
+static int mp_invmod_slow (mp_int * a, mp_int * b, mp_int * c)
+{
+  mp_int  x, y, u, v, A, B, C, D;
+  int     res;
+
+  /* b cannot be negative */
+  if (b->sign == MP_NEG || mp_iszero(b) == 1) {
+    return MP_VAL;
+  }
+
+  /* init temps */
+  if ((res = mp_init_multi(&x, &y, &u, &v, 
+                           &A, &B, &C, &D, NULL)) != MP_OKAY) {
+     return res;
+  }
+
+  /* x = a, y = b */
+  if ((res = mp_mod(a, b, &x)) != MP_OKAY) {
+      goto LBL_ERR;
+  }
+  if ((res = mp_copy (b, &y)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+
+  /* 2. [modified] if x,y are both even then return an error! */
+  if (mp_iseven (&x) == 1 && mp_iseven (&y) == 1) {
+    res = MP_VAL;
+    goto LBL_ERR;
+  }
+
+  /* 3. u=x, v=y, A=1, B=0, C=0,D=1 */
+  if ((res = mp_copy (&x, &u)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+  if ((res = mp_copy (&y, &v)) != MP_OKAY) {
+    goto LBL_ERR;
+  }
+  mp_set (&A, 1);
+  mp_set (&D, 1);
+
+top:
+  /* 4.  while u is even do */
+  while (mp_iseven (&u) == 1) {
+    /* 4.1 u = u/2 */
+    if ((res = mp_div_2 (&u, &u)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    /* 4.2 if A or B is odd then */
+    if (mp_isodd (&A) == 1 || mp_isodd (&B) == 1) {
+      /* A = (A+y)/2, B = (B-x)/2 */
+      if ((res = mp_add (&A, &y, &A)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((res = mp_sub (&B, &x, &B)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+    }
+    /* A = A/2, B = B/2 */
+    if ((res = mp_div_2 (&A, &A)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    if ((res = mp_div_2 (&B, &B)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* 5.  while v is even do */
+  while (mp_iseven (&v) == 1) {
+    /* 5.1 v = v/2 */
+    if ((res = mp_div_2 (&v, &v)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    /* 5.2 if C or D is odd then */
+    if (mp_isodd (&C) == 1 || mp_isodd (&D) == 1) {
+      /* C = (C+y)/2, D = (D-x)/2 */
+      if ((res = mp_add (&C, &y, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+      if ((res = mp_sub (&D, &x, &D)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+    }
+    /* C = C/2, D = D/2 */
+    if ((res = mp_div_2 (&C, &C)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+    if ((res = mp_div_2 (&D, &D)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* 6.  if u >= v then */
+  if (mp_cmp (&u, &v) != MP_LT) {
+    /* u = u - v, A = A - C, B = B - D */
+    if ((res = mp_sub (&u, &v, &u)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&A, &C, &A)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&B, &D, &B)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  } else {
+    /* v - v - u, C = C - A, D = D - B */
+    if ((res = mp_sub (&v, &u, &v)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&C, &A, &C)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+
+    if ((res = mp_sub (&D, &B, &D)) != MP_OKAY) {
+      goto LBL_ERR;
+    }
+  }
+
+  /* if not zero goto step 4 */
+  if (mp_iszero (&u) == 0)
+    goto top;
+
+  /* now a = C, b = D, gcd == g*v */
+
+  /* if v != 1 then there is no inverse */
+  if (mp_cmp_d (&v, 1) != MP_EQ) {
+    res = MP_VAL;
+    goto LBL_ERR;
+  }
+
+  /* if its too low */
+  while (mp_cmp_d(&C, 0) == MP_LT) {
+      if ((res = mp_add(&C, b, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+  }
+  
+  /* too big */
+  while (mp_cmp_mag(&C, b) != MP_LT) {
+      if ((res = mp_sub(&C, b, &C)) != MP_OKAY) {
+         goto LBL_ERR;
+      }
+  }
+  
+  /* C is now the inverse */
+  mp_exch (&C, c);
+  res = MP_OKAY;
+LBL_ERR:mp_clear_multi (&x, &y, &u, &v, &A, &B, &C, &D, NULL);
+  return res;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* compare maginitude of two ints (unsigned) */
+static int mp_cmp_mag (mp_int * a, mp_int * b)
+{
+  int     n;
+  mp_digit *tmpa, *tmpb;
+
+  /* compare based on # of non-zero digits */
+  if (a->used > b->used) {
+    return MP_GT;
+  }
+  
+  if (a->used < b->used) {
+    return MP_LT;
+  }
+
+  /* alias for a */
+  tmpa = a->dp + (a->used - 1);
+
+  /* alias for b */
+  tmpb = b->dp + (a->used - 1);
+
+  /* compare based on digits  */
+  for (n = 0; n < a->used; ++n, --tmpa, --tmpb) {
+    if (*tmpa > *tmpb) {
+      return MP_GT;
+    }
+
+    if (*tmpa < *tmpb) {
+      return MP_LT;
+    }
+  }
+  return MP_EQ;
+}
+
+
+/* reads a unsigned char array, assumes the msb is stored first [big endian] */
+static int mp_read_unsigned_bin (mp_int * a, const unsigned char *b, int c)
+{
+  int     res;
+
+  /* make sure there are at least two digits */
+  if (a->alloc < 2) {
+     if ((res = mp_grow(a, 2)) != MP_OKAY) {
+        return res;
+     }
+  }
+
+  /* zero the int */
+  mp_zero (a);
+
+  /* read the bytes in */
+  while (c-- > 0) {
+    if ((res = mp_mul_2d (a, 8, a)) != MP_OKAY) {
+      return res;
+    }
+
+#ifndef MP_8BIT
+      a->dp[0] |= *b++;
+      a->used += 1;
+#else
+      a->dp[0] = (*b & MP_MASK);
+      a->dp[1] |= ((*b++ >> 7U) & 1);
+      a->used += 2;
+#endif
+  }
+  mp_clamp (a);
+  return MP_OKAY;
+}
+
+
+/* store in unsigned [big endian] format */
+static int mp_to_unsigned_bin (mp_int * a, unsigned char *b)
+{
+  int     x, res;
+  mp_int  t;
+
+  if ((res = mp_init_copy (&t, a)) != MP_OKAY) {
+    return res;
+  }
+
+  x = 0;
+  while (mp_iszero (&t) == 0) {
+#ifndef MP_8BIT
+      b[x++] = (unsigned char) (t.dp[0] & 255);
+#else
+      b[x++] = (unsigned char) (t.dp[0] | ((t.dp[1] & 0x01) << 7));
+#endif
+    if ((res = mp_div_2d (&t, 8, &t, NULL)) != MP_OKAY) {
+      mp_clear (&t);
+      return res;
+    }
+  }
+  bn_reverse (b, x);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* shift right by a certain bit count (store quotient in c, optional remainder in d) */
+static int mp_div_2d (mp_int * a, int b, mp_int * c, mp_int * d)
+{
+  mp_digit D, r, rr;
+  int     x, res;
+  mp_int  t;
+
+
+  /* if the shift count is <= 0 then we do no work */
+  if (b <= 0) {
+    res = mp_copy (a, c);
+    if (d != NULL) {
+      mp_zero (d);
+    }
+    return res;
+  }
+
+  if ((res = mp_init (&t)) != MP_OKAY) {
+    return res;
+  }
+
+  /* get the remainder */
+  if (d != NULL) {
+    if ((res = mp_mod_2d (a, b, &t)) != MP_OKAY) {
+      mp_clear (&t);
+      return res;
+    }
+  }
+
+  /* copy */
+  if ((res = mp_copy (a, c)) != MP_OKAY) {
+    mp_clear (&t);
+    return res;
+  }
+
+  /* shift by as many digits in the bit count */
+  if (b >= (int)DIGIT_BIT) {
+    mp_rshd (c, b / DIGIT_BIT);
+  }
+
+  /* shift any bit count < DIGIT_BIT */
+  D = (mp_digit) (b % DIGIT_BIT);
+  if (D != 0) {
+    register mp_digit *tmpc, mask, shift;
+
+    /* mask */
+    mask = (((mp_digit)1) << D) - 1;
+
+    /* shift for lsb */
+    shift = DIGIT_BIT - D;
+
+    /* alias */
+    tmpc = c->dp + (c->used - 1);
+
+    /* carry */
+    r = 0;
+    for (x = c->used - 1; x >= 0; x--) {
+      /* get the lower  bits of this word in a temp */
+      rr = *tmpc & mask;
+
+      /* shift the current word and mix in the carry bits from the previous word */
+      *tmpc = (*tmpc >> D) | (r << shift);
+      --tmpc;
+
+      /* set the carry to the carry bits of the current word found above */
+      r = rr;
+    }
+  }
+  mp_clamp (c);
+  if (d != NULL) {
+    mp_exch (&t, d);
+  }
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+static int mp_init_copy (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  if ((res = mp_init (a)) != MP_OKAY) {
+    return res;
+  }
+  return mp_copy (b, a);
+}
+
+
+/* set to zero */
+static void mp_zero (mp_int * a)
+{
+  int       n;
+  mp_digit *tmp;
+
+  a->sign = MP_ZPOS;
+  a->used = 0;
+
+  tmp = a->dp;
+  for (n = 0; n < a->alloc; n++) {
+     *tmp++ = 0;
+  }
+}
+
+
+/* copy, b = a */
+static int mp_copy (mp_int * a, mp_int * b)
+{
+  int     res, n;
+
+  /* if dst == src do nothing */
+  if (a == b) {
+    return MP_OKAY;
+  }
+
+  /* grow dest */
+  if (b->alloc < a->used) {
+     if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+        return res;
+     }
+  }
+
+  /* zero b and copy the parameters over */
+  {
+    register mp_digit *tmpa, *tmpb;
+
+    /* pointer aliases */
+
+    /* source */
+    tmpa = a->dp;
+
+    /* destination */
+    tmpb = b->dp;
+
+    /* copy all the digits */
+    for (n = 0; n < a->used; n++) {
+      *tmpb++ = *tmpa++;
+    }
+
+    /* clear high digits */
+    for (; n < b->used; n++) {
+      *tmpb++ = 0;
+    }
+  }
+
+  /* copy used count and sign */
+  b->used = a->used;
+  b->sign = a->sign;
+  return MP_OKAY;
+}
+
+
+/* shift right a certain amount of digits */
+static void mp_rshd (mp_int * a, int b)
+{
+  int     x;
+
+  /* if b <= 0 then ignore it */
+  if (b <= 0) {
+    return;
+  }
+
+  /* if b > used then simply zero it and return */
+  if (a->used <= b) {
+    mp_zero (a);
+    return;
+  }
+
+  {
+    register mp_digit *bottom, *top;
+
+    /* shift the digits down */
+
+    /* bottom */
+    bottom = a->dp;
+
+    /* top [offset into digits] */
+    top = a->dp + b;
+
+    /* this is implemented as a sliding window where 
+     * the window is b-digits long and digits from 
+     * the top of the window are copied to the bottom
+     *
+     * e.g.
+
+     b-2 | b-1 | b0 | b1 | b2 | ... | bb |   ---->
+                 /\                   |      ---->
+                  \-------------------/      ---->
+     */
+    for (x = 0; x < (a->used - b); x++) {
+      *bottom++ = *top++;
+    }
+
+    /* zero the top digits */
+    for (; x < a->used; x++) {
+      *bottom++ = 0;
+    }
+  }
+  
+  /* remove excess digits */
+  a->used -= b;
+}
+
+
+/* swap the elements of two integers, for cases where you can't simply swap the 
+ * mp_int pointers around
+ */
+static void mp_exch (mp_int * a, mp_int * b)
+{
+  mp_int  t;
+
+  t  = *a;
+  *a = *b;
+  *b = t;
+}
+
+
+/* trim unused digits 
+ *
+ * This is used to ensure that leading zero digits are
+ * trimed and the leading "used" digit will be non-zero
+ * Typically very fast.  Also fixes the sign if there
+ * are no more leading digits
+ */
+static void mp_clamp (mp_int * a)
+{
+  /* decrease used while the most significant digit is
+   * zero.
+   */
+  while (a->used > 0 && a->dp[a->used - 1] == 0) {
+    --(a->used);
+  }
+
+  /* reset the sign flag if used == 0 */
+  if (a->used == 0) {
+    a->sign = MP_ZPOS;
+  }
+}
+
+
+/* grow as required */
+static int mp_grow (mp_int * a, int size)
+{
+  int     i;
+  mp_digit *tmp;
+
+  /* if the alloc size is smaller alloc more ram */
+  if (a->alloc < size) {
+    /* ensure there are always at least MP_PREC digits extra on top */
+    size += (MP_PREC * 2) - (size % MP_PREC);
+
+    /* reallocate the array a->dp
+     *
+     * We store the return in a temporary variable
+     * in case the operation failed we don't want
+     * to overwrite the dp member of a.
+     */
+    tmp = OPT_CAST(mp_digit) XREALLOC (a->dp, sizeof (mp_digit) * size);
+    if (tmp == NULL) {
+      /* reallocation failed but "a" is still valid [can be freed] */
+      return MP_MEM;
+    }
+
+    /* reallocation succeeded so set a->dp */
+    a->dp = tmp;
+
+    /* zero excess digits */
+    i        = a->alloc;
+    a->alloc = size;
+    for (; i < a->alloc; i++) {
+      a->dp[i] = 0;
+    }
+  }
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_ABS_C
+/* b = |a| 
+ *
+ * Simple function copies the input and fixes the sign to positive
+ */
+static int mp_abs (mp_int * a, mp_int * b)
+{
+  int     res;
+
+  /* copy a to b */
+  if (a != b) {
+     if ((res = mp_copy (a, b)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  /* force the sign of b to positive */
+  b->sign = MP_ZPOS;
+
+  return MP_OKAY;
+}
+#endif
+
+
+/* set to a digit */
+static void mp_set (mp_int * a, mp_digit b)
+{
+  mp_zero (a);
+  a->dp[0] = b & MP_MASK;
+  a->used  = (a->dp[0] != 0) ? 1 : 0;
+}
+
+
+#ifndef LTM_NO_NEG_EXP
+/* b = a/2 */
+static int mp_div_2(mp_int * a, mp_int * b)
+{
+  int     x, res, oldused;
+
+  /* copy */
+  if (b->alloc < a->used) {
+    if ((res = mp_grow (b, a->used)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  oldused = b->used;
+  b->used = a->used;
+  {
+    register mp_digit r, rr, *tmpa, *tmpb;
+
+    /* source alias */
+    tmpa = a->dp + b->used - 1;
+
+    /* dest alias */
+    tmpb = b->dp + b->used - 1;
+
+    /* carry */
+    r = 0;
+    for (x = b->used - 1; x >= 0; x--) {
+      /* get the carry for the next iteration */
+      rr = *tmpa & 1;
+
+      /* shift the current digit, add in carry and store */
+      *tmpb-- = (*tmpa-- >> 1) | (r << (DIGIT_BIT - 1));
+
+      /* forward carry to next iteration */
+      r = rr;
+    }
+
+    /* zero excess digits */
+    tmpb = b->dp + b->used;
+    for (x = b->used; x < oldused; x++) {
+      *tmpb++ = 0;
+    }
+  }
+  b->sign = a->sign;
+  mp_clamp (b);
+  return MP_OKAY;
+}
+#endif /* LTM_NO_NEG_EXP */
+
+
+/* shift left by a certain bit count */
+static int mp_mul_2d (mp_int * a, int b, mp_int * c)
+{
+  mp_digit d;
+  int      res;
+
+  /* copy */
+  if (a != c) {
+     if ((res = mp_copy (a, c)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  if (c->alloc < (int)(c->used + b/DIGIT_BIT + 1)) {
+     if ((res = mp_grow (c, c->used + b / DIGIT_BIT + 1)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  /* shift by as many digits in the bit count */
+  if (b >= (int)DIGIT_BIT) {
+    if ((res = mp_lshd (c, b / DIGIT_BIT)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* shift any bit count < DIGIT_BIT */
+  d = (mp_digit) (b % DIGIT_BIT);
+  if (d != 0) {
+    register mp_digit *tmpc, shift, mask, r, rr;
+    register int x;
+
+    /* bitmask for carries */
+    mask = (((mp_digit)1) << d) - 1;
+
+    /* shift for msbs */
+    shift = DIGIT_BIT - d;
+
+    /* alias */
+    tmpc = c->dp;
+
+    /* carry */
+    r    = 0;
+    for (x = 0; x < c->used; x++) {
+      /* get the higher bits of the current word */
+      rr = (*tmpc >> shift) & mask;
+
+      /* shift the current word and OR in the carry */
+      *tmpc = ((*tmpc << d) | r) & MP_MASK;
+      ++tmpc;
+
+      /* set the carry to the carry bits of the current word */
+      r = rr;
+    }
+    
+    /* set final carry */
+    if (r != 0) {
+       c->dp[(c->used)++] = r;
+    }
+  }
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_INIT_MULTI_C
+static int mp_init_multi(mp_int *mp, ...) 
+{
+    mp_err res = MP_OKAY;      /* Assume ok until proven otherwise */
+    int n = 0;                 /* Number of ok inits */
+    mp_int* cur_arg = mp;
+    va_list args;
+
+    va_start(args, mp);        /* init args to next argument from caller */
+    while (cur_arg != NULL) {
+        if (mp_init(cur_arg) != MP_OKAY) {
+            /* Oops - error! Back-track and mp_clear what we already
+               succeeded in init-ing, then return error.
+            */
+            va_list clean_args;
+            
+            /* end the current list */
+            va_end(args);
+            
+            /* now start cleaning up */            
+            cur_arg = mp;
+            va_start(clean_args, mp);
+            while (n--) {
+                mp_clear(cur_arg);
+                cur_arg = va_arg(clean_args, mp_int*);
+            }
+            va_end(clean_args);
+            res = MP_MEM;
+            break;
+        }
+        n++;
+        cur_arg = va_arg(args, mp_int*);
+    }
+    va_end(args);
+    return res;                /* Assumed ok, if error flagged above. */
+}
+#endif
+
+
+#ifdef BN_MP_CLEAR_MULTI_C
+static void mp_clear_multi(mp_int *mp, ...) 
+{
+    mp_int* next_mp = mp;
+    va_list args;
+    va_start(args, mp);
+    while (next_mp != NULL) {
+        mp_clear(next_mp);
+        next_mp = va_arg(args, mp_int*);
+    }
+    va_end(args);
+}
+#endif
+
+
+/* shift left a certain amount of digits */
+static int mp_lshd (mp_int * a, int b)
+{
+  int     x, res;
+
+  /* if its less than zero return */
+  if (b <= 0) {
+    return MP_OKAY;
+  }
+
+  /* grow to fit the new digits */
+  if (a->alloc < a->used + b) {
+     if ((res = mp_grow (a, a->used + b)) != MP_OKAY) {
+       return res;
+     }
+  }
+
+  {
+    register mp_digit *top, *bottom;
+
+    /* increment the used by the shift amount then copy upwards */
+    a->used += b;
+
+    /* top */
+    top = a->dp + a->used - 1;
+
+    /* base */
+    bottom = a->dp + a->used - 1 - b;
+
+    /* much like mp_rshd this is implemented using a sliding window
+     * except the window goes the otherway around.  Copying from
+     * the bottom to the top.  see bn_mp_rshd.c for more info.
+     */
+    for (x = a->used - 1; x >= b; x--) {
+      *top-- = *bottom--;
+    }
+
+    /* zero the lower digits */
+    top = a->dp;
+    for (x = 0; x < b; x++) {
+      *top++ = 0;
+    }
+  }
+  return MP_OKAY;
+}
+
+
+/* returns the number of bits in an int */
+static int mp_count_bits (mp_int * a)
+{
+  int     r;
+  mp_digit q;
+
+  /* shortcut */
+  if (a->used == 0) {
+    return 0;
+  }
+
+  /* get number of digits and add that */
+  r = (a->used - 1) * DIGIT_BIT;
+  
+  /* take the last digit and count the bits in it */
+  q = a->dp[a->used - 1];
+  while (q > ((mp_digit) 0)) {
+    ++r;
+    q >>= ((mp_digit) 1);
+  }
+  return r;
+}
+
+
+/* calc a value mod 2**b */
+static int mp_mod_2d (mp_int * a, int b, mp_int * c)
+{
+  int     x, res;
+
+  /* if b is <= 0 then zero the int */
+  if (b <= 0) {
+    mp_zero (c);
+    return MP_OKAY;
+  }
+
+  /* if the modulus is larger than the value than return */
+  if (b >= (int) (a->used * DIGIT_BIT)) {
+    res = mp_copy (a, c);
+    return res;
+  }
+
+  /* copy */
+  if ((res = mp_copy (a, c)) != MP_OKAY) {
+    return res;
+  }
+
+  /* zero digits above the last digit of the modulus */
+  for (x = (b / DIGIT_BIT) + ((b % DIGIT_BIT) == 0 ? 0 : 1); x < c->used; x++) {
+    c->dp[x] = 0;
+  }
+  /* clear the digit that is not completely outside/inside the modulus */
+  c->dp[b / DIGIT_BIT] &=
+    (mp_digit) ((((mp_digit) 1) << (((mp_digit) b) % DIGIT_BIT)) - ((mp_digit) 1));
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_DIV_SMALL
+
+/* slower bit-bang division... also smaller */
+static int mp_div(mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+   mp_int ta, tb, tq, q;
+   int    res, n, n2;
+
+  /* is divisor zero ? */
+  if (mp_iszero (b) == 1) {
+    return MP_VAL;
+  }
+
+  /* if a < b then q=0, r = a */
+  if (mp_cmp_mag (a, b) == MP_LT) {
+    if (d != NULL) {
+      res = mp_copy (a, d);
+    } else {
+      res = MP_OKAY;
+    }
+    if (c != NULL) {
+      mp_zero (c);
+    }
+    return res;
+  }
+       
+  /* init our temps */
+  if ((res = mp_init_multi(&ta, &tb, &tq, &q, NULL) != MP_OKAY)) {
+     return res;
+  }
+
+
+  mp_set(&tq, 1);
+  n = mp_count_bits(a) - mp_count_bits(b);
+  if (((res = mp_abs(a, &ta)) != MP_OKAY) ||
+      ((res = mp_abs(b, &tb)) != MP_OKAY) || 
+      ((res = mp_mul_2d(&tb, n, &tb)) != MP_OKAY) ||
+      ((res = mp_mul_2d(&tq, n, &tq)) != MP_OKAY)) {
+      goto LBL_ERR;
+  }
+
+  while (n-- >= 0) {
+     if (mp_cmp(&tb, &ta) != MP_GT) {
+        if (((res = mp_sub(&ta, &tb, &ta)) != MP_OKAY) ||
+            ((res = mp_add(&q, &tq, &q)) != MP_OKAY)) {
+           goto LBL_ERR;
+        }
+     }
+     if (((res = mp_div_2d(&tb, 1, &tb, NULL)) != MP_OKAY) ||
+         ((res = mp_div_2d(&tq, 1, &tq, NULL)) != MP_OKAY)) {
+           goto LBL_ERR;
+     }
+  }
+
+  /* now q == quotient and ta == remainder */
+  n  = a->sign;
+  n2 = (a->sign == b->sign ? MP_ZPOS : MP_NEG);
+  if (c != NULL) {
+     mp_exch(c, &q);
+     c->sign  = (mp_iszero(c) == MP_YES) ? MP_ZPOS : n2;
+  }
+  if (d != NULL) {
+     mp_exch(d, &ta);
+     d->sign = (mp_iszero(d) == MP_YES) ? MP_ZPOS : n;
+  }
+LBL_ERR:
+   mp_clear_multi(&ta, &tb, &tq, &q, NULL);
+   return res;
+}
+
+#else
+
+/* integer signed division. 
+ * c*b + d == a [e.g. a/b, c=quotient, d=remainder]
+ * HAC pp.598 Algorithm 14.20
+ *
+ * Note that the description in HAC is horribly 
+ * incomplete.  For example, it doesn't consider 
+ * the case where digits are removed from 'x' in 
+ * the inner loop.  It also doesn't consider the 
+ * case that y has fewer than three digits, etc..
+ *
+ * The overall algorithm is as described as 
+ * 14.20 from HAC but fixed to treat these cases.
+*/
+static int mp_div (mp_int * a, mp_int * b, mp_int * c, mp_int * d)
+{
+  mp_int  q, x, y, t1, t2;
+  int     res, n, t, i, norm, neg;
+
+  /* is divisor zero ? */
+  if (mp_iszero (b) == 1) {
+    return MP_VAL;
+  }
+
+  /* if a < b then q=0, r = a */
+  if (mp_cmp_mag (a, b) == MP_LT) {
+    if (d != NULL) {
+      res = mp_copy (a, d);
+    } else {
+      res = MP_OKAY;
+    }
+    if (c != NULL) {
+      mp_zero (c);
+    }
+    return res;
+  }
+
+  if ((res = mp_init_size (&q, a->used + 2)) != MP_OKAY) {
+    return res;
+  }
+  q.used = a->used + 2;
+
+  if ((res = mp_init (&t1)) != MP_OKAY) {
+    goto LBL_Q;
+  }
+
+  if ((res = mp_init (&t2)) != MP_OKAY) {
+    goto LBL_T1;
+  }
+
+  if ((res = mp_init_copy (&x, a)) != MP_OKAY) {
+    goto LBL_T2;
+  }
+
+  if ((res = mp_init_copy (&y, b)) != MP_OKAY) {
+    goto LBL_X;
+  }
+
+  /* fix the sign */
+  neg = (a->sign == b->sign) ? MP_ZPOS : MP_NEG;
+  x.sign = y.sign = MP_ZPOS;
+
+  /* normalize both x and y, ensure that y >= b/2, [b == 2**DIGIT_BIT] */
+  norm = mp_count_bits(&y) % DIGIT_BIT;
+  if (norm < (int)(DIGIT_BIT-1)) {
+     norm = (DIGIT_BIT-1) - norm;
+     if ((res = mp_mul_2d (&x, norm, &x)) != MP_OKAY) {
+       goto LBL_Y;
+     }
+     if ((res = mp_mul_2d (&y, norm, &y)) != MP_OKAY) {
+       goto LBL_Y;
+     }
+  } else {
+     norm = 0;
+  }
+
+  /* note hac does 0 based, so if used==5 then its 0,1,2,3,4, e.g. use 4 */
+  n = x.used - 1;
+  t = y.used - 1;
+
+  /* while (x >= y*b**n-t) do { q[n-t] += 1; x -= y*b**{n-t} } */
+  if ((res = mp_lshd (&y, n - t)) != MP_OKAY) { /* y = y*b**{n-t} */
+    goto LBL_Y;
+  }
+
+  while (mp_cmp (&x, &y) != MP_LT) {
+    ++(q.dp[n - t]);
+    if ((res = mp_sub (&x, &y, &x)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+  }
+
+  /* reset y by shifting it back down */
+  mp_rshd (&y, n - t);
+
+  /* step 3. for i from n down to (t + 1) */
+  for (i = n; i >= (t + 1); i--) {
+    if (i > x.used) {
+      continue;
+    }
+
+    /* step 3.1 if xi == yt then set q{i-t-1} to b-1, 
+     * otherwise set q{i-t-1} to (xi*b + x{i-1})/yt */
+    if (x.dp[i] == y.dp[t]) {
+      q.dp[i - t - 1] = ((((mp_digit)1) << DIGIT_BIT) - 1);
+    } else {
+      mp_word tmp;
+      tmp = ((mp_word) x.dp[i]) << ((mp_word) DIGIT_BIT);
+      tmp |= ((mp_word) x.dp[i - 1]);
+      tmp /= ((mp_word) y.dp[t]);
+      if (tmp > (mp_word) MP_MASK)
+        tmp = MP_MASK;
+      q.dp[i - t - 1] = (mp_digit) (tmp & (mp_word) (MP_MASK));
+    }
+
+    /* while (q{i-t-1} * (yt * b + y{t-1})) > 
+             xi * b**2 + xi-1 * b + xi-2 
+     
+       do q{i-t-1} -= 1; 
+    */
+    q.dp[i - t - 1] = (q.dp[i - t - 1] + 1) & MP_MASK;
+    do {
+      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1) & MP_MASK;
+
+      /* find left hand */
+      mp_zero (&t1);
+      t1.dp[0] = (t - 1 < 0) ? 0 : y.dp[t - 1];
+      t1.dp[1] = y.dp[t];
+      t1.used = 2;
+      if ((res = mp_mul_d (&t1, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+
+      /* find right hand */
+      t2.dp[0] = (i - 2 < 0) ? 0 : x.dp[i - 2];
+      t2.dp[1] = (i - 1 < 0) ? 0 : x.dp[i - 1];
+      t2.dp[2] = x.dp[i];
+      t2.used = 3;
+    } while (mp_cmp_mag(&t1, &t2) == MP_GT);
+
+    /* step 3.3 x = x - q{i-t-1} * y * b**{i-t-1} */
+    if ((res = mp_mul_d (&y, q.dp[i - t - 1], &t1)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    if ((res = mp_sub (&x, &t1, &x)) != MP_OKAY) {
+      goto LBL_Y;
+    }
+
+    /* if x < 0 then { x = x + y*b**{i-t-1}; q{i-t-1} -= 1; } */
+    if (x.sign == MP_NEG) {
+      if ((res = mp_copy (&y, &t1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+      if ((res = mp_lshd (&t1, i - t - 1)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+      if ((res = mp_add (&x, &t1, &x)) != MP_OKAY) {
+        goto LBL_Y;
+      }
+
+      q.dp[i - t - 1] = (q.dp[i - t - 1] - 1UL) & MP_MASK;
+    }
+  }
+
+  /* now q is the quotient and x is the remainder 
+   * [which we have to normalize] 
+   */
+  
+  /* get sign before writing to c */
+  x.sign = x.used == 0 ? MP_ZPOS : a->sign;
+
+  if (c != NULL) {
+    mp_clamp (&q);
+    mp_exch (&q, c);
+    c->sign = neg;
+  }
+
+  if (d != NULL) {
+    mp_div_2d (&x, norm, &x, NULL);
+    mp_exch (&x, d);
+  }
+
+  res = MP_OKAY;
+
+LBL_Y:mp_clear (&y);
+LBL_X:mp_clear (&x);
+LBL_T2:mp_clear (&t2);
+LBL_T1:mp_clear (&t1);
+LBL_Q:mp_clear (&q);
+  return res;
+}
+
+#endif
+
+
+#ifdef MP_LOW_MEM
+   #define TAB_SIZE 32
+#else
+   #define TAB_SIZE 256
+#endif
+
+static int s_mp_exptmod (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+  mp_int  M[TAB_SIZE], res, mu;
+  mp_digit buf;
+  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+  int (*redux)(mp_int*,mp_int*,mp_int*);
+
+  /* find window size */
+  x = mp_count_bits (X);
+  if (x <= 7) {
+    winsize = 2;
+  } else if (x <= 36) {
+    winsize = 3;
+  } else if (x <= 140) {
+    winsize = 4;
+  } else if (x <= 450) {
+    winsize = 5;
+  } else if (x <= 1303) {
+    winsize = 6;
+  } else if (x <= 3529) {
+    winsize = 7;
+  } else {
+    winsize = 8;
+  }
+
+#ifdef MP_LOW_MEM
+    if (winsize > 5) {
+       winsize = 5;
+    }
+#endif
+
+  /* init M array */
+  /* init first cell */
+  if ((err = mp_init(&M[1])) != MP_OKAY) {
+     return err; 
+  }
+
+  /* now init the second half of the array */
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    if ((err = mp_init(&M[x])) != MP_OKAY) {
+      for (y = 1<<(winsize-1); y < x; y++) {
+        mp_clear (&M[y]);
+      }
+      mp_clear(&M[1]);
+      return err;
+    }
+  }
+
+  /* create mu, used for Barrett reduction */
+  if ((err = mp_init (&mu)) != MP_OKAY) {
+    goto LBL_M;
+  }
+  
+  if (redmode == 0) {
+     if ((err = mp_reduce_setup (&mu, P)) != MP_OKAY) {
+        goto LBL_MU;
+     }
+     redux = mp_reduce;
+  } else {
+     if ((err = mp_reduce_2k_setup_l (P, &mu)) != MP_OKAY) {
+        goto LBL_MU;
+     }
+     redux = mp_reduce_2k_l;
+  }    
+
+  /* create M table
+   *
+   * The M table contains powers of the base, 
+   * e.g. M[x] = G**x mod P
+   *
+   * The first half of the table is not 
+   * computed though accept for M[0] and M[1]
+   */
+  if ((err = mp_mod (G, P, &M[1])) != MP_OKAY) {
+    goto LBL_MU;
+  }
+
+  /* compute the value at M[1<<(winsize-1)] by squaring 
+   * M[1] (winsize-1) times 
+   */
+  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+    goto LBL_MU;
+  }
+
+  for (x = 0; x < (winsize - 1); x++) {
+    /* square it */
+    if ((err = mp_sqr (&M[1 << (winsize - 1)], 
+                       &M[1 << (winsize - 1)])) != MP_OKAY) {
+      goto LBL_MU;
+    }
+
+    /* reduce modulo P */
+    if ((err = redux (&M[1 << (winsize - 1)], P, &mu)) != MP_OKAY) {
+      goto LBL_MU;
+    }
+  }
+
+  /* create upper table, that is M[x] = M[x-1] * M[1] (mod P)
+   * for x = (2**(winsize - 1) + 1) to (2**winsize - 1)
+   */
+  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+      goto LBL_MU;
+    }
+    if ((err = redux (&M[x], P, &mu)) != MP_OKAY) {
+      goto LBL_MU;
+    }
+  }
+
+  /* setup result */
+  if ((err = mp_init (&res)) != MP_OKAY) {
+    goto LBL_MU;
+  }
+  mp_set (&res, 1);
+
+  /* set initial mode and bit cnt */
+  mode   = 0;
+  bitcnt = 1;
+  buf    = 0;
+  digidx = X->used - 1;
+  bitcpy = 0;
+  bitbuf = 0;
+
+  for (;;) {
+    /* grab next digit as required */
+    if (--bitcnt == 0) {
+      /* if digidx == -1 we are out of digits */
+      if (digidx == -1) {
+        break;
+      }
+      /* read next digit and reset the bitcnt */
+      buf    = X->dp[digidx--];
+      bitcnt = (int) DIGIT_BIT;
+    }
+
+    /* grab the next msb from the exponent */
+    y     = (buf >> (mp_digit)(DIGIT_BIT - 1)) & 1;
+    buf <<= (mp_digit)1;
+
+    /* if the bit is zero and mode == 0 then we ignore it
+     * These represent the leading zero bits before the first 1 bit
+     * in the exponent.  Technically this opt is not required but it
+     * does lower the # of trivial squaring/reductions used
+     */
+    if (mode == 0 && y == 0) {
+      continue;
+    }
+
+    /* if the bit is zero and mode == 1 then we square */
+    if (mode == 1 && y == 0) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      continue;
+    }
+
+    /* else we add it to the window */
+    bitbuf |= (y << (winsize - ++bitcpy));
+    mode    = 2;
+
+    if (bitcpy == winsize) {
+      /* ok window is filled so square as required and multiply  */
+      /* square first */
+      for (x = 0; x < winsize; x++) {
+        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+
+      /* then multiply */
+      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* empty window and reset */
+      bitcpy = 0;
+      bitbuf = 0;
+      mode   = 1;
+    }
+  }
+
+  /* if bits remain then square/multiply */
+  if (mode == 2 && bitcpy > 0) {
+    /* square then multiply if the bit is set */
+    for (x = 0; x < bitcpy; x++) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      bitbuf <<= 1;
+      if ((bitbuf & (1 << winsize)) != 0) {
+        /* then multiply */
+        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, &mu)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+    }
+  }
+
+  mp_exch (&res, Y);
+  err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_MU:mp_clear (&mu);
+LBL_M:
+  mp_clear(&M[1]);
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    mp_clear (&M[x]);
+  }
+  return err;
+}
+
+
+/* computes b = a*a */
+static int mp_sqr (mp_int * a, mp_int * b)
+{
+  int     res;
+
+#ifdef BN_MP_TOOM_SQR_C
+  /* use Toom-Cook? */
+  if (a->used >= TOOM_SQR_CUTOFF) {
+    res = mp_toom_sqr(a, b);
+  /* Karatsuba? */
+  } else 
+#endif
+#ifdef BN_MP_KARATSUBA_SQR_C
+if (a->used >= KARATSUBA_SQR_CUTOFF) {
+    res = mp_karatsuba_sqr (a, b);
+  } else 
+#endif
+  {
+#ifdef BN_FAST_S_MP_SQR_C
+    /* can we use the fast comba multiplier? */
+    if ((a->used * 2 + 1) < MP_WARRAY && 
+         a->used < 
+         (1 << (sizeof(mp_word) * CHAR_BIT - 2*DIGIT_BIT - 1))) {
+      res = fast_s_mp_sqr (a, b);
+    } else
+#endif
+#ifdef BN_S_MP_SQR_C
+      res = s_mp_sqr (a, b);
+#else
+#error mp_sqr could fail
+      res = MP_VAL;
+#endif
+  }
+  b->sign = MP_ZPOS;
+  return res;
+}
+
+
+/* reduces a modulo n where n is of the form 2**p - d 
+   This differs from reduce_2k since "d" can be larger
+   than a single digit.
+*/
+static int mp_reduce_2k_l(mp_int *a, mp_int *n, mp_int *d)
+{
+   mp_int q;
+   int    p, res;
+   
+   if ((res = mp_init(&q)) != MP_OKAY) {
+      return res;
+   }
+   
+   p = mp_count_bits(n);    
+top:
+   /* q = a/2**p, a = a mod 2**p */
+   if ((res = mp_div_2d(a, p, &q, a)) != MP_OKAY) {
+      goto ERR;
+   }
+   
+   /* q = q * d */
+   if ((res = mp_mul(&q, d, &q)) != MP_OKAY) { 
+      goto ERR;
+   }
+   
+   /* a = a + q */
+   if ((res = s_mp_add(a, &q, a)) != MP_OKAY) {
+      goto ERR;
+   }
+   
+   if (mp_cmp_mag(a, n) != MP_LT) {
+      s_mp_sub(a, n, a);
+      goto top;
+   }
+   
+ERR:
+   mp_clear(&q);
+   return res;
+}
+
+
+/* determines the setup value */
+static int mp_reduce_2k_setup_l(mp_int *a, mp_int *d)
+{
+   int    res;
+   mp_int tmp;
+   
+   if ((res = mp_init(&tmp)) != MP_OKAY) {
+      return res;
+   }
+   
+   if ((res = mp_2expt(&tmp, mp_count_bits(a))) != MP_OKAY) {
+      goto ERR;
+   }
+   
+   if ((res = s_mp_sub(&tmp, a, d)) != MP_OKAY) {
+      goto ERR;
+   }
+   
+ERR:
+   mp_clear(&tmp);
+   return res;
+}
+
+
+/* computes a = 2**b 
+ *
+ * Simple algorithm which zeroes the int, grows it then just sets one bit
+ * as required.
+ */
+static int mp_2expt (mp_int * a, int b)
+{
+  int     res;
+
+  /* zero a as per default */
+  mp_zero (a);
+
+  /* grow a to accomodate the single bit */
+  if ((res = mp_grow (a, b / DIGIT_BIT + 1)) != MP_OKAY) {
+    return res;
+  }
+
+  /* set the used count of where the bit will go */
+  a->used = b / DIGIT_BIT + 1;
+
+  /* put the single bit in its place */
+  a->dp[b / DIGIT_BIT] = ((mp_digit)1) << (b % DIGIT_BIT);
+
+  return MP_OKAY;
+}
+
+
+/* pre-calculate the value required for Barrett reduction
+ * For a given modulus "b" it calulates the value required in "a"
+ */
+static int mp_reduce_setup (mp_int * a, mp_int * b)
+{
+  int     res;
+  
+  if ((res = mp_2expt (a, b->used * 2 * DIGIT_BIT)) != MP_OKAY) {
+    return res;
+  }
+  return mp_div (a, b, a, NULL);
+}
+
+
+/* reduces x mod m, assumes 0 < x < m**2, mu is 
+ * precomputed via mp_reduce_setup.
+ * From HAC pp.604 Algorithm 14.42
+ */
+static int mp_reduce (mp_int * x, mp_int * m, mp_int * mu)
+{
+  mp_int  q;
+  int     res, um = m->used;
+
+  /* q = x */
+  if ((res = mp_init_copy (&q, x)) != MP_OKAY) {
+    return res;
+  }
+
+  /* q1 = x / b**(k-1)  */
+  mp_rshd (&q, um - 1);         
+
+  /* according to HAC this optimization is ok */
+  if (((unsigned long) um) > (((mp_digit)1) << (DIGIT_BIT - 1))) {
+    if ((res = mp_mul (&q, mu, &q)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  } else {
+#ifdef BN_S_MP_MUL_HIGH_DIGS_C
+    if ((res = s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+#elif defined(BN_FAST_S_MP_MUL_HIGH_DIGS_C)
+    if ((res = fast_s_mp_mul_high_digs (&q, mu, &q, um)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+#else 
+    { 
+#error mp_reduce would always fail
+      res = MP_VAL;
+      goto CLEANUP;
+    }
+#endif
+  }
+
+  /* q3 = q2 / b**(k+1) */
+  mp_rshd (&q, um + 1);         
+
+  /* x = x mod b**(k+1), quick (no division) */
+  if ((res = mp_mod_2d (x, DIGIT_BIT * (um + 1), x)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* q = q * m mod b**(k+1), quick (no division) */
+  if ((res = s_mp_mul_digs (&q, m, &q, um + 1)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* x = x - q */
+  if ((res = mp_sub (x, &q, x)) != MP_OKAY) {
+    goto CLEANUP;
+  }
+
+  /* If x < 0, add b**(k+1) to it */
+  if (mp_cmp_d (x, 0) == MP_LT) {
+    mp_set (&q, 1);
+    if ((res = mp_lshd (&q, um + 1)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+    if ((res = mp_add (x, &q, x)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  }
+
+  /* Back off if it's too big */
+  while (mp_cmp (x, m) != MP_LT) {
+    if ((res = s_mp_sub (x, m, x)) != MP_OKAY) {
+      goto CLEANUP;
+    }
+  }
+  
+CLEANUP:
+  mp_clear (&q);
+
+  return res;
+}
+
+
+/* multiplies |a| * |b| and only computes upto digs digits of result
+ * HAC pp. 595, Algorithm 14.12  Modified so you can control how 
+ * many digits of output are created.
+ */
+static int s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  mp_int  t;
+  int     res, pa, pb, ix, iy;
+  mp_digit u;
+  mp_word r;
+  mp_digit tmpx, *tmpt, *tmpy;
+
+  /* can we use the fast multiplier? */
+  if (((digs) < MP_WARRAY) &&
+      MIN (a->used, b->used) < 
+          (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+    return fast_s_mp_mul_digs (a, b, c, digs);
+  }
+
+  if ((res = mp_init_size (&t, digs)) != MP_OKAY) {
+    return res;
+  }
+  t.used = digs;
+
+  /* compute the digits of the product directly */
+  pa = a->used;
+  for (ix = 0; ix < pa; ix++) {
+    /* set the carry to zero */
+    u = 0;
+
+    /* limit ourselves to making digs digits of output */
+    pb = MIN (b->used, digs - ix);
+
+    /* setup some aliases */
+    /* copy of the digit from a used within the nested loop */
+    tmpx = a->dp[ix];
+    
+    /* an alias for the destination shifted ix places */
+    tmpt = t.dp + ix;
+    
+    /* an alias for the digits of b */
+    tmpy = b->dp;
+
+    /* compute the columns of the output and propagate the carry */
+    for (iy = 0; iy < pb; iy++) {
+      /* compute the column as a mp_word */
+      r       = ((mp_word)*tmpt) +
+                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+                ((mp_word) u);
+
+      /* the new column is the lower part of the result */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* get the carry word from the result */
+      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+    }
+    /* set carry if it is placed below digs */
+    if (ix + iy < digs) {
+      *tmpt = u;
+    }
+  }
+
+  mp_clamp (&t);
+  mp_exch (&t, c);
+
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* Fast (comba) multiplier
+ *
+ * This is the fast column-array [comba] multiplier.  It is 
+ * designed to compute the columns of the product first 
+ * then handle the carries afterwards.  This has the effect 
+ * of making the nested loops that compute the columns very
+ * simple and schedulable on super-scalar processors.
+ *
+ * This has been modified to produce a variable number of 
+ * digits of output so if say only a half-product is required 
+ * you don't have to compute the upper half (a feature 
+ * required for fast Barrett reduction).
+ *
+ * Based on Algorithm 14.12 on pp.595 of HAC.
+ *
+ */
+static int fast_s_mp_mul_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  int     olduse, res, pa, ix, iz;
+  mp_digit W[MP_WARRAY];
+  register mp_word  _W;
+
+  /* grow the destination as required */
+  if (c->alloc < digs) {
+    if ((res = mp_grow (c, digs)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* number of output digits to produce */
+  pa = MIN(digs, a->used + b->used);
+
+  /* clear the carry */
+  _W = 0;
+  for (ix = 0; ix < pa; ix++) { 
+      int      tx, ty;
+      int      iy;
+      mp_digit *tmpx, *tmpy;
+
+      /* get offsets into the two bignums */
+      ty = MIN(b->used-1, ix);
+      tx = ix - ty;
+
+      /* setup temp aliases */
+      tmpx = a->dp + tx;
+      tmpy = b->dp + ty;
+
+      /* this is the number of times the loop will iterrate, essentially 
+         while (tx++ < a->used && ty-- >= 0) { ... }
+       */
+      iy = MIN(a->used-tx, ty+1);
+
+      /* execute loop */
+      for (iz = 0; iz < iy; ++iz) {
+         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+
+      }
+
+      /* store term */
+      W[ix] = ((mp_digit)_W) & MP_MASK;
+
+      /* make next carry */
+      _W = _W >> ((mp_word)DIGIT_BIT);
+ }
+
+  /* setup dest */
+  olduse  = c->used;
+  c->used = pa;
+
+  {
+    register mp_digit *tmpc;
+    tmpc = c->dp;
+    for (ix = 0; ix < pa+1; ix++) {
+      /* now extract the previous digit [below the carry] */
+      *tmpc++ = W[ix];
+    }
+
+    /* clear unused digits [that existed in the old copy of c] */
+    for (; ix < olduse; ix++) {
+      *tmpc++ = 0;
+    }
+  }
+  mp_clamp (c);
+  return MP_OKAY;
+}
+
+
+/* init an mp_init for a given size */
+static int mp_init_size (mp_int * a, int size)
+{
+  int x;
+
+  /* pad size so there are always extra digits */
+  size += (MP_PREC * 2) - (size % MP_PREC);    
+  
+  /* alloc mem */
+  a->dp = OPT_CAST(mp_digit) XMALLOC (sizeof (mp_digit) * size);
+  if (a->dp == NULL) {
+    return MP_MEM;
+  }
+
+  /* set the members */
+  a->used  = 0;
+  a->alloc = size;
+  a->sign  = MP_ZPOS;
+
+  /* zero the digits */
+  for (x = 0; x < size; x++) {
+      a->dp[x] = 0;
+  }
+
+  return MP_OKAY;
+}
+
+
+/* low level squaring, b = a*a, HAC pp.596-597, Algorithm 14.16 */
+static int s_mp_sqr (mp_int * a, mp_int * b)
+{
+  mp_int  t;
+  int     res, ix, iy, pa;
+  mp_word r;
+  mp_digit u, tmpx, *tmpt;
+
+  pa = a->used;
+  if ((res = mp_init_size (&t, 2*pa + 1)) != MP_OKAY) {
+    return res;
+  }
+
+  /* default used is maximum possible size */
+  t.used = 2*pa + 1;
+
+  for (ix = 0; ix < pa; ix++) {
+    /* first calculate the digit at 2*ix */
+    /* calculate double precision result */
+    r = ((mp_word) t.dp[2*ix]) +
+        ((mp_word)a->dp[ix])*((mp_word)a->dp[ix]);
+
+    /* store lower part in result */
+    t.dp[ix+ix] = (mp_digit) (r & ((mp_word) MP_MASK));
+
+    /* get the carry */
+    u           = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+
+    /* left hand side of A[ix] * A[iy] */
+    tmpx        = a->dp[ix];
+
+    /* alias for where to store the results */
+    tmpt        = t.dp + (2*ix + 1);
+    
+    for (iy = ix + 1; iy < pa; iy++) {
+      /* first calculate the product */
+      r       = ((mp_word)tmpx) * ((mp_word)a->dp[iy]);
+
+      /* now calculate the double precision result, note we use
+       * addition instead of *2 since it's easier to optimize
+       */
+      r       = ((mp_word) *tmpt) + r + r + ((mp_word) u);
+
+      /* store lower part */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* get carry */
+      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+    }
+    /* propagate upwards */
+    while (u != ((mp_digit) 0)) {
+      r       = ((mp_word) *tmpt) + ((mp_word) u);
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+      u       = (mp_digit)(r >> ((mp_word) DIGIT_BIT));
+    }
+  }
+
+  mp_clamp (&t);
+  mp_exch (&t, b);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+/* multiplies |a| * |b| and does not compute the lower digs digits
+ * [meant to get the higher part of the product]
+ */
+static int s_mp_mul_high_digs (mp_int * a, mp_int * b, mp_int * c, int digs)
+{
+  mp_int  t;
+  int     res, pa, pb, ix, iy;
+  mp_digit u;
+  mp_word r;
+  mp_digit tmpx, *tmpt, *tmpy;
+
+  /* can we use the fast multiplier? */
+#ifdef BN_FAST_S_MP_MUL_HIGH_DIGS_C
+  if (((a->used + b->used + 1) < MP_WARRAY)
+      && MIN (a->used, b->used) < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+    return fast_s_mp_mul_high_digs (a, b, c, digs);
+  }
+#endif
+
+  if ((res = mp_init_size (&t, a->used + b->used + 1)) != MP_OKAY) {
+    return res;
+  }
+  t.used = a->used + b->used + 1;
+
+  pa = a->used;
+  pb = b->used;
+  for (ix = 0; ix < pa; ix++) {
+    /* clear the carry */
+    u = 0;
+
+    /* left hand side of A[ix] * B[iy] */
+    tmpx = a->dp[ix];
+
+    /* alias to the address of where the digits will be stored */
+    tmpt = &(t.dp[digs]);
+
+    /* alias for where to read the right hand side from */
+    tmpy = b->dp + (digs - ix);
+
+    for (iy = digs - ix; iy < pb; iy++) {
+      /* calculate the double precision result */
+      r       = ((mp_word)*tmpt) +
+                ((mp_word)tmpx) * ((mp_word)*tmpy++) +
+                ((mp_word) u);
+
+      /* get the lower part */
+      *tmpt++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+      /* carry the carry */
+      u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+    }
+    *tmpt = u;
+  }
+  mp_clamp (&t);
+  mp_exch (&t, c);
+  mp_clear (&t);
+  return MP_OKAY;
+}
+
+
+#ifdef BN_MP_MONTGOMERY_SETUP_C
+/* setups the montgomery reduction stuff */
+static int
+mp_montgomery_setup (mp_int * n, mp_digit * rho)
+{
+  mp_digit x, b;
+
+/* fast inversion mod 2**k
+ *
+ * Based on the fact that
+ *
+ * XA = 1 (mod 2**n)  =>  (X(2-XA)) A = 1 (mod 2**2n)
+ *                    =>  2*X*A - X*X*A*A = 1
+ *                    =>  2*(1) - (1)     = 1
+ */
+  b = n->dp[0];
+
+  if ((b & 1) == 0) {
+    return MP_VAL;
+  }
+
+  x = (((b + 2) & 4) << 1) + b; /* here x*a==1 mod 2**4 */
+  x *= 2 - b * x;               /* here x*a==1 mod 2**8 */
+#if !defined(MP_8BIT)
+  x *= 2 - b * x;               /* here x*a==1 mod 2**16 */
+#endif
+#if defined(MP_64BIT) || !(defined(MP_8BIT) || defined(MP_16BIT))
+  x *= 2 - b * x;               /* here x*a==1 mod 2**32 */
+#endif
+#ifdef MP_64BIT
+  x *= 2 - b * x;               /* here x*a==1 mod 2**64 */
+#endif
+
+  /* rho = -1/m mod b */
+  *rho = (unsigned long)(((mp_word)1 << ((mp_word) DIGIT_BIT)) - x) & MP_MASK;
+
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+/* computes xR**-1 == x (mod N) via Montgomery Reduction
+ *
+ * This is an optimized implementation of montgomery_reduce
+ * which uses the comba method to quickly calculate the columns of the
+ * reduction.
+ *
+ * Based on Algorithm 14.32 on pp.601 of HAC.
+*/
+int fast_mp_montgomery_reduce (mp_int * x, mp_int * n, mp_digit rho)
+{
+  int     ix, res, olduse;
+  mp_word W[MP_WARRAY];
+
+  /* get old used count */
+  olduse = x->used;
+
+  /* grow a as required */
+  if (x->alloc < n->used + 1) {
+    if ((res = mp_grow (x, n->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* first we have to get the digits of the input into
+   * an array of double precision words W[...]
+   */
+  {
+    register mp_word *_W;
+    register mp_digit *tmpx;
+
+    /* alias for the W[] array */
+    _W   = W;
+
+    /* alias for the digits of  x*/
+    tmpx = x->dp;
+
+    /* copy the digits of a into W[0..a->used-1] */
+    for (ix = 0; ix < x->used; ix++) {
+      *_W++ = *tmpx++;
+    }
+
+    /* zero the high words of W[a->used..m->used*2] */
+    for (; ix < n->used * 2 + 1; ix++) {
+      *_W++ = 0;
+    }
+  }
+
+  /* now we proceed to zero successive digits
+   * from the least significant upwards
+   */
+  for (ix = 0; ix < n->used; ix++) {
+    /* mu = ai * m' mod b
+     *
+     * We avoid a double precision multiplication (which isn't required)
+     * by casting the value down to a mp_digit.  Note this requires
+     * that W[ix-1] have  the carry cleared (see after the inner loop)
+     */
+    register mp_digit mu;
+    mu = (mp_digit) (((W[ix] & MP_MASK) * rho) & MP_MASK);
+
+    /* a = a + mu * m * b**i
+     *
+     * This is computed in place and on the fly.  The multiplication
+     * by b**i is handled by offseting which columns the results
+     * are added to.
+     *
+     * Note the comba method normally doesn't handle carries in the
+     * inner loop In this case we fix the carry from the previous
+     * column since the Montgomery reduction requires digits of the
+     * result (so far) [see above] to work.  This is
+     * handled by fixing up one carry after the inner loop.  The
+     * carry fixups are done in order so after these loops the
+     * first m->used words of W[] have the carries fixed
+     */
+    {
+      register int iy;
+      register mp_digit *tmpn;
+      register mp_word *_W;
+
+      /* alias for the digits of the modulus */
+      tmpn = n->dp;
+
+      /* Alias for the columns set by an offset of ix */
+      _W = W + ix;
+
+      /* inner loop */
+      for (iy = 0; iy < n->used; iy++) {
+          *_W++ += ((mp_word)mu) * ((mp_word)*tmpn++);
+      }
+    }
+
+    /* now fix carry for next digit, W[ix+1] */
+    W[ix + 1] += W[ix] >> ((mp_word) DIGIT_BIT);
+  }
+
+  /* now we have to propagate the carries and
+   * shift the words downward [all those least
+   * significant digits we zeroed].
+   */
+  {
+    register mp_digit *tmpx;
+    register mp_word *_W, *_W1;
+
+    /* nox fix rest of carries */
+
+    /* alias for current word */
+    _W1 = W + ix;
+
+    /* alias for next word, where the carry goes */
+    _W = W + ++ix;
+
+    for (; ix <= n->used * 2 + 1; ix++) {
+      *_W++ += *_W1++ >> ((mp_word) DIGIT_BIT);
+    }
+
+    /* copy out, A = A/b**n
+     *
+     * The result is A/b**n but instead of converting from an
+     * array of mp_word to mp_digit than calling mp_rshd
+     * we just copy them in the right order
+     */
+
+    /* alias for destination word */
+    tmpx = x->dp;
+
+    /* alias for shifted double precision result */
+    _W = W + n->used;
+
+    for (ix = 0; ix < n->used + 1; ix++) {
+      *tmpx++ = (mp_digit)(*_W++ & ((mp_word) MP_MASK));
+    }
+
+    /* zero oldused digits, if the input a was larger than
+     * m->used+1 we'll have to clear the digits
+     */
+    for (; ix < olduse; ix++) {
+      *tmpx++ = 0;
+    }
+  }
+
+  /* set the max used and clamp */
+  x->used = n->used + 1;
+  mp_clamp (x);
+
+  /* if A >= m then A = A - m */
+  if (mp_cmp_mag (x, n) != MP_LT) {
+    return s_mp_sub (x, n, x);
+  }
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_2_C
+/* b = a*2 */
+static int mp_mul_2(mp_int * a, mp_int * b)
+{
+  int     x, res, oldused;
+
+  /* grow to accomodate result */
+  if (b->alloc < a->used + 1) {
+    if ((res = mp_grow (b, a->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  oldused = b->used;
+  b->used = a->used;
+
+  {
+    register mp_digit r, rr, *tmpa, *tmpb;
+
+    /* alias for source */
+    tmpa = a->dp;
+    
+    /* alias for dest */
+    tmpb = b->dp;
+
+    /* carry */
+    r = 0;
+    for (x = 0; x < a->used; x++) {
+    
+      /* get what will be the *next* carry bit from the 
+       * MSB of the current digit 
+       */
+      rr = *tmpa >> ((mp_digit)(DIGIT_BIT - 1));
+      
+      /* now shift up this digit, add in the carry [from the previous] */
+      *tmpb++ = ((*tmpa++ << ((mp_digit)1)) | r) & MP_MASK;
+      
+      /* copy the carry that would be from the source 
+       * digit into the next iteration 
+       */
+      r = rr;
+    }
+
+    /* new leading digit? */
+    if (r != 0) {
+      /* add a MSB which is always 1 at this point */
+      *tmpb = 1;
+      ++(b->used);
+    }
+
+    /* now zero any excess digits on the destination 
+     * that we didn't write to 
+     */
+    tmpb = b->dp + b->used;
+    for (x = b->used; x < oldused; x++) {
+      *tmpb++ = 0;
+    }
+  }
+  b->sign = a->sign;
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+/*
+ * shifts with subtractions when the result is greater than b.
+ *
+ * The method is slightly modified to shift B unconditionally upto just under
+ * the leading bit of b.  This saves alot of multiple precision shifting.
+ */
+static int mp_montgomery_calc_normalization (mp_int * a, mp_int * b)
+{
+  int     x, bits, res;
+
+  /* how many bits of last digit does b use */
+  bits = mp_count_bits (b) % DIGIT_BIT;
+
+  if (b->used > 1) {
+     if ((res = mp_2expt (a, (b->used - 1) * DIGIT_BIT + bits - 1)) != MP_OKAY) {
+        return res;
+     }
+  } else {
+     mp_set(a, 1);
+     bits = 1;
+  }
+
+
+  /* now compute C = A * B mod b */
+  for (x = bits - 1; x < (int)DIGIT_BIT; x++) {
+    if ((res = mp_mul_2 (a, a)) != MP_OKAY) {
+      return res;
+    }
+    if (mp_cmp_mag (a, b) != MP_LT) {
+      if ((res = s_mp_sub (a, b, a)) != MP_OKAY) {
+        return res;
+      }
+    }
+  }
+
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_EXPTMOD_FAST_C
+/* computes Y == G**X mod P, HAC pp.616, Algorithm 14.85
+ *
+ * Uses a left-to-right k-ary sliding window to compute the modular exponentiation.
+ * The value of k changes based on the size of the exponent.
+ *
+ * Uses Montgomery or Diminished Radix reduction [whichever appropriate]
+ */
+
+static int mp_exptmod_fast (mp_int * G, mp_int * X, mp_int * P, mp_int * Y, int redmode)
+{
+  mp_int  M[TAB_SIZE], res;
+  mp_digit buf, mp;
+  int     err, bitbuf, bitcpy, bitcnt, mode, digidx, x, y, winsize;
+
+  /* use a pointer to the reduction algorithm.  This allows us to use
+   * one of many reduction algorithms without modding the guts of
+   * the code with if statements everywhere.
+   */
+  int     (*redux)(mp_int*,mp_int*,mp_digit);
+
+  /* find window size */
+  x = mp_count_bits (X);
+  if (x <= 7) {
+    winsize = 2;
+  } else if (x <= 36) {
+    winsize = 3;
+  } else if (x <= 140) {
+    winsize = 4;
+  } else if (x <= 450) {
+    winsize = 5;
+  } else if (x <= 1303) {
+    winsize = 6;
+  } else if (x <= 3529) {
+    winsize = 7;
+  } else {
+    winsize = 8;
+  }
+
+#ifdef MP_LOW_MEM
+  if (winsize > 5) {
+     winsize = 5;
+  }
+#endif
+
+  /* init M array */
+  /* init first cell */
+  if ((err = mp_init(&M[1])) != MP_OKAY) {
+     return err;
+  }
+
+  /* now init the second half of the array */
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    if ((err = mp_init(&M[x])) != MP_OKAY) {
+      for (y = 1<<(winsize-1); y < x; y++) {
+        mp_clear (&M[y]);
+      }
+      mp_clear(&M[1]);
+      return err;
+    }
+  }
+
+  /* determine and setup reduction code */
+  if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_SETUP_C     
+     /* now setup montgomery  */
+     if ((err = mp_montgomery_setup (P, &mp)) != MP_OKAY) {
+        goto LBL_M;
+     }
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+
+     /* automatically pick the comba one if available (saves quite a few calls/ifs) */
+#ifdef BN_FAST_MP_MONTGOMERY_REDUCE_C
+     if (((P->used * 2 + 1) < MP_WARRAY) &&
+          P->used < (1 << ((CHAR_BIT * sizeof (mp_word)) - (2 * DIGIT_BIT)))) {
+        redux = fast_mp_montgomery_reduce;
+     } else 
+#endif
+     {
+#ifdef BN_MP_MONTGOMERY_REDUCE_C
+        /* use slower baseline Montgomery method */
+        redux = mp_montgomery_reduce;
+#else
+        err = MP_VAL;
+        goto LBL_M;
+#endif
+     }
+  } else if (redmode == 1) {
+#if defined(BN_MP_DR_SETUP_C) && defined(BN_MP_DR_REDUCE_C)
+     /* setup DR reduction for moduli of the form B**k - b */
+     mp_dr_setup(P, &mp);
+     redux = mp_dr_reduce;
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+  } else {
+#if defined(BN_MP_REDUCE_2K_SETUP_C) && defined(BN_MP_REDUCE_2K_C)
+     /* setup DR reduction for moduli of the form 2**k - b */
+     if ((err = mp_reduce_2k_setup(P, &mp)) != MP_OKAY) {
+        goto LBL_M;
+     }
+     redux = mp_reduce_2k;
+#else
+     err = MP_VAL;
+     goto LBL_M;
+#endif
+  }
+
+  /* setup result */
+  if ((err = mp_init (&res)) != MP_OKAY) {
+    goto LBL_M;
+  }
+
+  /* create M table
+   *
+
+   *
+   * The first half of the table is not computed though accept for M[0] and M[1]
+   */
+
+  if (redmode == 0) {
+#ifdef BN_MP_MONTGOMERY_CALC_NORMALIZATION_C
+     /* now we need R mod m */
+     if ((err = mp_montgomery_calc_normalization (&res, P)) != MP_OKAY) {
+       goto LBL_RES;
+     }
+#else 
+     err = MP_VAL;
+     goto LBL_RES;
+#endif
+
+     /* now set M[1] to G * R mod m */
+     if ((err = mp_mulmod (G, &res, P, &M[1])) != MP_OKAY) {
+       goto LBL_RES;
+     }
+  } else {
+     mp_set(&res, 1);
+     if ((err = mp_mod(G, P, &M[1])) != MP_OKAY) {
+        goto LBL_RES;
+     }
+  }
+
+  /* compute the value at M[1<<(winsize-1)] by squaring M[1] (winsize-1) times */
+  if ((err = mp_copy (&M[1], &M[1 << (winsize - 1)])) != MP_OKAY) {
+    goto LBL_RES;
+  }
+
+  for (x = 0; x < (winsize - 1); x++) {
+    if ((err = mp_sqr (&M[1 << (winsize - 1)], &M[1 << (winsize - 1)])) != MP_OKAY) {
+      goto LBL_RES;
+    }
+    if ((err = redux (&M[1 << (winsize - 1)], P, mp)) != MP_OKAY) {
+      goto LBL_RES;
+    }
+  }
+
+  /* create upper table */
+  for (x = (1 << (winsize - 1)) + 1; x < (1 << winsize); x++) {
+    if ((err = mp_mul (&M[x - 1], &M[1], &M[x])) != MP_OKAY) {
+      goto LBL_RES;
+    }
+    if ((err = redux (&M[x], P, mp)) != MP_OKAY) {
+      goto LBL_RES;
+    }
+  }
+
+  /* set initial mode and bit cnt */
+  mode   = 0;
+  bitcnt = 1;
+  buf    = 0;
+  digidx = X->used - 1;
+  bitcpy = 0;
+  bitbuf = 0;
+
+  for (;;) {
+    /* grab next digit as required */
+    if (--bitcnt == 0) {
+      /* if digidx == -1 we are out of digits so break */
+      if (digidx == -1) {
+        break;
+      }
+      /* read next digit and reset bitcnt */
+      buf    = X->dp[digidx--];
+      bitcnt = (int)DIGIT_BIT;
+    }
+
+    /* grab the next msb from the exponent */
+    y     = (mp_digit)(buf >> (DIGIT_BIT - 1)) & 1;
+    buf <<= (mp_digit)1;
+
+    /* if the bit is zero and mode == 0 then we ignore it
+     * These represent the leading zero bits before the first 1 bit
+     * in the exponent.  Technically this opt is not required but it
+     * does lower the # of trivial squaring/reductions used
+     */
+    if (mode == 0 && y == 0) {
+      continue;
+    }
+
+    /* if the bit is zero and mode == 1 then we square */
+    if (mode == 1 && y == 0) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      continue;
+    }
+
+    /* else we add it to the window */
+    bitbuf |= (y << (winsize - ++bitcpy));
+    mode    = 2;
+
+    if (bitcpy == winsize) {
+      /* ok window is filled so square as required and multiply  */
+      /* square first */
+      for (x = 0; x < winsize; x++) {
+        if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, mp)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+
+      /* then multiply */
+      if ((err = mp_mul (&res, &M[bitbuf], &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* empty window and reset */
+      bitcpy = 0;
+      bitbuf = 0;
+      mode   = 1;
+    }
+  }
+
+  /* if bits remain then square/multiply */
+  if (mode == 2 && bitcpy > 0) {
+    /* square then multiply if the bit is set */
+    for (x = 0; x < bitcpy; x++) {
+      if ((err = mp_sqr (&res, &res)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+      if ((err = redux (&res, P, mp)) != MP_OKAY) {
+        goto LBL_RES;
+      }
+
+      /* get next bit of the window */
+      bitbuf <<= 1;
+      if ((bitbuf & (1 << winsize)) != 0) {
+        /* then multiply */
+        if ((err = mp_mul (&res, &M[1], &res)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+        if ((err = redux (&res, P, mp)) != MP_OKAY) {
+          goto LBL_RES;
+        }
+      }
+    }
+  }
+
+  if (redmode == 0) {
+     /* fixup result if Montgomery reduction is used
+      * recall that any value in a Montgomery system is
+      * actually multiplied by R mod n.  So we have
+      * to reduce one more time to cancel out the factor
+      * of R.
+      */
+     if ((err = redux(&res, P, mp)) != MP_OKAY) {
+       goto LBL_RES;
+     }
+  }
+
+  /* swap res with Y */
+  mp_exch (&res, Y);
+  err = MP_OKAY;
+LBL_RES:mp_clear (&res);
+LBL_M:
+  mp_clear(&M[1]);
+  for (x = 1<<(winsize-1); x < (1 << winsize); x++) {
+    mp_clear (&M[x]);
+  }
+  return err;
+}
+#endif
+
+
+#ifdef BN_FAST_S_MP_SQR_C
+/* the jist of squaring...
+ * you do like mult except the offset of the tmpx [one that 
+ * starts closer to zero] can't equal the offset of tmpy.  
+ * So basically you set up iy like before then you min it with
+ * (ty-tx) so that it never happens.  You double all those 
+ * you add in the inner loop
+
+After that loop you do the squares and add them in.
+*/
+
+static int fast_s_mp_sqr (mp_int * a, mp_int * b)
+{
+  int       olduse, res, pa, ix, iz;
+  mp_digit   W[MP_WARRAY], *tmpx;
+  mp_word   W1;
+
+  /* grow the destination as required */
+  pa = a->used + a->used;
+  if (b->alloc < pa) {
+    if ((res = mp_grow (b, pa)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* number of output digits to produce */
+  W1 = 0;
+  for (ix = 0; ix < pa; ix++) { 
+      int      tx, ty, iy;
+      mp_word  _W;
+      mp_digit *tmpy;
+
+      /* clear counter */
+      _W = 0;
+
+      /* get offsets into the two bignums */
+      ty = MIN(a->used-1, ix);
+      tx = ix - ty;
+
+      /* setup temp aliases */
+      tmpx = a->dp + tx;
+      tmpy = a->dp + ty;
+
+      /* this is the number of times the loop will iterrate, essentially
+         while (tx++ < a->used && ty-- >= 0) { ... }
+       */
+      iy = MIN(a->used-tx, ty+1);
+
+      /* now for squaring tx can never equal ty 
+       * we halve the distance since they approach at a rate of 2x
+       * and we have to round because odd cases need to be executed
+       */
+      iy = MIN(iy, (ty-tx+1)>>1);
+
+      /* execute loop */
+      for (iz = 0; iz < iy; iz++) {
+         _W += ((mp_word)*tmpx++)*((mp_word)*tmpy--);
+      }
+
+      /* double the inner product and add carry */
+      _W = _W + _W + W1;
+
+      /* even columns have the square term in them */
+      if ((ix&1) == 0) {
+         _W += ((mp_word)a->dp[ix>>1])*((mp_word)a->dp[ix>>1]);
+      }
+
+      /* store it */
+      W[ix] = (mp_digit)(_W & MP_MASK);
+
+      /* make next carry */
+      W1 = _W >> ((mp_word)DIGIT_BIT);
+  }
+
+  /* setup dest */
+  olduse  = b->used;
+  b->used = a->used+a->used;
+
+  {
+    mp_digit *tmpb;
+    tmpb = b->dp;
+    for (ix = 0; ix < pa; ix++) {
+      *tmpb++ = W[ix] & MP_MASK;
+    }
+
+    /* clear unused digits [that existed in the old copy of c] */
+    for (; ix < olduse; ix++) {
+      *tmpb++ = 0;
+    }
+  }
+  mp_clamp (b);
+  return MP_OKAY;
+}
+#endif
+
+
+#ifdef BN_MP_MUL_D_C
+/* multiply by a digit */
+static int
+mp_mul_d (mp_int * a, mp_digit b, mp_int * c)
+{
+  mp_digit u, *tmpa, *tmpc;
+  mp_word  r;
+  int      ix, res, olduse;
+
+  /* make sure c is big enough to hold a*b */
+  if (c->alloc < a->used + 1) {
+    if ((res = mp_grow (c, a->used + 1)) != MP_OKAY) {
+      return res;
+    }
+  }
+
+  /* get the original destinations used count */
+  olduse = c->used;
+
+  /* set the sign */
+  c->sign = a->sign;
+
+  /* alias for a->dp [source] */
+  tmpa = a->dp;
+
+  /* alias for c->dp [dest] */
+  tmpc = c->dp;
+
+  /* zero carry */
+  u = 0;
+
+  /* compute columns */
+  for (ix = 0; ix < a->used; ix++) {
+    /* compute product and carry sum for this term */
+    r       = ((mp_word) u) + ((mp_word)*tmpa++) * ((mp_word)b);
+
+    /* mask off higher bits to get a single digit */
+    *tmpc++ = (mp_digit) (r & ((mp_word) MP_MASK));
+
+    /* send carry into next iteration */
+    u       = (mp_digit) (r >> ((mp_word) DIGIT_BIT));
+  }
+
+  /* store final carry [if any] and increment ix offset  */
+  *tmpc++ = u;
+  ++ix;
+
+  /* now zero digits above the top */
+  while (ix++ < olduse) {
+     *tmpc++ = 0;
+  }
+
+  /* set used count */
+  c->used = a->used + 1;
+  mp_clamp(c);
+
+  return MP_OKAY;
+}
+#endif
diff --git a/src/tls/pkcs1.c b/src/tls/pkcs1.c
new file mode 100644 (file)
index 0000000..72ebd87
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-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 "common.h"
+#include "rsa.h"
+#include "pkcs1.h"
+
+
+static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen,
+                                          const u8 *in, size_t inlen,
+                                          u8 *out, size_t *outlen)
+{
+       size_t ps_len;
+       u8 *pos;
+
+       /*
+        * PKCS #1 v1.5, 8.1:
+        *
+        * EB = 00 || BT || PS || 00 || D
+        * BT = 00 or 01 for private-key operation; 02 for public-key operation
+        * PS = k-3-||D||; at least eight octets
+        * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero)
+        * k = length of modulus in octets (modlen)
+        */
+
+       if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) {
+               wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer "
+                          "lengths (modlen=%lu outlen=%lu inlen=%lu)",
+                          __func__, (unsigned long) modlen,
+                          (unsigned long) *outlen,
+                          (unsigned long) inlen);
+               return -1;
+       }
+
+       pos = out;
+       *pos++ = 0x00;
+       *pos++ = block_type; /* BT */
+       ps_len = modlen - inlen - 3;
+       switch (block_type) {
+       case 0:
+               os_memset(pos, 0x00, ps_len);
+               pos += ps_len;
+               break;
+       case 1:
+               os_memset(pos, 0xff, ps_len);
+               pos += ps_len;
+               break;
+       case 2:
+               if (os_get_random(pos, ps_len) < 0) {
+                       wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get "
+                                  "random data for PS", __func__);
+                       return -1;
+               }
+               while (ps_len--) {
+                       if (*pos == 0x00)
+                               *pos = 0x01;
+                       pos++;
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type "
+                          "%d", __func__, block_type);
+               return -1;
+       }
+       *pos++ = 0x00;
+       os_memcpy(pos, in, inlen); /* D */
+
+       return 0;
+}
+
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+                 int use_private, const u8 *in, size_t inlen,
+                 u8 *out, size_t *outlen)
+{
+       size_t modlen;
+
+       modlen = crypto_rsa_get_modulus_len(key);
+
+       if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen,
+                                           out, outlen) < 0)
+               return -1;
+
+       return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private);
+}
+
+
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+                                 const u8 *in, size_t inlen,
+                                 u8 *out, size_t *outlen)
+{
+       int res;
+       u8 *pos, *end;
+
+       res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1);
+       if (res)
+               return res;
+
+       if (*outlen < 2 || out[0] != 0 || out[1] != 2)
+               return -1;
+
+       /* Skip PS (pseudorandom non-zero octets) */
+       pos = out + 2;
+       end = out + *outlen;
+       while (*pos && pos < end)
+               pos++;
+       if (pos == end)
+               return -1;
+       pos++;
+
+       *outlen -= pos - out;
+
+       /* Strip PKCS #1 header */
+       os_memmove(out, pos, *outlen);
+
+       return 0;
+}
+
+
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+                            const u8 *crypt, size_t crypt_len,
+                            u8 *plain, size_t *plain_len)
+{
+       size_t len;
+       u8 *pos;
+
+       len = *plain_len;
+       if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0)
+               return -1;
+
+       /*
+        * PKCS #1 v1.5, 8.1:
+        *
+        * EB = 00 || BT || PS || 00 || D
+        * BT = 00 or 01
+        * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
+        * k = length of modulus in octets
+        */
+
+       if (len < 3 + 8 + 16 /* min hash len */ ||
+           plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+                          "structure");
+               return -1;
+       }
+
+       pos = plain + 3;
+       if (plain[1] == 0x00) {
+               /* BT = 00 */
+               if (plain[2] != 0x00) {
+                       wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+                                  "PS (BT=00)");
+                       return -1;
+               }
+               while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
+                       pos++;
+       } else {
+               /* BT = 01 */
+               if (plain[2] != 0xff) {
+                       wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+                                  "PS (BT=01)");
+                       return -1;
+               }
+               while (pos < plain + len && *pos == 0xff)
+                       pos++;
+       }
+
+       if (pos - plain - 2 < 8) {
+               /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+               wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature "
+                          "padding");
+               return -1;
+       }
+
+       if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
+                          "structure (2)");
+               return -1;
+       }
+       pos++;
+       len -= pos - plain;
+
+       /* Strip PKCS #1 header */
+       os_memmove(plain, pos, len);
+       *plain_len = len;
+
+       return 0;
+}
diff --git a/src/tls/pkcs1.h b/src/tls/pkcs1.h
new file mode 100644 (file)
index 0000000..68872b1
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * PKCS #1 (RSA Encryption)
+ * Copyright (c) 2006-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.
+ */
+
+#ifndef PKCS1_H
+#define PKCS1_H
+
+int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
+                 int use_private, const u8 *in, size_t inlen,
+                 u8 *out, size_t *outlen);
+int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
+                                 const u8 *in, size_t inlen,
+                                 u8 *out, size_t *outlen);
+int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
+                            const u8 *crypt, size_t crypt_len,
+                            u8 *plain, size_t *plain_len);
+
+#endif /* PKCS1_H */
diff --git a/src/tls/pkcs5.c b/src/tls/pkcs5.c
new file mode 100644 (file)
index 0000000..4291b84
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 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 "common.h"
+#include "crypto/crypto.h"
+#include "crypto/md5.h"
+#include "asn1.h"
+#include "pkcs5.h"
+
+
+struct pkcs5_params {
+       enum pkcs5_alg {
+               PKCS5_ALG_UNKNOWN,
+               PKCS5_ALG_MD5_DES_CBC
+       } alg;
+       u8 salt[8];
+       size_t salt_len;
+       unsigned int iter_count;
+};
+
+
+enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid)
+{
+       if (oid->len == 7 &&
+           oid->oid[0] == 1 /* iso */ &&
+           oid->oid[1] == 2 /* member-body */ &&
+           oid->oid[2] == 840 /* us */ &&
+           oid->oid[3] == 113549 /* rsadsi */ &&
+           oid->oid[4] == 1 /* pkcs */ &&
+           oid->oid[5] == 5 /* pkcs-5 */ &&
+           oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */)
+               return PKCS5_ALG_MD5_DES_CBC;
+
+       return PKCS5_ALG_UNKNOWN;
+}
+
+
+static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len,
+                           struct pkcs5_params *params)
+{
+       struct asn1_hdr hdr;
+       const u8 *enc_alg_end, *pos, *end;
+       struct asn1_oid oid;
+       char obuf[80];
+
+       /* AlgorithmIdentifier */
+
+       enc_alg_end = enc_alg + enc_alg_len;
+
+       os_memset(params, 0, sizeof(*params));
+
+       if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID "
+                          "(algorithm)");
+               return -1;
+       }
+
+       asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+       wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf);
+       params->alg = pkcs5_get_alg(&oid);
+       if (params->alg == PKCS5_ALG_UNKNOWN) {
+               wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption "
+                          "algorithm %s", obuf);
+               return -1;
+       }
+
+       /*
+        * PKCS#5, Section 8
+        * PBEParameter ::= SEQUENCE {
+        *   salt OCTET STRING SIZE(8),
+        *   iterationCount INTEGER }
+        */
+
+       if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE "
+                          "(PBEParameter) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       end = hdr.payload + hdr.length;
+
+       /* salt OCTET STRING SIZE(8) */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING ||
+           hdr.length != 8) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) "
+                          "(salt) - found class %d tag 0x%x size %d",
+                          hdr.class, hdr.tag, hdr.length);
+               return -1;
+       }
+       pos = hdr.payload + hdr.length;
+       os_memcpy(params->salt, hdr.payload, hdr.length);
+       params->salt_len = hdr.length;
+       wpa_hexdump(MSG_DEBUG, "PKCS #5: salt",
+                   params->salt, params->salt_len);
+
+       /* iterationCount INTEGER */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found "
+                          "class %d tag 0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+       if (hdr.length == 1)
+               params->iter_count = *hdr.payload;
+       else if (hdr.length == 2)
+               params->iter_count = WPA_GET_BE16(hdr.payload);
+       else if (hdr.length == 4)
+               params->iter_count = WPA_GET_BE32(hdr.payload);
+       else {
+               wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value "
+                           " (iterationCount)",
+                           hdr.payload, hdr.length);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x",
+                  params->iter_count);
+       if (params->iter_count == 0 || params->iter_count > 0xffff) {
+               wpa_printf(MSG_INFO, "PKCS #5: Unsupported "
+                          "iterationCount=0x%x", params->iter_count);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params,
+                                               const char *passwd)
+{
+       unsigned int i;
+       u8 hash[MD5_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       if (params->alg != PKCS5_ALG_MD5_DES_CBC)
+               return NULL;
+
+       addr[0] = (const u8 *) passwd;
+       len[0] = os_strlen(passwd);
+       addr[1] = params->salt;
+       len[1] = params->salt_len;
+       if (md5_vector(2, addr, len, hash) < 0)
+               return NULL;
+       addr[0] = hash;
+       len[0] = MD5_MAC_LEN;
+       for (i = 1; i < params->iter_count; i++) {
+               if (md5_vector(1, addr, len, hash) < 0)
+                       return NULL;
+       }
+       /* TODO: DES key parity bits(?) */
+       wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8);
+       wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8);
+
+       return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8);
+}
+
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+                  const u8 *enc_data, size_t enc_data_len,
+                  const char *passwd, size_t *data_len)
+{
+       struct crypto_cipher *ctx;
+       u8 *eb, pad;
+       struct pkcs5_params params;
+       unsigned int i;
+
+       if (pkcs5_get_params(enc_alg, enc_alg_len, &params) < 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters");
+               return NULL;
+       }
+
+       ctx = pkcs5_crypto_init(&params, passwd);
+       if (ctx == NULL) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto");
+               return NULL;
+       }
+
+       /* PKCS #5, Section 7 - Decryption process */
+       if (enc_data_len < 16 || enc_data_len % 8) {
+               wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext "
+                          "%d", (int) enc_data_len);
+               crypto_cipher_deinit(ctx);
+               return NULL;
+       }
+
+       eb = os_malloc(enc_data_len);
+       if (eb == NULL) {
+               crypto_cipher_deinit(ctx);
+               return NULL;
+       }
+
+       if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB");
+               crypto_cipher_deinit(ctx);
+               os_free(eb);
+               return NULL;
+       }
+       crypto_cipher_deinit(ctx);
+
+       pad = eb[enc_data_len - 1];
+       if (pad > 8) {
+               wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad);
+               os_free(eb);
+               return NULL;
+       }
+       for (i = enc_data_len - pad; i < enc_data_len; i++) {
+               if (eb[i] != pad) {
+                       wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS",
+                                   eb + enc_data_len - pad, pad);
+                       os_free(eb);
+                       return NULL;
+               }
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)",
+                       eb, enc_data_len - pad);
+
+       *data_len = enc_data_len - pad;
+       return eb;
+}
diff --git a/src/tls/pkcs5.h b/src/tls/pkcs5.h
new file mode 100644 (file)
index 0000000..6ed3923
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * PKCS #5 (Password-based Encryption)
+ * Copyright (c) 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.
+ */
+
+#ifndef PKCS5_H
+#define PKCS5_H
+
+u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len,
+                  const u8 *enc_data, size_t enc_data_len,
+                  const char *passwd, size_t *data_len);
+
+#endif /* PKCS5_H */
diff --git a/src/tls/pkcs8.c b/src/tls/pkcs8.c
new file mode 100644 (file)
index 0000000..69ab262
--- /dev/null
@@ -0,0 +1,193 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-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 "common.h"
+#include "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+#include "pkcs5.h"
+#include "pkcs8.h"
+
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+       struct bignum *zero;
+       struct asn1_oid oid;
+       char obuf[80];
+
+       /* PKCS #8, Chapter 6 */
+
+       /* PrivateKeyInfo ::= SEQUENCE */
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+                          "header (SEQUENCE); assume PKCS #8 not used");
+               return NULL;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /* version Version (Version ::= INTEGER) */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found "
+                          "class %d tag 0x%x; assume PKCS #8 not used",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       zero = bignum_init();
+       if (zero == NULL)
+               return NULL;
+
+       if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER");
+               bignum_deinit(zero);
+               return NULL;
+       }
+       pos = hdr.payload + hdr.length;
+
+       if (bignum_cmp_d(zero, 0) != 0) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the "
+                          "beginning of private key; not found; assume "
+                          "PKCS #8 not used");
+               bignum_deinit(zero);
+               return NULL;
+       }
+       bignum_deinit(zero);
+
+       /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier
+        * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+                          "assume PKCS #8 not used",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID "
+                          "(algorithm); assume PKCS #8 not used");
+               return NULL;
+       }
+
+       asn1_oid_to_str(&oid, obuf, sizeof(obuf));
+       wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf);
+
+       if (oid.len != 7 ||
+           oid.oid[0] != 1 /* iso */ ||
+           oid.oid[1] != 2 /* member-body */ ||
+           oid.oid[2] != 840 /* us */ ||
+           oid.oid[3] != 113549 /* rsadsi */ ||
+           oid.oid[4] != 1 /* pkcs */ ||
+           oid.oid[5] != 1 /* pkcs-1 */ ||
+           oid.oid[6] != 1 /* rsaEncryption */) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key "
+                          "algorithm %s", obuf);
+               return NULL;
+       }
+
+       pos = hdr.payload + hdr.length;
+
+       /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+                          "(privateKey) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey");
+
+       return (struct crypto_private_key *)
+               crypto_rsa_import_private_key(hdr.payload, hdr.length);
+}
+
+
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end, *enc_alg;
+       size_t enc_alg_len;
+       u8 *data;
+       size_t data_len;
+
+       if (passwd == NULL)
+               return NULL;
+
+       /*
+        * PKCS #8, Chapter 7
+        * EncryptedPrivateKeyInfo ::= SEQUENCE {
+        *   encryptionAlgorithm EncryptionAlgorithmIdentifier,
+        *   encryptedData EncryptedData }
+        * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+        * EncryptedData ::= OCTET STRING
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 "
+                          "header (SEQUENCE); assume encrypted PKCS #8 not "
+                          "used");
+               return NULL;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /* encryptionAlgorithm EncryptionAlgorithmIdentifier */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x; "
+                          "assume encrypted PKCS #8 not used",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+       enc_alg = hdr.payload;
+       enc_alg_len = hdr.length;
+       pos = hdr.payload + hdr.length;
+
+       /* encryptedData EncryptedData */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING "
+                          "(encryptedData) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length,
+                            passwd, &data_len);
+       if (data) {
+               struct crypto_private_key *key;
+               key = pkcs8_key_import(data, data_len);
+               os_free(data);
+               return key;
+       }
+
+       return NULL;
+}
diff --git a/src/tls/pkcs8.h b/src/tls/pkcs8.h
new file mode 100644 (file)
index 0000000..dac517c
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * PKCS #8 (Private-key information syntax)
+ * Copyright (c) 2006-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.
+ */
+
+#ifndef PKCS8_H
+#define PKCS8_H
+
+struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len);
+struct crypto_private_key *
+pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd);
+
+#endif /* PKCS8_H */
diff --git a/src/tls/rsa.c b/src/tls/rsa.c
new file mode 100644 (file)
index 0000000..3084adc
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * RSA
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "asn1.h"
+#include "bignum.h"
+#include "rsa.h"
+
+
+struct crypto_rsa_key {
+       int private_key; /* whether private key is set */
+       struct bignum *n; /* modulus (p * q) */
+       struct bignum *e; /* public exponent */
+       /* The following parameters are available only if private_key is set */
+       struct bignum *d; /* private exponent */
+       struct bignum *p; /* prime p (factor of n) */
+       struct bignum *q; /* prime q (factor of n) */
+       struct bignum *dmp1; /* d mod (p - 1); CRT exponent */
+       struct bignum *dmq1; /* d mod (q - 1); CRT exponent */
+       struct bignum *iqmp; /* 1 / q mod p; CRT coefficient */
+};
+
+
+static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end,
+                                          struct bignum *num)
+{
+       struct asn1_hdr hdr;
+
+       if (pos == NULL)
+               return NULL;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected INTEGER - found class %d "
+                          "tag 0x%x", hdr.class, hdr.tag);
+               return NULL;
+       }
+
+       if (bignum_set_unsigned_bin(num, hdr.payload, hdr.length) < 0) {
+               wpa_printf(MSG_DEBUG, "RSA: Failed to parse INTEGER");
+               return NULL;
+       }
+
+       return hdr.payload + hdr.length;
+}
+
+
+/**
+ * crypto_rsa_import_public_key - Import an RSA public key
+ * @buf: Key buffer (DER encoded RSA public key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the public key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len)
+{
+       struct crypto_rsa_key *key;
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       key = os_zalloc(sizeof(*key));
+       if (key == NULL)
+               return NULL;
+
+       key->n = bignum_init();
+       key->e = bignum_init();
+       if (key->n == NULL || key->e == NULL) {
+               crypto_rsa_free(key);
+               return NULL;
+       }
+
+       /*
+        * PKCS #1, 7.1:
+        * RSAPublicKey ::= SEQUENCE {
+        *     modulus INTEGER, -- n
+        *     publicExponent INTEGER -- e 
+        * }
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+                          "(public key) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               goto error;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       pos = crypto_rsa_parse_integer(pos, end, key->n);
+       pos = crypto_rsa_parse_integer(pos, end, key->e);
+
+       if (pos == NULL)
+               goto error;
+
+       if (pos != end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "RSA: Extra data in public key SEQUENCE",
+                           pos, end - pos);
+               goto error;
+       }
+
+       return key;
+
+error:
+       crypto_rsa_free(key);
+       return NULL;
+}
+
+
+/**
+ * crypto_rsa_import_private_key - Import an RSA private key
+ * @buf: Key buffer (DER encoded RSA private key)
+ * @len: Key buffer length in bytes
+ * Returns: Pointer to the private key or %NULL on failure
+ */
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len)
+{
+       struct crypto_rsa_key *key;
+       struct bignum *zero;
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       key = os_zalloc(sizeof(*key));
+       if (key == NULL)
+               return NULL;
+
+       key->private_key = 1;
+
+       key->n = bignum_init();
+       key->e = bignum_init();
+       key->d = bignum_init();
+       key->p = bignum_init();
+       key->q = bignum_init();
+       key->dmp1 = bignum_init();
+       key->dmq1 = bignum_init();
+       key->iqmp = bignum_init();
+
+       if (key->n == NULL || key->e == NULL || key->d == NULL ||
+           key->p == NULL || key->q == NULL || key->dmp1 == NULL ||
+           key->dmq1 == NULL || key->iqmp == NULL) {
+               crypto_rsa_free(key);
+               return NULL;
+       }
+
+       /*
+        * PKCS #1, 7.2:
+        * RSAPrivateKey ::= SEQUENCE {
+        *    version Version,
+        *    modulus INTEGER, -- n
+        *    publicExponent INTEGER, -- e
+        *    privateExponent INTEGER, -- d
+        *    prime1 INTEGER, -- p
+        *    prime2 INTEGER, -- q
+        *    exponent1 INTEGER, -- d mod (p-1)
+        *    exponent2 INTEGER, -- d mod (q-1)
+        *    coefficient INTEGER -- (inverse of q) mod p
+        * }
+        *
+        * Version ::= INTEGER -- shall be 0 for this version of the standard
+        */
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected SEQUENCE "
+                          "(public key) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               goto error;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       zero = bignum_init();
+       if (zero == NULL)
+               goto error;
+       pos = crypto_rsa_parse_integer(pos, end, zero);
+       if (pos == NULL || bignum_cmp_d(zero, 0) != 0) {
+               wpa_printf(MSG_DEBUG, "RSA: Expected zero INTEGER in the "
+                          "beginning of private key; not found");
+               bignum_deinit(zero);
+               goto error;
+       }
+       bignum_deinit(zero);
+
+       pos = crypto_rsa_parse_integer(pos, end, key->n);
+       pos = crypto_rsa_parse_integer(pos, end, key->e);
+       pos = crypto_rsa_parse_integer(pos, end, key->d);
+       pos = crypto_rsa_parse_integer(pos, end, key->p);
+       pos = crypto_rsa_parse_integer(pos, end, key->q);
+       pos = crypto_rsa_parse_integer(pos, end, key->dmp1);
+       pos = crypto_rsa_parse_integer(pos, end, key->dmq1);
+       pos = crypto_rsa_parse_integer(pos, end, key->iqmp);
+
+       if (pos == NULL)
+               goto error;
+
+       if (pos != end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "RSA: Extra data in public key SEQUENCE",
+                           pos, end - pos);
+               goto error;
+       }
+
+       return key;
+
+error:
+       crypto_rsa_free(key);
+       return NULL;
+}
+
+
+/**
+ * crypto_rsa_get_modulus_len - Get the modulus length of the RSA key
+ * @key: RSA key
+ * Returns: Modulus length of the key
+ */
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key)
+{
+       return bignum_get_unsigned_bin_len(key->n);
+}
+
+
+/**
+ * crypto_rsa_exptmod - RSA modular exponentiation
+ * @in: Input data
+ * @inlen: Input data length
+ * @out: Buffer for output data
+ * @outlen: Maximum size of the output buffer and used size on success
+ * @key: RSA key
+ * @use_private: 1 = Use RSA private key, 0 = Use RSA public key
+ * Returns: 0 on success, -1 on failure
+ */
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+                      struct crypto_rsa_key *key, int use_private)
+{
+       struct bignum *tmp, *a = NULL, *b = NULL;
+       int ret = -1;
+       size_t modlen;
+
+       if (use_private && !key->private_key)
+               return -1;
+
+       tmp = bignum_init();
+       if (tmp == NULL)
+               return -1;
+
+       if (bignum_set_unsigned_bin(tmp, in, inlen) < 0)
+               goto error;
+       if (bignum_cmp(key->n, tmp) < 0) {
+               /* Too large input value for the RSA key modulus */
+               goto error;
+       }
+
+       if (use_private) {
+               /*
+                * Decrypt (or sign) using Chinese remainer theorem to speed
+                * up calculation. This is equivalent to tmp = tmp^d mod n
+                * (which would require more CPU to calculate directly).
+                *
+                * dmp1 = (1/e) mod (p-1)
+                * dmq1 = (1/e) mod (q-1)
+                * iqmp = (1/q) mod p, where p > q
+                * m1 = c^dmp1 mod p
+                * m2 = c^dmq1 mod q
+                * h = q^-1 (m1 - m2) mod p
+                * m = m2 + hq
+                */
+               a = bignum_init();
+               b = bignum_init();
+               if (a == NULL || b == NULL)
+                       goto error;
+
+               /* a = tmp^dmp1 mod p */
+               if (bignum_exptmod(tmp, key->dmp1, key->p, a) < 0)
+                       goto error;
+
+               /* b = tmp^dmq1 mod q */
+               if (bignum_exptmod(tmp, key->dmq1, key->q, b) < 0)
+                       goto error;
+
+               /* tmp = (a - b) * (1/q mod p) (mod p) */
+               if (bignum_sub(a, b, tmp) < 0 ||
+                   bignum_mulmod(tmp, key->iqmp, key->p, tmp) < 0)
+                       goto error;
+
+               /* tmp = b + q * tmp */
+               if (bignum_mul(tmp, key->q, tmp) < 0 ||
+                   bignum_add(tmp, b, tmp) < 0)
+                       goto error;
+       } else {
+               /* Encrypt (or verify signature) */
+               /* tmp = tmp^e mod N */
+               if (bignum_exptmod(tmp, key->e, key->n, tmp) < 0)
+                       goto error;
+       }
+
+       modlen = crypto_rsa_get_modulus_len(key);
+       if (modlen > *outlen) {
+               *outlen = modlen;
+               goto error;
+       }
+
+       if (bignum_get_unsigned_bin_len(tmp) > modlen)
+               goto error; /* should never happen */
+
+       *outlen = modlen;
+       os_memset(out, 0, modlen);
+       if (bignum_get_unsigned_bin(
+                   tmp, out +
+                   (modlen - bignum_get_unsigned_bin_len(tmp)), NULL) < 0)
+               goto error;
+
+       ret = 0;
+
+error:
+       bignum_deinit(tmp);
+       bignum_deinit(a);
+       bignum_deinit(b);
+       return ret;
+}
+
+
+/**
+ * crypto_rsa_free - Free RSA key
+ * @key: RSA key to be freed
+ *
+ * This function frees an RSA key imported with either
+ * crypto_rsa_import_public_key() or crypto_rsa_import_private_key().
+ */
+void crypto_rsa_free(struct crypto_rsa_key *key)
+{
+       if (key) {
+               bignum_deinit(key->n);
+               bignum_deinit(key->e);
+               bignum_deinit(key->d);
+               bignum_deinit(key->p);
+               bignum_deinit(key->q);
+               bignum_deinit(key->dmp1);
+               bignum_deinit(key->dmq1);
+               bignum_deinit(key->iqmp);
+               os_free(key);
+       }
+}
diff --git a/src/tls/rsa.h b/src/tls/rsa.h
new file mode 100644 (file)
index 0000000..ac50dfd
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * RSA
+ * Copyright (c) 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 RSA_H
+#define RSA_H
+
+struct crypto_rsa_key;
+
+struct crypto_rsa_key *
+crypto_rsa_import_public_key(const u8 *buf, size_t len);
+struct crypto_rsa_key *
+crypto_rsa_import_private_key(const u8 *buf, size_t len);
+size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key);
+int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
+                      struct crypto_rsa_key *key, int use_private);
+void crypto_rsa_free(struct crypto_rsa_key *key);
+
+#endif /* RSA_H */
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
new file mode 100644 (file)
index 0000000..afb6031
--- /dev/null
@@ -0,0 +1,667 @@
+/*
+ * TLSv1 client (RFC 2246)
+ * Copyright (c) 2006-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 "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description)
+{
+       conn->alert_level = level;
+       conn->alert_description = description;
+}
+
+
+void tlsv1_client_free_dh(struct tlsv1_client *conn)
+{
+       os_free(conn->dh_p);
+       os_free(conn->dh_g);
+       os_free(conn->dh_ys);
+       conn->dh_p = conn->dh_g = conn->dh_ys = NULL;
+}
+
+
+int tls_derive_pre_master_secret(u8 *pre_master_secret)
+{
+       WPA_PUT_BE16(pre_master_secret, TLS_VERSION);
+       if (os_get_random(pre_master_secret + 2,
+                         TLS_PRE_MASTER_SECRET_LEN - 2))
+               return -1;
+       return 0;
+}
+
+
+int tls_derive_keys(struct tlsv1_client *conn,
+                   const u8 *pre_master_secret, size_t pre_master_secret_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+       u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+       u8 *pos;
+       size_t key_block_len;
+
+       if (pre_master_secret) {
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+                               pre_master_secret, pre_master_secret_len);
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+               if (tls_prf(pre_master_secret, pre_master_secret_len,
+                           "master secret", seed, 2 * TLS_RANDOM_LEN,
+                           conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+                                  "master_secret");
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+                               conn->master_secret, TLS_MASTER_SECRET_LEN);
+       }
+
+       os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+       os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+       key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                            conn->rl.iv_size);
+       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "key expansion", seed, 2 * TLS_RANDOM_LEN,
+                   key_block, key_block_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+                       key_block, key_block_len);
+
+       pos = key_block;
+
+       /* client_write_MAC_secret */
+       os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+       /* server_write_MAC_secret */
+       os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+
+       /* client_write_key */
+       os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+       /* server_write_key */
+       os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+
+       /* client_write_IV */
+       os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+       pos += conn->rl.iv_size;
+       /* server_write_IV */
+       os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+       pos += conn->rl.iv_size;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_handshake - Process TLS handshake
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * @appl_data: Pointer to application data pointer, or %NULL if dropped
+ * @appl_data_len: Pointer to variable that is set to appl_data length
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+                           const u8 *in_data, size_t in_len,
+                           size_t *out_len, u8 **appl_data,
+                           size_t *appl_data_len)
+{
+       const u8 *pos, *end;
+       u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+       size_t in_msg_len;
+       int no_appl_data;
+
+       if (conn->state == CLIENT_HELLO) {
+               if (in_len)
+                       return NULL;
+               return tls_send_client_hello(conn, out_len);
+       }
+
+       if (in_data == NULL || in_len == 0)
+               return NULL;
+
+       pos = in_data;
+       end = in_data + in_len;
+       in_msg = os_malloc(in_len);
+       if (in_msg == NULL)
+               return NULL;
+
+       /* Each received packet may include multiple records */
+       while (pos < end) {
+               in_msg_len = in_len;
+               if (tlsv1_record_receive(&conn->rl, pos, end - pos,
+                                        in_msg, &in_msg_len, &alert)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+                                  "record failed");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       goto failed;
+               }
+               ct = pos[0];
+
+               in_pos = in_msg;
+               in_end = in_msg + in_msg_len;
+
+               /* Each received record may include multiple messages of the
+                * same ContentType. */
+               while (in_pos < in_end) {
+                       in_msg_len = in_end - in_pos;
+                       if (tlsv1_client_process_handshake(conn, ct, in_pos,
+                                                          &in_msg_len,
+                                                          appl_data,
+                                                          appl_data_len) < 0)
+                               goto failed;
+                       in_pos += in_msg_len;
+               }
+
+               pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+       }
+
+       os_free(in_msg);
+       in_msg = NULL;
+
+       no_appl_data = appl_data == NULL || *appl_data == NULL;
+       msg = tlsv1_client_handshake_write(conn, out_len, no_appl_data);
+
+failed:
+       os_free(in_msg);
+       if (conn->alert_level) {
+               conn->state = FAILED;
+               os_free(msg);
+               msg = tlsv1_client_send_alert(conn, conn->alert_level,
+                                             conn->alert_description,
+                                             out_len);
+       } else if (msg == NULL) {
+               msg = os_zalloc(1);
+               *out_len = 0;
+       }
+
+       return msg;
+}
+
+
+/**
+ * tlsv1_client_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length 
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       size_t rlen;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+                       in_data, in_len);
+
+       os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+                             out_data, out_len, in_len, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return rlen;
+}
+
+
+/**
+ * tlsv1_client_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_client_decrypt(struct tlsv1_client *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       const u8 *in_end, *pos;
+       int res;
+       u8 alert, *out_end, *out_pos;
+       size_t olen;
+
+       pos = in_data;
+       in_end = in_data + in_len;
+       out_pos = out_data;
+       out_end = out_data + out_len;
+
+       while (pos < in_end) {
+               if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+                                  "0x%x", pos[0]);
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               olen = out_end - out_pos;
+               res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+                                          out_pos, &olen, &alert);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+                                  "failed");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       return -1;
+               }
+               out_pos += olen;
+               if (out_pos > out_end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+                                  "for processing the received record");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+
+               pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+       }
+
+       return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_client_global_init - Initialize TLSv1 client
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 client functions.
+ */
+int tlsv1_client_global_init(void)
+{
+       return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_client_global_deinit - Deinitialize TLSv1 client
+ *
+ * This function can be used to deinitialize the TLSv1 client that was
+ * initialized by calling tlsv1_client_global_init(). No TLSv1 client functions
+ * can be called after this before calling tlsv1_client_global_init() again.
+ */
+void tlsv1_client_global_deinit(void)
+{
+       crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_client_init - Initialize TLSv1 client connection
+ * Returns: Pointer to TLSv1 client connection data or %NULL on failure
+ */
+struct tlsv1_client * tlsv1_client_init(void)
+{
+       struct tlsv1_client *conn;
+       size_t count;
+       u16 *suites;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+                          "hash");
+               os_free(conn);
+               return NULL;
+       }
+
+       count = 0;
+       suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+       suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+       suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+       conn->num_cipher_suites = count;
+
+       return conn;
+}
+
+
+/**
+ * tlsv1_client_deinit - Deinitialize TLSv1 client connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ */
+void tlsv1_client_deinit(struct tlsv1_client *conn)
+{
+       crypto_public_key_free(conn->server_rsa_key);
+       tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+       tlsv1_record_change_write_cipher(&conn->rl);
+       tlsv1_record_change_read_cipher(&conn->rl);
+       tls_verify_hash_free(&conn->verify);
+       os_free(conn->client_hello_ext);
+       tlsv1_client_free_dh(conn);
+       tlsv1_cred_free(conn->cred);
+       os_free(conn);
+}
+
+
+/**
+ * tlsv1_client_established - Check whether connection has been established
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_client_established(struct tlsv1_client *conn)
+{
+       return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_client_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+                    int server_random_first, u8 *out, size_t out_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+
+       if (conn->state != ESTABLISHED)
+               return -1;
+
+       if (server_random_first) {
+               os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+                         TLS_RANDOM_LEN);
+       } else {
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+       }
+
+       return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                      label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_client_get_cipher - Get current cipher name
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+                           size_t buflen)
+{
+       char *cipher;
+
+       switch (conn->rl.cipher_suite) {
+       case TLS_RSA_WITH_RC4_128_MD5:
+               cipher = "RC4-MD5";
+               break;
+       case TLS_RSA_WITH_RC4_128_SHA:
+               cipher = "RC4-SHA";
+               break;
+       case TLS_RSA_WITH_DES_CBC_SHA:
+               cipher = "DES-CBC-SHA";
+               break;
+       case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+               cipher = "DES-CBC3-SHA";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+               cipher = "ADH-AES-128-SHA";
+               break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA:
+               cipher = "AES-256-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "AES-128-SHA";
+               break;
+       default:
+               return -1;
+       }
+
+       if (os_strlcpy(buf, cipher, buflen) >= buflen)
+               return -1;
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_shutdown(struct tlsv1_client *conn)
+{
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+                          "hash");
+               return -1;
+       }
+
+       tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+       tlsv1_record_change_write_cipher(&conn->rl);
+       tlsv1_record_change_read_cipher(&conn->rl);
+
+       conn->certificate_requested = 0;
+       crypto_public_key_free(conn->server_rsa_key);
+       conn->server_rsa_key = NULL;
+       conn->session_resumed = 0;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_resumed - Was session resumption used
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_client_resumed(struct tlsv1_client *conn)
+{
+       return !!conn->session_resumed;
+}
+
+
+/**
+ * tlsv1_client_hello_ext - Set TLS extension for ClientHello
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ext_type: Extension type
+ * @data: Extension payload (%NULL to remove extension)
+ * @data_len: Extension payload length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+                          const u8 *data, size_t data_len)
+{
+       u8 *pos;
+
+       conn->session_ticket_included = 0;
+       os_free(conn->client_hello_ext);
+       conn->client_hello_ext = NULL;
+       conn->client_hello_ext_len = 0;
+
+       if (data == NULL || data_len == 0)
+               return 0;
+
+       pos = conn->client_hello_ext = os_malloc(6 + data_len);
+       if (pos == NULL)
+               return -1;
+
+       WPA_PUT_BE16(pos, 4 + data_len);
+       pos += 2;
+       WPA_PUT_BE16(pos, ext_type);
+       pos += 2;
+       WPA_PUT_BE16(pos, data_len);
+       pos += 2;
+       os_memcpy(pos, data, data_len);
+       conn->client_hello_ext_len = 6 + data_len;
+
+       if (ext_type == TLS_EXT_PAC_OPAQUE) {
+               conn->session_ticket_included = 1;
+               wpa_printf(MSG_DEBUG, "TLSv1: Using session ticket");
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys)
+{
+       os_memset(keys, 0, sizeof(*keys));
+       if (conn->state == CLIENT_HELLO)
+               return -1;
+
+       keys->client_random = conn->client_random;
+       keys->client_random_len = TLS_RANDOM_LEN;
+
+       if (conn->state != SERVER_HELLO) {
+               keys->server_random = conn->server_random;
+               keys->server_random_len = TLS_RANDOM_LEN;
+               keys->master_key = conn->master_secret;
+               keys->master_key_len = TLS_MASTER_SECRET_LEN;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn)
+{
+       if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+               return -1;
+
+       return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                   conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_client_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers)
+{
+       size_t count;
+       u16 *suites;
+
+       /* TODO: implement proper configuration of cipher suites */
+       if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+               count = 0;
+               suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+               suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+               suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+
+               /*
+                * Cisco AP (at least 350 and 1200 series) local authentication
+                * server does not know how to search cipher suites from the
+                * list and seem to require that the last entry in the list is
+                * the one that it wants to use. However, TLS specification
+                * requires the list to be in the client preference order. As a
+                * workaround, add anon-DH AES-128-SHA1 again at the end of the
+                * list to allow the Cisco code to find it.
+                */
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+               conn->num_cipher_suites = count;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_client_set_cred - Set client credentials
+ * @conn: TLSv1 client connection data from tlsv1_client_init()
+ * @cred: Credentials from tlsv1_cred_alloc()
+ * Returns: 0 on success, -1 on failure
+ *
+ * On success, the client takes ownership of the credentials block and caller
+ * must not free it. On failure, caller is responsible for freeing the
+ * credential block.
+ */
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+                         struct tlsv1_credentials *cred)
+{
+       tlsv1_cred_free(conn->cred);
+       conn->cred = cred;
+       return 0;
+}
+
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+                                       tlsv1_client_session_ticket_cb cb,
+                                       void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+                  cb, ctx);
+       conn->session_ticket_cb = cb;
+       conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
new file mode 100644 (file)
index 0000000..16ad57d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * TLSv1 client (RFC 2246)
+ * Copyright (c) 2006-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 TLSV1_CLIENT_H
+#define TLSV1_CLIENT_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_client;
+
+int tlsv1_client_global_init(void);
+void tlsv1_client_global_deinit(void);
+struct tlsv1_client * tlsv1_client_init(void);
+void tlsv1_client_deinit(struct tlsv1_client *conn);
+int tlsv1_client_established(struct tlsv1_client *conn);
+int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+                    int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
+                           const u8 *in_data, size_t in_len,
+                           size_t *out_len, u8 **appl_data,
+                           size_t *appl_data_len);
+int tlsv1_client_encrypt(struct tlsv1_client *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len);
+int tlsv1_client_decrypt(struct tlsv1_client *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len);
+int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
+                           size_t buflen);
+int tlsv1_client_shutdown(struct tlsv1_client *conn);
+int tlsv1_client_resumed(struct tlsv1_client *conn);
+int tlsv1_client_hello_ext(struct tlsv1_client *conn, int ext_type,
+                          const u8 *data, size_t data_len);
+int tlsv1_client_get_keys(struct tlsv1_client *conn, struct tls_keys *keys);
+int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn);
+int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers);
+int tlsv1_client_set_cred(struct tlsv1_client *conn,
+                         struct tlsv1_credentials *cred);
+
+typedef int (*tlsv1_client_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_client_set_session_ticket_cb(struct tlsv1_client *conn,
+                                       tlsv1_client_session_ticket_cb cb,
+                                       void *ctx);
+
+#endif /* TLSV1_CLIENT_H */
diff --git a/src/tls/tlsv1_client_i.h b/src/tls/tlsv1_client_i.h
new file mode 100644 (file)
index 0000000..7fe179f
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * TLSv1 client - internal structures
+ * Copyright (c) 2006-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 TLSV1_CLIENT_I_H
+#define TLSV1_CLIENT_I_H
+
+struct tlsv1_client {
+       enum {
+               CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+               SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+               SERVER_HELLO_DONE, CLIENT_KEY_EXCHANGE, CHANGE_CIPHER_SPEC,
+               SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED, ACK_FINISHED,
+               ESTABLISHED, FAILED
+       } state;
+
+       struct tlsv1_record_layer rl;
+
+       u8 session_id[TLS_SESSION_ID_MAX_LEN];
+       size_t session_id_len;
+       u8 client_random[TLS_RANDOM_LEN];
+       u8 server_random[TLS_RANDOM_LEN];
+       u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+       u8 alert_level;
+       u8 alert_description;
+
+       unsigned int certificate_requested:1;
+       unsigned int session_resumed:1;
+       unsigned int session_ticket_included:1;
+       unsigned int use_session_ticket:1;
+
+       struct crypto_public_key *server_rsa_key;
+
+       struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+       u16 cipher_suites[MAX_CIPHER_COUNT];
+       size_t num_cipher_suites;
+
+       u16 prev_cipher_suite;
+
+       u8 *client_hello_ext;
+       size_t client_hello_ext_len;
+
+       /* The prime modulus used for Diffie-Hellman */
+       u8 *dh_p;
+       size_t dh_p_len;
+       /* The generator used for Diffie-Hellman */
+       u8 *dh_g;
+       size_t dh_g_len;
+       /* The server's Diffie-Hellman public value */
+       u8 *dh_ys;
+       size_t dh_ys_len;
+
+       struct tlsv1_credentials *cred;
+
+       tlsv1_client_session_ticket_cb session_ticket_cb;
+       void *session_ticket_cb_ctx;
+};
+
+
+void tls_alert(struct tlsv1_client *conn, u8 level, u8 description);
+void tlsv1_client_free_dh(struct tlsv1_client *conn);
+int tls_derive_pre_master_secret(u8 *pre_master_secret);
+int tls_derive_keys(struct tlsv1_client *conn,
+                   const u8 *pre_master_secret, size_t pre_master_secret_len);
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len);
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+                            u8 description, size_t *out_len);
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+                                 int no_appl_data);
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+                                  const u8 *buf, size_t *len,
+                                  u8 **out_data, size_t *out_len);
+
+#endif /* TLSV1_CLIENT_I_H */
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
new file mode 100644 (file)
index 0000000..ed3f260
--- /dev/null
@@ -0,0 +1,976 @@
+/*
+ * TLSv1 client - read handshake message
+ * Copyright (c) 2006-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 "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len);
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len);
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+                                        const u8 *in_data, size_t *in_len);
+
+
+static int tls_process_server_hello(struct tlsv1_client *conn, u8 ct,
+                                   const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, i;
+       u16 cipher_suite;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4)
+               goto decode_error;
+
+       /* HandshakeType msg_type */
+       if (*pos != TLS_HANDSHAKE_TYPE_SERVER_HELLO) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ServerHello)", *pos);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHello");
+       pos++;
+       /* uint24 length */
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left)
+               goto decode_error;
+
+       /* body - ServerHello */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerHello", pos, len);
+       end = pos + len;
+
+       /* ProtocolVersion server_version */
+       if (end - pos < 2)
+               goto decode_error;
+       if (WPA_GET_BE16(pos) != TLS_VERSION) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+                          "ServerHello");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_PROTOCOL_VERSION);
+               return -1;
+       }
+       pos += 2;
+
+       /* Random random */
+       if (end - pos < TLS_RANDOM_LEN)
+               goto decode_error;
+
+       os_memcpy(conn->server_random, pos, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+                   conn->server_random, TLS_RANDOM_LEN);
+
+       /* SessionID session_id */
+       if (end - pos < 1)
+               goto decode_error;
+       if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+               goto decode_error;
+       if (conn->session_id_len && conn->session_id_len == *pos &&
+           os_memcmp(conn->session_id, pos + 1, conn->session_id_len) == 0) {
+               pos += 1 + conn->session_id_len;
+               wpa_printf(MSG_DEBUG, "TLSv1: Resuming old session");
+               conn->session_resumed = 1;
+       } else {
+               conn->session_id_len = *pos;
+               pos++;
+               os_memcpy(conn->session_id, pos, conn->session_id_len);
+               pos += conn->session_id_len;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+                   conn->session_id, conn->session_id_len);
+
+       /* CipherSuite cipher_suite */
+       if (end - pos < 2)
+               goto decode_error;
+       cipher_suite = WPA_GET_BE16(pos);
+       pos += 2;
+       for (i = 0; i < conn->num_cipher_suites; i++) {
+               if (cipher_suite == conn->cipher_suites[i])
+                       break;
+       }
+       if (i == conn->num_cipher_suites) {
+               wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+                          "cipher suite 0x%04x", cipher_suite);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (conn->session_resumed && cipher_suite != conn->prev_cipher_suite) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Server selected a different "
+                          "cipher suite for a resumed connection (0x%04x != "
+                          "0x%04x)", cipher_suite, conn->prev_cipher_suite);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+                          "record layer");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       conn->prev_cipher_suite = cipher_suite;
+
+       /* CompressionMethod compression_method */
+       if (end - pos < 1)
+               goto decode_error;
+       if (*pos != TLS_COMPRESSION_NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Server selected unexpected "
+                          "compression 0x%02x", *pos);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+       pos++;
+
+       if (end != pos) {
+               /* TODO: ServerHello extensions */
+               wpa_hexdump(MSG_DEBUG, "TLSv1: Unexpected extra data in the "
+                           "end of ServerHello", pos, end - pos);
+               goto decode_error;
+       }
+
+       if (conn->session_ticket_included && conn->session_ticket_cb) {
+               /* TODO: include SessionTicket extension if one was included in
+                * ServerHello */
+               int res = conn->session_ticket_cb(
+                       conn->session_ticket_cb_ctx, NULL, 0,
+                       conn->client_random, conn->server_random,
+                       conn->master_secret);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+                                  "indicated failure");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_HANDSHAKE_FAILURE);
+                       return -1;
+               }
+               conn->use_session_ticket = !!res;
+       }
+
+       if ((conn->session_resumed || conn->use_session_ticket) &&
+           tls_derive_keys(conn, NULL, 0)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *in_len = end - in_data;
+
+       conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+               SERVER_CHANGE_CIPHER_SPEC : SERVER_CERTIFICATE;
+
+       return 0;
+
+decode_error:
+       wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ServerHello");
+       tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+       return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
+                                  const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, list_len, cert_len, idx;
+       u8 type;
+       struct x509_certificate *chain = NULL, *last = NULL, *cert;
+       int reason;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+                          "(len=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE)
+               return tls_process_server_key_exchange(conn, ct, in_data,
+                                                      in_len);
+       if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+               return tls_process_certificate_request(conn, ct, in_data,
+                                                      in_len);
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+               return tls_process_server_hello_done(conn, ct, in_data,
+                                                    in_len);
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected Certificate/"
+                          "ServerKeyExchange/CertificateRequest/"
+                          "ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "TLSv1: Received Certificate (certificate_list len %lu)",
+                  (unsigned long) len);
+
+       /*
+        * opaque ASN.1Cert<2^24-1>;
+        *
+        * struct {
+        *     ASN.1Cert certificate_list<1..2^24-1>;
+        * } Certificate;
+        */
+
+       end = pos + len;
+
+       if (end - pos < 3) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+                          "(left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       list_len = WPA_GET_BE24(pos);
+       pos += 3;
+
+       if ((size_t) (end - pos) != list_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+                          "length (len=%lu left=%lu)",
+                          (unsigned long) list_len,
+                          (unsigned long) (end - pos));
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       idx = 0;
+       while (pos < end) {
+               if (end - pos < 3) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "certificate_list");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               cert_len = WPA_GET_BE24(pos);
+               pos += 3;
+
+               if ((size_t) (end - pos) < cert_len) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+                                  "length (len=%lu left=%lu)",
+                                  (unsigned long) cert_len,
+                                  (unsigned long) (end - pos));
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+                          (unsigned long) idx, (unsigned long) cert_len);
+
+               if (idx == 0) {
+                       crypto_public_key_free(conn->server_rsa_key);
+                       if (tls_parse_cert(pos, cert_len,
+                                          &conn->server_rsa_key)) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                          "the certificate");
+                               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_BAD_CERTIFICATE);
+                               x509_certificate_chain_free(chain);
+                               return -1;
+                       }
+               }
+
+               cert = x509_certificate_parse(pos, cert_len);
+               if (cert == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "the certificate");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_BAD_CERTIFICATE);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               if (last == NULL)
+                       chain = cert;
+               else
+                       last->next = cert;
+               last = cert;
+
+               idx++;
+               pos += cert_len;
+       }
+
+       if (conn->cred &&
+           x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+                                           &reason) < 0) {
+               int tls_reason;
+               wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+                          "validation failed (reason=%d)", reason);
+               switch (reason) {
+               case X509_VALIDATE_BAD_CERTIFICATE:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+                       tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_REVOKED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_EXPIRED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+                       tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+                       break;
+               case X509_VALIDATE_UNKNOWN_CA:
+                       tls_reason = TLS_ALERT_UNKNOWN_CA;
+                       break;
+               default:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               }
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+               x509_certificate_chain_free(chain);
+               return -1;
+       }
+
+       x509_certificate_chain_free(chain);
+
+       *in_len = end - in_data;
+
+       conn->state = SERVER_KEY_EXCHANGE;
+
+       return 0;
+}
+
+
+static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
+                                       const u8 *buf, size_t len)
+{
+       const u8 *pos, *end;
+
+       tlsv1_client_free_dh(conn);
+
+       pos = buf;
+       end = buf + len;
+
+       if (end - pos < 3)
+               goto fail;
+       conn->dh_p_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
+                          (unsigned long) conn->dh_p_len);
+               goto fail;
+       }
+       conn->dh_p = os_malloc(conn->dh_p_len);
+       if (conn->dh_p == NULL)
+               goto fail;
+       os_memcpy(conn->dh_p, pos, conn->dh_p_len);
+       pos += conn->dh_p_len;
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH p (prime)",
+                   conn->dh_p, conn->dh_p_len);
+
+       if (end - pos < 3)
+               goto fail;
+       conn->dh_g_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+               goto fail;
+       conn->dh_g = os_malloc(conn->dh_g_len);
+       if (conn->dh_g == NULL)
+               goto fail;
+       os_memcpy(conn->dh_g, pos, conn->dh_g_len);
+       pos += conn->dh_g_len;
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH g (generator)",
+                   conn->dh_g, conn->dh_g_len);
+       if (conn->dh_g_len == 1 && conn->dh_g[0] < 2)
+               goto fail;
+
+       if (end - pos < 3)
+               goto fail;
+       conn->dh_ys_len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+               goto fail;
+       conn->dh_ys = os_malloc(conn->dh_ys_len);
+       if (conn->dh_ys == NULL)
+               goto fail;
+       os_memcpy(conn->dh_ys, pos, conn->dh_ys_len);
+       pos += conn->dh_ys_len;
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+                   conn->dh_ys, conn->dh_ys_len);
+
+       return 0;
+
+fail:
+       wpa_printf(MSG_DEBUG, "TLSv1: Processing DH params failed");
+       tlsv1_client_free_dh(conn);
+       return -1;
+}
+
+
+static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+       const struct tls_cipher_suite *suite;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerKeyExchange "
+                          "(Left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerKeyExchange "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type == TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST)
+               return tls_process_certificate_request(conn, ct, in_data,
+                                                      in_len);
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+               return tls_process_server_hello_done(conn, ct, in_data,
+                                                    in_len);
+       if (type != TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ServerKeyExchange/"
+                          "CertificateRequest/ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ServerKeyExchange");
+
+       if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not allowed "
+                          "with the selected cipher suite");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+               if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "TLSv1: UnexpectedServerKeyExchange");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       *in_len = end - in_data;
+
+       conn->state = SERVER_CERTIFICATE_REQUEST;
+
+       return 0;
+}
+
+
+static int tls_process_certificate_request(struct tlsv1_client *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateRequest "
+                          "(left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in CertificateRequest "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type == TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE)
+               return tls_process_server_hello_done(conn, ct, in_data,
+                                                    in_len);
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected CertificateRequest/"
+                          "ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateRequest");
+
+       conn->certificate_requested = 1;
+
+       *in_len = end - in_data;
+
+       conn->state = SERVER_HELLO_DONE;
+
+       return 0;
+}
+
+
+static int tls_process_server_hello_done(struct tlsv1_client *conn, u8 ct,
+                                        const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ServerHelloDone "
+                          "(left=%lu)", (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ServerHelloDone "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       end = pos + len;
+
+       if (type != TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ServerHelloDone)", type);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ServerHelloDone");
+
+       *in_len = end - in_data;
+
+       conn->state = CLIENT_KEY_EXCHANGE;
+
+       return 0;
+}
+
+
+static int tls_process_server_change_cipher_spec(struct tlsv1_client *conn,
+                                                u8 ct, const u8 *in_data,
+                                                size_t *in_len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received content type 0x%x", ct);
+               if (conn->use_session_ticket) {
+                       int res;
+                       wpa_printf(MSG_DEBUG, "TLSv1: Server may have "
+                                  "rejected SessionTicket");
+                       conn->use_session_ticket = 0;
+
+                       /* Notify upper layers that SessionTicket failed */
+                       res = conn->session_ticket_cb(
+                               conn->session_ticket_cb_ctx, NULL, 0, NULL,
+                               NULL, NULL);
+                       if (res < 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket "
+                                          "callback indicated failure");
+                               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                         TLS_ALERT_HANDSHAKE_FAILURE);
+                               return -1;
+                       }
+
+                       conn->state = SERVER_CERTIFICATE;
+                       return tls_process_certificate(conn, ct, in_data,
+                                                      in_len);
+               }
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received data 0x%x", *pos);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+       if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+                          "for record layer");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *in_len = pos + 1 - in_data;
+
+       conn->state = SERVER_FINISHED;
+
+       return 0;
+}
+
+
+static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
+                                      const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, hlen;
+       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+                          "Finished",
+                          (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+                          "type 0x%x", pos[0]);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       len = WPA_GET_BE24(pos + 1);
+
+       pos += 4;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+                          "(len=%lu > left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       end = pos + len;
+       if (len != TLS_VERIFY_DATA_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+                          "in Finished: %lu (expected %d)",
+                          (unsigned long) len, TLS_VERIFY_DATA_LEN);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+                   pos, TLS_VERIFY_DATA_LEN);
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_server == NULL ||
+           crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_server = NULL;
+               crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+               conn->verify.sha1_server = NULL;
+               return -1;
+       }
+       conn->verify.md5_server = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_server == NULL ||
+           crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_server = NULL;
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_server = NULL;
+
+       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+                   verify_data, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+                       verify_data, TLS_VERIFY_DATA_LEN);
+
+       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+               wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+       *in_len = end - in_data;
+
+       conn->state = (conn->session_resumed || conn->use_session_ticket) ?
+               CHANGE_CIPHER_SPEC : ACK_FINISHED;
+
+       return 0;
+}
+
+
+static int tls_process_application_data(struct tlsv1_client *conn, u8 ct,
+                                       const u8 *in_data, size_t *in_len,
+                                       u8 **out_data, size_t *out_len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Application Data; "
+                          "received content type 0x%x", ct);
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: Application Data included in Handshake",
+                   pos, left);
+
+       *out_data = os_malloc(left);
+       if (*out_data) {
+               os_memcpy(*out_data, pos, left);
+               *out_len = left;
+       }
+
+       return 0;
+}
+
+
+int tlsv1_client_process_handshake(struct tlsv1_client *conn, u8 ct,
+                                  const u8 *buf, size_t *len,
+                                  u8 **out_data, size_t *out_len)
+{
+       if (ct == TLS_CONTENT_TYPE_ALERT) {
+               if (*len < 2) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+                          buf[0], buf[1]);
+               *len = 2;
+               conn->state = FAILED;
+               return -1;
+       }
+
+       if (ct == TLS_CONTENT_TYPE_HANDSHAKE && *len >= 4 &&
+           buf[0] == TLS_HANDSHAKE_TYPE_HELLO_REQUEST) {
+               size_t hr_len = WPA_GET_BE24(buf + 1);
+               if (hr_len > *len - 4) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: HelloRequest underflow");
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Ignored HelloRequest");
+               *len = 4 + hr_len;
+               return 0;
+       }
+
+       switch (conn->state) {
+       case SERVER_HELLO:
+               if (tls_process_server_hello(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_CERTIFICATE:
+               if (tls_process_certificate(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_KEY_EXCHANGE:
+               if (tls_process_server_key_exchange(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_CERTIFICATE_REQUEST:
+               if (tls_process_certificate_request(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_HELLO_DONE:
+               if (tls_process_server_hello_done(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_CHANGE_CIPHER_SPEC:
+               if (tls_process_server_change_cipher_spec(conn, ct, buf, len))
+                       return -1;
+               break;
+       case SERVER_FINISHED:
+               if (tls_process_server_finished(conn, ct, buf, len))
+                       return -1;
+               break;
+       case ACK_FINISHED:
+               if (out_data &&
+                   tls_process_application_data(conn, ct, buf, len, out_data,
+                                                out_len))
+                       return -1;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+                          "while processing received message",
+                          conn->state);
+               return -1;
+       }
+
+       if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+               tls_verify_hash_add(&conn->verify, buf, *len);
+
+       return 0;
+}
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
new file mode 100644 (file)
index 0000000..b47425f
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * TLSv1 client - write handshake message
+ * Copyright (c) 2006-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 "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_client.h"
+#include "tlsv1_client_i.h"
+
+
+static size_t tls_client_cert_chain_der_len(struct tlsv1_client *conn)
+{
+       size_t len = 0;
+       struct x509_certificate *cert;
+
+       if (conn->cred == NULL)
+               return 0;
+
+       cert = conn->cred->cert;
+       while (cert) {
+               len += 3 + cert->cert_len;
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+
+       return len;
+}
+
+
+u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
+{
+       u8 *hello, *end, *pos, *hs_length, *hs_start, *rhdr;
+       struct os_time now;
+       size_t len, i;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ClientHello");
+       *out_len = 0;
+
+       os_get_time(&now);
+       WPA_PUT_BE32(conn->client_random, now.sec);
+       if (os_get_random(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
+               wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+                          "client_random");
+               return NULL;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+                   conn->client_random, TLS_RANDOM_LEN);
+
+       len = 100 + conn->num_cipher_suites * 2 + conn->client_hello_ext_len;
+       hello = os_malloc(len);
+       if (hello == NULL)
+               return NULL;
+       end = hello + len;
+
+       rhdr = hello;
+       pos = rhdr + TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_HELLO;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ClientHello */
+       /* ProtocolVersion client_version */
+       WPA_PUT_BE16(pos, TLS_VERSION);
+       pos += 2;
+       /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+       os_memcpy(pos, conn->client_random, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       /* SessionID session_id */
+       *pos++ = conn->session_id_len;
+       os_memcpy(pos, conn->session_id, conn->session_id_len);
+       pos += conn->session_id_len;
+       /* CipherSuite cipher_suites<2..2^16-1> */
+       WPA_PUT_BE16(pos, 2 * conn->num_cipher_suites);
+       pos += 2;
+       for (i = 0; i < conn->num_cipher_suites; i++) {
+               WPA_PUT_BE16(pos, conn->cipher_suites[i]);
+               pos += 2;
+       }
+       /* CompressionMethod compression_methods<1..2^8-1> */
+       *pos++ = 1;
+       *pos++ = TLS_COMPRESSION_NULL;
+
+       if (conn->client_hello_ext) {
+               os_memcpy(pos, conn->client_hello_ext,
+                         conn->client_hello_ext_len);
+               pos += conn->client_hello_ext_len;
+       }
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, out_len) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(hello);
+               return NULL;
+       }
+
+       conn->state = SERVER_HELLO;
+
+       return hello;
+}
+
+
+static int tls_write_client_certificate(struct tlsv1_client *conn,
+                                       u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+       size_t rlen;
+       struct x509_certificate *cert;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - Certificate */
+       /* uint24 length (to be filled) */
+       cert_start = pos;
+       pos += 3;
+       cert = conn->cred ? conn->cred->cert : NULL;
+       while (cert) {
+               if (pos + 3 + cert->cert_len > end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+                                  "for Certificate (cert_len=%lu left=%lu)",
+                                  (unsigned long) cert->cert_len,
+                                  (unsigned long) (end - pos));
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               WPA_PUT_BE24(pos, cert->cert_len);
+               pos += 3;
+               os_memcpy(pos, cert->cert_start, cert->cert_len);
+               pos += cert->cert_len;
+
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+       if (conn->cred == NULL || cert == conn->cred->cert || cert == NULL) {
+               /*
+                * Client was not configured with all the needed certificates
+                * to form a full certificate chain. The server may fail to
+                * validate the chain unless it is configured with all the
+                * missing CA certificates.
+                */
+               wpa_printf(MSG_DEBUG, "TLSv1: Full client certificate chain "
+                          "not configured - validation may fail");
+       }
+       WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+       /* ClientDiffieHellmanPublic */
+       u8 *csecret, *csecret_start, *dh_yc, *shared;
+       size_t csecret_len, dh_yc_len, shared_len;
+
+       csecret_len = conn->dh_p_len;
+       csecret = os_malloc(csecret_len);
+       if (csecret == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                          "memory for Yc (Diffie-Hellman)");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       if (os_get_random(csecret, csecret_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+                          "data for Diffie-Hellman");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               return -1;
+       }
+
+       if (os_memcmp(csecret, conn->dh_p, csecret_len) > 0)
+               csecret[0] = 0; /* make sure Yc < p */
+
+       csecret_start = csecret;
+       while (csecret_len > 1 && *csecret_start == 0) {
+               csecret_start++;
+               csecret_len--;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH client's secret value",
+                       csecret_start, csecret_len);
+
+       /* Yc = g^csecret mod p */
+       dh_yc_len = conn->dh_p_len;
+       dh_yc = os_malloc(dh_yc_len);
+       if (dh_yc == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                          "memory for Diffie-Hellman");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               return -1;
+       }
+       if (crypto_mod_exp(conn->dh_g, conn->dh_g_len,
+                          csecret_start, csecret_len,
+                          conn->dh_p, conn->dh_p_len,
+                          dh_yc, &dh_yc_len)) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               os_free(dh_yc);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+                   dh_yc, dh_yc_len);
+
+       WPA_PUT_BE16(*pos, dh_yc_len);
+       *pos += 2;
+       if (*pos + dh_yc_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough room in the "
+                          "message buffer for Yc");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               os_free(dh_yc);
+               return -1;
+       }
+       os_memcpy(*pos, dh_yc, dh_yc_len);
+       *pos += dh_yc_len;
+       os_free(dh_yc);
+
+       shared_len = conn->dh_p_len;
+       shared = os_malloc(shared_len);
+       if (shared == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+                          "DH");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               return -1;
+       }
+
+       /* shared = Ys^csecret mod p */
+       if (crypto_mod_exp(conn->dh_ys, conn->dh_ys_len,
+                          csecret_start, csecret_len,
+                          conn->dh_p, conn->dh_p_len,
+                          shared, &shared_len)) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(csecret);
+               os_free(shared);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+                       shared, shared_len);
+
+       os_memset(csecret_start, 0, csecret_len);
+       os_free(csecret);
+       if (tls_derive_keys(conn, shared, shared_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               os_free(shared);
+               return -1;
+       }
+       os_memset(shared, 0, shared_len);
+       os_free(shared);
+       tlsv1_client_free_dh(conn);
+       return 0;
+}
+
+
+static int tlsv1_key_x_rsa(struct tlsv1_client *conn, u8 **pos, u8 *end)
+{
+       u8 pre_master_secret[TLS_PRE_MASTER_SECRET_LEN];
+       size_t clen;
+       int res;
+
+       if (tls_derive_pre_master_secret(pre_master_secret) < 0 ||
+           tls_derive_keys(conn, pre_master_secret,
+                           TLS_PRE_MASTER_SECRET_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /* EncryptedPreMasterSecret */
+       if (conn->server_rsa_key == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No server RSA key to "
+                          "use for encrypting pre-master secret");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /* RSA encrypted value is encoded with PKCS #1 v1.5 block type 2. */
+       *pos += 2;
+       clen = end - *pos;
+       res = crypto_public_key_encrypt_pkcs1_v15(
+               conn->server_rsa_key,
+               pre_master_secret, TLS_PRE_MASTER_SECRET_LEN,
+               *pos, &clen);
+       os_memset(pre_master_secret, 0, TLS_PRE_MASTER_SECRET_LEN);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: RSA encryption failed");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       WPA_PUT_BE16(*pos - 2, clen);
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Encrypted pre_master_secret",
+                   *pos, clen);
+       *pos += clen;
+
+       return 0;
+}
+
+
+static int tls_write_client_key_exchange(struct tlsv1_client *conn,
+                                        u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+       tls_key_exchange keyx;
+       const struct tls_cipher_suite *suite;
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite == NULL)
+               keyx = TLS_KEY_X_NULL;
+       else
+               keyx = suite->key_exchange;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ClientKeyExchange");
+
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ClientKeyExchange */
+       if (keyx == TLS_KEY_X_DH_anon) {
+               if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+                       return -1;
+       } else {
+               if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
+                       return -1;
+       }
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
+                                              u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
+       size_t rlen, hlen, clen;
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+       enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateVerify");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+
+       /*
+        * RFC 2246: 7.4.3 and 7.4.8:
+        * Signature signature
+        *
+        * RSA:
+        * digitally-signed struct {
+        *     opaque md5_hash[16];
+        *     opaque sha_hash[20];
+        * };
+        *
+        * DSA:
+        * digitally-signed struct {
+        *     opaque sha_hash[20];
+        * };
+        *
+        * The hash values are calculated over all handshake messages sent or
+        * received starting at ClientHello up to, but not including, this
+        * CertificateVerify message, including the type and length fields of
+        * the handshake messages.
+        */
+
+       hpos = hash;
+
+       if (alg == SIGN_ALG_RSA) {
+               hlen = MD5_MAC_LEN;
+               if (conn->verify.md5_cert == NULL ||
+                   crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+               {
+                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                 TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.md5_cert = NULL;
+                       crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+                       conn->verify.sha1_cert = NULL;
+                       return -1;
+               }
+               hpos += MD5_MAC_LEN;
+       } else
+               crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+       conn->verify.md5_cert = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_cert == NULL ||
+           crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+               conn->verify.sha1_cert = NULL;
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_cert = NULL;
+
+       if (alg == SIGN_ALG_RSA)
+               hlen += MD5_MAC_LEN;
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+       /*
+        * RFC 2246, 4.7:
+        * In digital signing, one-way hash functions are used as input for a
+        * signing algorithm. A digitally-signed element is encoded as an
+        * opaque vector <0..2^16-1>, where the length is specified by the
+        * signing algorithm and key.
+        *
+        * In RSA signing, a 36-byte structure of two hashes (one SHA and one
+        * MD5) is signed (encrypted with the private key). It is encoded with
+        * PKCS #1 block type 0 or type 1 as described in [PKCS1].
+        */
+       signed_start = pos; /* length to be filled */
+       pos += 2;
+       clen = end - pos;
+       if (conn->cred == NULL ||
+           crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+                                         pos, &clen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       WPA_PUT_BE16(signed_start, clen);
+
+       pos += clen;
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_client_change_cipher_spec(struct tlsv1_client *conn,
+                                              u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr;
+       size_t rlen;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+       *pos = TLS_CHANGE_CIPHER_SPEC;
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+                             rhdr, end - rhdr, 1, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+                          "record layer");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *msgpos = rhdr + rlen;
+
+       return 0;
+}
+
+
+static int tls_write_client_finished(struct tlsv1_client *conn,
+                                    u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen, hlen;
+       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+       /* Encrypted Handshake Message: Finished */
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_client == NULL ||
+           crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_client = NULL;
+               crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+               conn->verify.sha1_client = NULL;
+               return -1;
+       }
+       conn->verify.md5_client = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_client == NULL ||
+           crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_client = NULL;
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_client = NULL;
+
+       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+                   verify_data, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+                       verify_data, TLS_VERIFY_DATA_LEN);
+
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
+       pos += TLS_VERIFY_DATA_LEN;
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       pos = rhdr + rlen;
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static u8 * tls_send_client_key_exchange(struct tlsv1_client *conn,
+                                        size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+       size_t msglen;
+
+       *out_len = 0;
+
+       msglen = 1000;
+       if (conn->certificate_requested)
+               msglen += tls_client_cert_chain_der_len(conn);
+
+       msg = os_malloc(msglen);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + msglen;
+
+       if (conn->certificate_requested) {
+               if (tls_write_client_certificate(conn, &pos, end) < 0) {
+                       os_free(msg);
+                       return NULL;
+               }
+       }
+
+       if (tls_write_client_key_exchange(conn, &pos, end) < 0 ||
+           (conn->certificate_requested && conn->cred && conn->cred->key &&
+            tls_write_client_certificate_verify(conn, &pos, end) < 0) ||
+           tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+           tls_write_client_finished(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       conn->state = SERVER_CHANGE_CIPHER_SPEC;
+
+       return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_client *conn,
+                                       size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+
+       *out_len = 0;
+
+       msg = os_malloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + 1000;
+
+       if (tls_write_client_change_cipher_spec(conn, &pos, end) < 0 ||
+           tls_write_client_finished(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Session resumption completed "
+                  "successfully");
+       conn->state = ESTABLISHED;
+
+       return msg;
+}
+
+
+u8 * tlsv1_client_handshake_write(struct tlsv1_client *conn, size_t *out_len,
+                                 int no_appl_data)
+{
+       switch (conn->state) {
+       case CLIENT_KEY_EXCHANGE:
+               return tls_send_client_key_exchange(conn, out_len);
+       case CHANGE_CIPHER_SPEC:
+               return tls_send_change_cipher_spec(conn, out_len);
+       case ACK_FINISHED:
+               wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed "
+                          "successfully");
+               conn->state = ESTABLISHED;
+               *out_len = 0;
+               if (no_appl_data) {
+                       /* Need to return something to get final TLS ACK. */
+                       return os_malloc(1);
+               }
+               return NULL;
+       default:
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+                          "generating reply", conn->state);
+               return NULL;
+       }
+}
+
+
+u8 * tlsv1_client_send_alert(struct tlsv1_client *conn, u8 level,
+                            u8 description, size_t *out_len)
+{
+       u8 *alert, *pos, *length;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+       *out_len = 0;
+
+       alert = os_malloc(10);
+       if (alert == NULL)
+               return NULL;
+
+       pos = alert;
+
+       /* TLSPlaintext */
+       /* ContentType type */
+       *pos++ = TLS_CONTENT_TYPE_ALERT;
+       /* ProtocolVersion version */
+       WPA_PUT_BE16(pos, TLS_VERSION);
+       pos += 2;
+       /* uint16 length (to be filled) */
+       length = pos;
+       pos += 2;
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Alert */
+       /* AlertLevel level */
+       *pos++ = level;
+       /* AlertDescription description */
+       *pos++ = description;
+
+       WPA_PUT_BE16(length, pos - length - 2);
+       *out_len = pos - alert;
+
+       return alert;
+}
diff --git a/src/tls/tlsv1_common.c b/src/tls/tlsv1_common.c
new file mode 100644 (file)
index 0000000..2f9dd0f
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * TLSv1 common routines
+ * Copyright (c) 2006-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 "x509v3.h"
+#include "tlsv1_common.h"
+
+
+/*
+ * TODO:
+ * RFC 2246 Section 9: Mandatory to implement TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA
+ * Add support for commonly used cipher suites; don't bother with exportable
+ * suites.
+ */ 
+
+static const struct tls_cipher_suite tls_cipher_suites[] = {
+       { TLS_NULL_WITH_NULL_NULL, TLS_KEY_X_NULL, TLS_CIPHER_NULL,
+         TLS_HASH_NULL },
+       { TLS_RSA_WITH_RC4_128_MD5, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+         TLS_HASH_MD5 },
+       { TLS_RSA_WITH_RC4_128_SHA, TLS_KEY_X_RSA, TLS_CIPHER_RC4_128,
+         TLS_HASH_SHA },
+       { TLS_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_DES_CBC,
+         TLS_HASH_SHA },
+       { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
+         TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
+       { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_DES_CBC, TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+       { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
+         TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
+       { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
+         TLS_HASH_SHA },
+       { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
+         TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA }
+};
+
+#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
+#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+
+
+static const struct tls_cipher_data tls_ciphers[] = {
+       { TLS_CIPHER_NULL,         TLS_CIPHER_STREAM,  0,  0,  0,
+         CRYPTO_CIPHER_NULL },
+       { TLS_CIPHER_IDEA_CBC,     TLS_CIPHER_BLOCK,  16, 16,  8,
+         CRYPTO_CIPHER_NULL },
+       { TLS_CIPHER_RC2_CBC_40,   TLS_CIPHER_BLOCK,   5, 16,  0,
+         CRYPTO_CIPHER_ALG_RC2 },
+       { TLS_CIPHER_RC4_40,       TLS_CIPHER_STREAM,  5, 16,  0,
+         CRYPTO_CIPHER_ALG_RC4 },
+       { TLS_CIPHER_RC4_128,      TLS_CIPHER_STREAM, 16, 16,  0,
+         CRYPTO_CIPHER_ALG_RC4 },
+       { TLS_CIPHER_DES40_CBC,    TLS_CIPHER_BLOCK,   5,  8,  8,
+         CRYPTO_CIPHER_ALG_DES },
+       { TLS_CIPHER_DES_CBC,      TLS_CIPHER_BLOCK,   8,  8,  8,
+         CRYPTO_CIPHER_ALG_DES },
+       { TLS_CIPHER_3DES_EDE_CBC, TLS_CIPHER_BLOCK,  24, 24,  8,
+         CRYPTO_CIPHER_ALG_3DES },
+       { TLS_CIPHER_AES_128_CBC,  TLS_CIPHER_BLOCK,  16, 16, 16,
+         CRYPTO_CIPHER_ALG_AES },
+       { TLS_CIPHER_AES_256_CBC,  TLS_CIPHER_BLOCK,  32, 32, 16,
+         CRYPTO_CIPHER_ALG_AES }
+};
+
+#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+
+
+/**
+ * tls_get_cipher_suite - Get TLS cipher suite
+ * @suite: Cipher suite identifier
+ * Returns: Pointer to the cipher data or %NULL if not found
+ */
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite)
+{
+       size_t i;
+       for (i = 0; i < NUM_TLS_CIPHER_SUITES; i++)
+               if (tls_cipher_suites[i].suite == suite)
+                       return &tls_cipher_suites[i];
+       return NULL;
+}
+
+
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher)
+{
+       size_t i;
+       for (i = 0; i < NUM_TLS_CIPHER_DATA; i++)
+               if (tls_ciphers[i].cipher == cipher)
+                       return &tls_ciphers[i];
+       return NULL;
+}
+
+
+int tls_server_key_exchange_allowed(tls_cipher cipher)
+{
+       const struct tls_cipher_suite *suite;
+
+       /* RFC 2246, Section 7.4.3 */
+       suite = tls_get_cipher_suite(cipher);
+       if (suite == NULL)
+               return 0;
+
+       switch (suite->key_exchange) {
+       case TLS_KEY_X_DHE_DSS:
+       case TLS_KEY_X_DHE_DSS_EXPORT:
+       case TLS_KEY_X_DHE_RSA:
+       case TLS_KEY_X_DHE_RSA_EXPORT:
+       case TLS_KEY_X_DH_anon_EXPORT:
+       case TLS_KEY_X_DH_anon:
+               return 1;
+       case TLS_KEY_X_RSA_EXPORT:
+               return 1 /* FIX: public key len > 512 bits */;
+       default:
+               return 0;
+       }
+}
+
+
+/**
+ * tls_parse_cert - Parse DER encoded X.509 certificate and get public key
+ * @buf: ASN.1 DER encoded certificate
+ * @len: Length of the buffer
+ * @pk: Buffer for returning the allocated public key
+ * Returns: 0 on success, -1 on failure
+ *
+ * This functions parses an ASN.1 DER encoded X.509 certificate and retrieves
+ * the public key from it. The caller is responsible for freeing the public key
+ * by calling crypto_public_key_free().
+ */
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk)
+{
+       struct x509_certificate *cert;
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Parse ASN.1 DER certificate",
+                   buf, len);
+
+       *pk = crypto_public_key_from_cert(buf, len);
+       if (*pk)
+               return 0;
+
+       cert = x509_certificate_parse(buf, len);
+       if (cert == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse X.509 "
+                          "certificate");
+               return -1;
+       }
+
+       /* TODO
+        * verify key usage (must allow encryption)
+        *
+        * All certificate profiles, key and cryptographic formats are
+        * defined by the IETF PKIX working group [PKIX]. When a key
+        * usage extension is present, the digitalSignature bit must be
+        * set for the key to be eligible for signing, as described
+        * above, and the keyEncipherment bit must be present to allow
+        * encryption, as described above. The keyAgreement bit must be
+        * set on Diffie-Hellman certificates. (PKIX: RFC 3280)
+        */
+
+       *pk = crypto_public_key_import(cert->public_key, cert->public_key_len);
+       x509_certificate_free(cert);
+
+       if (*pk == NULL) {
+               wpa_printf(MSG_ERROR, "TLSv1: Failed to import "
+                          "server public key");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int tls_verify_hash_init(struct tls_verify_hash *verify)
+{
+       tls_verify_hash_free(verify);
+       verify->md5_client = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       verify->md5_server = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       verify->md5_cert = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       verify->sha1_client = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       verify->sha1_server = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       verify->sha1_cert = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       if (verify->md5_client == NULL || verify->md5_server == NULL ||
+           verify->md5_cert == NULL || verify->sha1_client == NULL ||
+           verify->sha1_server == NULL || verify->sha1_cert == NULL) {
+               tls_verify_hash_free(verify);
+               return -1;
+       }
+       return 0;
+}
+
+
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+                        size_t len)
+{
+       if (verify->md5_client && verify->sha1_client) {
+               crypto_hash_update(verify->md5_client, buf, len);
+               crypto_hash_update(verify->sha1_client, buf, len);
+       }
+       if (verify->md5_server && verify->sha1_server) {
+               crypto_hash_update(verify->md5_server, buf, len);
+               crypto_hash_update(verify->sha1_server, buf, len);
+       }
+       if (verify->md5_cert && verify->sha1_cert) {
+               crypto_hash_update(verify->md5_cert, buf, len);
+               crypto_hash_update(verify->sha1_cert, buf, len);
+       }
+}
+
+
+void tls_verify_hash_free(struct tls_verify_hash *verify)
+{
+       crypto_hash_finish(verify->md5_client, NULL, NULL);
+       crypto_hash_finish(verify->md5_server, NULL, NULL);
+       crypto_hash_finish(verify->md5_cert, NULL, NULL);
+       crypto_hash_finish(verify->sha1_client, NULL, NULL);
+       crypto_hash_finish(verify->sha1_server, NULL, NULL);
+       crypto_hash_finish(verify->sha1_cert, NULL, NULL);
+       verify->md5_client = NULL;
+       verify->md5_server = NULL;
+       verify->md5_cert = NULL;
+       verify->sha1_client = NULL;
+       verify->sha1_server = NULL;
+       verify->sha1_cert = NULL;
+}
diff --git a/src/tls/tlsv1_common.h b/src/tls/tlsv1_common.h
new file mode 100644 (file)
index 0000000..763a4af
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * TLSv1 common definitions
+ * Copyright (c) 2006-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 TLSV1_COMMON_H
+#define TLSV1_COMMON_H
+
+#include "crypto/crypto.h"
+
+#define TLS_VERSION 0x0301 /* TLSv1 */
+#define TLS_RANDOM_LEN 32
+#define TLS_PRE_MASTER_SECRET_LEN 48
+#define TLS_MASTER_SECRET_LEN 48
+#define TLS_SESSION_ID_MAX_LEN 32
+#define TLS_VERIFY_DATA_LEN 12
+
+/* HandshakeType */
+enum {
+       TLS_HANDSHAKE_TYPE_HELLO_REQUEST = 0,
+       TLS_HANDSHAKE_TYPE_CLIENT_HELLO = 1,
+       TLS_HANDSHAKE_TYPE_SERVER_HELLO = 2,
+       TLS_HANDSHAKE_TYPE_NEW_SESSION_TICKET = 4 /* RFC 4507 */,
+       TLS_HANDSHAKE_TYPE_CERTIFICATE = 11,
+       TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE = 12,
+       TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST = 13,
+       TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE = 14,
+       TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY = 15,
+       TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE = 16,
+       TLS_HANDSHAKE_TYPE_FINISHED = 20,
+       TLS_HANDSHAKE_TYPE_CERTIFICATE_URL = 21 /* RFC 4366 */,
+       TLS_HANDSHAKE_TYPE_CERTIFICATE_STATUS = 22 /* RFC 4366 */
+};
+
+/* CipherSuite */
+#define TLS_NULL_WITH_NULL_NULL                        0x0000 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_MD5                  0x0001 /* RFC 2246 */
+#define TLS_RSA_WITH_NULL_SHA                  0x0002 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC4_40_MD5         0x0003 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_MD5               0x0004 /* RFC 2246 */
+#define TLS_RSA_WITH_RC4_128_SHA               0x0005 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5     0x0006 /* RFC 2246 */
+#define TLS_RSA_WITH_IDEA_CBC_SHA              0x0007 /* RFC 2246 */
+#define TLS_RSA_EXPORT_WITH_DES40_CBC_SHA      0x0008 /* RFC 2246 */
+#define TLS_RSA_WITH_DES_CBC_SHA               0x0009 /* RFC 2246 */
+#define TLS_RSA_WITH_3DES_EDE_CBC_SHA          0x000A /* RFC 2246 */
+#define TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA   0x000B /* RFC 2246 */
+#define TLS_DH_DSS_WITH_DES_CBC_SHA            0x000C /* RFC 2246 */
+#define TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA       0x000D /* RFC 2246 */
+#define TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA   0x000E /* RFC 2246 */
+#define TLS_DH_RSA_WITH_DES_CBC_SHA            0x000F /* RFC 2246 */
+#define TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA       0x0010 /* RFC 2246 */
+#define TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA  0x0011 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_DES_CBC_SHA           0x0012 /* RFC 2246 */
+#define TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA      0x0013 /* RFC 2246 */
+#define TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA  0x0014 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_DES_CBC_SHA           0x0015 /* RFC 2246 */
+#define TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA      0x0016 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_RC4_40_MD5     0x0017 /* RFC 2246 */
+#define TLS_DH_anon_WITH_RC4_128_MD5           0x0018 /* RFC 2246 */
+#define TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA  0x0019 /* RFC 2246 */
+#define TLS_DH_anon_WITH_DES_CBC_SHA           0x001A /* RFC 2246 */
+#define TLS_DH_anon_WITH_3DES_EDE_CBC_SHA      0x001B /* RFC 2246 */
+#define TLS_RSA_WITH_AES_128_CBC_SHA           0x002F /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_128_CBC_SHA                0x0030 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_128_CBC_SHA                0x0031 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_128_CBC_SHA       0x0032 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_128_CBC_SHA       0x0033 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_128_CBC_SHA       0x0034 /* RFC 3268 */
+#define TLS_RSA_WITH_AES_256_CBC_SHA           0x0035 /* RFC 3268 */
+#define TLS_DH_DSS_WITH_AES_256_CBC_SHA                0x0036 /* RFC 3268 */
+#define TLS_DH_RSA_WITH_AES_256_CBC_SHA                0x0037 /* RFC 3268 */
+#define TLS_DHE_DSS_WITH_AES_256_CBC_SHA       0x0038 /* RFC 3268 */
+#define TLS_DHE_RSA_WITH_AES_256_CBC_SHA       0x0039 /* RFC 3268 */
+#define TLS_DH_anon_WITH_AES_256_CBC_SHA       0x003A /* RFC 3268 */
+
+/* CompressionMethod */
+#define TLS_COMPRESSION_NULL 0
+
+/* AlertLevel */
+#define TLS_ALERT_LEVEL_WARNING 1
+#define TLS_ALERT_LEVEL_FATAL 2
+
+/* AlertDescription */
+#define TLS_ALERT_CLOSE_NOTIFY                 0
+#define TLS_ALERT_UNEXPECTED_MESSAGE           10
+#define TLS_ALERT_BAD_RECORD_MAC               20
+#define TLS_ALERT_DECRYPTION_FAILED            21
+#define TLS_ALERT_RECORD_OVERFLOW              22
+#define TLS_ALERT_DECOMPRESSION_FAILURE                30
+#define TLS_ALERT_HANDSHAKE_FAILURE            40
+#define TLS_ALERT_BAD_CERTIFICATE              42
+#define TLS_ALERT_UNSUPPORTED_CERTIFICATE      43
+#define TLS_ALERT_CERTIFICATE_REVOKED          44
+#define TLS_ALERT_CERTIFICATE_EXPIRED          45
+#define TLS_ALERT_CERTIFICATE_UNKNOWN          46
+#define TLS_ALERT_ILLEGAL_PARAMETER            47
+#define TLS_ALERT_UNKNOWN_CA                   48
+#define TLS_ALERT_ACCESS_DENIED                        49
+#define TLS_ALERT_DECODE_ERROR                 50
+#define TLS_ALERT_DECRYPT_ERROR                        51
+#define TLS_ALERT_EXPORT_RESTRICTION           60
+#define TLS_ALERT_PROTOCOL_VERSION             70
+#define TLS_ALERT_INSUFFICIENT_SECURITY                71
+#define TLS_ALERT_INTERNAL_ERROR               80
+#define TLS_ALERT_USER_CANCELED                        90
+#define TLS_ALERT_NO_RENEGOTIATION             100
+#define TLS_ALERT_UNSUPPORTED_EXTENSION                110 /* RFC 4366 */
+#define TLS_ALERT_CERTIFICATE_UNOBTAINABLE     111 /* RFC 4366 */
+#define TLS_ALERT_UNRECOGNIZED_NAME            112 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_STATUS_RESPONSE      113 /* RFC 4366 */
+#define TLS_ALERT_BAD_CERTIFICATE_HASH_VALUE   114 /* RFC 4366 */
+
+/* ChangeCipherSpec */
+enum {
+       TLS_CHANGE_CIPHER_SPEC = 1
+};
+
+/* TLS Extensions */
+#define TLS_EXT_SERVER_NAME                    0 /* RFC 4366 */
+#define TLS_EXT_MAX_FRAGMENT_LENGTH            1 /* RFC 4366 */
+#define TLS_EXT_CLIENT_CERTIFICATE_URL         2 /* RFC 4366 */
+#define TLS_EXT_TRUSTED_CA_KEYS                        3 /* RFC 4366 */
+#define TLS_EXT_TRUNCATED_HMAC                 4 /* RFC 4366 */
+#define TLS_EXT_STATUS_REQUEST                 5 /* RFC 4366 */
+#define TLS_EXT_SESSION_TICKET                 35 /* RFC 4507 */
+
+#define TLS_EXT_PAC_OPAQUE TLS_EXT_SESSION_TICKET /* EAP-FAST terminology */
+
+
+typedef enum {
+       TLS_KEY_X_NULL,
+       TLS_KEY_X_RSA,
+       TLS_KEY_X_RSA_EXPORT,
+       TLS_KEY_X_DH_DSS_EXPORT,
+       TLS_KEY_X_DH_DSS,
+       TLS_KEY_X_DH_RSA_EXPORT,
+       TLS_KEY_X_DH_RSA,
+       TLS_KEY_X_DHE_DSS_EXPORT,
+       TLS_KEY_X_DHE_DSS,
+       TLS_KEY_X_DHE_RSA_EXPORT,
+       TLS_KEY_X_DHE_RSA,
+       TLS_KEY_X_DH_anon_EXPORT,
+       TLS_KEY_X_DH_anon
+} tls_key_exchange;
+
+typedef enum {
+       TLS_CIPHER_NULL,
+       TLS_CIPHER_RC4_40,
+       TLS_CIPHER_RC4_128,
+       TLS_CIPHER_RC2_CBC_40,
+       TLS_CIPHER_IDEA_CBC,
+       TLS_CIPHER_DES40_CBC,
+       TLS_CIPHER_DES_CBC,
+       TLS_CIPHER_3DES_EDE_CBC,
+       TLS_CIPHER_AES_128_CBC,
+       TLS_CIPHER_AES_256_CBC
+} tls_cipher;
+
+typedef enum {
+       TLS_HASH_NULL,
+       TLS_HASH_MD5,
+       TLS_HASH_SHA
+} tls_hash;
+
+struct tls_cipher_suite {
+       u16 suite;
+       tls_key_exchange key_exchange;
+       tls_cipher cipher;
+       tls_hash hash;
+};
+
+typedef enum {
+       TLS_CIPHER_STREAM,
+       TLS_CIPHER_BLOCK
+} tls_cipher_type;
+
+struct tls_cipher_data {
+       tls_cipher cipher;
+       tls_cipher_type type;
+       size_t key_material;
+       size_t expanded_key_material;
+       size_t block_size; /* also iv_size */
+       enum crypto_cipher_alg alg;
+};
+
+
+struct tls_verify_hash {
+       struct crypto_hash *md5_client;
+       struct crypto_hash *sha1_client;
+       struct crypto_hash *md5_server;
+       struct crypto_hash *sha1_server;
+       struct crypto_hash *md5_cert;
+       struct crypto_hash *sha1_cert;
+};
+
+
+const struct tls_cipher_suite * tls_get_cipher_suite(u16 suite);
+const struct tls_cipher_data * tls_get_cipher_data(tls_cipher cipher);
+int tls_server_key_exchange_allowed(tls_cipher cipher);
+int tls_parse_cert(const u8 *buf, size_t len, struct crypto_public_key **pk);
+int tls_verify_hash_init(struct tls_verify_hash *verify);
+void tls_verify_hash_add(struct tls_verify_hash *verify, const u8 *buf,
+                        size_t len);
+void tls_verify_hash_free(struct tls_verify_hash *verify);
+
+#endif /* TLSV1_COMMON_H */
diff --git a/src/tls/tlsv1_cred.c b/src/tls/tlsv1_cred.c
new file mode 100644 (file)
index 0000000..aa467ef
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-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 "common.h"
+#include "base64.h"
+#include "crypto/crypto.h"
+#include "x509v3.h"
+#include "tlsv1_cred.h"
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void)
+{
+       struct tlsv1_credentials *cred;
+       cred = os_zalloc(sizeof(*cred));
+       return cred;
+}
+
+
+void tlsv1_cred_free(struct tlsv1_credentials *cred)
+{
+       if (cred == NULL)
+               return;
+
+       x509_certificate_chain_free(cred->trusted_certs);
+       x509_certificate_chain_free(cred->cert);
+       crypto_private_key_free(cred->key);
+       os_free(cred->dh_p);
+       os_free(cred->dh_g);
+       os_free(cred);
+}
+
+
+static int tlsv1_add_cert_der(struct x509_certificate **chain,
+                             const u8 *buf, size_t len)
+{
+       struct x509_certificate *cert;
+       char name[128];
+
+       cert = x509_certificate_parse(buf, len);
+       if (cert == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: %s - failed to parse certificate",
+                          __func__);
+               return -1;
+       }
+
+       cert->next = *chain;
+       *chain = cert;
+
+       x509_name_string(&cert->subject, name, sizeof(name));
+       wpa_printf(MSG_DEBUG, "TLSv1: Added certificate: %s", name);
+
+       return 0;
+}
+
+
+static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----";
+static const char *pem_cert_end = "-----END CERTIFICATE-----";
+static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----";
+static const char *pem_key_end = "-----END RSA PRIVATE KEY-----";
+static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----";
+static const char *pem_key2_end = "-----END PRIVATE KEY-----";
+static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----";
+static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----";
+
+
+static const u8 * search_tag(const char *tag, const u8 *buf, size_t len)
+{
+       size_t i, plen;
+
+       plen = os_strlen(tag);
+       if (len < plen)
+               return NULL;
+
+       for (i = 0; i < len - plen; i++) {
+               if (os_memcmp(buf + i, tag, plen) == 0)
+                       return buf + i;
+       }
+
+       return NULL;
+}
+
+
+static int tlsv1_add_cert(struct x509_certificate **chain,
+                         const u8 *buf, size_t len)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+
+       pos = search_tag(pem_cert_begin, buf, len);
+       if (!pos) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No PEM certificate tag found - "
+                          "assume DER format");
+               return tlsv1_add_cert_der(chain, buf, len);
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format certificate into "
+                  "DER format");
+
+       while (pos) {
+               pos += os_strlen(pem_cert_begin);
+               end = search_tag(pem_cert_end, pos, buf + len - pos);
+               if (end == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Could not find PEM "
+                                  "certificate end tag (%s)", pem_cert_end);
+                       return -1;
+               }
+
+               der = base64_decode(pos, end - pos, &der_len);
+               if (der == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM "
+                                  "certificate");
+                       return -1;
+               }
+
+               if (tlsv1_add_cert_der(chain, der, der_len) < 0) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM "
+                                  "certificate after DER conversion");
+                       os_free(der);
+                       return -1;
+               }
+
+               os_free(der);
+
+               end += os_strlen(pem_cert_end);
+               pos = search_tag(pem_cert_begin, end, buf + len - end);
+       }
+
+       return 0;
+}
+
+
+static int tlsv1_set_cert_chain(struct x509_certificate **chain,
+                               const char *cert, const u8 *cert_blob,
+                               size_t cert_blob_len)
+{
+       if (cert_blob)
+               return tlsv1_add_cert(chain, cert_blob, cert_blob_len);
+
+       if (cert) {
+               u8 *buf;
+               size_t len;
+               int ret;
+
+               buf = (u8 *) os_readfile(cert, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+                                  cert);
+                       return -1;
+               }
+
+               ret = tlsv1_add_cert(chain, buf, len);
+               os_free(buf);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_ca_cert - Set trusted CA certificate(s)
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: ca_cert_blob length
+ * @path: Path to CA certificates (not yet supported)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+                     const u8 *cert_blob, size_t cert_blob_len,
+                     const char *path)
+{
+       if (tlsv1_set_cert_chain(&cred->trusted_certs, cert,
+                                cert_blob, cert_blob_len) < 0)
+               return -1;
+
+       if (path) {
+               /* TODO: add support for reading number of certificate files */
+               wpa_printf(MSG_INFO, "TLSv1: Use of CA certificate directory "
+                          "not yet supported");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_cert - Set certificate
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @cert: File or reference name for X.509 certificate in PEM or DER format
+ * @cert_blob: cert as inlined data or %NULL if not used
+ * @cert_blob_len: cert_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+                  const u8 *cert_blob, size_t cert_blob_len)
+{
+       return tlsv1_set_cert_chain(&cred->cert, cert,
+                                   cert_blob, cert_blob_len);
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+       struct crypto_private_key *pkey;
+
+       pos = search_tag(pem_key_begin, key, len);
+       if (!pos) {
+               pos = search_tag(pem_key2_begin, key, len);
+               if (!pos)
+                       return NULL;
+               pos += os_strlen(pem_key2_begin);
+               end = search_tag(pem_key2_end, pos, key + len - pos);
+               if (!end)
+                       return NULL;
+       } else {
+               pos += os_strlen(pem_key_begin);
+               end = search_tag(pem_key_end, pos, key + len - pos);
+               if (!end)
+                       return NULL;
+       }
+
+       der = base64_decode(pos, end - pos, &der_len);
+       if (!der)
+               return NULL;
+       pkey = crypto_private_key_import(der, der_len, NULL);
+       os_free(der);
+       return pkey;
+}
+
+
+static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key,
+                                                        size_t len,
+                                                        const char *passwd)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+       struct crypto_private_key *pkey;
+
+       if (passwd == NULL)
+               return NULL;
+       pos = search_tag(pem_key_enc_begin, key, len);
+       if (!pos)
+               return NULL;
+       pos += os_strlen(pem_key_enc_begin);
+       end = search_tag(pem_key_enc_end, pos, key + len - pos);
+       if (!end)
+               return NULL;
+
+       der = base64_decode(pos, end - pos, &der_len);
+       if (!der)
+               return NULL;
+       pkey = crypto_private_key_import(der, der_len, passwd);
+       os_free(der);
+       return pkey;
+}
+
+
+static int tlsv1_set_key(struct tlsv1_credentials *cred,
+                        const u8 *key, size_t len, const char *passwd)
+{
+       cred->key = crypto_private_key_import(key, len, passwd);
+       if (cred->key == NULL)
+               cred->key = tlsv1_set_key_pem(key, len);
+       if (cred->key == NULL)
+               cred->key = tlsv1_set_key_enc_pem(key, len, passwd);
+       if (cred->key == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key");
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_private_key - Set private key
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @private_key: File or reference name for the key in PEM or DER format
+ * @private_key_passwd: Passphrase for decrypted private key, %NULL if no
+ * passphrase is used.
+ * @private_key_blob: private_key as inlined data or %NULL if not used
+ * @private_key_blob_len: private_key_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+                         const char *private_key,
+                         const char *private_key_passwd,
+                         const u8 *private_key_blob,
+                         size_t private_key_blob_len)
+{
+       crypto_private_key_free(cred->key);
+       cred->key = NULL;
+
+       if (private_key_blob)
+               return tlsv1_set_key(cred, private_key_blob,
+                                    private_key_blob_len,
+                                    private_key_passwd);
+
+       if (private_key) {
+               u8 *buf;
+               size_t len;
+               int ret;
+
+               buf = (u8 *) os_readfile(private_key, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+                                  private_key);
+                       return -1;
+               }
+
+               ret = tlsv1_set_key(cred, buf, len, private_key_passwd);
+               os_free(buf);
+               return ret;
+       }
+
+       return 0;
+}
+
+
+static int tlsv1_set_dhparams_der(struct tlsv1_credentials *cred,
+                                 const u8 *dh, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       pos = dh;
+       end = dh + len;
+
+       /*
+        * DHParameter ::= SEQUENCE {
+        *   prime INTEGER, -- p
+        *   base INTEGER, -- g
+        *   privateValueLength INTEGER OPTIONAL }
+        */
+
+       /* DHParamer ::= SEQUENCE */
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "DH: DH parameters did not start with a "
+                          "valid SEQUENCE - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+
+       /* prime INTEGER */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0)
+               return -1;
+
+       if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for p; "
+                          "class=%d tag=0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "DH: prime (p)", hdr.payload, hdr.length);
+       if (hdr.length == 0)
+               return -1;
+       os_free(cred->dh_p);
+       cred->dh_p = os_malloc(hdr.length);
+       if (cred->dh_p == NULL)
+               return -1;
+       os_memcpy(cred->dh_p, hdr.payload, hdr.length);
+       cred->dh_p_len = hdr.length;
+       pos = hdr.payload + hdr.length;
+
+       /* base INTEGER */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0)
+               return -1;
+
+       if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "DH: No INTEGER tag found for g; "
+                          "class=%d tag=0x%x", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "DH: base (g)", hdr.payload, hdr.length);
+       if (hdr.length == 0)
+               return -1;
+       os_free(cred->dh_g);
+       cred->dh_g = os_malloc(hdr.length);
+       if (cred->dh_g == NULL)
+               return -1;
+       os_memcpy(cred->dh_g, hdr.payload, hdr.length);
+       cred->dh_g_len = hdr.length;
+
+       return 0;
+}
+
+
+static const char *pem_dhparams_begin = "-----BEGIN DH PARAMETERS-----";
+static const char *pem_dhparams_end = "-----END DH PARAMETERS-----";
+
+
+static int tlsv1_set_dhparams_blob(struct tlsv1_credentials *cred,
+                                  const u8 *buf, size_t len)
+{
+       const u8 *pos, *end;
+       unsigned char *der;
+       size_t der_len;
+
+       pos = search_tag(pem_dhparams_begin, buf, len);
+       if (!pos) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No PEM dhparams tag found - "
+                          "assume DER format");
+               return tlsv1_set_dhparams_der(cred, buf, len);
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Converting PEM format dhparams into DER "
+                  "format");
+
+       pos += os_strlen(pem_dhparams_begin);
+       end = search_tag(pem_dhparams_end, pos, buf + len - pos);
+       if (end == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Could not find PEM dhparams end "
+                          "tag (%s)", pem_dhparams_end);
+               return -1;
+       }
+
+       der = base64_decode(pos, end - pos, &der_len);
+       if (der == NULL) {
+               wpa_printf(MSG_INFO, "TLSv1: Could not decode PEM dhparams");
+               return -1;
+       }
+
+       if (tlsv1_set_dhparams_der(cred, der, der_len) < 0) {
+               wpa_printf(MSG_INFO, "TLSv1: Failed to parse PEM dhparams "
+                          "DER conversion");
+               os_free(der);
+               return -1;
+       }
+
+       os_free(der);
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_set_dhparams - Set Diffie-Hellman parameters
+ * @cred: TLSv1 credentials from tlsv1_cred_alloc()
+ * @dh_file: File or reference name for the DH params in PEM or DER format
+ * @dh_blob: DH params as inlined data or %NULL if not used
+ * @dh_blob_len: dh_blob length
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+                      const u8 *dh_blob, size_t dh_blob_len)
+{
+       if (dh_blob)
+               return tlsv1_set_dhparams_blob(cred, dh_blob, dh_blob_len);
+
+       if (dh_file) {
+               u8 *buf;
+               size_t len;
+               int ret;
+
+               buf = (u8 *) os_readfile(dh_file, &len);
+               if (buf == NULL) {
+                       wpa_printf(MSG_INFO, "TLSv1: Failed to read '%s'",
+                                  dh_file);
+                       return -1;
+               }
+
+               ret = tlsv1_set_dhparams_blob(cred, buf, len);
+               os_free(buf);
+               return ret;
+       }
+
+       return 0;
+}
diff --git a/src/tls/tlsv1_cred.h b/src/tls/tlsv1_cred.h
new file mode 100644 (file)
index 0000000..8425fe4
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * TLSv1 credentials
+ * Copyright (c) 2006-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 TLSV1_CRED_H
+#define TLSV1_CRED_H
+
+struct tlsv1_credentials {
+       struct x509_certificate *trusted_certs;
+       struct x509_certificate *cert;
+       struct crypto_private_key *key;
+
+       /* Diffie-Hellman parameters */
+       u8 *dh_p; /* prime */
+       size_t dh_p_len;
+       u8 *dh_g; /* generator */
+       size_t dh_g_len;
+};
+
+
+struct tlsv1_credentials * tlsv1_cred_alloc(void);
+void tlsv1_cred_free(struct tlsv1_credentials *cred);
+int tlsv1_set_ca_cert(struct tlsv1_credentials *cred, const char *cert,
+                     const u8 *cert_blob, size_t cert_blob_len,
+                     const char *path);
+int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert,
+                  const u8 *cert_blob, size_t cert_blob_len);
+int tlsv1_set_private_key(struct tlsv1_credentials *cred,
+                         const char *private_key,
+                         const char *private_key_passwd,
+                         const u8 *private_key_blob,
+                         size_t private_key_blob_len);
+int tlsv1_set_dhparams(struct tlsv1_credentials *cred, const char *dh_file,
+                      const u8 *dh_blob, size_t dh_blob_len);
+
+#endif /* TLSV1_CRED_H */
diff --git a/src/tls/tlsv1_record.c b/src/tls/tlsv1_record.c
new file mode 100644 (file)
index 0000000..e811f0e
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-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 "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+
+
+/**
+ * tlsv1_record_set_cipher_suite - TLS record layer: Set cipher suite
+ * @rl: Pointer to TLS record layer data
+ * @cipher_suite: New cipher suite
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to prepare TLS record layer for cipher suite change.
+ * tlsv1_record_change_write_cipher() and
+ * tlsv1_record_change_read_cipher() functions can then be used to change the
+ * currently used ciphers.
+ */
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+                                 u16 cipher_suite)
+{
+       const struct tls_cipher_suite *suite;
+       const struct tls_cipher_data *data;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Selected cipher suite: 0x%04x",
+                  cipher_suite);
+       rl->cipher_suite = cipher_suite;
+
+       suite = tls_get_cipher_suite(cipher_suite);
+       if (suite == NULL)
+               return -1;
+
+       if (suite->hash == TLS_HASH_MD5) {
+               rl->hash_alg = CRYPTO_HASH_ALG_HMAC_MD5;
+               rl->hash_size = MD5_MAC_LEN;
+       } else if (suite->hash == TLS_HASH_SHA) {
+               rl->hash_alg = CRYPTO_HASH_ALG_HMAC_SHA1;
+               rl->hash_size = SHA1_MAC_LEN;
+       }
+
+       data = tls_get_cipher_data(suite->cipher);
+       if (data == NULL)
+               return -1;
+
+       rl->key_material_len = data->key_material;
+       rl->iv_size = data->block_size;
+       rl->cipher_alg = data->alg;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_change_write_cipher - TLS record layer: Change write cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for writing.
+ */
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New write cipher suite "
+                  "0x%04x", rl->cipher_suite);
+       rl->write_cipher_suite = rl->cipher_suite;
+       os_memset(rl->write_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+       if (rl->write_cbc) {
+               crypto_cipher_deinit(rl->write_cbc);
+               rl->write_cbc = NULL;
+       }
+       if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+               rl->write_cbc = crypto_cipher_init(rl->cipher_alg,
+                                                  rl->write_iv, rl->write_key,
+                                                  rl->key_material_len);
+               if (rl->write_cbc == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+                                  "cipher");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_change_read_cipher - TLS record layer: Change read cipher
+ * @rl: Pointer to TLS record layer data
+ * Returns: 0 on success (cipher changed), -1 on failure
+ *
+ * This function changes TLS record layer to use the new cipher suite
+ * configured with tlsv1_record_set_cipher_suite() for reading.
+ */
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - New read cipher suite "
+                  "0x%04x", rl->cipher_suite);
+       rl->read_cipher_suite = rl->cipher_suite;
+       os_memset(rl->read_seq_num, 0, TLS_SEQ_NUM_LEN);
+
+       if (rl->read_cbc) {
+               crypto_cipher_deinit(rl->read_cbc);
+               rl->read_cbc = NULL;
+       }
+       if (rl->cipher_alg != CRYPTO_CIPHER_NULL) {
+               rl->read_cbc = crypto_cipher_init(rl->cipher_alg,
+                                                 rl->read_iv, rl->read_key,
+                                                 rl->key_material_len);
+               if (rl->read_cbc == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize "
+                                  "cipher");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_send - TLS record layer: Send a message
+ * @rl: Pointer to TLS record layer data
+ * @content_type: Content type (TLS_CONTENT_TYPE_*)
+ * @buf: Buffer to send (with TLS_RECORD_HEADER_LEN octets reserved in the
+ * beginning for record layer to fill in; payload filled in after this and
+ * extra space in the end for HMAC).
+ * @buf_size: Maximum buf size
+ * @payload_len: Length of the payload
+ * @out_len: Buffer for returning the used buf length
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function fills in the TLS record layer header, adds HMAC, and encrypts
+ * the data using the current write cipher.
+ */
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+                     size_t buf_size, size_t payload_len, size_t *out_len)
+{
+       u8 *pos, *ct_start, *length, *payload;
+       struct crypto_hash *hmac;
+       size_t clen;
+
+       pos = buf;
+       /* ContentType type */
+       ct_start = pos;
+       *pos++ = content_type;
+       /* ProtocolVersion version */
+       WPA_PUT_BE16(pos, TLS_VERSION);
+       pos += 2;
+       /* uint16 length */
+       length = pos;
+       WPA_PUT_BE16(length, payload_len);
+       pos += 2;
+
+       /* opaque fragment[TLSPlaintext.length] */
+       payload = pos;
+       pos += payload_len;
+
+       if (rl->write_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+               hmac = crypto_hash_init(rl->hash_alg, rl->write_mac_secret,
+                                       rl->hash_size);
+               if (hmac == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+                                  "to initialize HMAC");
+                       return -1;
+               }
+               crypto_hash_update(hmac, rl->write_seq_num, TLS_SEQ_NUM_LEN);
+               /* type + version + length + fragment */
+               crypto_hash_update(hmac, ct_start, pos - ct_start);
+               clen = buf + buf_size - pos;
+               if (clen < rl->hash_size) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Not "
+                                  "enough room for MAC");
+                       crypto_hash_finish(hmac, NULL, NULL);
+                       return -1;
+               }
+
+               if (crypto_hash_finish(hmac, pos, &clen) < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+                                  "to calculate HMAC");
+                       return -1;
+               }
+               wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Write HMAC",
+                           pos, clen);
+               pos += clen;
+               if (rl->iv_size) {
+                       size_t len = pos - payload;
+                       size_t pad;
+                       pad = (len + 1) % rl->iv_size;
+                       if (pad)
+                               pad = rl->iv_size - pad;
+                       if (pos + pad + 1 > buf + buf_size) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: No room for "
+                                          "block cipher padding");
+                               return -1;
+                       }
+                       os_memset(pos, pad, pad + 1);
+                       pos += pad + 1;
+               }
+
+               if (crypto_cipher_encrypt(rl->write_cbc, payload,
+                                         payload, pos - payload) < 0)
+                       return -1;
+       }
+
+       WPA_PUT_BE16(length, pos - length - 2);
+       inc_byte_array(rl->write_seq_num, TLS_SEQ_NUM_LEN);
+
+       *out_len = pos - buf;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_record_receive - TLS record layer: Process a received message
+ * @rl: Pointer to TLS record layer data
+ * @in_data: Received data
+ * @in_len: Length of the received data
+ * @out_data: Buffer for output data (must be at least as long as in_data)
+ * @out_len: Set to maximum out_data length by caller; used to return the
+ * length of the used data
+ * @alert: Buffer for returning an alert value on failure
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function decrypts the received message, verifies HMAC and TLS record
+ * layer header.
+ */
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t *out_len, u8 *alert)
+{
+       size_t i, rlen, hlen;
+       u8 padlen;
+       struct crypto_hash *hmac;
+       u8 len[2], hash[100];
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Record Layer - Received",
+                   in_data, in_len);
+
+       if (in_len < TLS_RECORD_HEADER_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (in_len=%lu)",
+                          (unsigned long) in_len);
+               *alert = TLS_ALERT_DECODE_ERROR;
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received content type %d version %d.%d "
+                  "length %d", in_data[0], in_data[1], in_data[2],
+                  WPA_GET_BE16(in_data + 3));
+
+       if (in_data[0] != TLS_CONTENT_TYPE_HANDSHAKE &&
+           in_data[0] != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC &&
+           in_data[0] != TLS_CONTENT_TYPE_ALERT &&
+           in_data[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type 0x%x",
+                          in_data[0]);
+               *alert = TLS_ALERT_UNEXPECTED_MESSAGE;
+               return -1;
+       }
+
+       if (WPA_GET_BE16(in_data + 1) != TLS_VERSION) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version "
+                          "%d.%d", in_data[1], in_data[2]);
+               *alert = TLS_ALERT_PROTOCOL_VERSION;
+               return -1;
+       }
+
+       rlen = WPA_GET_BE16(in_data + 3);
+
+       /* TLSCiphertext must not be more than 2^14+2048 bytes */
+       if (TLS_RECORD_HEADER_LEN + rlen > 18432) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+                          (unsigned long) (TLS_RECORD_HEADER_LEN + rlen));
+               *alert = TLS_ALERT_RECORD_OVERFLOW;
+               return -1;
+       }
+
+       in_data += TLS_RECORD_HEADER_LEN;
+       in_len -= TLS_RECORD_HEADER_LEN;
+
+       if (rlen > in_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not all record data included "
+                          "(rlen=%lu > in_len=%lu)",
+                          (unsigned long) rlen, (unsigned long) in_len);
+               *alert = TLS_ALERT_DECODE_ERROR;
+               return -1;
+       }
+
+       in_len = rlen;
+
+       if (*out_len < in_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough output buffer for "
+                          "processing received record");
+               *alert = TLS_ALERT_INTERNAL_ERROR;
+               return -1;
+       }
+
+       os_memcpy(out_data, in_data, in_len);
+       *out_len = in_len;
+
+       if (rl->read_cipher_suite != TLS_NULL_WITH_NULL_NULL) {
+               if (crypto_cipher_decrypt(rl->read_cbc, out_data,
+                                         out_data, in_len) < 0) {
+                       *alert = TLS_ALERT_DECRYPTION_FAILED;
+                       return -1;
+               }
+               if (rl->iv_size) {
+                       if (in_len == 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Too short record"
+                                          " (no pad)");
+                               *alert = TLS_ALERT_DECODE_ERROR;
+                               return -1;
+                       }
+                       padlen = out_data[in_len - 1];
+                       if (padlen >= in_len) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Incorrect pad "
+                                          "length (%u, in_len=%lu) in "
+                                          "received record",
+                                          padlen, (unsigned long) in_len);
+                               *alert = TLS_ALERT_DECRYPTION_FAILED;
+                               return -1;
+                       }
+                       for (i = in_len - padlen; i < in_len; i++) {
+                               if (out_data[i] != padlen) {
+                                       wpa_hexdump(MSG_DEBUG,
+                                                   "TLSv1: Invalid pad in "
+                                                   "received record",
+                                                   out_data + in_len - padlen,
+                                                   padlen);
+                                       *alert = TLS_ALERT_DECRYPTION_FAILED;
+                                       return -1;
+                               }
+                       }
+
+                       *out_len -= padlen + 1;
+               }
+
+               wpa_hexdump(MSG_MSGDUMP,
+                           "TLSv1: Record Layer - Decrypted data",
+                           out_data, in_len);
+
+               if (*out_len < rl->hash_size) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Too short record; no "
+                                  "hash value");
+                       *alert = TLS_ALERT_INTERNAL_ERROR;
+                       return -1;
+               }
+
+               *out_len -= rl->hash_size;
+
+               hmac = crypto_hash_init(rl->hash_alg, rl->read_mac_secret,
+                                       rl->hash_size);
+               if (hmac == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+                                  "to initialize HMAC");
+                       *alert = TLS_ALERT_INTERNAL_ERROR;
+                       return -1;
+               }
+
+               crypto_hash_update(hmac, rl->read_seq_num, TLS_SEQ_NUM_LEN);
+               /* type + version + length + fragment */
+               crypto_hash_update(hmac, in_data - TLS_RECORD_HEADER_LEN, 3);
+               WPA_PUT_BE16(len, *out_len);
+               crypto_hash_update(hmac, len, 2);
+               crypto_hash_update(hmac, out_data, *out_len);
+               hlen = sizeof(hash);
+               if (crypto_hash_finish(hmac, hash, &hlen) < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record Layer - Failed "
+                                  "to calculate HMAC");
+                       return -1;
+               }
+               if (hlen != rl->hash_size ||
+                   os_memcmp(hash, out_data + *out_len, hlen) != 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
+                                  "received message");
+                       *alert = TLS_ALERT_BAD_RECORD_MAC;
+                       return -1;
+               }
+       }
+
+       /* TLSCompressed must not be more than 2^14+1024 bytes */
+       if (TLS_RECORD_HEADER_LEN + *out_len > 17408) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Record overflow (len=%lu)",
+                          (unsigned long) (TLS_RECORD_HEADER_LEN + *out_len));
+               *alert = TLS_ALERT_RECORD_OVERFLOW;
+               return -1;
+       }
+
+       inc_byte_array(rl->read_seq_num, TLS_SEQ_NUM_LEN);
+
+       return 0;
+}
diff --git a/src/tls/tlsv1_record.h b/src/tls/tlsv1_record.h
new file mode 100644 (file)
index 0000000..9c7c0a4
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * TLSv1 Record Protocol
+ * Copyright (c) 2006-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 TLSV1_RECORD_H
+#define TLSV1_RECORD_H
+
+#include "crypto/crypto.h"
+
+#define TLS_MAX_WRITE_MAC_SECRET_LEN 20
+#define TLS_MAX_WRITE_KEY_LEN 32
+#define TLS_MAX_IV_LEN 16
+#define TLS_MAX_KEY_BLOCK_LEN (2 * (TLS_MAX_WRITE_MAC_SECRET_LEN + \
+                                   TLS_MAX_WRITE_KEY_LEN + TLS_MAX_IV_LEN))
+
+#define TLS_SEQ_NUM_LEN 8
+#define TLS_RECORD_HEADER_LEN 5
+
+/* ContentType */
+enum {
+       TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC = 20,
+       TLS_CONTENT_TYPE_ALERT = 21,
+       TLS_CONTENT_TYPE_HANDSHAKE = 22,
+       TLS_CONTENT_TYPE_APPLICATION_DATA = 23
+};
+
+struct tlsv1_record_layer {
+       u8 write_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+       u8 read_mac_secret[TLS_MAX_WRITE_MAC_SECRET_LEN];
+       u8 write_key[TLS_MAX_WRITE_KEY_LEN];
+       u8 read_key[TLS_MAX_WRITE_KEY_LEN];
+       u8 write_iv[TLS_MAX_IV_LEN];
+       u8 read_iv[TLS_MAX_IV_LEN];
+
+       size_t hash_size;
+       size_t key_material_len;
+       size_t iv_size; /* also block_size */
+
+       enum crypto_hash_alg hash_alg;
+       enum crypto_cipher_alg cipher_alg;
+
+       u8 write_seq_num[TLS_SEQ_NUM_LEN];
+       u8 read_seq_num[TLS_SEQ_NUM_LEN];
+
+       u16 cipher_suite;
+       u16 write_cipher_suite;
+       u16 read_cipher_suite;
+
+       struct crypto_cipher *write_cbc;
+       struct crypto_cipher *read_cbc;
+};
+
+
+int tlsv1_record_set_cipher_suite(struct tlsv1_record_layer *rl,
+                                 u16 cipher_suite);
+int tlsv1_record_change_write_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_change_read_cipher(struct tlsv1_record_layer *rl);
+int tlsv1_record_send(struct tlsv1_record_layer *rl, u8 content_type, u8 *buf,
+                     size_t buf_size, size_t payload_len, size_t *out_len);
+int tlsv1_record_receive(struct tlsv1_record_layer *rl,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t *out_len, u8 *alert);
+
+#endif /* TLSV1_RECORD_H */
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
new file mode 100644 (file)
index 0000000..6a61235
--- /dev/null
@@ -0,0 +1,592 @@
+/*
+ * TLSv1 server (RFC 2246)
+ * Copyright (c) 2006-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 "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+/* TODO:
+ * Support for a message fragmented across several records (RFC 2246, 6.2.1)
+ */
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
+{
+       conn->alert_level = level;
+       conn->alert_description = description;
+}
+
+
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+                            const u8 *pre_master_secret,
+                            size_t pre_master_secret_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+       u8 key_block[TLS_MAX_KEY_BLOCK_LEN];
+       u8 *pos;
+       size_t key_block_len;
+
+       if (pre_master_secret) {
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: pre_master_secret",
+                               pre_master_secret, pre_master_secret_len);
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+               if (tls_prf(pre_master_secret, pre_master_secret_len,
+                           "master secret", seed, 2 * TLS_RANDOM_LEN,
+                           conn->master_secret, TLS_MASTER_SECRET_LEN)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive "
+                                  "master_secret");
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: master_secret",
+                               conn->master_secret, TLS_MASTER_SECRET_LEN);
+       }
+
+       os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+       os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random, TLS_RANDOM_LEN);
+       key_block_len = 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                            conn->rl.iv_size);
+       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "key expansion", seed, 2 * TLS_RANDOM_LEN,
+                   key_block, key_block_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive key_block");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: key_block",
+                       key_block, key_block_len);
+
+       pos = key_block;
+
+       /* client_write_MAC_secret */
+       os_memcpy(conn->rl.read_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+       /* server_write_MAC_secret */
+       os_memcpy(conn->rl.write_mac_secret, pos, conn->rl.hash_size);
+       pos += conn->rl.hash_size;
+
+       /* client_write_key */
+       os_memcpy(conn->rl.read_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+       /* server_write_key */
+       os_memcpy(conn->rl.write_key, pos, conn->rl.key_material_len);
+       pos += conn->rl.key_material_len;
+
+       /* client_write_IV */
+       os_memcpy(conn->rl.read_iv, pos, conn->rl.iv_size);
+       pos += conn->rl.iv_size;
+       /* server_write_IV */
+       os_memcpy(conn->rl.write_iv, pos, conn->rl.iv_size);
+       pos += conn->rl.iv_size;
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_handshake - Process TLS handshake
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Input data from TLS peer
+ * @in_len: Input data length
+ * @out_len: Length of the output buffer.
+ * Returns: Pointer to output data, %NULL on failure
+ */
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+                           const u8 *in_data, size_t in_len,
+                           size_t *out_len)
+{
+       const u8 *pos, *end;
+       u8 *msg = NULL, *in_msg, *in_pos, *in_end, alert, ct;
+       size_t in_msg_len;
+
+       if (in_data == NULL || in_len == 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No input data to server");
+               return NULL;
+       }
+
+       pos = in_data;
+       end = in_data + in_len;
+       in_msg = os_malloc(in_len);
+       if (in_msg == NULL)
+               return NULL;
+
+       /* Each received packet may include multiple records */
+       while (pos < end) {
+               in_msg_len = in_len;
+               if (tlsv1_record_receive(&conn->rl, pos, end - pos,
+                                        in_msg, &in_msg_len, &alert)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Processing received "
+                                  "record failed");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       goto failed;
+               }
+               ct = pos[0];
+
+               in_pos = in_msg;
+               in_end = in_msg + in_msg_len;
+
+               /* Each received record may include multiple messages of the
+                * same ContentType. */
+               while (in_pos < in_end) {
+                       in_msg_len = in_end - in_pos;
+                       if (tlsv1_server_process_handshake(conn, ct, in_pos,
+                                                          &in_msg_len) < 0)
+                               goto failed;
+                       in_pos += in_msg_len;
+               }
+
+               pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+       }
+
+       os_free(in_msg);
+       in_msg = NULL;
+
+       msg = tlsv1_server_handshake_write(conn, out_len);
+
+failed:
+       os_free(in_msg);
+       if (conn->alert_level) {
+               if (conn->state == FAILED) {
+                       /* Avoid alert loops */
+                       wpa_printf(MSG_DEBUG, "TLSv1: Drop alert loop");
+                       os_free(msg);
+                       return NULL;
+               }
+               conn->state = FAILED;
+               os_free(msg);
+               msg = tlsv1_server_send_alert(conn, conn->alert_level,
+                                             conn->alert_description,
+                                             out_len);
+       }
+
+       return msg;
+}
+
+
+/**
+ * tlsv1_server_encrypt - Encrypt data into TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to plaintext data to be encrypted
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (encrypted TLS data)
+ * @out_len: Maximum out_data length 
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * send data in the encrypted tunnel.
+ */
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       size_t rlen;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Plaintext AppData",
+                       in_data, in_len);
+
+       os_memcpy(out_data + TLS_RECORD_HEADER_LEN, in_data, in_len);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_APPLICATION_DATA,
+                             out_data, out_len, in_len, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return rlen;
+}
+
+
+/**
+ * tlsv1_server_decrypt - Decrypt data from TLS tunnel
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @in_data: Pointer to input buffer (encrypted TLS data)
+ * @in_len: Input buffer length
+ * @out_data: Pointer to output buffer (decrypted data from TLS tunnel)
+ * @out_len: Maximum out_data length
+ * Returns: Number of bytes written to out_data, -1 on failure
+ *
+ * This function is used after TLS handshake has been completed successfully to
+ * receive data from the encrypted tunnel.
+ */
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len)
+{
+       const u8 *in_end, *pos;
+       int res;
+       u8 alert, *out_end, *out_pos;
+       size_t olen;
+
+       pos = in_data;
+       in_end = in_data + in_len;
+       out_pos = out_data;
+       out_end = out_data + out_len;
+
+       while (pos < in_end) {
+               if (pos[0] != TLS_CONTENT_TYPE_APPLICATION_DATA) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
+                                  "0x%x", pos[0]);
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               olen = out_end - out_pos;
+               res = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
+                                          out_pos, &olen, &alert);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
+                                  "failed");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+                       return -1;
+               }
+               out_pos += olen;
+               if (out_pos > out_end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
+                                  "for processing the received record");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+
+               pos += TLS_RECORD_HEADER_LEN + WPA_GET_BE16(pos + 3);
+       }
+
+       return out_pos - out_data;
+}
+
+
+/**
+ * tlsv1_server_global_init - Initialize TLSv1 server
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before using any other TLSv1 server functions.
+ */
+int tlsv1_server_global_init(void)
+{
+       return crypto_global_init();
+}
+
+
+/**
+ * tlsv1_server_global_deinit - Deinitialize TLSv1 server
+ *
+ * This function can be used to deinitialize the TLSv1 server that was
+ * initialized by calling tlsv1_server_global_init(). No TLSv1 server functions
+ * can be called after this before calling tlsv1_server_global_init() again.
+ */
+void tlsv1_server_global_deinit(void)
+{
+       crypto_global_deinit();
+}
+
+
+/**
+ * tlsv1_server_init - Initialize TLSv1 server connection
+ * @cred: Pointer to server credentials from tlsv1_server_cred_alloc()
+ * Returns: Pointer to TLSv1 server connection data or %NULL on failure
+ */
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
+{
+       struct tlsv1_server *conn;
+       size_t count;
+       u16 *suites;
+
+       conn = os_zalloc(sizeof(*conn));
+       if (conn == NULL)
+               return NULL;
+
+       conn->cred = cred;
+
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to initialize verify "
+                          "hash");
+               os_free(conn);
+               return NULL;
+       }
+
+       count = 0;
+       suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+       suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+       suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+       suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+       conn->num_cipher_suites = count;
+
+       return conn;
+}
+
+
+static void tlsv1_server_clear_data(struct tlsv1_server *conn)
+{
+       tlsv1_record_set_cipher_suite(&conn->rl, TLS_NULL_WITH_NULL_NULL);
+       tlsv1_record_change_write_cipher(&conn->rl);
+       tlsv1_record_change_read_cipher(&conn->rl);
+       tls_verify_hash_free(&conn->verify);
+
+       crypto_public_key_free(conn->client_rsa_key);
+       conn->client_rsa_key = NULL;
+
+       os_free(conn->session_ticket);
+       conn->session_ticket = NULL;
+       conn->session_ticket_len = 0;
+       conn->use_session_ticket = 0;
+
+       os_free(conn->dh_secret);
+       conn->dh_secret = NULL;
+       conn->dh_secret_len = 0;
+}
+
+
+/**
+ * tlsv1_server_deinit - Deinitialize TLSv1 server connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ */
+void tlsv1_server_deinit(struct tlsv1_server *conn)
+{
+       tlsv1_server_clear_data(conn);
+       os_free(conn);
+}
+
+
+/**
+ * tlsv1_server_established - Check whether connection has been established
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if connection is established, 0 if not
+ */
+int tlsv1_server_established(struct tlsv1_server *conn)
+{
+       return conn->state == ESTABLISHED;
+}
+
+
+/**
+ * tlsv1_server_prf - Use TLS-PRF to derive keying material
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @label: Label (e.g., description of the key) for PRF
+ * @server_random_first: seed is 0 = client_random|server_random,
+ * 1 = server_random|client_random
+ * @out: Buffer for output data from TLS-PRF
+ * @out_len: Length of the output buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+                    int server_random_first, u8 *out, size_t out_len)
+{
+       u8 seed[2 * TLS_RANDOM_LEN];
+
+       if (conn->state != ESTABLISHED)
+               return -1;
+
+       if (server_random_first) {
+               os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
+                         TLS_RANDOM_LEN);
+       } else {
+               os_memcpy(seed, conn->client_random, TLS_RANDOM_LEN);
+               os_memcpy(seed + TLS_RANDOM_LEN, conn->server_random,
+                         TLS_RANDOM_LEN);
+       }
+
+       return tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                      label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+}
+
+
+/**
+ * tlsv1_server_get_cipher - Get current cipher name
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @buf: Buffer for the cipher name
+ * @buflen: buf size
+ * Returns: 0 on success, -1 on failure
+ *
+ * Get the name of the currently used cipher.
+ */
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+                           size_t buflen)
+{
+       char *cipher;
+
+       switch (conn->rl.cipher_suite) {
+       case TLS_RSA_WITH_RC4_128_MD5:
+               cipher = "RC4-MD5";
+               break;
+       case TLS_RSA_WITH_RC4_128_SHA:
+               cipher = "RC4-SHA";
+               break;
+       case TLS_RSA_WITH_DES_CBC_SHA:
+               cipher = "DES-CBC-SHA";
+               break;
+       case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
+               cipher = "DES-CBC3-SHA";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA:
+               cipher = "ADH-AES-128-SHA";
+               break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA:
+               cipher = "AES-256-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "AES-128-SHA";
+               break;
+       default:
+               return -1;
+       }
+
+       if (os_strlcpy(buf, cipher, buflen) >= buflen)
+               return -1;
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_shutdown - Shutdown TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_shutdown(struct tlsv1_server *conn)
+{
+       conn->state = CLIENT_HELLO;
+
+       if (tls_verify_hash_init(&conn->verify) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to re-initialize verify "
+                          "hash");
+               return -1;
+       }
+
+       tlsv1_server_clear_data(conn);
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_resumed - Was session resumption used
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: 1 if current session used session resumption, 0 if not
+ */
+int tlsv1_server_resumed(struct tlsv1_server *conn)
+{
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keys - Get master key and random data from TLS connection
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @keys: Structure of key/random data (filled on success)
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys)
+{
+       os_memset(keys, 0, sizeof(*keys));
+       if (conn->state == CLIENT_HELLO)
+               return -1;
+
+       keys->client_random = conn->client_random;
+       keys->client_random_len = TLS_RANDOM_LEN;
+
+       if (conn->state != SERVER_HELLO) {
+               keys->server_random = conn->server_random;
+               keys->server_random_len = TLS_RANDOM_LEN;
+               keys->master_key = conn->master_secret;
+               keys->master_key_len = TLS_MASTER_SECRET_LEN;
+       }
+
+       return 0;
+}
+
+
+/**
+ * tlsv1_server_get_keyblock_size - Get TLS key_block size
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * Returns: Size of the key_block for the negotiated cipher suite or -1 on
+ * failure
+ */
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn)
+{
+       if (conn->state == CLIENT_HELLO || conn->state == SERVER_HELLO)
+               return -1;
+
+       return 2 * (conn->rl.hash_size + conn->rl.key_material_len +
+                   conn->rl.iv_size);
+}
+
+
+/**
+ * tlsv1_server_set_cipher_list - Configure acceptable cipher suites
+ * @conn: TLSv1 server connection data from tlsv1_server_init()
+ * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers
+ * (TLS_CIPHER_*).
+ * Returns: 0 on success, -1 on failure
+ */
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers)
+{
+       size_t count;
+       u16 *suites;
+
+       /* TODO: implement proper configuration of cipher suites */
+       if (ciphers[0] == TLS_CIPHER_ANON_DH_AES128_SHA) {
+               count = 0;
+               suites = conn->cipher_suites;
+#ifndef CONFIG_CRYPTO_INTERNAL
+               suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+               suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+               suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
+               suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
+               suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
+#ifndef CONFIG_CRYPTO_INTERNAL
+               suites[count++] = TLS_DH_anon_WITH_AES_256_CBC_SHA;
+#endif /* CONFIG_CRYPTO_INTERNAL */
+               suites[count++] = TLS_DH_anon_WITH_AES_128_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_3DES_EDE_CBC_SHA;
+               suites[count++] = TLS_DH_anon_WITH_RC4_128_MD5;
+               suites[count++] = TLS_DH_anon_WITH_DES_CBC_SHA;
+               conn->num_cipher_suites = count;
+       }
+
+       return 0;
+}
+
+
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer)
+{
+       conn->verify_peer = verify_peer;
+       return 0;
+}
+
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+                                       tlsv1_server_session_ticket_cb cb,
+                                       void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback set %p (ctx %p)",
+                  cb, ctx);
+       conn->session_ticket_cb = cb;
+       conn->session_ticket_cb_ctx = ctx;
+}
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
new file mode 100644 (file)
index 0000000..00c536c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * TLSv1 server (RFC 2246)
+ * Copyright (c) 2006-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 TLSV1_SERVER_H
+#define TLSV1_SERVER_H
+
+#include "tlsv1_cred.h"
+
+struct tlsv1_server;
+
+int tlsv1_server_global_init(void);
+void tlsv1_server_global_deinit(void);
+struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
+void tlsv1_server_deinit(struct tlsv1_server *conn);
+int tlsv1_server_established(struct tlsv1_server *conn);
+int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+                    int server_random_first, u8 *out, size_t out_len);
+u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
+                           const u8 *in_data, size_t in_len, size_t *out_len);
+int tlsv1_server_encrypt(struct tlsv1_server *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len);
+int tlsv1_server_decrypt(struct tlsv1_server *conn,
+                        const u8 *in_data, size_t in_len,
+                        u8 *out_data, size_t out_len);
+int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
+                           size_t buflen);
+int tlsv1_server_shutdown(struct tlsv1_server *conn);
+int tlsv1_server_resumed(struct tlsv1_server *conn);
+int tlsv1_server_get_keys(struct tlsv1_server *conn, struct tls_keys *keys);
+int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn);
+int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers);
+int tlsv1_server_set_verify(struct tlsv1_server *conn, int verify_peer);
+
+typedef int (*tlsv1_server_session_ticket_cb)
+(void *ctx, const u8 *ticket, size_t len, const u8 *client_random,
+ const u8 *server_random, u8 *master_secret);
+
+void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
+                                       tlsv1_server_session_ticket_cb cb,
+                                       void *ctx);
+
+#endif /* TLSV1_SERVER_H */
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
new file mode 100644 (file)
index 0000000..d11ea75
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * TLSv1 server - internal structures
+ * Copyright (c) 2006-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 TLSV1_SERVER_I_H
+#define TLSV1_SERVER_I_H
+
+struct tlsv1_server {
+       enum {
+               CLIENT_HELLO, SERVER_HELLO, SERVER_CERTIFICATE,
+               SERVER_KEY_EXCHANGE, SERVER_CERTIFICATE_REQUEST,
+               SERVER_HELLO_DONE, CLIENT_CERTIFICATE, CLIENT_KEY_EXCHANGE,
+               CERTIFICATE_VERIFY, CHANGE_CIPHER_SPEC, CLIENT_FINISHED,
+               SERVER_CHANGE_CIPHER_SPEC, SERVER_FINISHED,
+               ESTABLISHED, FAILED
+       } state;
+
+       struct tlsv1_record_layer rl;
+
+       u8 session_id[TLS_SESSION_ID_MAX_LEN];
+       size_t session_id_len;
+       u8 client_random[TLS_RANDOM_LEN];
+       u8 server_random[TLS_RANDOM_LEN];
+       u8 master_secret[TLS_MASTER_SECRET_LEN];
+
+       u8 alert_level;
+       u8 alert_description;
+
+       struct crypto_public_key *client_rsa_key;
+
+       struct tls_verify_hash verify;
+
+#define MAX_CIPHER_COUNT 30
+       u16 cipher_suites[MAX_CIPHER_COUNT];
+       size_t num_cipher_suites;
+
+       u16 cipher_suite;
+
+       struct tlsv1_credentials *cred;
+
+       int verify_peer;
+       u16 client_version;
+
+       u8 *session_ticket;
+       size_t session_ticket_len;
+
+       tlsv1_server_session_ticket_cb session_ticket_cb;
+       void *session_ticket_cb_ctx;
+
+       int use_session_ticket;
+
+       u8 *dh_secret;
+       size_t dh_secret_len;
+};
+
+
+void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description);
+int tlsv1_server_derive_keys(struct tlsv1_server *conn,
+                            const u8 *pre_master_secret,
+                            size_t pre_master_secret_len);
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len);
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+                            u8 description, size_t *out_len);
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+                                  const u8 *buf, size_t *len);
+
+#endif /* TLSV1_SERVER_I_H */
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
new file mode 100644 (file)
index 0000000..49e811f
--- /dev/null
@@ -0,0 +1,1134 @@
+/*
+ * TLSv1 server - read handshake message
+ * Copyright (c) 2006-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 "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len);
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+                                         u8 ct, const u8 *in_data,
+                                         size_t *in_len);
+
+
+static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
+                                   const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end, *c;
+       size_t left, len, i, j;
+       u16 cipher_suite;
+       u16 num_suites;
+       int compr_null_found;
+       u16 ext_type, ext_len;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4)
+               goto decode_error;
+
+       /* HandshakeType msg_type */
+       if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ClientHello)", *pos);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello");
+       pos++;
+       /* uint24 length */
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left)
+               goto decode_error;
+
+       /* body - ClientHello */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello", pos, len);
+       end = pos + len;
+
+       /* ProtocolVersion client_version */
+       if (end - pos < 2)
+               goto decode_error;
+       conn->client_version = WPA_GET_BE16(pos);
+       wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
+                  conn->client_version >> 8, conn->client_version & 0xff);
+       if (conn->client_version < TLS_VERSION) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
+                          "ClientHello");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_PROTOCOL_VERSION);
+               return -1;
+       }
+       pos += 2;
+
+       /* Random random */
+       if (end - pos < TLS_RANDOM_LEN)
+               goto decode_error;
+
+       os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client_random",
+                   conn->client_random, TLS_RANDOM_LEN);
+
+       /* SessionID session_id */
+       if (end - pos < 1)
+               goto decode_error;
+       if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+               goto decode_error;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
+       pos += 1 + *pos;
+       /* TODO: add support for session resumption */
+
+       /* CipherSuite cipher_suites<2..2^16-1> */
+       if (end - pos < 2)
+               goto decode_error;
+       num_suites = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < num_suites)
+               goto decode_error;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
+                   pos, num_suites);
+       if (num_suites & 1)
+               goto decode_error;
+       num_suites /= 2;
+
+       cipher_suite = 0;
+       for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+               c = pos;
+               for (j = 0; j < num_suites; j++) {
+                       u16 tmp = WPA_GET_BE16(c);
+                       c += 2;
+                       if (!cipher_suite && tmp == conn->cipher_suites[i]) {
+                               cipher_suite = tmp;
+                               break;
+                       }
+               }
+       }
+       pos += num_suites * 2;
+       if (!cipher_suite) {
+               wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
+                          "available");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (tlsv1_record_set_cipher_suite(&conn->rl, cipher_suite) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set CipherSuite for "
+                          "record layer");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       conn->cipher_suite = cipher_suite;
+
+       /* CompressionMethod compression_methods<1..2^8-1> */
+       if (end - pos < 1)
+               goto decode_error;
+       num_suites = *pos++;
+       if (end - pos < num_suites)
+               goto decode_error;
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
+                   pos, num_suites);
+       compr_null_found = 0;
+       for (i = 0; i < num_suites; i++) {
+               if (*pos++ == TLS_COMPRESSION_NULL)
+                       compr_null_found = 1;
+       }
+       if (!compr_null_found) {
+               wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
+                          "compression");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_ILLEGAL_PARAMETER);
+               return -1;
+       }
+
+       if (end - pos == 1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the "
+                           "end of ClientHello: 0x%02x", *pos);
+               goto decode_error;
+       }
+
+       if (end - pos >= 2) {
+               /* Extension client_hello_extension_list<0..2^16-1> */
+               ext_len = WPA_GET_BE16(pos);
+               pos += 2;
+
+               wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
+                          "extensions", ext_len);
+               if (end - pos != ext_len) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello "
+                                  "extension list length %u (expected %u)",
+                                  ext_len, (unsigned int) (end - pos));
+                       goto decode_error;
+               }
+
+               /*
+                * struct {
+                *   ExtensionType extension_type (0..65535)
+                *   opaque extension_data<0..2^16-1>
+                * } Extension;
+                */
+
+               while (pos < end) {
+                       if (end - pos < 2) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+                                          "extension_type field");
+                               goto decode_error;
+                       }
+
+                       ext_type = WPA_GET_BE16(pos);
+                       pos += 2;
+
+                       if (end - pos < 2) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+                                          "extension_data length field");
+                               goto decode_error;
+                       }
+
+                       ext_len = WPA_GET_BE16(pos);
+                       pos += 2;
+
+                       if (end - pos < ext_len) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
+                                          "extension_data field");
+                               goto decode_error;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
+                                  "type %u", ext_type);
+                       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
+                                   "Extension data", pos, ext_len);
+
+                       if (ext_type == TLS_EXT_SESSION_TICKET) {
+                               os_free(conn->session_ticket);
+                               conn->session_ticket = os_malloc(ext_len);
+                               if (conn->session_ticket) {
+                                       os_memcpy(conn->session_ticket, pos,
+                                                 ext_len);
+                                       conn->session_ticket_len = ext_len;
+                               }
+                       }
+
+                       pos += ext_len;
+               }
+       }
+
+       *in_len = end - in_data;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
+                  "ServerHello");
+       conn->state = SERVER_HELLO;
+
+       return 0;
+
+decode_error:
+       wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                          TLS_ALERT_DECODE_ERROR);
+       return -1;
+}
+
+
+static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
+                                  const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, list_len, cert_len, idx;
+       u8 type;
+       struct x509_certificate *chain = NULL, *last = NULL, *cert;
+       int reason;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
+                          "(len=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+               if (conn->verify_peer) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+                                  "Certificate");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               return tls_process_client_key_exchange(conn, ct, in_data,
+                                                      in_len);
+       }
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected Certificate/"
+                          "ClientKeyExchange)", type);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "TLSv1: Received Certificate (certificate_list len %lu)",
+                  (unsigned long) len);
+
+       /*
+        * opaque ASN.1Cert<2^24-1>;
+        *
+        * struct {
+        *     ASN.1Cert certificate_list<1..2^24-1>;
+        * } Certificate;
+        */
+
+       end = pos + len;
+
+       if (end - pos < 3) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
+                          "(left=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       list_len = WPA_GET_BE24(pos);
+       pos += 3;
+
+       if ((size_t) (end - pos) != list_len) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
+                          "length (len=%lu left=%lu)",
+                          (unsigned long) list_len,
+                          (unsigned long) (end - pos));
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       idx = 0;
+       while (pos < end) {
+               if (end - pos < 3) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "certificate_list");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               cert_len = WPA_GET_BE24(pos);
+               pos += 3;
+
+               if ((size_t) (end - pos) < cert_len) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
+                                  "length (len=%lu left=%lu)",
+                                  (unsigned long) cert_len,
+                                  (unsigned long) (end - pos));
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
+                          (unsigned long) idx, (unsigned long) cert_len);
+
+               if (idx == 0) {
+                       crypto_public_key_free(conn->client_rsa_key);
+                       if (tls_parse_cert(pos, cert_len,
+                                          &conn->client_rsa_key)) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                          "the certificate");
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_BAD_CERTIFICATE);
+                               x509_certificate_chain_free(chain);
+                               return -1;
+                       }
+               }
+
+               cert = x509_certificate_parse(pos, cert_len);
+               if (cert == NULL) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
+                                  "the certificate");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_BAD_CERTIFICATE);
+                       x509_certificate_chain_free(chain);
+                       return -1;
+               }
+
+               if (last == NULL)
+                       chain = cert;
+               else
+                       last->next = cert;
+               last = cert;
+
+               idx++;
+               pos += cert_len;
+       }
+
+       if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
+                                           &reason) < 0) {
+               int tls_reason;
+               wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
+                          "validation failed (reason=%d)", reason);
+               switch (reason) {
+               case X509_VALIDATE_BAD_CERTIFICATE:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_UNSUPPORTED_CERTIFICATE:
+                       tls_reason = TLS_ALERT_UNSUPPORTED_CERTIFICATE;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_REVOKED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_REVOKED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_EXPIRED:
+                       tls_reason = TLS_ALERT_CERTIFICATE_EXPIRED;
+                       break;
+               case X509_VALIDATE_CERTIFICATE_UNKNOWN:
+                       tls_reason = TLS_ALERT_CERTIFICATE_UNKNOWN;
+                       break;
+               case X509_VALIDATE_UNKNOWN_CA:
+                       tls_reason = TLS_ALERT_UNKNOWN_CA;
+                       break;
+               default:
+                       tls_reason = TLS_ALERT_BAD_CERTIFICATE;
+                       break;
+               }
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, tls_reason);
+               x509_certificate_chain_free(chain);
+               return -1;
+       }
+
+       x509_certificate_chain_free(chain);
+
+       *in_len = end - in_data;
+
+       conn->state = CLIENT_KEY_EXCHANGE;
+
+       return 0;
+}
+
+
+static int tls_process_client_key_exchange_rsa(
+       struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+       u8 *out;
+       size_t outlen, outbuflen;
+       u16 encr_len;
+       int res;
+       int use_random = 0;
+
+       if (end - pos < 2) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       encr_len = WPA_GET_BE16(pos);
+       pos += 2;
+
+       outbuflen = outlen = end - pos;
+       out = os_malloc(outlen >= TLS_PRE_MASTER_SECRET_LEN ?
+                       outlen : TLS_PRE_MASTER_SECRET_LEN);
+       if (out == NULL) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /*
+        * struct {
+        *   ProtocolVersion client_version;
+        *   opaque random[46];
+        * } PreMasterSecret;
+        *
+        * struct {
+        *   public-key-encrypted PreMasterSecret pre_master_secret;
+        * } EncryptedPreMasterSecret;
+        */
+
+       /*
+        * Note: To avoid Bleichenbacher attack, we do not report decryption or
+        * parsing errors from EncryptedPreMasterSecret processing to the
+        * client. Instead, a random pre-master secret is used to force the
+        * handshake to fail.
+        */
+
+       if (crypto_private_key_decrypt_pkcs1_v15(conn->cred->key,
+                                                pos, end - pos,
+                                                out, &outlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt "
+                          "PreMasterSecret (encr_len=%d outlen=%lu)",
+                          (int) (end - pos), (unsigned long) outlen);
+               use_random = 1;
+       }
+
+       if (outlen != TLS_PRE_MASTER_SECRET_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
+                          "length %lu", (unsigned long) outlen);
+               use_random = 1;
+       }
+
+       if (WPA_GET_BE16(out) != conn->client_version) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
+                          "ClientKeyExchange does not match with version in "
+                          "ClientHello");
+               use_random = 1;
+       }
+
+       if (use_random) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Using random premaster secret "
+                          "to avoid revealing information about private key");
+               outlen = TLS_PRE_MASTER_SECRET_LEN;
+               if (os_get_random(out, outlen)) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+                                  "data");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       os_free(out);
+                       return -1;
+               }
+       }
+
+       res = tlsv1_server_derive_keys(conn, out, outlen);
+
+       /* Clear the pre-master secret since it is not needed anymore */
+       os_memset(out, 0, outbuflen);
+       os_free(out);
+
+       if (res) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int tls_process_client_key_exchange_dh_anon(
+       struct tlsv1_server *conn, const u8 *pos, const u8 *end)
+{
+       const u8 *dh_yc;
+       u16 dh_yc_len;
+       u8 *shared;
+       size_t shared_len;
+       int res;
+
+       /*
+        * struct {
+        *   select (PublicValueEncoding) {
+        *     case implicit: struct { };
+        *     case explicit: opaque dh_Yc<1..2^16-1>;
+        *   } dh_public;
+        * } ClientDiffieHellmanPublic;
+        */
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
+                   pos, end - pos);
+
+       if (end == pos) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Implicit public value encoding "
+                          "not supported");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       if (end - pos < 3) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
+                          "length");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       dh_yc_len = WPA_GET_BE16(pos);
+       dh_yc = pos + 2;
+
+       if (dh_yc + dh_yc_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow "
+                          "(length %d)", dh_yc_len);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Yc (client's public value)",
+                   dh_yc, dh_yc_len);
+
+       if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+           conn->dh_secret == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       shared_len = conn->cred->dh_p_len;
+       shared = os_malloc(shared_len);
+       if (shared == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
+                          "DH");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       /* shared = Yc^secret mod p */
+       if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
+                          conn->dh_secret_len,
+                          conn->cred->dh_p, conn->cred->dh_p_len,
+                          shared, &shared_len)) {
+               os_free(shared);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: Shared secret from DH key exchange",
+                       shared, shared_len);
+
+       os_memset(conn->dh_secret, 0, conn->dh_secret_len);
+       os_free(conn->dh_secret);
+       conn->dh_secret = NULL;
+
+       res = tlsv1_server_derive_keys(conn, shared, shared_len);
+
+       /* Clear the pre-master secret since it is not needed anymore */
+       os_memset(shared, 0, shared_len);
+       os_free(shared);
+
+       if (res) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive keys");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
+                                          const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+       tls_key_exchange keyx;
+       const struct tls_cipher_suite *suite;
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
+                          "(Left=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange "
+                          "length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected ClientKeyExchange)", type);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange");
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite == NULL)
+               keyx = TLS_KEY_X_NULL;
+       else
+               keyx = suite->key_exchange;
+
+       if (keyx == TLS_KEY_X_DH_anon &&
+           tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+               return -1;
+
+       if (keyx != TLS_KEY_X_DH_anon &&
+           tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
+               return -1;
+
+       *in_len = end - in_data;
+
+       conn->state = CERTIFICATE_VERIFY;
+
+       return 0;
+}
+
+
+static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
+                                         const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len;
+       u8 type;
+       size_t hlen, buflen;
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
+       enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
+       u16 slen;
+
+       if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+               if (conn->verify_peer) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
+                                  "CertificateVerify");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_UNEXPECTED_MESSAGE);
+                       return -1;
+               }
+
+               return tls_process_change_cipher_spec(conn, ct, in_data,
+                                                     in_len);
+       }
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
+                          "message (len=%lu)", (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       type = *pos++;
+       len = WPA_GET_BE24(pos);
+       pos += 3;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify "
+                          "message length (len=%lu != left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       end = pos + len;
+
+       if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
+                          "message %d (expected CertificateVerify)", type);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify");
+
+       /*
+        * struct {
+        *   Signature signature;
+        * } CertificateVerify;
+        */
+
+       hpos = hash;
+
+       if (alg == SIGN_ALG_RSA) {
+               hlen = MD5_MAC_LEN;
+               if (conn->verify.md5_cert == NULL ||
+                   crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
+               {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       conn->verify.md5_cert = NULL;
+                       crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+                       conn->verify.sha1_cert = NULL;
+                       return -1;
+               }
+               hpos += MD5_MAC_LEN;
+       } else
+               crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+
+       conn->verify.md5_cert = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_cert == NULL ||
+           crypto_hash_finish(conn->verify.sha1_cert, hpos, &hlen) < 0) {
+               conn->verify.sha1_cert = NULL;
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_cert = NULL;
+
+       if (alg == SIGN_ALG_RSA)
+               hlen += MD5_MAC_LEN;
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
+
+       if (end - pos < 2) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       slen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < slen) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+       if (conn->client_rsa_key == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify "
+                          "signature");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       buflen = end - pos;
+       buf = os_malloc(end - pos);
+       if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key,
+                                           pos, end - pos, buf, &buflen) < 0)
+       {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+               os_free(buf);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+                       buf, buflen);
+
+       if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
+                          "CertificateVerify - did not match with calculated "
+                          "hash");
+               os_free(buf);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+
+       os_free(buf);
+
+       *in_len = end - in_data;
+
+       conn->state = CHANGE_CIPHER_SPEC;
+
+       return 0;
+}
+
+
+static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
+                                         u8 ct, const u8 *in_data,
+                                         size_t *in_len)
+{
+       const u8 *pos;
+       size_t left;
+
+       if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 1) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (*pos != TLS_CHANGE_CIPHER_SPEC) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
+                          "received data 0x%x", *pos);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+       if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
+                          "for record layer");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *in_len = pos + 1 - in_data;
+
+       conn->state = CLIENT_FINISHED;
+
+       return 0;
+}
+
+
+static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
+                                      const u8 *in_data, size_t *in_len)
+{
+       const u8 *pos, *end;
+       size_t left, len, hlen;
+       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
+                          "received content type 0x%x", ct);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       pos = in_data;
+       left = *in_len;
+
+       if (left < 4) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
+                          "Finished",
+                          (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+
+       if (pos[0] != TLS_HANDSHAKE_TYPE_FINISHED) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; received "
+                          "type 0x%x", pos[0]);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_UNEXPECTED_MESSAGE);
+               return -1;
+       }
+
+       len = WPA_GET_BE24(pos + 1);
+
+       pos += 4;
+       left -= 4;
+
+       if (len > left) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
+                          "(len=%lu > left=%lu)",
+                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       end = pos + len;
+       if (len != TLS_VERIFY_DATA_LEN) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
+                          "in Finished: %lu (expected %d)",
+                          (unsigned long) len, TLS_VERIFY_DATA_LEN);
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECODE_ERROR);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: verify_data in Finished",
+                   pos, TLS_VERIFY_DATA_LEN);
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_client == NULL ||
+           crypto_hash_finish(conn->verify.md5_client, hash, &hlen) < 0) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_client = NULL;
+               crypto_hash_finish(conn->verify.sha1_client, NULL, NULL);
+               conn->verify.sha1_client = NULL;
+               return -1;
+       }
+       conn->verify.md5_client = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_client == NULL ||
+           crypto_hash_finish(conn->verify.sha1_client, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_client = NULL;
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_client = NULL;
+
+       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "client finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+                   verify_data, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to derive verify_data");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_DECRYPT_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
+                       verify_data, TLS_VERIFY_DATA_LEN);
+
+       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+               wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+
+       *in_len = end - in_data;
+
+       if (conn->use_session_ticket) {
+               /* Abbreviated handshake using session ticket; RFC 4507 */
+               wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed "
+                          "successfully");
+               conn->state = ESTABLISHED;
+       } else {
+               /* Full handshake */
+               conn->state = SERVER_CHANGE_CIPHER_SPEC;
+       }
+
+       return 0;
+}
+
+
+int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
+                                  const u8 *buf, size_t *len)
+{
+       if (ct == TLS_CONTENT_TYPE_ALERT) {
+               if (*len < 2) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_DECODE_ERROR);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
+                          buf[0], buf[1]);
+               *len = 2;
+               conn->state = FAILED;
+               return -1;
+       }
+
+       switch (conn->state) {
+       case CLIENT_HELLO:
+               if (tls_process_client_hello(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CLIENT_CERTIFICATE:
+               if (tls_process_certificate(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CLIENT_KEY_EXCHANGE:
+               if (tls_process_client_key_exchange(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CERTIFICATE_VERIFY:
+               if (tls_process_certificate_verify(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CHANGE_CIPHER_SPEC:
+               if (tls_process_change_cipher_spec(conn, ct, buf, len))
+                       return -1;
+               break;
+       case CLIENT_FINISHED:
+               if (tls_process_client_finished(conn, ct, buf, len))
+                       return -1;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
+                          "while processing received message",
+                          conn->state);
+               return -1;
+       }
+
+       if (ct == TLS_CONTENT_TYPE_HANDSHAKE)
+               tls_verify_hash_add(&conn->verify, buf, *len);
+
+       return 0;
+}
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
new file mode 100644 (file)
index 0000000..6d1df7f
--- /dev/null
@@ -0,0 +1,790 @@
+/*
+ * TLSv1 server - write handshake message
+ * Copyright (c) 2006-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 "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/tls.h"
+#include "x509v3.h"
+#include "tlsv1_common.h"
+#include "tlsv1_record.h"
+#include "tlsv1_server.h"
+#include "tlsv1_server_i.h"
+
+
+static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
+{
+       size_t len = 0;
+       struct x509_certificate *cert;
+
+       cert = conn->cred->cert;
+       while (cert) {
+               len += 3 + cert->cert_len;
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+
+       return len;
+}
+
+
+static int tls_write_server_hello(struct tlsv1_server *conn,
+                                 u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       struct os_time now;
+       size_t rlen;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       os_get_time(&now);
+       WPA_PUT_BE32(conn->server_random, now.sec);
+       if (os_get_random(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
+               wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+                          "server_random");
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: server_random",
+                   conn->server_random, TLS_RANDOM_LEN);
+
+       conn->session_id_len = TLS_SESSION_ID_MAX_LEN;
+       if (os_get_random(conn->session_id, conn->session_id_len)) {
+               wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
+                          "session_id");
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: session_id",
+                   conn->session_id, conn->session_id_len);
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ServerHello */
+       /* ProtocolVersion server_version */
+       WPA_PUT_BE16(pos, TLS_VERSION);
+       pos += 2;
+       /* Random random: uint32 gmt_unix_time, opaque random_bytes */
+       os_memcpy(pos, conn->server_random, TLS_RANDOM_LEN);
+       pos += TLS_RANDOM_LEN;
+       /* SessionID session_id */
+       *pos++ = conn->session_id_len;
+       os_memcpy(pos, conn->session_id, conn->session_id_len);
+       pos += conn->session_id_len;
+       /* CipherSuite cipher_suite */
+       WPA_PUT_BE16(pos, conn->cipher_suite);
+       pos += 2;
+       /* CompressionMethod compression_method */
+       *pos++ = TLS_COMPRESSION_NULL;
+
+       if (conn->session_ticket && conn->session_ticket_cb) {
+               int res = conn->session_ticket_cb(
+                       conn->session_ticket_cb_ctx,
+                       conn->session_ticket, conn->session_ticket_len,
+                       conn->client_random, conn->server_random,
+                       conn->master_secret);
+               if (res < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
+                                  "indicated failure");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_HANDSHAKE_FAILURE);
+                       return -1;
+               }
+               conn->use_session_ticket = res;
+
+               if (conn->use_session_ticket) {
+                       if (tlsv1_server_derive_keys(conn, NULL, 0) < 0) {
+                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to "
+                                          "derive keys");
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_INTERNAL_ERROR);
+                               return -1;
+                       }
+               }
+
+               /*
+                * RFC 4507 specifies that server would include an empty
+                * SessionTicket extension in ServerHello and a
+                * NewSessionTicket message after the ServerHello. However,
+                * EAP-FAST (RFC 4851), i.e., the only user of SessionTicket
+                * extension at the moment, does not use such extensions.
+                *
+                * TODO: Add support for configuring RFC 4507 behavior and make
+                * EAP-FAST disable it.
+                */
+       }
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create TLS record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_certificate(struct tlsv1_server *conn,
+                                       u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length, *cert_start;
+       size_t rlen;
+       struct x509_certificate *cert;
+       const struct tls_cipher_suite *suite;
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Do not send Certificate when "
+                          "using anonymous DH");
+               return 0;
+       }
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - Certificate */
+       /* uint24 length (to be filled) */
+       cert_start = pos;
+       pos += 3;
+       cert = conn->cred->cert;
+       while (cert) {
+               if (pos + 3 + cert->cert_len > end) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space "
+                                  "for Certificate (cert_len=%lu left=%lu)",
+                                  (unsigned long) cert->cert_len,
+                                  (unsigned long) (end - pos));
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               WPA_PUT_BE24(pos, cert->cert_len);
+               pos += 3;
+               os_memcpy(pos, cert->cert_start, cert->cert_len);
+               pos += cert->cert_len;
+
+               if (x509_certificate_self_signed(cert))
+                       break;
+               cert = x509_certificate_get_subject(conn->cred->trusted_certs,
+                                                   &cert->issuer);
+       }
+       if (cert == conn->cred->cert || cert == NULL) {
+               /*
+                * Server was not configured with all the needed certificates
+                * to form a full certificate chain. The client may fail to
+                * validate the chain unless it is configured with all the
+                * missing CA certificates.
+                */
+               wpa_printf(MSG_DEBUG, "TLSv1: Full server certificate chain "
+                          "not configured - validation may fail");
+       }
+       WPA_PUT_BE24(cert_start, pos - cert_start - 3);
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_key_exchange(struct tlsv1_server *conn,
+                                        u8 **msgpos, u8 *end)
+{
+       tls_key_exchange keyx;
+       const struct tls_cipher_suite *suite;
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+       u8 *dh_ys;
+       size_t dh_ys_len;
+
+       suite = tls_get_cipher_suite(conn->rl.cipher_suite);
+       if (suite == NULL)
+               keyx = TLS_KEY_X_NULL;
+       else
+               keyx = suite->key_exchange;
+
+       if (!tls_server_key_exchange_allowed(conn->rl.cipher_suite)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No ServerKeyExchange needed");
+               return 0;
+       }
+
+       if (keyx != TLS_KEY_X_DH_anon) {
+               /* TODO? */
+               wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
+                          "supported with key exchange type %d", keyx);
+               return -1;
+       }
+
+       if (conn->cred == NULL || conn->cred->dh_p == NULL ||
+           conn->cred->dh_g == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for "
+                          "ServerKeyExhcange");
+               return -1;
+       }
+
+       os_free(conn->dh_secret);
+       conn->dh_secret_len = conn->cred->dh_p_len;
+       conn->dh_secret = os_malloc(conn->dh_secret_len);
+       if (conn->dh_secret == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
+                          "memory for secret (Diffie-Hellman)");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       if (os_get_random(conn->dh_secret, conn->dh_secret_len)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to get random "
+                          "data for Diffie-Hellman");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(conn->dh_secret);
+               conn->dh_secret = NULL;
+               return -1;
+       }
+
+       if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
+           0)
+               conn->dh_secret[0] = 0; /* make sure secret < p */
+
+       pos = conn->dh_secret;
+       while (pos + 1 < conn->dh_secret + conn->dh_secret_len && *pos == 0)
+               pos++;
+       if (pos != conn->dh_secret) {
+               os_memmove(conn->dh_secret, pos,
+                          conn->dh_secret_len - (pos - conn->dh_secret));
+               conn->dh_secret_len -= pos - conn->dh_secret;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: DH server's secret value",
+                       conn->dh_secret, conn->dh_secret_len);
+
+       /* Ys = g^secret mod p */
+       dh_ys_len = conn->cred->dh_p_len;
+       dh_ys = os_malloc(dh_ys_len);
+       if (dh_ys == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
+                          "Diffie-Hellman");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
+                          conn->dh_secret, conn->dh_secret_len,
+                          conn->cred->dh_p, conn->cred->dh_p_len,
+                          dh_ys, &dh_ys_len)) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
+                   dh_ys, dh_ys_len);
+
+       /*
+        * struct {
+        *    select (KeyExchangeAlgorithm) {
+        *       case diffie_hellman:
+        *          ServerDHParams params;
+        *          Signature signed_params;
+        *       case rsa:
+        *          ServerRSAParams params;
+        *          Signature signed_params;
+        *    };
+        * } ServerKeyExchange;
+        *
+        * struct {
+        *    opaque dh_p<1..2^16-1>;
+        *    opaque dh_g<1..2^16-1>;
+        *    opaque dh_Ys<1..2^16-1>;
+        * } ServerDHParams;
+        */
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_SERVER_KEY_EXCHANGE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+
+       /* body - ServerDHParams */
+       /* dh_p */
+       if (pos + 2 + conn->cred->dh_p_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+                          "dh_p");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+       WPA_PUT_BE16(pos, conn->cred->dh_p_len);
+       pos += 2;
+       os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
+       pos += conn->cred->dh_p_len;
+
+       /* dh_g */
+       if (pos + 2 + conn->cred->dh_g_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+                          "dh_g");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+       WPA_PUT_BE16(pos, conn->cred->dh_g_len);
+       pos += 2;
+       os_memcpy(pos, conn->cred->dh_g, conn->cred->dh_g_len);
+       pos += conn->cred->dh_g_len;
+
+       /* dh_Ys */
+       if (pos + 2 + dh_ys_len > end) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
+                          "dh_Ys");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               os_free(dh_ys);
+               return -1;
+       }
+       WPA_PUT_BE16(pos, dh_ys_len);
+       pos += 2;
+       os_memcpy(pos, dh_ys, dh_ys_len);
+       pos += dh_ys_len;
+       os_free(dh_ys);
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_certificate_request(struct tlsv1_server *conn,
+                                               u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+
+       if (!conn->verify_peer) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No CertificateRequest needed");
+               return 0;
+       }
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_CERTIFICATE_REQUEST;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - CertificateRequest */
+
+       /*
+        * enum {
+        *   rsa_sign(1), dss_sign(2), rsa_fixed_dh(3), dss_fixed_dh(4),
+        *   (255)
+        * } ClientCertificateType;
+        * ClientCertificateType certificate_types<1..2^8-1>
+        */
+       *pos++ = 1;
+       *pos++ = 1; /* rsa_sign */
+
+       /*
+        * opaque DistinguishedName<1..2^16-1>
+        * DistinguishedName certificate_authorities<3..2^16-1>
+        */
+       /* TODO: add support for listing DNs for trusted CAs */
+       WPA_PUT_BE16(pos, 0);
+       pos += 2;
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_hello_done(struct tlsv1_server *conn,
+                                      u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_SERVER_HELLO_DONE;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       /* body - ServerHelloDone (empty) */
+
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       pos = rhdr + rlen;
+
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
+                                              u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr;
+       size_t rlen;
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+       *pos = TLS_CHANGE_CIPHER_SPEC;
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC,
+                             rhdr, end - rhdr, 1, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       if (tlsv1_record_change_write_cipher(&conn->rl) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to set write cipher for "
+                          "record layer");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       *msgpos = rhdr + rlen;
+
+       return 0;
+}
+
+
+static int tls_write_server_finished(struct tlsv1_server *conn,
+                                    u8 **msgpos, u8 *end)
+{
+       u8 *pos, *rhdr, *hs_start, *hs_length;
+       size_t rlen, hlen;
+       u8 verify_data[TLS_VERIFY_DATA_LEN];
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+
+       pos = *msgpos;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+
+       /* Encrypted Handshake Message: Finished */
+
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_server == NULL ||
+           crypto_hash_finish(conn->verify.md5_server, hash, &hlen) < 0) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_server = NULL;
+               crypto_hash_finish(conn->verify.sha1_server, NULL, NULL);
+               conn->verify.sha1_server = NULL;
+               return -1;
+       }
+       conn->verify.md5_server = NULL;
+       hlen = SHA1_MAC_LEN;
+       if (conn->verify.sha1_server == NULL ||
+           crypto_hash_finish(conn->verify.sha1_server, hash + MD5_MAC_LEN,
+                              &hlen) < 0) {
+               conn->verify.sha1_server = NULL;
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       conn->verify.sha1_server = NULL;
+
+       if (tls_prf(conn->master_secret, TLS_MASTER_SECRET_LEN,
+                   "server finished", hash, MD5_MAC_LEN + SHA1_MAC_LEN,
+                   verify_data, TLS_VERIFY_DATA_LEN)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to generate verify_data");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
+                       verify_data, TLS_VERIFY_DATA_LEN);
+
+       rhdr = pos;
+       pos += TLS_RECORD_HEADER_LEN;
+       /* Handshake */
+       hs_start = pos;
+       /* HandshakeType msg_type */
+       *pos++ = TLS_HANDSHAKE_TYPE_FINISHED;
+       /* uint24 length (to be filled) */
+       hs_length = pos;
+       pos += 3;
+       os_memcpy(pos, verify_data, TLS_VERIFY_DATA_LEN);
+       pos += TLS_VERIFY_DATA_LEN;
+       WPA_PUT_BE24(hs_length, pos - hs_length - 3);
+       tls_verify_hash_add(&conn->verify, hs_start, pos - hs_start);
+
+       if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
+                             rhdr, end - rhdr, pos - hs_start, &rlen) < 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to create a record");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               return -1;
+       }
+
+       pos = rhdr + rlen;
+
+       *msgpos = pos;
+
+       return 0;
+}
+
+
+static u8 * tls_send_server_hello(struct tlsv1_server *conn, size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+       size_t msglen;
+
+       *out_len = 0;
+
+       msglen = 1000 + tls_server_cert_chain_der_len(conn);
+
+       msg = os_malloc(msglen);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + msglen;
+
+       if (tls_write_server_hello(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       if (conn->use_session_ticket) {
+               /* Abbreviated handshake using session ticket; RFC 4507 */
+               if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+                   tls_write_server_finished(conn, &pos, end) < 0) {
+                       os_free(msg);
+                       return NULL;
+               }
+
+               *out_len = pos - msg;
+
+               conn->state = CHANGE_CIPHER_SPEC;
+
+               return msg;
+       }
+
+       /* Full handshake */
+       if (tls_write_server_certificate(conn, &pos, end) < 0 ||
+           tls_write_server_key_exchange(conn, &pos, end) < 0 ||
+           tls_write_server_certificate_request(conn, &pos, end) < 0 ||
+           tls_write_server_hello_done(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       conn->state = CLIENT_CERTIFICATE;
+
+       return msg;
+}
+
+
+static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
+                                       size_t *out_len)
+{
+       u8 *msg, *end, *pos;
+
+       *out_len = 0;
+
+       msg = os_malloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       pos = msg;
+       end = msg + 1000;
+
+       if (tls_write_server_change_cipher_spec(conn, &pos, end) < 0 ||
+           tls_write_server_finished(conn, &pos, end) < 0) {
+               os_free(msg);
+               return NULL;
+       }
+
+       *out_len = pos - msg;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+       conn->state = ESTABLISHED;
+
+       return msg;
+}
+
+
+u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
+{
+       switch (conn->state) {
+       case SERVER_HELLO:
+               return tls_send_server_hello(conn, out_len);
+       case SERVER_CHANGE_CIPHER_SPEC:
+               return tls_send_change_cipher_spec(conn, out_len);
+       default:
+               if (conn->state == ESTABLISHED && conn->use_session_ticket) {
+                       /* Abbreviated handshake was already completed. */
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
+                          "generating reply", conn->state);
+               return NULL;
+       }
+}
+
+
+u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
+                            u8 description, size_t *out_len)
+{
+       u8 *alert, *pos, *length;
+
+       wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+       *out_len = 0;
+
+       alert = os_malloc(10);
+       if (alert == NULL)
+               return NULL;
+
+       pos = alert;
+
+       /* TLSPlaintext */
+       /* ContentType type */
+       *pos++ = TLS_CONTENT_TYPE_ALERT;
+       /* ProtocolVersion version */
+       WPA_PUT_BE16(pos, TLS_VERSION);
+       pos += 2;
+       /* uint16 length (to be filled) */
+       length = pos;
+       pos += 2;
+       /* opaque fragment[TLSPlaintext.length] */
+
+       /* Alert */
+       /* AlertLevel level */
+       *pos++ = level;
+       /* AlertDescription description */
+       *pos++ = description;
+
+       WPA_PUT_BE16(length, pos - length - 2);
+       *out_len = pos - alert;
+
+       return alert;
+}
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
new file mode 100644 (file)
index 0000000..bc93df6
--- /dev/null
@@ -0,0 +1,1985 @@
+/*
+ * X.509v3 certificate parsing and processing (RFC 3280 profile)
+ * Copyright (c) 2006-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 "crypto/crypto.h"
+#include "asn1.h"
+#include "x509v3.h"
+
+
+static void x509_free_name(struct x509_name *name)
+{
+       size_t i;
+
+       for (i = 0; i < name->num_attr; i++) {
+               os_free(name->attr[i].value);
+               name->attr[i].value = NULL;
+               name->attr[i].type = X509_NAME_ATTR_NOT_USED;
+       }
+       name->num_attr = 0;
+       os_free(name->email);
+       name->email = NULL;
+
+       os_free(name->alt_email);
+       os_free(name->dns);
+       os_free(name->uri);
+       os_free(name->ip);
+       name->alt_email = name->dns = name->uri = NULL;
+       name->ip = NULL;
+       name->ip_len = 0;
+       os_memset(&name->rid, 0, sizeof(name->rid));
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate
+ * @cert: Certificate to be freed
+ */
+void x509_certificate_free(struct x509_certificate *cert)
+{
+       if (cert == NULL)
+               return;
+       if (cert->next) {
+               wpa_printf(MSG_DEBUG, "X509: x509_certificate_free: cer=%p "
+                          "was still on a list (next=%p)\n",
+                          cert, cert->next);
+       }
+       x509_free_name(&cert->issuer);
+       x509_free_name(&cert->subject);
+       os_free(cert->public_key);
+       os_free(cert->sign_value);
+       os_free(cert);
+}
+
+
+/**
+ * x509_certificate_free - Free an X.509 certificate chain
+ * @cert: Pointer to the first certificate in the chain
+ */
+void x509_certificate_chain_free(struct x509_certificate *cert)
+{
+       struct x509_certificate *next;
+
+       while (cert) {
+               next = cert->next;
+               cert->next = NULL;
+               x509_certificate_free(cert);
+               cert = next;
+       }
+}
+
+
+static int x509_whitespace(char c)
+{
+       return c == ' ' || c == '\t';
+}
+
+
+static void x509_str_strip_whitespace(char *a)
+{
+       char *ipos, *opos;
+       int remove_whitespace = 1;
+
+       ipos = opos = a;
+
+       while (*ipos) {
+               if (remove_whitespace && x509_whitespace(*ipos))
+                       ipos++;
+               else {
+                       remove_whitespace = x509_whitespace(*ipos);
+                       *opos++ = *ipos++;
+               }
+       }
+
+       *opos-- = '\0';
+       if (opos > a && x509_whitespace(*opos))
+               *opos = '\0';
+}
+
+
+static int x509_str_compare(const char *a, const char *b)
+{
+       char *aa, *bb;
+       int ret;
+
+       if (!a && b)
+               return -1;
+       if (a && !b)
+               return 1;
+       if (!a && !b)
+               return 0;
+
+       aa = os_strdup(a);
+       bb = os_strdup(b);
+
+       if (aa == NULL || bb == NULL) {
+               os_free(aa);
+               os_free(bb);
+               return os_strcasecmp(a, b);
+       }
+
+       x509_str_strip_whitespace(aa);
+       x509_str_strip_whitespace(bb);
+
+       ret = os_strcasecmp(aa, bb);
+
+       os_free(aa);
+       os_free(bb);
+
+       return ret;
+}
+
+
+/**
+ * x509_name_compare - Compare X.509 certificate names
+ * @a: Certificate name
+ * @b: Certificate name
+ * Returns: <0, 0, or >0 based on whether a is less than, equal to, or
+ * greater than b
+ */
+int x509_name_compare(struct x509_name *a, struct x509_name *b)
+{
+       int res;
+       size_t i;
+
+       if (!a && b)
+               return -1;
+       if (a && !b)
+               return 1;
+       if (!a && !b)
+               return 0;
+       if (a->num_attr < b->num_attr)
+               return -1;
+       if (a->num_attr > b->num_attr)
+               return 1;
+
+       for (i = 0; i < a->num_attr; i++) {
+               if (a->attr[i].type < b->attr[i].type)
+                       return -1;
+               if (a->attr[i].type > b->attr[i].type)
+                       return -1;
+               res = x509_str_compare(a->attr[i].value, b->attr[i].value);
+               if (res)
+                       return res;
+       }
+       res = x509_str_compare(a->email, b->email);
+       if (res)
+               return res;
+
+       return 0;
+}
+
+
+static int x509_parse_algorithm_identifier(
+       const u8 *buf, size_t len,
+       struct x509_algorithm_identifier *id, const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       /*
+        * AlgorithmIdentifier ::= SEQUENCE {
+        *     algorithm            OBJECT IDENTIFIER,
+        *     parameters           ANY DEFINED BY algorithm OPTIONAL
+        * }
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       if (end > buf + len)
+               return -1;
+
+       *next = end;
+
+       if (asn1_get_oid(pos, end - pos, &id->oid, &pos))
+               return -1;
+
+       /* TODO: optional parameters */
+
+       return 0;
+}
+
+
+static int x509_parse_public_key(const u8 *buf, size_t len,
+                                struct x509_certificate *cert,
+                                const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+
+       /*
+        * SubjectPublicKeyInfo ::= SEQUENCE {
+        *     algorithm            AlgorithmIdentifier,
+        *     subjectPublicKey     BIT STRING
+        * }
+        */
+
+       pos = buf;
+       end = buf + len;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(SubjectPublicKeyInfo) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+
+       if (pos + hdr.length > end)
+               return -1;
+       end = pos + hdr.length;
+       *next = end;
+
+       if (x509_parse_algorithm_identifier(pos, end - pos,
+                                           &cert->public_key_alg, &pos))
+               return -1;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_BITSTRING) {
+               wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+                          "(subjectPublicKey) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       if (hdr.length < 1)
+               return -1;
+       pos = hdr.payload;
+       if (*pos) {
+               wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+                          *pos);
+               /*
+                * TODO: should this be rejected? X.509 certificates are
+                * unlikely to use such a construction. Now we would end up
+                * including the extra bits in the buffer which may also be
+                * ok.
+                */
+       }
+       os_free(cert->public_key);
+       cert->public_key = os_malloc(hdr.length - 1);
+       if (cert->public_key == NULL) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+                          "public key");
+               return -1;
+       }
+       os_memcpy(cert->public_key, pos + 1, hdr.length - 1);
+       cert->public_key_len = hdr.length - 1;
+       wpa_hexdump(MSG_MSGDUMP, "X509: subjectPublicKey",
+                   cert->public_key, cert->public_key_len);
+
+       return 0;
+}
+
+
+static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name,
+                          const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end;
+       struct asn1_oid oid;
+       char *val;
+
+       /*
+        * Name ::= CHOICE { RDNSequence }
+        * RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
+        * RelativeDistinguishedName ::= SET OF AttributeTypeAndValue
+        * AttributeTypeAndValue ::= SEQUENCE {
+        *     type     AttributeType,
+        *     value    AttributeValue
+        * }
+        * AttributeType ::= OBJECT IDENTIFIER
+        * AttributeValue ::= ANY DEFINED BY AttributeType
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(Name / RDNSequencer) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+
+       if (pos + hdr.length > buf + len)
+               return -1;
+
+       end = *next = pos + hdr.length;
+
+       while (pos < end) {
+               enum x509_name_attr_type type;
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL ||
+                   hdr.tag != ASN1_TAG_SET) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected SET "
+                                  "(RelativeDistinguishedName) - found class "
+                                  "%d tag 0x%x", hdr.class, hdr.tag);
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               set_pos = hdr.payload;
+               pos = set_end = hdr.payload + hdr.length;
+
+               if (asn1_get_next(set_pos, set_end - set_pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL ||
+                   hdr.tag != ASN1_TAG_SEQUENCE) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                                  "(AttributeTypeAndValue) - found class %d "
+                                  "tag 0x%x", hdr.class, hdr.tag);
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               seq_pos = hdr.payload;
+               seq_end = hdr.payload + hdr.length;
+
+               if (asn1_get_oid(seq_pos, seq_end - seq_pos, &oid, &seq_pos)) {
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               if (asn1_get_next(seq_pos, seq_end - seq_pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL) {
+                       wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                                  "AttributeValue");
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               /* RFC 3280:
+                * MUST: country, organization, organizational-unit,
+                * distinguished name qualifier, state or province name,
+                * common name, serial number.
+                * SHOULD: locality, title, surname, given name, initials,
+                * pseudonym, generation qualifier.
+                * MUST: domainComponent (RFC 2247).
+                */
+               type = X509_NAME_ATTR_NOT_USED;
+               if (oid.len == 4 &&
+                   oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) {
+                       /* id-at ::= 2.5.4 */
+                       switch (oid.oid[3]) {
+                       case 3:
+                               /* commonName */
+                               type = X509_NAME_ATTR_CN;
+                               break;
+                       case 6:
+                               /*  countryName */
+                               type = X509_NAME_ATTR_C;
+                               break;
+                       case 7:
+                               /* localityName */
+                               type = X509_NAME_ATTR_L;
+                               break;
+                       case 8:
+                               /* stateOrProvinceName */
+                               type = X509_NAME_ATTR_ST;
+                               break;
+                       case 10:
+                               /* organizationName */
+                               type = X509_NAME_ATTR_O;
+                               break;
+                       case 11:
+                               /* organizationalUnitName */
+                               type = X509_NAME_ATTR_OU;
+                               break;
+                       }
+               } else if (oid.len == 7 &&
+                          oid.oid[0] == 1 && oid.oid[1] == 2 &&
+                          oid.oid[2] == 840 && oid.oid[3] == 113549 &&
+                          oid.oid[4] == 1 && oid.oid[5] == 9 &&
+                          oid.oid[6] == 1) {
+                       /* 1.2.840.113549.1.9.1 - e-mailAddress */
+                       os_free(name->email);
+                       name->email = os_malloc(hdr.length + 1);
+                       if (name->email == NULL) {
+                               x509_free_name(name);
+                               return -1;
+                       }
+                       os_memcpy(name->email, hdr.payload, hdr.length);
+                       name->email[hdr.length] = '\0';
+                       continue;
+               } else if (oid.len == 7 &&
+                          oid.oid[0] == 0 && oid.oid[1] == 9 &&
+                          oid.oid[2] == 2342 && oid.oid[3] == 19200300 &&
+                          oid.oid[4] == 100 && oid.oid[5] == 1 &&
+                          oid.oid[6] == 25) {
+                       /* 0.9.2342.19200300.100.1.25 - domainComponent */
+                       type = X509_NAME_ATTR_DC;
+               }
+
+               if (type == X509_NAME_ATTR_NOT_USED) {
+                       wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID",
+                                   (u8 *) oid.oid,
+                                   oid.len * sizeof(oid.oid[0]));
+                       wpa_hexdump_ascii(MSG_MSGDUMP, "X509: Attribute Data",
+                                         hdr.payload, hdr.length);
+                       continue;
+               }
+
+               if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) {
+                       wpa_printf(MSG_INFO, "X509: Too many Name attributes");
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               val = os_malloc(hdr.length + 1);
+               if (val == NULL) {
+                       x509_free_name(name);
+                       return -1;
+               }
+               os_memcpy(val, hdr.payload, hdr.length);
+               val[hdr.length] = '\0';
+               if (os_strlen(val) != hdr.length) {
+                       wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                                  "embedded NUL byte in a string (%s[NUL])",
+                                  val);
+                       x509_free_name(name);
+                       return -1;
+               }
+
+               name->attr[name->num_attr].type = type;
+               name->attr[name->num_attr].value = val;
+               name->num_attr++;
+       }
+
+       return 0;
+}
+
+
+static char * x509_name_attr_str(enum x509_name_attr_type type)
+{
+       switch (type) {
+       case X509_NAME_ATTR_NOT_USED:
+               return "[N/A]";
+       case X509_NAME_ATTR_DC:
+               return "DC";
+       case X509_NAME_ATTR_CN:
+               return "CN";
+       case X509_NAME_ATTR_C:
+               return "C";
+       case X509_NAME_ATTR_L:
+               return "L";
+       case X509_NAME_ATTR_ST:
+               return "ST";
+       case X509_NAME_ATTR_O:
+               return "O";
+       case X509_NAME_ATTR_OU:
+               return "OU";
+       }
+       return "?";
+}
+
+
+/**
+ * x509_name_string - Convert an X.509 certificate name into a string
+ * @name: Name to convert
+ * @buf: Buffer for the string
+ * @len: Maximum buffer length
+ */
+void x509_name_string(struct x509_name *name, char *buf, size_t len)
+{
+       char *pos, *end;
+       int ret;
+       size_t i;
+
+       if (len == 0)
+               return;
+
+       pos = buf;
+       end = buf + len;
+
+       for (i = 0; i < name->num_attr; i++) {
+               ret = os_snprintf(pos, end - pos, "%s=%s, ",
+                                 x509_name_attr_str(name->attr[i].type),
+                                 name->attr[i].value);
+               if (ret < 0 || ret >= end - pos)
+                       goto done;
+               pos += ret;
+       }
+
+       if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') {
+               pos--;
+               *pos = '\0';
+               pos--;
+               *pos = '\0';
+       }
+
+       if (name->email) {
+               ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
+                                 name->email);
+               if (ret < 0 || ret >= end - pos)
+                       goto done;
+               pos += ret;
+       }
+
+done:
+       end[-1] = '\0';
+}
+
+
+static int x509_parse_time(const u8 *buf, size_t len, u8 asn1_tag,
+                          os_time_t *val)
+{
+       const char *pos;
+       int year, month, day, hour, min, sec;
+
+       /*
+        * Time ::= CHOICE {
+        *     utcTime        UTCTime,
+        *     generalTime    GeneralizedTime
+        * }
+        *
+        * UTCTime: YYMMDDHHMMSSZ
+        * GeneralizedTime: YYYYMMDDHHMMSSZ
+        */
+
+       pos = (const char *) buf;
+
+       switch (asn1_tag) {
+       case ASN1_TAG_UTCTIME:
+               if (len != 13 || buf[12] != 'Z') {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+                                         "UTCTime format", buf, len);
+                       return -1;
+               }
+               if (sscanf(pos, "%02d", &year) != 1) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+                                         "UTCTime year", buf, len);
+                       return -1;
+               }
+               if (year < 50)
+                       year += 2000;
+               else
+                       year += 1900;
+               pos += 2;
+               break;
+       case ASN1_TAG_GENERALIZEDTIME:
+               if (len != 15 || buf[14] != 'Z') {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Unrecognized "
+                                         "GeneralizedTime format", buf, len);
+                       return -1;
+               }
+               if (sscanf(pos, "%04d", &year) != 1) {
+                       wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse "
+                                         "GeneralizedTime year", buf, len);
+                       return -1;
+               }
+               pos += 4;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "X509: Expected UTCTime or "
+                          "GeneralizedTime - found tag 0x%x", asn1_tag);
+               return -1;
+       }
+
+       if (sscanf(pos, "%02d", &month) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(month)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &day) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(day)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &hour) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(hour)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &min) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(min)", buf, len);
+               return -1;
+       }
+       pos += 2;
+
+       if (sscanf(pos, "%02d", &sec) != 1) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse Time "
+                                 "(sec)", buf, len);
+               return -1;
+       }
+
+       if (os_mktime(year, month, day, hour, min, sec, val) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to convert Time",
+                                 buf, len);
+               if (year < 1970) {
+                       /*
+                        * At least some test certificates have been configured
+                        * to use dates prior to 1970. Set the date to
+                        * beginning of 1970 to handle these case.
+                        */
+                       wpa_printf(MSG_DEBUG, "X509: Year=%d before epoch - "
+                                  "assume epoch as the time", year);
+                       *val = 0;
+                       return 0;
+               }
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_validity(const u8 *buf, size_t len,
+                              struct x509_certificate *cert, const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos;
+       size_t plen;
+
+       /*
+        * Validity ::= SEQUENCE {
+        *     notBefore      Time,
+        *     notAfter       Time
+        * }
+        *
+        * RFC 3280, 4.1.2.5:
+        * CAs conforming to this profile MUST always encode certificate
+        * validity dates through the year 2049 as UTCTime; certificate
+        * validity dates in 2050 or later MUST be encoded as GeneralizedTime.
+        */
+
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(Validity) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       plen = hdr.length;
+
+       if (pos + plen > buf + len)
+               return -1;
+
+       *next = pos + plen;
+
+       if (asn1_get_next(pos, plen, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+                           &cert->not_before) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notBefore "
+                                 "Time", hdr.payload, hdr.length);
+               return -1;
+       }
+
+       pos = hdr.payload + hdr.length;
+       plen = *next - pos;
+
+       if (asn1_get_next(pos, plen, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           x509_parse_time(hdr.payload, hdr.length, hdr.tag,
+                           &cert->not_after) < 0) {
+               wpa_hexdump_ascii(MSG_DEBUG, "X509: Failed to parse notAfter "
+                                 "Time", hdr.payload, hdr.length);
+               return -1;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "X509: Validity: notBefore: %lu notAfter: %lu",
+                  (unsigned long) cert->not_before,
+                  (unsigned long) cert->not_after);
+
+       return 0;
+}
+
+
+static int x509_id_ce_oid(struct asn1_oid *oid)
+{
+       /* id-ce arc from X.509 for standard X.509v3 extensions */
+       return oid->len >= 4 &&
+               oid->oid[0] == 2 /* joint-iso-ccitt */ &&
+               oid->oid[1] == 5 /* ds */ &&
+               oid->oid[2] == 29 /* id-ce */;
+}
+
+
+static int x509_parse_ext_key_usage(struct x509_certificate *cert,
+                                   const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+
+       /*
+        * KeyUsage ::= BIT STRING {
+        *     digitalSignature        (0),
+        *     nonRepudiation          (1),
+        *     keyEncipherment         (2),
+        *     dataEncipherment        (3),
+        *     keyAgreement            (4),
+        *     keyCertSign             (5),
+        *     cRLSign                 (6),
+        *     encipherOnly            (7),
+        *     decipherOnly            (8) }
+        */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_BITSTRING ||
+           hdr.length < 1) {
+               wpa_printf(MSG_DEBUG, "X509: Expected BIT STRING in "
+                          "KeyUsage; found %d tag 0x%x len %d",
+                          hdr.class, hdr.tag, hdr.length);
+               return -1;
+       }
+
+       cert->extensions_present |= X509_EXT_KEY_USAGE;
+       cert->key_usage = asn1_bit_string_to_long(hdr.payload, hdr.length);
+
+       wpa_printf(MSG_DEBUG, "X509: KeyUsage 0x%lx", cert->key_usage);
+
+       return 0;
+}
+
+
+static int x509_parse_ext_basic_constraints(struct x509_certificate *cert,
+                                           const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+       unsigned long value;
+       size_t left;
+
+       /*
+        * BasicConstraints ::= SEQUENCE {
+        * cA                      BOOLEAN DEFAULT FALSE,
+        * pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
+        */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+                          "BasicConstraints; found %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       cert->extensions_present |= X509_EXT_BASIC_CONSTRAINTS;
+
+       if (hdr.length == 0)
+               return 0;
+
+       if (asn1_get_next(hdr.payload, hdr.length, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                          "BasicConstraints");
+               return -1;
+       }
+
+       if (hdr.tag == ASN1_TAG_BOOLEAN) {
+               if (hdr.length != 1) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected "
+                                  "Boolean length (%u) in BasicConstraints",
+                                  hdr.length);
+                       return -1;
+               }
+               cert->ca = hdr.payload[0];
+
+               if (hdr.payload + hdr.length == pos + len) {
+                       wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d",
+                                  cert->ca);
+                       return 0;
+               }
+
+               if (asn1_get_next(hdr.payload + hdr.length, len - hdr.length,
+                                 &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_UNIVERSAL) {
+                       wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                                  "BasicConstraints");
+                       return -1;
+               }
+       }
+
+       if (hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "X509: Expected INTEGER in "
+                          "BasicConstraints; found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       left = hdr.length;
+       value = 0;
+       while (left) {
+               value <<= 8;
+               value |= *pos++;
+               left--;
+       }
+
+       cert->path_len_constraint = value;
+       cert->extensions_present |= X509_EXT_PATH_LEN_CONSTRAINT;
+
+       wpa_printf(MSG_DEBUG, "X509: BasicConstraints - cA=%d "
+                  "pathLenConstraint=%lu",
+                  cert->ca, cert->path_len_constraint);
+
+       return 0;
+}
+
+
+static int x509_parse_alt_name_rfc8222(struct x509_name *name,
+                                      const u8 *pos, size_t len)
+{
+       /* rfc822Name IA5String */
+       wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len);
+       os_free(name->alt_email);
+       name->alt_email = os_zalloc(len + 1);
+       if (name->alt_email == NULL)
+               return -1;
+       os_memcpy(name->alt_email, pos, len);
+       if (os_strlen(name->alt_email) != len) {
+               wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                          "embedded NUL byte in rfc822Name (%s[NUL])",
+                          name->alt_email);
+               os_free(name->alt_email);
+               name->alt_email = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+
+static int x509_parse_alt_name_dns(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       /* dNSName IA5String */
+       wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len);
+       os_free(name->dns);
+       name->dns = os_zalloc(len + 1);
+       if (name->dns == NULL)
+               return -1;
+       os_memcpy(name->dns, pos, len);
+       if (os_strlen(name->dns) != len) {
+               wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                          "embedded NUL byte in dNSName (%s[NUL])",
+                          name->dns);
+               os_free(name->dns);
+               name->dns = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+
+static int x509_parse_alt_name_uri(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       /* uniformResourceIdentifier IA5String */
+       wpa_hexdump_ascii(MSG_MSGDUMP,
+                         "X509: altName - uniformResourceIdentifier",
+                         pos, len);
+       os_free(name->uri);
+       name->uri = os_zalloc(len + 1);
+       if (name->uri == NULL)
+               return -1;
+       os_memcpy(name->uri, pos, len);
+       if (os_strlen(name->uri) != len) {
+               wpa_printf(MSG_INFO, "X509: Reject certificate with "
+                          "embedded NUL byte in uniformResourceIdentifier "
+                          "(%s[NUL])", name->uri);
+               os_free(name->uri);
+               name->uri = NULL;
+               return -1;
+       }
+       return 0;
+}
+
+
+static int x509_parse_alt_name_ip(struct x509_name *name,
+                                      const u8 *pos, size_t len)
+{
+       /* iPAddress OCTET STRING */
+       wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len);
+       os_free(name->ip);
+       name->ip = os_malloc(len);
+       if (name->ip == NULL)
+               return -1;
+       os_memcpy(name->ip, pos, len);
+       name->ip_len = len;
+       return 0;
+}
+
+
+static int x509_parse_alt_name_rid(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       char buf[80];
+
+       /* registeredID OBJECT IDENTIFIER */
+       if (asn1_parse_oid(pos, len, &name->rid) < 0)
+               return -1;
+
+       asn1_oid_to_str(&name->rid, buf, sizeof(buf));
+       wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf);
+
+       return 0;
+}
+
+
+static int x509_parse_ext_alt_name(struct x509_name *name,
+                                  const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *p, *end;
+
+       /*
+        * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
+        *
+        * GeneralName ::= CHOICE {
+        *     otherName                       [0]     OtherName,
+        *     rfc822Name                      [1]     IA5String,
+        *     dNSName                         [2]     IA5String,
+        *     x400Address                     [3]     ORAddress,
+        *     directoryName                   [4]     Name,
+        *     ediPartyName                    [5]     EDIPartyName,
+        *     uniformResourceIdentifier       [6]     IA5String,
+        *     iPAddress                       [7]     OCTET STRING,
+        *     registeredID                    [8]     OBJECT IDENTIFIER }
+        *
+        * OtherName ::= SEQUENCE {
+        *     type-id    OBJECT IDENTIFIER,
+        *     value      [0] EXPLICIT ANY DEFINED BY type-id }
+        *
+        * EDIPartyName ::= SEQUENCE {
+        *     nameAssigner            [0]     DirectoryString OPTIONAL,
+        *     partyName               [1]     DirectoryString }
+        */
+
+       for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) {
+               int res;
+
+               if (asn1_get_next(p, end - p, &hdr) < 0) {
+                       wpa_printf(MSG_DEBUG, "X509: Failed to parse "
+                                  "SubjectAltName item");
+                       return -1;
+               }
+
+               if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC)
+                       continue;
+
+               switch (hdr.tag) {
+               case 1:
+                       res = x509_parse_alt_name_rfc8222(name, hdr.payload,
+                                                         hdr.length);
+                       break;
+               case 2:
+                       res = x509_parse_alt_name_dns(name, hdr.payload,
+                                                     hdr.length);
+                       break;
+               case 6:
+                       res = x509_parse_alt_name_uri(name, hdr.payload,
+                                                     hdr.length);
+                       break;
+               case 7:
+                       res = x509_parse_alt_name_ip(name, hdr.payload,
+                                                    hdr.length);
+                       break;
+               case 8:
+                       res = x509_parse_alt_name_rid(name, hdr.payload,
+                                                     hdr.length);
+                       break;
+               case 0: /* TODO: otherName */
+               case 3: /* TODO: x500Address */
+               case 4: /* TODO: directoryName */
+               case 5: /* TODO: ediPartyName */
+               default:
+                       res = 0;
+                       break;
+               }
+               if (res < 0)
+                       return res;
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert,
+                                          const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+
+       /* SubjectAltName ::= GeneralNames */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+                          "SubjectAltName; found %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: SubjectAltName");
+       cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME;
+
+       if (hdr.length == 0)
+               return 0;
+
+       return x509_parse_ext_alt_name(&cert->subject, hdr.payload,
+                                      hdr.length);
+}
+
+
+static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert,
+                                         const u8 *pos, size_t len)
+{
+       struct asn1_hdr hdr;
+
+       /* IssuerAltName ::= GeneralNames */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in "
+                          "IssuerAltName; found %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: IssuerAltName");
+       cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME;
+
+       if (hdr.length == 0)
+               return 0;
+
+       return x509_parse_ext_alt_name(&cert->issuer, hdr.payload,
+                                      hdr.length);
+}
+
+
+static int x509_parse_extension_data(struct x509_certificate *cert,
+                                    struct asn1_oid *oid,
+                                    const u8 *pos, size_t len)
+{
+       if (!x509_id_ce_oid(oid))
+               return 1;
+
+       /* TODO: add other extensions required by RFC 3280, Ch 4.2:
+        * certificate policies (section 4.2.1.5)
+        * name constraints (section 4.2.1.11)
+        * policy constraints (section 4.2.1.12)
+        * extended key usage (section 4.2.1.13)
+        * inhibit any-policy (section 4.2.1.15)
+        */
+       switch (oid->oid[3]) {
+       case 15: /* id-ce-keyUsage */
+               return x509_parse_ext_key_usage(cert, pos, len);
+       case 17: /* id-ce-subjectAltName */
+               return x509_parse_ext_subject_alt_name(cert, pos, len);
+       case 18: /* id-ce-issuerAltName */
+               return x509_parse_ext_issuer_alt_name(cert, pos, len);
+       case 19: /* id-ce-basicConstraints */
+               return x509_parse_ext_basic_constraints(cert, pos, len);
+       default:
+               return 1;
+       }
+}
+
+
+static int x509_parse_extension(struct x509_certificate *cert,
+                               const u8 *pos, size_t len, const u8 **next)
+{
+       const u8 *end;
+       struct asn1_hdr hdr;
+       struct asn1_oid oid;
+       int critical_ext = 0, res;
+       char buf[80];
+
+       /*
+        * Extension  ::=  SEQUENCE  {
+        *     extnID      OBJECT IDENTIFIER,
+        *     critical    BOOLEAN DEFAULT FALSE,
+        *     extnValue   OCTET STRING
+        * }
+        */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+                          "Extensions: class %d tag 0x%x; expected SEQUENCE",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       *next = end = pos + hdr.length;
+
+       if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data for "
+                          "Extension (expected OID)");
+               return -1;
+       }
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           (hdr.tag != ASN1_TAG_BOOLEAN &&
+            hdr.tag != ASN1_TAG_OCTETSTRING)) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header in "
+                          "Extensions: class %d tag 0x%x; expected BOOLEAN "
+                          "or OCTET STRING", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       if (hdr.tag == ASN1_TAG_BOOLEAN) {
+               if (hdr.length != 1) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected "
+                                  "Boolean length (%u)", hdr.length);
+                       return -1;
+               }
+               critical_ext = hdr.payload[0];
+               pos = hdr.payload;
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   (hdr.class != ASN1_CLASS_UNIVERSAL &&
+                    hdr.class != ASN1_CLASS_PRIVATE) ||
+                   hdr.tag != ASN1_TAG_OCTETSTRING) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 header "
+                                  "in Extensions: class %d tag 0x%x; "
+                                  "expected OCTET STRING",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+       }
+
+       asn1_oid_to_str(&oid, buf, sizeof(buf));
+       wpa_printf(MSG_DEBUG, "X509: Extension: extnID=%s critical=%d",
+                  buf, critical_ext);
+       wpa_hexdump(MSG_MSGDUMP, "X509: extnValue", hdr.payload, hdr.length);
+
+       res = x509_parse_extension_data(cert, &oid, hdr.payload, hdr.length);
+       if (res < 0)
+               return res;
+       if (res == 1 && critical_ext) {
+               wpa_printf(MSG_INFO, "X509: Unknown critical extension %s",
+                          buf);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_extensions(struct x509_certificate *cert,
+                                const u8 *pos, size_t len)
+{
+       const u8 *end;
+       struct asn1_hdr hdr;
+
+       /* Extensions  ::=  SEQUENCE SIZE (1..MAX) OF Extension */
+
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Unexpected ASN.1 data "
+                          "for Extensions: class %d tag 0x%x; "
+                          "expected SEQUENCE", hdr.class, hdr.tag);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       while (pos < end) {
+               if (x509_parse_extension(cert, pos, end - pos, &pos)
+                   < 0)
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
+                                     struct x509_certificate *cert,
+                                     const u8 **next)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end;
+       size_t left;
+       char sbuf[128];
+       unsigned long value;
+
+       /* tbsCertificate TBSCertificate ::= SEQUENCE */
+       if (asn1_get_next(buf, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: tbsCertificate did not start "
+                          "with a valid SEQUENCE - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+       pos = hdr.payload;
+       end = *next = pos + hdr.length;
+
+       /*
+        * version [0]  EXPLICIT Version DEFAULT v1
+        * Version  ::=  INTEGER  {  v1(0), v2(1), v3(2)  }
+        */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0)
+               return -1;
+       pos = hdr.payload;
+
+       if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC) {
+               if (asn1_get_next(pos, end - pos, &hdr) < 0)
+                       return -1;
+
+               if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+                   hdr.tag != ASN1_TAG_INTEGER) {
+                       wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+                                  "version field - found class %d tag 0x%x",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+               if (hdr.length != 1) {
+                       wpa_printf(MSG_DEBUG, "X509: Unexpected version field "
+                                  "length %u (expected 1)", hdr.length);
+                       return -1;
+               }
+               pos = hdr.payload;
+               left = hdr.length;
+               value = 0;
+               while (left) {
+                       value <<= 8;
+                       value |= *pos++;
+                       left--;
+               }
+
+               cert->version = value;
+               if (cert->version != X509_CERT_V1 &&
+                   cert->version != X509_CERT_V2 &&
+                   cert->version != X509_CERT_V3) {
+                       wpa_printf(MSG_DEBUG, "X509: Unsupported version %d",
+                                  cert->version + 1);
+                       return -1;
+               }
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0)
+                       return -1;
+       } else
+               cert->version = X509_CERT_V1;
+       wpa_printf(MSG_MSGDUMP, "X509: Version X.509v%d", cert->version + 1);
+
+       /* serialNumber CertificateSerialNumber ::= INTEGER */
+       if (hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_INTEGER) {
+               wpa_printf(MSG_DEBUG, "X509: No INTEGER tag found for "
+                          "serialNumber; class=%d tag=0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       left = hdr.length;
+       while (left) {
+               cert->serial_number <<= 8;
+               cert->serial_number |= *pos++;
+               left--;
+       }
+       wpa_printf(MSG_MSGDUMP, "X509: serialNumber %lu", cert->serial_number);
+
+       /* signature AlgorithmIdentifier */
+       if (x509_parse_algorithm_identifier(pos, end - pos, &cert->signature,
+                                           &pos))
+               return -1;
+
+       /* issuer Name */
+       if (x509_parse_name(pos, end - pos, &cert->issuer, &pos))
+               return -1;
+       x509_name_string(&cert->issuer, sbuf, sizeof(sbuf));
+       wpa_printf(MSG_MSGDUMP, "X509: issuer %s", sbuf);
+
+       /* validity Validity */
+       if (x509_parse_validity(pos, end - pos, cert, &pos))
+               return -1;
+
+       /* subject Name */
+       if (x509_parse_name(pos, end - pos, &cert->subject, &pos))
+               return -1;
+       x509_name_string(&cert->subject, sbuf, sizeof(sbuf));
+       wpa_printf(MSG_MSGDUMP, "X509: subject %s", sbuf);
+
+       /* subjectPublicKeyInfo SubjectPublicKeyInfo */
+       if (x509_parse_public_key(pos, end - pos, cert, &pos))
+               return -1;
+
+       if (pos == end)
+               return 0;
+
+       if (cert->version == X509_CERT_V1)
+               return 0;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+               wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+                          " tag to parse optional tbsCertificate "
+                          "field(s); parsed class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               return -1;
+       }
+
+       if (hdr.tag == 1) {
+               /* issuerUniqueID  [1]  IMPLICIT UniqueIdentifier OPTIONAL */
+               wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
+               /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+               if (hdr.payload + hdr.length == end)
+                       return 0;
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+                                  " tag to parse optional tbsCertificate "
+                                  "field(s); parsed class %d tag 0x%x",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+       }
+
+       if (hdr.tag == 2) {
+               /* subjectUniqueID [2]  IMPLICIT UniqueIdentifier OPTIONAL */
+               wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
+               /* TODO: parse UniqueIdentifier ::= BIT STRING */
+
+               if (hdr.payload + hdr.length == end)
+                       return 0;
+
+               if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+                   hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) {
+                       wpa_printf(MSG_DEBUG, "X509: Expected Context-Specific"
+                                  " tag to parse optional tbsCertificate "
+                                  "field(s); parsed class %d tag 0x%x",
+                                  hdr.class, hdr.tag);
+                       return -1;
+               }
+       }
+
+       if (hdr.tag != 3) {
+               wpa_printf(MSG_DEBUG, "X509: Ignored unexpected "
+                          "Context-Specific tag %d in optional "
+                          "tbsCertificate fields", hdr.tag);
+               return 0;
+       }
+
+       /* extensions      [3]  EXPLICIT Extensions OPTIONAL */
+
+       if (cert->version != X509_CERT_V3) {
+               wpa_printf(MSG_DEBUG, "X509: X.509%d certificate and "
+                          "Extensions data which are only allowed for "
+                          "version 3", cert->version + 1);
+               return -1;
+       }
+
+       if (x509_parse_extensions(cert, hdr.payload, hdr.length) < 0)
+               return -1;
+
+       pos = hdr.payload + hdr.length;
+       if (pos < end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "X509: Ignored extra tbsCertificate data",
+                           pos, end - pos);
+       }
+
+       return 0;
+}
+
+
+static int x509_rsadsi_oid(struct asn1_oid *oid)
+{
+       return oid->len >= 4 &&
+               oid->oid[0] == 1 /* iso */ &&
+               oid->oid[1] == 2 /* member-body */ &&
+               oid->oid[2] == 840 /* us */ &&
+               oid->oid[3] == 113549 /* rsadsi */;
+}
+
+
+static int x509_pkcs_oid(struct asn1_oid *oid)
+{
+       return oid->len >= 5 &&
+               x509_rsadsi_oid(oid) &&
+               oid->oid[4] == 1 /* pkcs */;
+}
+
+
+static int x509_digest_oid(struct asn1_oid *oid)
+{
+       return oid->len >= 5 &&
+               x509_rsadsi_oid(oid) &&
+               oid->oid[4] == 2 /* digestAlgorithm */;
+}
+
+
+static int x509_sha1_oid(struct asn1_oid *oid)
+{
+       return oid->len == 6 &&
+               oid->oid[0] == 1 /* iso */ &&
+               oid->oid[1] == 3 /* identified-organization */ &&
+               oid->oid[2] == 14 /* oiw */ &&
+               oid->oid[3] == 3 /* secsig */ &&
+               oid->oid[4] == 2 /* algorithms */ &&
+               oid->oid[5] == 26 /* id-sha1 */;
+}
+
+
+static int x509_sha256_oid(struct asn1_oid *oid)
+{
+       return oid->len == 9 &&
+               oid->oid[0] == 2 /* joint-iso-itu-t */ &&
+               oid->oid[1] == 16 /* country */ &&
+               oid->oid[2] == 840 /* us */ &&
+               oid->oid[3] == 1 /* organization */ &&
+               oid->oid[4] == 101 /* gov */ &&
+               oid->oid[5] == 3 /* csor */ &&
+               oid->oid[6] == 4 /* nistAlgorithm */ &&
+               oid->oid[7] == 2 /* hashAlgs */ &&
+               oid->oid[8] == 1 /* sha256 */;
+}
+
+
+/**
+ * x509_certificate_parse - Parse a X.509 certificate in DER format
+ * @buf: Pointer to the X.509 certificate in DER format
+ * @len: Buffer length
+ * Returns: Pointer to the parsed certificate or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned certificate by calling
+ * x509_certificate_free().
+ */
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len)
+{
+       struct asn1_hdr hdr;
+       const u8 *pos, *end, *hash_start;
+       struct x509_certificate *cert;
+
+       cert = os_zalloc(sizeof(*cert) + len);
+       if (cert == NULL)
+               return NULL;
+       os_memcpy(cert + 1, buf, len);
+       cert->cert_start = (u8 *) (cert + 1);
+       cert->cert_len = len;
+
+       pos = buf;
+       end = buf + len;
+
+       /* RFC 3280 - X.509 v3 certificate / ASN.1 DER */
+
+       /* Certificate ::= SEQUENCE */
+       if (asn1_get_next(pos, len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Certificate did not start with "
+                          "a valid SEQUENCE - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       pos = hdr.payload;
+
+       if (pos + hdr.length > end) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+
+       if (pos + hdr.length < end) {
+               wpa_hexdump(MSG_MSGDUMP, "X509: Ignoring extra data after DER "
+                           "encoded certificate",
+                           pos + hdr.length, end - pos + hdr.length);
+               end = pos + hdr.length;
+       }
+
+       hash_start = pos;
+       cert->tbs_cert_start = cert->cert_start + (hash_start - buf);
+       if (x509_parse_tbs_certificate(pos, end - pos, cert, &pos)) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       cert->tbs_cert_len = pos - hash_start;
+
+       /* signatureAlgorithm AlgorithmIdentifier */
+       if (x509_parse_algorithm_identifier(pos, end - pos,
+                                           &cert->signature_alg, &pos)) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+
+       /* signatureValue BIT STRING */
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_BITSTRING) {
+               wpa_printf(MSG_DEBUG, "X509: Expected BITSTRING "
+                          "(signatureValue) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       if (hdr.length < 1) {
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       pos = hdr.payload;
+       if (*pos) {
+               wpa_printf(MSG_DEBUG, "X509: BITSTRING - %d unused bits",
+                          *pos);
+               /* PKCS #1 v1.5 10.2.1:
+                * It is an error if the length in bits of the signature S is
+                * not a multiple of eight.
+                */
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       os_free(cert->sign_value);
+       cert->sign_value = os_malloc(hdr.length - 1);
+       if (cert->sign_value == NULL) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to allocate memory for "
+                          "signatureValue");
+               x509_certificate_free(cert);
+               return NULL;
+       }
+       os_memcpy(cert->sign_value, pos + 1, hdr.length - 1);
+       cert->sign_value_len = hdr.length - 1;
+       wpa_hexdump(MSG_MSGDUMP, "X509: signature",
+                   cert->sign_value, cert->sign_value_len);
+
+       return cert;
+}
+
+
+/**
+ * x509_certificate_check_signature - Verify certificate signature
+ * @issuer: Issuer certificate
+ * @cert: Certificate to be verified
+ * Returns: 0 if cert has a valid signature that was signed by the issuer,
+ * -1 if not
+ */
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+                                    struct x509_certificate *cert)
+{
+       struct crypto_public_key *pk;
+       u8 *data;
+       const u8 *pos, *end, *next, *da_end;
+       size_t data_len;
+       struct asn1_hdr hdr;
+       struct asn1_oid oid;
+       u8 hash[32];
+       size_t hash_len;
+
+       if (!x509_pkcs_oid(&cert->signature.oid) ||
+           cert->signature.oid.len != 7 ||
+           cert->signature.oid.oid[5] != 1 /* pkcs-1 */) {
+               wpa_printf(MSG_DEBUG, "X509: Unrecognized signature "
+                          "algorithm");
+               return -1;
+       }
+
+       pk = crypto_public_key_import(issuer->public_key,
+                                     issuer->public_key_len);
+       if (pk == NULL)
+               return -1;
+
+       data_len = cert->sign_value_len;
+       data = os_malloc(data_len);
+       if (data == NULL) {
+               crypto_public_key_free(pk);
+               return -1;
+       }
+
+       if (crypto_public_key_decrypt_pkcs1(pk, cert->sign_value,
+                                           cert->sign_value_len, data,
+                                           &data_len) < 0) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to decrypt signature");
+               crypto_public_key_free(pk);
+               os_free(data);
+               return -1;
+       }
+       crypto_public_key_free(pk);
+
+       wpa_hexdump(MSG_MSGDUMP, "X509: Signature data D", data, data_len);
+
+       /*
+        * PKCS #1 v1.5, 10.1.2:
+        *
+        * DigestInfo ::= SEQUENCE {
+        *     digestAlgorithm DigestAlgorithmIdentifier,
+        *     digest Digest
+        * }
+        *
+        * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+        *
+        * Digest ::= OCTET STRING
+        *
+        */
+       if (asn1_get_next(data, data_len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(DigestInfo) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(data);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /*
+        * X.509:
+        * AlgorithmIdentifier ::= SEQUENCE {
+        *     algorithm            OBJECT IDENTIFIER,
+        *     parameters           ANY DEFINED BY algorithm OPTIONAL
+        * }
+        */
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE "
+                          "(AlgorithmIdentifier) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(data);
+               return -1;
+       }
+       da_end = hdr.payload + hdr.length;
+
+       if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+               wpa_printf(MSG_DEBUG, "X509: Failed to parse digestAlgorithm");
+               os_free(data);
+               return -1;
+       }
+
+       if (x509_sha1_oid(&oid)) {
+               if (cert->signature.oid.oid[6] !=
+                   5 /* sha-1WithRSAEncryption */) {
+                       wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA1 "
+                                  "does not match with certificate "
+                                  "signatureAlgorithm (%lu)",
+                                  cert->signature.oid.oid[6]);
+                       os_free(data);
+                       return -1;
+               }
+               goto skip_digest_oid;
+       }
+
+       if (x509_sha256_oid(&oid)) {
+               if (cert->signature.oid.oid[6] !=
+                   11 /* sha2561WithRSAEncryption */) {
+                       wpa_printf(MSG_DEBUG, "X509: digestAlgorithm SHA256 "
+                                  "does not match with certificate "
+                                  "signatureAlgorithm (%lu)",
+                                  cert->signature.oid.oid[6]);
+                       os_free(data);
+                       return -1;
+               }
+               goto skip_digest_oid;
+       }
+
+       if (!x509_digest_oid(&oid)) {
+               wpa_printf(MSG_DEBUG, "X509: Unrecognized digestAlgorithm");
+               os_free(data);
+               return -1;
+       }
+       switch (oid.oid[5]) {
+       case 5: /* md5 */
+               if (cert->signature.oid.oid[6] != 4 /* md5WithRSAEncryption */)
+               {
+                       wpa_printf(MSG_DEBUG, "X509: digestAlgorithm MD5 does "
+                                  "not match with certificate "
+                                  "signatureAlgorithm (%lu)",
+                                  cert->signature.oid.oid[6]);
+                       os_free(data);
+                       return -1;
+               }
+               break;
+       case 2: /* md2 */
+       case 4: /* md4 */
+       default:
+               wpa_printf(MSG_DEBUG, "X509: Unsupported digestAlgorithm "
+                          "(%lu)", oid.oid[5]);
+               os_free(data);
+               return -1;
+       }
+
+skip_digest_oid:
+       /* Digest ::= OCTET STRING */
+       pos = da_end;
+       end = data + data_len;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG, "X509: Expected OCTETSTRING "
+                          "(Digest) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(data);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "X509: Decrypted Digest",
+                   hdr.payload, hdr.length);
+
+       switch (cert->signature.oid.oid[6]) {
+       case 4: /* md5WithRSAEncryption */
+               md5_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+                          hash);
+               hash_len = 16;
+               wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (MD5)",
+                           hash, hash_len);
+               break;
+       case 5: /* sha-1WithRSAEncryption */
+               sha1_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+                           hash);
+               hash_len = 20;
+               wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA1)",
+                           hash, hash_len);
+               break;
+       case 11: /* sha256WithRSAEncryption */
+               sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len,
+                             hash);
+               hash_len = 32;
+               wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)",
+                           hash, hash_len);
+               break;
+       case 2: /* md2WithRSAEncryption */
+       case 12: /* sha384WithRSAEncryption */
+       case 13: /* sha512WithRSAEncryption */
+       default:
+               wpa_printf(MSG_INFO, "X509: Unsupported certificate signature "
+                          "algorithm (%lu)", cert->signature.oid.oid[6]);
+               os_free(data);
+               return -1;
+       }
+
+       if (hdr.length != hash_len ||
+           os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+               wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
+                          "with calculated tbsCertificate hash");
+               os_free(data);
+               return -1;
+       }
+
+       os_free(data);
+
+       wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
+                  "calculated tbsCertificate hash");
+
+       return 0;
+}
+
+
+static int x509_valid_issuer(const struct x509_certificate *cert)
+{
+       if ((cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS) &&
+           !cert->ca) {
+               wpa_printf(MSG_DEBUG, "X509: Non-CA certificate used as an "
+                          "issuer");
+               return -1;
+       }
+
+       if (cert->version == X509_CERT_V3 &&
+           !(cert->extensions_present & X509_EXT_BASIC_CONSTRAINTS)) {
+               wpa_printf(MSG_DEBUG, "X509: v3 CA certificate did not "
+                          "include BasicConstraints extension");
+               return -1;
+       }
+
+       if ((cert->extensions_present & X509_EXT_KEY_USAGE) &&
+           !(cert->key_usage & X509_KEY_USAGE_KEY_CERT_SIGN)) {
+               wpa_printf(MSG_DEBUG, "X509: Issuer certificate did not have "
+                          "keyCertSign bit in Key Usage");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * x509_certificate_chain_validate - Validate X.509 certificate chain
+ * @trusted: List of trusted certificates
+ * @chain: Certificate chain to be validated (first chain must be issued by
+ * signed by the second certificate in the chain and so on)
+ * @reason: Buffer for returning failure reason (X509_VALIDATE_*)
+ * Returns: 0 if chain is valid, -1 if not
+ */
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+                                   struct x509_certificate *chain,
+                                   int *reason)
+{
+       long unsigned idx;
+       int chain_trusted = 0;
+       struct x509_certificate *cert, *trust;
+       char buf[128];
+       struct os_time now;
+
+       *reason = X509_VALIDATE_OK;
+
+       wpa_printf(MSG_DEBUG, "X509: Validate certificate chain");
+       os_get_time(&now);
+
+       for (cert = chain, idx = 0; cert; cert = cert->next, idx++) {
+               x509_name_string(&cert->subject, buf, sizeof(buf)); 
+               wpa_printf(MSG_DEBUG, "X509: %lu: %s", idx, buf);
+
+               if (chain_trusted)
+                       continue;
+
+               if ((unsigned long) now.sec <
+                   (unsigned long) cert->not_before ||
+                   (unsigned long) now.sec >
+                   (unsigned long) cert->not_after) {
+                       wpa_printf(MSG_INFO, "X509: Certificate not valid "
+                                  "(now=%lu not_before=%lu not_after=%lu)",
+                                  now.sec, cert->not_before, cert->not_after);
+                       *reason = X509_VALIDATE_CERTIFICATE_EXPIRED;
+                       return -1;
+               }
+
+               if (cert->next) {
+                       if (x509_name_compare(&cert->issuer,
+                                             &cert->next->subject) != 0) {
+                               wpa_printf(MSG_DEBUG, "X509: Certificate "
+                                          "chain issuer name mismatch");
+                               x509_name_string(&cert->issuer, buf,
+                                                sizeof(buf)); 
+                               wpa_printf(MSG_DEBUG, "X509: cert issuer: %s",
+                                          buf);
+                               x509_name_string(&cert->next->subject, buf,
+                                                sizeof(buf)); 
+                               wpa_printf(MSG_DEBUG, "X509: next cert "
+                                          "subject: %s", buf);
+                               *reason = X509_VALIDATE_CERTIFICATE_UNKNOWN;
+                               return -1;
+                       }
+
+                       if (x509_valid_issuer(cert->next) < 0) {
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       if ((cert->next->extensions_present &
+                            X509_EXT_PATH_LEN_CONSTRAINT) &&
+                           idx > cert->next->path_len_constraint) {
+                               wpa_printf(MSG_DEBUG, "X509: pathLenConstraint"
+                                          " not met (idx=%lu issuer "
+                                          "pathLenConstraint=%lu)", idx,
+                                          cert->next->path_len_constraint);
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       if (x509_certificate_check_signature(cert->next, cert)
+                           < 0) {
+                               wpa_printf(MSG_DEBUG, "X509: Invalid "
+                                          "certificate signature within "
+                                          "chain");
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+               }
+
+               for (trust = trusted; trust; trust = trust->next) {
+                       if (x509_name_compare(&cert->issuer, &trust->subject)
+                           == 0)
+                               break;
+               }
+
+               if (trust) {
+                       wpa_printf(MSG_DEBUG, "X509: Found issuer from the "
+                                  "list of trusted certificates");
+                       if (x509_valid_issuer(trust) < 0) {
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       if (x509_certificate_check_signature(trust, cert) < 0)
+                       {
+                               wpa_printf(MSG_DEBUG, "X509: Invalid "
+                                          "certificate signature");
+                               *reason = X509_VALIDATE_BAD_CERTIFICATE;
+                               return -1;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "X509: Trusted certificate "
+                                  "found to complete the chain");
+                       chain_trusted = 1;
+               }
+       }
+
+       if (!chain_trusted) {
+               wpa_printf(MSG_DEBUG, "X509: Did not find any of the issuers "
+                          "from the list of trusted certificates");
+               if (trusted) {
+                       *reason = X509_VALIDATE_UNKNOWN_CA;
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "X509: Certificate chain validation "
+                          "disabled - ignore unknown CA issue");
+       }
+
+       wpa_printf(MSG_DEBUG, "X509: Certificate chain valid");
+
+       return 0;
+}
+
+
+/**
+ * x509_certificate_get_subject - Get a certificate based on Subject name
+ * @chain: Certificate chain to search through
+ * @name: Subject name to search for
+ * Returns: Pointer to the certificate with the given Subject name or
+ * %NULL on failure
+ */
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+                            struct x509_name *name)
+{
+       struct x509_certificate *cert;
+
+       for (cert = chain; cert; cert = cert->next) {
+               if (x509_name_compare(&cert->subject, name) == 0)
+                       return cert;
+       }
+       return NULL;
+}
+
+
+/**
+ * x509_certificate_self_signed - Is the certificate self-signed?
+ * @cert: Certificate
+ * Returns: 1 if certificate is self-signed, 0 if not
+ */
+int x509_certificate_self_signed(struct x509_certificate *cert)
+{
+       return x509_name_compare(&cert->issuer, &cert->subject) == 0;
+}
diff --git a/src/tls/x509v3.h b/src/tls/x509v3.h
new file mode 100644 (file)
index 0000000..37292d7
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * X.509v3 certificate parsing and processing
+ * Copyright (c) 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 X509V3_H
+#define X509V3_H
+
+#include "asn1.h"
+
+struct x509_algorithm_identifier {
+       struct asn1_oid oid;
+};
+
+struct x509_name_attr {
+       enum x509_name_attr_type {
+               X509_NAME_ATTR_NOT_USED,
+               X509_NAME_ATTR_DC,
+               X509_NAME_ATTR_CN,
+               X509_NAME_ATTR_C,
+               X509_NAME_ATTR_L,
+               X509_NAME_ATTR_ST,
+               X509_NAME_ATTR_O,
+               X509_NAME_ATTR_OU
+       } type;
+       char *value;
+};
+
+#define X509_MAX_NAME_ATTRIBUTES 20
+
+struct x509_name {
+       struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES];
+       size_t num_attr;
+       char *email; /* emailAddress */
+
+       /* from alternative name extension */
+       char *alt_email; /* rfc822Name */
+       char *dns; /* dNSName */
+       char *uri; /* uniformResourceIdentifier */
+       u8 *ip; /* iPAddress */
+       size_t ip_len; /* IPv4: 4, IPv6: 16 */
+       struct asn1_oid rid; /* registeredID */
+};
+
+struct x509_certificate {
+       struct x509_certificate *next;
+       enum { X509_CERT_V1 = 0, X509_CERT_V2 = 1, X509_CERT_V3 = 2 } version;
+       unsigned long serial_number;
+       struct x509_algorithm_identifier signature;
+       struct x509_name issuer;
+       struct x509_name subject;
+       os_time_t not_before;
+       os_time_t not_after;
+       struct x509_algorithm_identifier public_key_alg;
+       u8 *public_key;
+       size_t public_key_len;
+       struct x509_algorithm_identifier signature_alg;
+       u8 *sign_value;
+       size_t sign_value_len;
+
+       /* Extensions */
+       unsigned int extensions_present;
+#define X509_EXT_BASIC_CONSTRAINTS             (1 << 0)
+#define X509_EXT_PATH_LEN_CONSTRAINT           (1 << 1)
+#define X509_EXT_KEY_USAGE                     (1 << 2)
+#define X509_EXT_SUBJECT_ALT_NAME              (1 << 3)
+#define X509_EXT_ISSUER_ALT_NAME               (1 << 4)
+
+       /* BasicConstraints */
+       int ca; /* cA */
+       unsigned long path_len_constraint; /* pathLenConstraint */
+
+       /* KeyUsage */
+       unsigned long key_usage;
+#define X509_KEY_USAGE_DIGITAL_SIGNATURE       (1 << 0)
+#define X509_KEY_USAGE_NON_REPUDIATION         (1 << 1)
+#define X509_KEY_USAGE_KEY_ENCIPHERMENT                (1 << 2)
+#define X509_KEY_USAGE_DATA_ENCIPHERMENT       (1 << 3)
+#define X509_KEY_USAGE_KEY_AGREEMENT           (1 << 4)
+#define X509_KEY_USAGE_KEY_CERT_SIGN           (1 << 5)
+#define X509_KEY_USAGE_CRL_SIGN                        (1 << 6)
+#define X509_KEY_USAGE_ENCIPHER_ONLY           (1 << 7)
+#define X509_KEY_USAGE_DECIPHER_ONLY           (1 << 8)
+
+       /*
+        * The DER format certificate follows struct x509_certificate. These
+        * pointers point to that buffer.
+        */
+       const u8 *cert_start;
+       size_t cert_len;
+       const u8 *tbs_cert_start;
+       size_t tbs_cert_len;
+};
+
+enum {
+       X509_VALIDATE_OK,
+       X509_VALIDATE_BAD_CERTIFICATE,
+       X509_VALIDATE_UNSUPPORTED_CERTIFICATE,
+       X509_VALIDATE_CERTIFICATE_REVOKED,
+       X509_VALIDATE_CERTIFICATE_EXPIRED,
+       X509_VALIDATE_CERTIFICATE_UNKNOWN,
+       X509_VALIDATE_UNKNOWN_CA
+};
+
+void x509_certificate_free(struct x509_certificate *cert);
+struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len);
+void x509_name_string(struct x509_name *name, char *buf, size_t len);
+int x509_name_compare(struct x509_name *a, struct x509_name *b);
+void x509_certificate_chain_free(struct x509_certificate *cert);
+int x509_certificate_check_signature(struct x509_certificate *issuer,
+                                    struct x509_certificate *cert);
+int x509_certificate_chain_validate(struct x509_certificate *trusted,
+                                   struct x509_certificate *chain,
+                                   int *reason);
+struct x509_certificate *
+x509_certificate_get_subject(struct x509_certificate *chain,
+                            struct x509_name *name);
+int x509_certificate_self_signed(struct x509_certificate *cert);
+
+#endif /* X509V3_H */
diff --git a/src/utils/.gitignore b/src/utils/.gitignore
new file mode 100644 (file)
index 0000000..833734f
--- /dev/null
@@ -0,0 +1 @@
+libutils.a
diff --git a/src/utils/Makefile b/src/utils/Makefile
new file mode 100644 (file)
index 0000000..527cf3e
--- /dev/null
@@ -0,0 +1,36 @@
+all: libutils.a
+
+clean:
+       rm -f *~ *.o *.d libutils.a
+
+install:
+       @echo Nothing to be made.
+
+
+include ../lib.rules
+
+#CFLAGS += -DWPA_TRACE
+CFLAGS += -DCONFIG_IPV6
+
+LIB_OBJS= \
+       base64.o \
+       common.o \
+       ip_addr.o \
+       radiotap.o \
+       trace.o \
+       uuid.o \
+       wpa_debug.o \
+       wpabuf.o
+
+# Pick correct OS wrapper implementation
+LIB_OBJS += os_unix.o
+
+# Pick correct event loop implementation
+LIB_OBJS += eloop.o
+
+#LIB_OBJS += pcsc_funcs.o
+
+libutils.a: $(LIB_OBJS)
+       $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/src/utils/base64.c b/src/utils/base64.c
new file mode 100644 (file)
index 0000000..155bfce
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * 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 "os.h"
+#include "base64.h"
+
+static const unsigned char base64_table[65] =
+       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * base64_encode - Base64 encode
+ * @src: Data to be encoded
+ * @len: Length of the data to be encoded
+ * @out_len: Pointer to output length variable, or %NULL if not used
+ * Returns: Allocated buffer of out_len bytes of encoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer. Returned buffer is
+ * nul terminated to make it easier to use as a C string. The nul terminator is
+ * not included in out_len.
+ */
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+                             size_t *out_len)
+{
+       unsigned char *out, *pos;
+       const unsigned char *end, *in;
+       size_t olen;
+       int line_len;
+
+       olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
+       olen += olen / 72; /* line feeds */
+       olen++; /* nul termination */
+       if (olen < len)
+               return NULL; /* integer overflow */
+       out = os_malloc(olen);
+       if (out == NULL)
+               return NULL;
+
+       end = src + len;
+       in = src;
+       pos = out;
+       line_len = 0;
+       while (end - in >= 3) {
+               *pos++ = base64_table[in[0] >> 2];
+               *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
+               *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+               *pos++ = base64_table[in[2] & 0x3f];
+               in += 3;
+               line_len += 4;
+               if (line_len >= 72) {
+                       *pos++ = '\n';
+                       line_len = 0;
+               }
+       }
+
+       if (end - in) {
+               *pos++ = base64_table[in[0] >> 2];
+               if (end - in == 1) {
+                       *pos++ = base64_table[(in[0] & 0x03) << 4];
+                       *pos++ = '=';
+               } else {
+                       *pos++ = base64_table[((in[0] & 0x03) << 4) |
+                                             (in[1] >> 4)];
+                       *pos++ = base64_table[(in[1] & 0x0f) << 2];
+               }
+               *pos++ = '=';
+               line_len += 4;
+       }
+
+       if (line_len)
+               *pos++ = '\n';
+
+       *pos = '\0';
+       if (out_len)
+               *out_len = pos - out;
+       return out;
+}
+
+
+/**
+ * base64_decode - Base64 decode
+ * @src: Data to be decoded
+ * @len: Length of the data to be decoded
+ * @out_len: Pointer to output length variable
+ * Returns: Allocated buffer of out_len bytes of decoded data,
+ * or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer.
+ */
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+                             size_t *out_len)
+{
+       unsigned char dtable[256], *out, *pos, in[4], block[4], tmp;
+       size_t i, count, olen;
+
+       os_memset(dtable, 0x80, 256);
+       for (i = 0; i < sizeof(base64_table) - 1; i++)
+               dtable[base64_table[i]] = (unsigned char) i;
+       dtable['='] = 0;
+
+       count = 0;
+       for (i = 0; i < len; i++) {
+               if (dtable[src[i]] != 0x80)
+                       count++;
+       }
+
+       if (count == 0 || count % 4)
+               return NULL;
+
+       olen = count / 4 * 3;
+       pos = out = os_malloc(olen);
+       if (out == NULL)
+               return NULL;
+
+       count = 0;
+       for (i = 0; i < len; i++) {
+               tmp = dtable[src[i]];
+               if (tmp == 0x80)
+                       continue;
+
+               in[count] = src[i];
+               block[count] = tmp;
+               count++;
+               if (count == 4) {
+                       *pos++ = (block[0] << 2) | (block[1] >> 4);
+                       *pos++ = (block[1] << 4) | (block[2] >> 2);
+                       *pos++ = (block[2] << 6) | block[3];
+                       count = 0;
+               }
+       }
+
+       if (pos > out) {
+               if (in[2] == '=')
+                       pos -= 2;
+               else if (in[3] == '=')
+                       pos--;
+       }
+
+       *out_len = pos - out;
+       return out;
+}
diff --git a/src/utils/base64.h b/src/utils/base64.h
new file mode 100644 (file)
index 0000000..73312dd
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Base64 encoding/decoding (RFC1341)
+ * 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.
+ */
+
+#ifndef BASE64_H
+#define BASE64_h
+
+unsigned char * base64_encode(const unsigned char *src, size_t len,
+                             size_t *out_len);
+unsigned char * base64_decode(const unsigned char *src, size_t len,
+                             size_t *out_len);
+
+#endif /* BASE64_H */
diff --git a/src/utils/build_config.h b/src/utils/build_config.h
new file mode 100644 (file)
index 0000000..3666778
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * wpa_supplicant/hostapd - Build time configuration defines
+ * Copyright (c) 2005-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.
+ *
+ * This header file can be used to define configuration defines that were
+ * originally defined in Makefile. This is mainly meant for IDE use or for
+ * systems that do not have suitable 'make' tool. In these cases, it may be
+ * easier to have a single place for defining all the needed C pre-processor
+ * defines.
+ */
+
+#ifndef BUILD_CONFIG_H
+#define BUILD_CONFIG_H
+
+/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */
+
+#ifdef CONFIG_WIN32_DEFAULTS
+#define CONFIG_NATIVE_WINDOWS
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_WINPCAP
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define PCSC_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_NAMED_PIPE
+#define CONFIG_DRIVER_NDIS
+#define CONFIG_NDIS_EVENTS_INTEGRATED
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define _CRT_SECURE_NO_DEPRECATE
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_WIN32_DEFAULTS */
+
+#ifdef __SYMBIAN32__
+#define OS_NO_C_LIB_DEFINES
+#define CONFIG_ANSI_C_EXTRA
+#define CONFIG_NO_WPA_MSG
+#define CONFIG_NO_HOSTAPD_LOGGER
+#define CONFIG_NO_STDOUT_DEBUG
+#define CONFIG_BACKEND_FILE
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_FAST
+#endif /* __SYMBIAN32__ */
+
+#ifdef CONFIG_XCODE_DEFAULTS
+#define CONFIG_DRIVER_OSX
+#define CONFIG_BACKEND_FILE
+#define IEEE8021X_EAPOL
+#define PKCS12_FUNCS
+#define CONFIG_CTRL_IFACE
+#define CONFIG_CTRL_IFACE_UNIX
+#define CONFIG_DEBUG_FILE
+#define EAP_MD5
+#define EAP_TLS
+#define EAP_MSCHAPv2
+#define EAP_PEAP
+#define EAP_TTLS
+#define EAP_GTC
+#define EAP_OTP
+#define EAP_LEAP
+#define EAP_TNC
+#define CONFIG_WPS
+#define EAP_WSC
+
+#ifdef USE_INTERNAL_CRYPTO
+#define CONFIG_TLS_INTERNAL_CLIENT
+#define CONFIG_INTERNAL_LIBTOMMATH
+#define CONFIG_CRYPTO_INTERNAL
+#endif /* USE_INTERNAL_CRYPTO */
+#endif /* CONFIG_XCODE_DEFAULTS */
+
+#endif /* BUILD_CONFIG_H */
diff --git a/src/utils/common.c b/src/utils/common.c
new file mode 100644 (file)
index 0000000..1b8ea80
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+
+static int hex2num(char c)
+{
+       if (c >= '0' && c <= '9')
+               return c - '0';
+       if (c >= 'a' && c <= 'f')
+               return c - 'a' + 10;
+       if (c >= 'A' && c <= 'F')
+               return c - 'A' + 10;
+       return -1;
+}
+
+
+static int hex2byte(const char *hex)
+{
+       int a, b;
+       a = hex2num(*hex++);
+       if (a < 0)
+               return -1;
+       b = hex2num(*hex++);
+       if (b < 0)
+               return -1;
+       return (a << 4) | b;
+}
+
+
+/**
+ * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
+ * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_aton(const char *txt, u8 *addr)
+{
+       int i;
+
+       for (i = 0; i < 6; i++) {
+               int a, b;
+
+               a = hex2num(*txt++);
+               if (a < 0)
+                       return -1;
+               b = hex2num(*txt++);
+               if (b < 0)
+                       return -1;
+               *addr++ = (a << 4) | b;
+               if (i < 5 && *txt++ != ':')
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format)
+ * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455)
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * Returns: Characters used (> 0) on success, -1 on failure
+ */
+int hwaddr_aton2(const char *txt, u8 *addr)
+{
+       int i;
+       const char *pos = txt;
+
+       for (i = 0; i < 6; i++) {
+               int a, b;
+
+               while (*pos == ':' || *pos == '.' || *pos == '-')
+                       pos++;
+
+               a = hex2num(*pos++);
+               if (a < 0)
+                       return -1;
+               b = hex2num(*pos++);
+               if (b < 0)
+                       return -1;
+               *addr++ = (a << 4) | b;
+       }
+
+       return pos - txt;
+}
+
+
+/**
+ * hexstr2bin - Convert ASCII hex string into binary data
+ * @hex: ASCII hex string (e.g., "01ab")
+ * @buf: Buffer for the binary data
+ * @len: Length of the text to convert in bytes (of buf); hex will be double
+ * this size
+ * Returns: 0 on success, -1 on failure (invalid hex string)
+ */
+int hexstr2bin(const char *hex, u8 *buf, size_t len)
+{
+       size_t i;
+       int a;
+       const char *ipos = hex;
+       u8 *opos = buf;
+
+       for (i = 0; i < len; i++) {
+               a = hex2byte(ipos);
+               if (a < 0)
+                       return -1;
+               *opos++ = a;
+               ipos += 2;
+       }
+       return 0;
+}
+
+
+/**
+ * inc_byte_array - Increment arbitrary length byte array by one
+ * @counter: Pointer to byte array
+ * @len: Length of the counter in bytes
+ *
+ * This function increments the last byte of the counter by one and continues
+ * rolling over to more significant bytes if the byte was incremented from
+ * 0xff to 0x00.
+ */
+void inc_byte_array(u8 *counter, size_t len)
+{
+       int pos = len - 1;
+       while (pos >= 0) {
+               counter[pos]++;
+               if (counter[pos] != 0)
+                       break;
+               pos--;
+       }
+}
+
+
+void wpa_get_ntp_timestamp(u8 *buf)
+{
+       struct os_time now;
+       u32 sec, usec;
+       be32 tmp;
+
+       /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */
+       os_get_time(&now);
+       sec = now.sec + 2208988800U; /* Epoch to 1900 */
+       /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */
+       usec = now.usec;
+       usec = 4295 * usec - (usec >> 5) - (usec >> 9);
+       tmp = host_to_be32(sec);
+       os_memcpy(buf, (u8 *) &tmp, 4);
+       tmp = host_to_be32(usec);
+       os_memcpy(buf + 4, (u8 *) &tmp, 4);
+}
+
+
+static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
+                                   size_t len, int uppercase)
+{
+       size_t i;
+       char *pos = buf, *end = buf + buf_size;
+       int ret;
+       if (buf_size == 0)
+               return 0;
+       for (i = 0; i < len; i++) {
+               ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
+                                 data[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return pos - buf;
+               }
+               pos += ret;
+       }
+       end[-1] = '\0';
+       return pos - buf;
+}
+
+/**
+ * wpa_snprintf_hex - Print data as a hex string into a buffer
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len)
+{
+       return _wpa_snprintf_hex(buf, buf_size, data, len, 0);
+}
+
+
+/**
+ * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf
+ * @buf: Memory area to use as the output buffer
+ * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1)
+ * @data: Data to be printed
+ * @len: Length of data in bytes
+ * Returns: Number of bytes written
+ */
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+                              size_t len)
+{
+       return _wpa_snprintf_hex(buf, buf_size, data, len, 1);
+}
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#ifdef _WIN32_WCE
+void perror(const char *s)
+{
+       wpa_printf(MSG_ERROR, "%s: GetLastError: %d",
+                  s, (int) GetLastError());
+}
+#endif /* _WIN32_WCE */
+
+
+int optind = 1;
+int optopt;
+char *optarg;
+
+int getopt(int argc, char *const argv[], const char *optstring)
+{
+       static int optchr = 1;
+       char *cp;
+
+       if (optchr == 1) {
+               if (optind >= argc) {
+                       /* all arguments processed */
+                       return EOF;
+               }
+
+               if (argv[optind][0] != '-' || argv[optind][1] == '\0') {
+                       /* no option characters */
+                       return EOF;
+               }
+       }
+
+       if (os_strcmp(argv[optind], "--") == 0) {
+               /* no more options */
+               optind++;
+               return EOF;
+       }
+
+       optopt = argv[optind][optchr];
+       cp = os_strchr(optstring, optopt);
+       if (cp == NULL || optopt == ':') {
+               if (argv[optind][++optchr] == '\0') {
+                       optchr = 1;
+                       optind++;
+               }
+               return '?';
+       }
+
+       if (cp[1] == ':') {
+               /* Argument required */
+               optchr = 1;
+               if (argv[optind][optchr + 1]) {
+                       /* No space between option and argument */
+                       optarg = &argv[optind++][optchr + 1];
+               } else if (++optind >= argc) {
+                       /* option requires an argument */
+                       return '?';
+               } else {
+                       /* Argument in the next argv */
+                       optarg = argv[optind++];
+               }
+       } else {
+               /* No argument */
+               if (argv[optind][++optchr] == '\0') {
+                       optchr = 1;
+                       optind++;
+               }
+               optarg = NULL;
+       }
+       return *cp;
+}
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+/**
+ * wpa_unicode2ascii_inplace - Convert unicode string into ASCII
+ * @str: Pointer to string to convert
+ *
+ * This function converts a unicode string to ASCII using the same
+ * buffer for output. If UNICODE is not set, the buffer is not
+ * modified.
+ */
+void wpa_unicode2ascii_inplace(TCHAR *str)
+{
+#ifdef UNICODE
+       char *dst = (char *) str;
+       while (*str)
+               *dst++ = (char) *str++;
+       *dst = '\0';
+#endif /* UNICODE */
+}
+
+
+TCHAR * wpa_strdup_tchar(const char *str)
+{
+#ifdef UNICODE
+       TCHAR *buf;
+       buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR));
+       if (buf == NULL)
+               return NULL;
+       wsprintf(buf, L"%S", str);
+       return buf;
+#else /* UNICODE */
+       return os_strdup(str);
+#endif /* UNICODE */
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+/**
+ * wpa_ssid_txt - Convert SSID to a printable string
+ * @ssid: SSID (32-octet string)
+ * @ssid_len: Length of ssid in octets
+ * Returns: Pointer to a printable string
+ *
+ * This function can be used to convert SSIDs into printable form. In most
+ * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard
+ * does not limit the used character set, so anything could be used in an SSID.
+ *
+ * This function uses a static buffer, so only one call can be used at the
+ * time, i.e., this is not re-entrant and the returned buffer must be used
+ * before calling this again.
+ */
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len)
+{
+       static char ssid_txt[33];
+       char *pos;
+
+       if (ssid_len > 32)
+               ssid_len = 32;
+       os_memcpy(ssid_txt, ssid, ssid_len);
+       ssid_txt[ssid_len] = '\0';
+       for (pos = ssid_txt; *pos != '\0'; pos++) {
+               if ((u8) *pos < 32 || (u8) *pos >= 127)
+                       *pos = '_';
+       }
+       return ssid_txt;
+}
+
+
+void * __hide_aliasing_typecast(void *foo)
+{
+       return foo;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
new file mode 100644 (file)
index 0000000..f17bf69
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * wpa_supplicant/hostapd / common helper functions, etc.
+ * 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 COMMON_H
+#define COMMON_H
+
+#include "os.h"
+
+#if defined(__linux__) || defined(__GLIBC__)
+#include <endian.h>
+#include <byteswap.h>
+#endif /* __linux__ */
+
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) || \
+    defined(__OpenBSD__)
+#include <sys/types.h>
+#include <sys/endian.h>
+#define __BYTE_ORDER   _BYTE_ORDER
+#define        __LITTLE_ENDIAN _LITTLE_ENDIAN
+#define        __BIG_ENDIAN    _BIG_ENDIAN
+#ifdef __OpenBSD__
+#define bswap_16 swap16
+#define bswap_32 swap32
+#define bswap_64 swap64
+#else /* __OpenBSD__ */
+#define bswap_16 bswap16
+#define bswap_32 bswap32
+#define bswap_64 bswap64
+#endif /* __OpenBSD__ */
+#endif /* defined(__FreeBSD__) || defined(__NetBSD__) ||
+       * defined(__DragonFly__) || defined(__OpenBSD__) */
+
+#ifdef __APPLE__
+#include <sys/types.h>
+#include <machine/endian.h>
+#define __BYTE_ORDER   _BYTE_ORDER
+#define __LITTLE_ENDIAN        _LITTLE_ENDIAN
+#define __BIG_ENDIAN   _BIG_ENDIAN
+static inline unsigned short bswap_16(unsigned short v)
+{
+       return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int bswap_32(unsigned int v)
+{
+       return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+               ((v & 0xff0000) >> 8) | (v >> 24);
+}
+#endif /* __APPLE__ */
+
+#ifdef CONFIG_TI_COMPILER
+#define __BIG_ENDIAN 4321
+#define __LITTLE_ENDIAN 1234
+#ifdef __big_endian__
+#define __BYTE_ORDER __BIG_ENDIAN
+#else
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif
+#endif /* CONFIG_TI_COMPILER */
+
+#ifdef __SYMBIAN32__
+#define __BIG_ENDIAN 4321
+#define __LITTLE_ENDIAN 1234
+#define __BYTE_ORDER __LITTLE_ENDIAN
+#endif /* __SYMBIAN32__ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+
+typedef int socklen_t;
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0 /* not supported */
+#endif
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#ifdef _MSC_VER
+#define inline __inline
+
+#undef vsnprintf
+#define vsnprintf _vsnprintf
+#undef close
+#define close closesocket
+#endif /* _MSC_VER */
+
+
+/* Define platform specific integer types */
+
+#ifdef _MSC_VER
+typedef UINT64 u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef INT64 s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* _MSC_VER */
+
+#ifdef __vxworks
+typedef unsigned long long u64;
+typedef UINT32 u32;
+typedef UINT16 u16;
+typedef UINT8 u8;
+typedef long long s64;
+typedef INT32 s32;
+typedef INT16 s16;
+typedef INT8 s8;
+#define WPA_TYPES_DEFINED
+#endif /* __vxworks */
+
+#ifdef CONFIG_TI_COMPILER
+#ifdef _LLONG_AVAILABLE
+typedef unsigned long long u64;
+#else
+/*
+ * TODO: 64-bit variable not available. Using long as a workaround to test the
+ * build, but this will likely not work for all operations.
+ */
+typedef unsigned long u64;
+#endif
+typedef unsigned int u32;
+typedef unsigned short u16;
+typedef unsigned char u8;
+#define WPA_TYPES_DEFINED
+#endif /* CONFIG_TI_COMPILER */
+
+#ifdef __SYMBIAN32__
+#define __REMOVE_PLATSEC_DIAGNOSTICS__
+#include <e32def.h>
+typedef TUint64 u64;
+typedef TUint32 u32;
+typedef TUint16 u16;
+typedef TUint8 u8;
+#define WPA_TYPES_DEFINED
+#endif /* __SYMBIAN32__ */
+
+#ifndef WPA_TYPES_DEFINED
+#ifdef CONFIG_USE_INTTYPES_H
+#include <inttypes.h>
+#else
+#include <stdint.h>
+#endif
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint16_t u16;
+typedef uint8_t u8;
+typedef int64_t s64;
+typedef int32_t s32;
+typedef int16_t s16;
+typedef int8_t s8;
+#define WPA_TYPES_DEFINED
+#endif /* !WPA_TYPES_DEFINED */
+
+
+/* Define platform specific byte swapping macros */
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+
+static inline unsigned short wpa_swap_16(unsigned short v)
+{
+       return ((v & 0xff) << 8) | (v >> 8);
+}
+
+static inline unsigned int wpa_swap_32(unsigned int v)
+{
+       return ((v & 0xff) << 24) | ((v & 0xff00) << 8) |
+               ((v & 0xff0000) >> 8) | (v >> 24);
+}
+
+#define le_to_host16(n) (n)
+#define host_to_le16(n) (n)
+#define be_to_host16(n) wpa_swap_16(n)
+#define host_to_be16(n) wpa_swap_16(n)
+#define le_to_host32(n) (n)
+#define be_to_host32(n) wpa_swap_32(n)
+#define host_to_be32(n) wpa_swap_32(n)
+
+#define WPA_BYTE_SWAP_DEFINED
+
+#endif /* __CYGWIN__ || CONFIG_NATIVE_WINDOWS */
+
+
+#ifndef WPA_BYTE_SWAP_DEFINED
+
+#ifndef __BYTE_ORDER
+#ifndef __LITTLE_ENDIAN
+#ifndef __BIG_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#define __BIG_ENDIAN 4321
+#if defined(sparc)
+#define __BYTE_ORDER __BIG_ENDIAN
+#endif
+#endif /* __BIG_ENDIAN */
+#endif /* __LITTLE_ENDIAN */
+#endif /* __BYTE_ORDER */
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define le_to_host16(n) ((__force u16) (le16) (n))
+#define host_to_le16(n) ((__force le16) (u16) (n))
+#define be_to_host16(n) bswap_16((__force u16) (be16) (n))
+#define host_to_be16(n) ((__force be16) bswap_16((n)))
+#define le_to_host32(n) ((__force u32) (le32) (n))
+#define host_to_le32(n) ((__force le32) (u32) (n))
+#define be_to_host32(n) bswap_32((__force u32) (be32) (n))
+#define host_to_be32(n) ((__force be32) bswap_32((n)))
+#define le_to_host64(n) ((__force u64) (le64) (n))
+#define host_to_le64(n) ((__force le64) (u64) (n))
+#define be_to_host64(n) bswap_64((__force u64) (be64) (n))
+#define host_to_be64(n) ((__force be64) bswap_64((n)))
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define le_to_host16(n) bswap_16(n)
+#define host_to_le16(n) bswap_16(n)
+#define be_to_host16(n) (n)
+#define host_to_be16(n) (n)
+#define le_to_host32(n) bswap_32(n)
+#define be_to_host32(n) (n)
+#define host_to_be32(n) (n)
+#define le_to_host64(n) bswap_64(n)
+#define host_to_le64(n) bswap_64(n)
+#define be_to_host64(n) (n)
+#define host_to_be64(n) (n)
+#ifndef WORDS_BIGENDIAN
+#define WORDS_BIGENDIAN
+#endif
+#else
+#error Could not determine CPU byte order
+#endif
+
+#define WPA_BYTE_SWAP_DEFINED
+#endif /* !WPA_BYTE_SWAP_DEFINED */
+
+
+/* Macros for handling unaligned memory accesses */
+
+#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1]))
+#define WPA_PUT_BE16(a, val)                   \
+       do {                                    \
+               (a)[0] = ((u16) (val)) >> 8;    \
+               (a)[1] = ((u16) (val)) & 0xff;  \
+       } while (0)
+
+#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0]))
+#define WPA_PUT_LE16(a, val)                   \
+       do {                                    \
+               (a)[1] = ((u16) (val)) >> 8;    \
+               (a)[0] = ((u16) (val)) & 0xff;  \
+       } while (0)
+
+#define WPA_GET_BE24(a) ((((u32) (a)[0]) << 16) | (((u32) (a)[1]) << 8) | \
+                        ((u32) (a)[2]))
+#define WPA_PUT_BE24(a, val)                                   \
+       do {                                                    \
+               (a)[0] = (u8) ((((u32) (val)) >> 16) & 0xff);   \
+               (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);    \
+               (a)[2] = (u8) (((u32) (val)) & 0xff);           \
+       } while (0)
+
+#define WPA_GET_BE32(a) ((((u32) (a)[0]) << 24) | (((u32) (a)[1]) << 16) | \
+                        (((u32) (a)[2]) << 8) | ((u32) (a)[3]))
+#define WPA_PUT_BE32(a, val)                                   \
+       do {                                                    \
+               (a)[0] = (u8) ((((u32) (val)) >> 24) & 0xff);   \
+               (a)[1] = (u8) ((((u32) (val)) >> 16) & 0xff);   \
+               (a)[2] = (u8) ((((u32) (val)) >> 8) & 0xff);    \
+               (a)[3] = (u8) (((u32) (val)) & 0xff);           \
+       } while (0)
+
+#define WPA_GET_LE32(a) ((((u32) (a)[3]) << 24) | (((u32) (a)[2]) << 16) | \
+                        (((u32) (a)[1]) << 8) | ((u32) (a)[0]))
+#define WPA_PUT_LE32(a, val)                                   \
+       do {                                                    \
+               (a)[3] = (u8) ((((u32) (val)) >> 24) & 0xff);   \
+               (a)[2] = (u8) ((((u32) (val)) >> 16) & 0xff);   \
+               (a)[1] = (u8) ((((u32) (val)) >> 8) & 0xff);    \
+               (a)[0] = (u8) (((u32) (val)) & 0xff);           \
+       } while (0)
+
+#define WPA_GET_BE64(a) ((((u64) (a)[0]) << 56) | (((u64) (a)[1]) << 48) | \
+                        (((u64) (a)[2]) << 40) | (((u64) (a)[3]) << 32) | \
+                        (((u64) (a)[4]) << 24) | (((u64) (a)[5]) << 16) | \
+                        (((u64) (a)[6]) << 8) | ((u64) (a)[7]))
+#define WPA_PUT_BE64(a, val)                           \
+       do {                                            \
+               (a)[0] = (u8) (((u64) (val)) >> 56);    \
+               (a)[1] = (u8) (((u64) (val)) >> 48);    \
+               (a)[2] = (u8) (((u64) (val)) >> 40);    \
+               (a)[3] = (u8) (((u64) (val)) >> 32);    \
+               (a)[4] = (u8) (((u64) (val)) >> 24);    \
+               (a)[5] = (u8) (((u64) (val)) >> 16);    \
+               (a)[6] = (u8) (((u64) (val)) >> 8);     \
+               (a)[7] = (u8) (((u64) (val)) & 0xff);   \
+       } while (0)
+
+#define WPA_GET_LE64(a) ((((u64) (a)[7]) << 56) | (((u64) (a)[6]) << 48) | \
+                        (((u64) (a)[5]) << 40) | (((u64) (a)[4]) << 32) | \
+                        (((u64) (a)[3]) << 24) | (((u64) (a)[2]) << 16) | \
+                        (((u64) (a)[1]) << 8) | ((u64) (a)[0]))
+
+
+#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_RSN_PREAUTH
+#define ETH_P_RSN_PREAUTH 0x88c7
+#endif /* ETH_P_RSN_PREAUTH */
+#ifndef ETH_P_RRB
+#define ETH_P_RRB 0x890D
+#endif /* ETH_P_RRB */
+
+
+#ifdef __GNUC__
+#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b))))
+#define STRUCT_PACKED __attribute__ ((packed))
+#else
+#define PRINTF_FORMAT(a,b)
+#define STRUCT_PACKED
+#endif
+
+
+#ifdef CONFIG_ANSI_C_EXTRA
+
+#if !defined(_MSC_VER) || _MSC_VER < 1400
+/* snprintf - used in number of places; sprintf() is _not_ a good replacement
+ * due to possible buffer overflow; see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for portable implementation of
+ * snprintf. */
+int snprintf(char *str, size_t size, const char *format, ...);
+
+/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */
+
+/* getopt - only used in main.c */
+int getopt(int argc, char *const argv[], const char *optstring);
+extern char *optarg;
+extern int optind;
+
+#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF
+#ifndef __socklen_t_defined
+typedef int socklen_t;
+#endif
+#endif
+
+/* inline - define as __inline or just define it to be empty, if needed */
+#ifdef CONFIG_NO_INLINE
+#define inline
+#else
+#define inline __inline
+#endif
+
+#ifndef __func__
+#define __func__ "__func__ not defined"
+#endif
+
+#ifndef bswap_16
+#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff))
+#endif
+
+#ifndef bswap_32
+#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \
+                    (((u32) (a) << 8) & 0xff0000) | \
+                    (((u32) (a) >> 8) & 0xff00) | \
+                    (((u32) (a) >> 24) & 0xff))
+#endif
+
+#ifndef MSG_DONTWAIT
+#define MSG_DONTWAIT 0
+#endif
+
+#ifdef _WIN32_WCE
+void perror(const char *s);
+#endif /* _WIN32_WCE */
+
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+#ifndef MAC2STR
+#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
+#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
+#endif
+
+#ifndef BIT
+#define BIT(x) (1 << (x))
+#endif
+
+/*
+ * Definitions for sparse validation
+ * (http://kernel.org/pub/linux/kernel/people/josh/sparse/)
+ */
+#ifdef __CHECKER__
+#define __force __attribute__((force))
+#define __bitwise __attribute__((bitwise))
+#else
+#define __force
+#define __bitwise
+#endif
+
+typedef u16 __bitwise be16;
+typedef u16 __bitwise le16;
+typedef u32 __bitwise be32;
+typedef u32 __bitwise le32;
+typedef u64 __bitwise be64;
+typedef u64 __bitwise le64;
+
+#ifndef __must_check
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __must_check __attribute__((__warn_unused_result__))
+#else
+#define __must_check
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
+int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_aton2(const char *txt, u8 *addr);
+int hexstr2bin(const char *hex, u8 *buf, size_t len);
+void inc_byte_array(u8 *counter, size_t len);
+void wpa_get_ntp_timestamp(u8 *buf);
+int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
+int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
+                              size_t len);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+void wpa_unicode2ascii_inplace(TCHAR *str);
+TCHAR * wpa_strdup_tchar(const char *str);
+#else /* CONFIG_NATIVE_WINDOWS */
+#define wpa_unicode2ascii_inplace(s) do { } while (0)
+#define wpa_strdup_tchar(s) strdup((s))
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+const char * wpa_ssid_txt(const u8 *ssid, size_t ssid_len);
+
+static inline int is_zero_ether_addr(const u8 *a)
+{
+       return !(a[0] | a[1] | a[2] | a[3] | a[4] | a[5]);
+}
+
+#include "wpa_debug.h"
+
+
+/*
+ * gcc 4.4 ends up generating strict-aliasing warnings about some very common
+ * networking socket uses that do not really result in a real problem and
+ * cannot be easily avoided with union-based type-punning due to struct
+ * definitions including another struct in system header files. To avoid having
+ * to fully disable strict-aliasing warnings, provide a mechanism to hide the
+ * typecast from aliasing for now. A cleaner solution will hopefully be found
+ * in the future to handle these cases.
+ */
+void * __hide_aliasing_typecast(void *foo);
+#define aliasing_hide_typecast(a,t) (t *) __hide_aliasing_typecast((a))
+
+#endif /* COMMON_H */
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
new file mode 100644 (file)
index 0000000..4b61598
--- /dev/null
@@ -0,0 +1,615 @@
+/*
+ * Event loop based on select() loop
+ * 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"
+
+#include "common.h"
+#include "trace.h"
+#include "list.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+       int sock;
+       void *eloop_data;
+       void *user_data;
+       eloop_sock_handler handler;
+       WPA_TRACE_REF(eloop);
+       WPA_TRACE_REF(user);
+       WPA_TRACE_INFO
+};
+
+struct eloop_timeout {
+       struct dl_list list;
+       struct os_time time;
+       void *eloop_data;
+       void *user_data;
+       eloop_timeout_handler handler;
+       WPA_TRACE_REF(eloop);
+       WPA_TRACE_REF(user);
+       WPA_TRACE_INFO
+};
+
+struct eloop_signal {
+       int sig;
+       void *user_data;
+       eloop_signal_handler handler;
+       int signaled;
+};
+
+struct eloop_sock_table {
+       int count;
+       struct eloop_sock *table;
+       int changed;
+};
+
+struct eloop_data {
+       int max_sock;
+
+       struct eloop_sock_table readers;
+       struct eloop_sock_table writers;
+       struct eloop_sock_table exceptions;
+
+       struct dl_list timeout;
+
+       int signal_count;
+       struct eloop_signal *signals;
+       int signaled;
+       int pending_terminate;
+
+       int terminate;
+       int reader_table_changed;
+};
+
+static struct eloop_data eloop;
+
+
+#ifdef WPA_TRACE
+
+static void eloop_sigsegv_handler(int sig)
+{
+       wpa_trace_show("eloop SIGSEGV");
+       abort();
+}
+
+static void eloop_trace_sock_add_ref(struct eloop_sock_table *table)
+{
+       int i;
+       if (table == NULL || table->table == NULL)
+               return;
+       for (i = 0; i < table->count; i++) {
+               wpa_trace_add_ref(&table->table[i], eloop,
+                                 table->table[i].eloop_data);
+               wpa_trace_add_ref(&table->table[i], user,
+                                 table->table[i].user_data);
+       }
+}
+
+
+static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table)
+{
+       int i;
+       if (table == NULL || table->table == NULL)
+               return;
+       for (i = 0; i < table->count; i++) {
+               wpa_trace_remove_ref(&table->table[i], eloop,
+                                    table->table[i].eloop_data);
+               wpa_trace_remove_ref(&table->table[i], user,
+                                    table->table[i].user_data);
+       }
+}
+
+#else /* WPA_TRACE */
+
+#define eloop_trace_sock_add_ref(table) do { } while (0)
+#define eloop_trace_sock_remove_ref(table) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+int eloop_init(void)
+{
+       os_memset(&eloop, 0, sizeof(eloop));
+       dl_list_init(&eloop.timeout);
+#ifdef WPA_TRACE
+       signal(SIGSEGV, eloop_sigsegv_handler);
+#endif /* WPA_TRACE */
+       return 0;
+}
+
+
+static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
+                                     int sock, eloop_sock_handler handler,
+                                     void *eloop_data, void *user_data)
+{
+       struct eloop_sock *tmp;
+
+       if (table == NULL)
+               return -1;
+
+       eloop_trace_sock_remove_ref(table);
+       tmp = (struct eloop_sock *)
+               os_realloc(table->table,
+                          (table->count + 1) * sizeof(struct eloop_sock));
+       if (tmp == NULL)
+               return -1;
+
+       tmp[table->count].sock = sock;
+       tmp[table->count].eloop_data = eloop_data;
+       tmp[table->count].user_data = user_data;
+       tmp[table->count].handler = handler;
+       wpa_trace_record(&tmp[table->count]);
+       table->count++;
+       table->table = tmp;
+       if (sock > eloop.max_sock)
+               eloop.max_sock = sock;
+       table->changed = 1;
+       eloop_trace_sock_add_ref(table);
+
+       return 0;
+}
+
+
+static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
+                                         int sock)
+{
+       int i;
+
+       if (table == NULL || table->table == NULL || table->count == 0)
+               return;
+
+       for (i = 0; i < table->count; i++) {
+               if (table->table[i].sock == sock)
+                       break;
+       }
+       if (i == table->count)
+               return;
+       eloop_trace_sock_remove_ref(table);
+       if (i != table->count - 1) {
+               os_memmove(&table->table[i], &table->table[i + 1],
+                          (table->count - i - 1) *
+                          sizeof(struct eloop_sock));
+       }
+       table->count--;
+       table->changed = 1;
+       eloop_trace_sock_add_ref(table);
+}
+
+
+static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
+                                    fd_set *fds)
+{
+       int i;
+
+       FD_ZERO(fds);
+
+       if (table->table == NULL)
+               return;
+
+       for (i = 0; i < table->count; i++)
+               FD_SET(table->table[i].sock, fds);
+}
+
+
+static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
+                                     fd_set *fds)
+{
+       int i;
+
+       if (table == NULL || table->table == NULL)
+               return;
+
+       table->changed = 0;
+       for (i = 0; i < table->count; i++) {
+               if (FD_ISSET(table->table[i].sock, fds)) {
+                       table->table[i].handler(table->table[i].sock,
+                                               table->table[i].eloop_data,
+                                               table->table[i].user_data);
+                       if (table->changed)
+                               break;
+               }
+       }
+}
+
+
+static void eloop_sock_table_destroy(struct eloop_sock_table *table)
+{
+       if (table) {
+               int i;
+               for (i = 0; i < table->count && table->table; i++) {
+                       wpa_printf(MSG_INFO, "ELOOP: remaining socket: "
+                                  "sock=%d eloop_data=%p user_data=%p "
+                                  "handler=%p",
+                                  table->table[i].sock,
+                                  table->table[i].eloop_data,
+                                  table->table[i].user_data,
+                                  table->table[i].handler);
+                       wpa_trace_dump_funcname("eloop unregistered socket "
+                                               "handler",
+                                               table->table[i].handler);
+                       wpa_trace_dump("eloop sock", &table->table[i]);
+               }
+               os_free(table->table);
+       }
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+                            void *eloop_data, void *user_data)
+{
+       return eloop_register_sock(sock, EVENT_TYPE_READ, handler,
+                                  eloop_data, user_data);
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+       eloop_unregister_sock(sock, EVENT_TYPE_READ);
+}
+
+
+static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type)
+{
+       switch (type) {
+       case EVENT_TYPE_READ:
+               return &eloop.readers;
+       case EVENT_TYPE_WRITE:
+               return &eloop.writers;
+       case EVENT_TYPE_EXCEPTION:
+               return &eloop.exceptions;
+       }
+
+       return NULL;
+}
+
+
+int eloop_register_sock(int sock, eloop_event_type type,
+                       eloop_sock_handler handler,
+                       void *eloop_data, void *user_data)
+{
+       struct eloop_sock_table *table;
+
+       table = eloop_get_sock_table(type);
+       return eloop_sock_table_add_sock(table, sock, handler,
+                                        eloop_data, user_data);
+}
+
+
+void eloop_unregister_sock(int sock, eloop_event_type type)
+{
+       struct eloop_sock_table *table;
+
+       table = eloop_get_sock_table(type);
+       eloop_sock_table_remove_sock(table, sock);
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+                          eloop_timeout_handler handler,
+                          void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *timeout, *tmp;
+
+       timeout = os_zalloc(sizeof(*timeout));
+       if (timeout == NULL)
+               return -1;
+       if (os_get_time(&timeout->time) < 0) {
+               os_free(timeout);
+               return -1;
+       }
+       timeout->time.sec += secs;
+       timeout->time.usec += usecs;
+       while (timeout->time.usec >= 1000000) {
+               timeout->time.sec++;
+               timeout->time.usec -= 1000000;
+       }
+       timeout->eloop_data = eloop_data;
+       timeout->user_data = user_data;
+       timeout->handler = handler;
+       wpa_trace_add_ref(timeout, eloop, eloop_data);
+       wpa_trace_add_ref(timeout, user, user_data);
+       wpa_trace_record(timeout);
+
+       /* Maintain timeouts in order of increasing time */
+       dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+               if (os_time_before(&timeout->time, &tmp->time)) {
+                       dl_list_add(tmp->list.prev, &timeout->list);
+                       return 0;
+               }
+       }
+       dl_list_add_tail(&eloop.timeout, &timeout->list);
+
+       return 0;
+}
+
+
+static void eloop_remove_timeout(struct eloop_timeout *timeout)
+{
+       dl_list_del(&timeout->list);
+       wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data);
+       wpa_trace_remove_ref(timeout, user, timeout->user_data);
+       os_free(timeout);
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+                        void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *timeout, *prev;
+       int removed = 0;
+
+       dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+                             struct eloop_timeout, list) {
+               if (timeout->handler == handler &&
+                   (timeout->eloop_data == eloop_data ||
+                    eloop_data == ELOOP_ALL_CTX) &&
+                   (timeout->user_data == user_data ||
+                    user_data == ELOOP_ALL_CTX)) {
+                       eloop_remove_timeout(timeout);
+                       removed++;
+               }
+       }
+
+       return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+                               void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *tmp;
+
+       dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void eloop_handle_alarm(int sig)
+{
+       wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in "
+                  "two seconds. Looks like there\n"
+                  "is a bug that ends up in a busy loop that "
+                  "prevents clean shutdown.\n"
+                  "Killing program forcefully.\n");
+       exit(1);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static void eloop_handle_signal(int sig)
+{
+       int i;
+
+#ifndef CONFIG_NATIVE_WINDOWS
+       if ((sig == SIGINT || sig == SIGTERM) && !eloop.pending_terminate) {
+               /* Use SIGALRM to break out from potential busy loops that
+                * would not allow the program to be killed. */
+               eloop.pending_terminate = 1;
+               signal(SIGALRM, eloop_handle_alarm);
+               alarm(2);
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       eloop.signaled++;
+       for (i = 0; i < eloop.signal_count; i++) {
+               if (eloop.signals[i].sig == sig) {
+                       eloop.signals[i].signaled++;
+                       break;
+               }
+       }
+}
+
+
+static void eloop_process_pending_signals(void)
+{
+       int i;
+
+       if (eloop.signaled == 0)
+               return;
+       eloop.signaled = 0;
+
+       if (eloop.pending_terminate) {
+#ifndef CONFIG_NATIVE_WINDOWS
+               alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+               eloop.pending_terminate = 0;
+       }
+
+       for (i = 0; i < eloop.signal_count; i++) {
+               if (eloop.signals[i].signaled) {
+                       eloop.signals[i].signaled = 0;
+                       eloop.signals[i].handler(eloop.signals[i].sig,
+                                                eloop.signals[i].user_data);
+               }
+       }
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+                         void *user_data)
+{
+       struct eloop_signal *tmp;
+
+       tmp = (struct eloop_signal *)
+               os_realloc(eloop.signals,
+                          (eloop.signal_count + 1) *
+                          sizeof(struct eloop_signal));
+       if (tmp == NULL)
+               return -1;
+
+       tmp[eloop.signal_count].sig = sig;
+       tmp[eloop.signal_count].user_data = user_data;
+       tmp[eloop.signal_count].handler = handler;
+       tmp[eloop.signal_count].signaled = 0;
+       eloop.signal_count++;
+       eloop.signals = tmp;
+       signal(sig, eloop_handle_signal);
+
+       return 0;
+}
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+                                   void *user_data)
+{
+       int ret = eloop_register_signal(SIGINT, handler, user_data);
+       if (ret == 0)
+               ret = eloop_register_signal(SIGTERM, handler, user_data);
+       return ret;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+                                  void *user_data)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+       return 0;
+#else /* CONFIG_NATIVE_WINDOWS */
+       return eloop_register_signal(SIGHUP, handler, user_data);
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+void eloop_run(void)
+{
+       fd_set *rfds, *wfds, *efds;
+       int res;
+       struct timeval _tv;
+       struct os_time tv, now;
+
+       rfds = os_malloc(sizeof(*rfds));
+       wfds = os_malloc(sizeof(*wfds));
+       efds = os_malloc(sizeof(*efds));
+       if (rfds == NULL || wfds == NULL || efds == NULL)
+               goto out;
+
+       while (!eloop.terminate &&
+              (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
+               eloop.writers.count > 0 || eloop.exceptions.count > 0)) {
+               struct eloop_timeout *timeout;
+               timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+                                       list);
+               if (timeout) {
+                       os_get_time(&now);
+                       if (os_time_before(&now, &timeout->time))
+                               os_time_sub(&timeout->time, &now, &tv);
+                       else
+                               tv.sec = tv.usec = 0;
+                       _tv.tv_sec = tv.sec;
+                       _tv.tv_usec = tv.usec;
+               }
+
+               eloop_sock_table_set_fds(&eloop.readers, rfds);
+               eloop_sock_table_set_fds(&eloop.writers, wfds);
+               eloop_sock_table_set_fds(&eloop.exceptions, efds);
+               res = select(eloop.max_sock + 1, rfds, wfds, efds,
+                            timeout ? &_tv : NULL);
+               if (res < 0 && errno != EINTR && errno != 0) {
+                       perror("select");
+                       goto out;
+               }
+               eloop_process_pending_signals();
+
+               /* check if some registered timeouts have occurred */
+               timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
+                                       list);
+               if (timeout) {
+                       os_get_time(&now);
+                       if (!os_time_before(&now, &timeout->time)) {
+                               void *eloop_data = timeout->eloop_data;
+                               void *user_data = timeout->user_data;
+                               eloop_timeout_handler handler =
+                                       timeout->handler;
+                               eloop_remove_timeout(timeout);
+                               handler(eloop_data, user_data);
+                       }
+
+               }
+
+               if (res <= 0)
+                       continue;
+
+               eloop_sock_table_dispatch(&eloop.readers, rfds);
+               eloop_sock_table_dispatch(&eloop.writers, wfds);
+               eloop_sock_table_dispatch(&eloop.exceptions, efds);
+       }
+
+out:
+       os_free(rfds);
+       os_free(wfds);
+       os_free(efds);
+}
+
+
+void eloop_terminate(void)
+{
+       eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+       struct eloop_timeout *timeout, *prev;
+       struct os_time now;
+
+       os_get_time(&now);
+       dl_list_for_each_safe(timeout, prev, &eloop.timeout,
+                             struct eloop_timeout, list) {
+               int sec, usec;
+               sec = timeout->time.sec - now.sec;
+               usec = timeout->time.usec - now.usec;
+               if (timeout->time.usec < now.usec) {
+                       sec--;
+                       usec += 1000000;
+               }
+               wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d "
+                          "eloop_data=%p user_data=%p handler=%p",
+                          sec, usec, timeout->eloop_data, timeout->user_data,
+                          timeout->handler);
+               wpa_trace_dump_funcname("eloop unregistered timeout handler",
+                                       timeout->handler);
+               wpa_trace_dump("eloop timeout", timeout);
+               eloop_remove_timeout(timeout);
+       }
+       eloop_sock_table_destroy(&eloop.readers);
+       eloop_sock_table_destroy(&eloop.writers);
+       eloop_sock_table_destroy(&eloop.exceptions);
+       os_free(eloop.signals);
+}
+
+
+int eloop_terminated(void)
+{
+       return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+       fd_set rfds;
+
+       if (sock < 0)
+               return;
+
+       FD_ZERO(&rfds);
+       FD_SET(sock, &rfds);
+       select(sock + 1, &rfds, NULL, NULL, NULL);
+}
diff --git a/src/utils/eloop.h b/src/utils/eloop.h
new file mode 100644 (file)
index 0000000..1228f24
--- /dev/null
@@ -0,0 +1,316 @@
+/*
+ * Event loop
+ * 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.
+ *
+ * This file defines an event loop interface that supports processing events
+ * from registered timeouts (i.e., do something after N seconds), sockets
+ * (e.g., a new packet available for reading), and signals. eloop.c is an
+ * implementation of this interface using select() and sockets. This is
+ * suitable for most UNIX/POSIX systems. When porting to other operating
+ * systems, it may be necessary to replace that implementation with OS specific
+ * mechanisms.
+ */
+
+#ifndef ELOOP_H
+#define ELOOP_H
+
+/**
+ * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts
+ */
+#define ELOOP_ALL_CTX (void *) -1
+
+/**
+ * eloop_event_type - eloop socket event type for eloop_register_sock()
+ * @EVENT_TYPE_READ: Socket has data available for reading
+ * @EVENT_TYPE_WRITE: Socket has room for new data to be written
+ * @EVENT_TYPE_EXCEPTION: An exception has been reported
+ */
+typedef enum {
+       EVENT_TYPE_READ = 0,
+       EVENT_TYPE_WRITE,
+       EVENT_TYPE_EXCEPTION
+} eloop_event_type;
+
+/**
+ * eloop_sock_handler - eloop socket event callback type
+ * @sock: File descriptor number for the socket
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx);
+
+/**
+ * eloop_event_handler - eloop generic event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_timeout_handler - eloop timeout event callback type
+ * @eloop_ctx: Registered callback context data (eloop_data)
+ * @sock_ctx: Registered callback context data (user_data)
+ */
+typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx);
+
+/**
+ * eloop_signal_handler - eloop signal event callback type
+ * @sig: Signal number
+ * @signal_ctx: Registered callback context data (user_data from
+ * eloop_register_signal(), eloop_register_signal_terminate(), or
+ * eloop_register_signal_reconfig() call)
+ */
+typedef void (*eloop_signal_handler)(int sig, void *signal_ctx);
+
+/**
+ * eloop_init() - Initialize global event loop data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function must be called before any other eloop_* function.
+ */
+int eloop_init(void);
+
+/**
+ * eloop_register_read_sock - Register handler for read events
+ * @sock: File descriptor number for the socket
+ * @handler: Callback function to be called when data is available for reading
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a read socket notifier for the given file descriptor. The handler
+ * function will be called whenever data is available for reading from the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+                            void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_read_sock - Unregister handler for read events
+ * @sock: File descriptor number for the socket
+ *
+ * Unregister a read socket notifier that was previously registered with
+ * eloop_register_read_sock().
+ */
+void eloop_unregister_read_sock(int sock);
+
+/**
+ * eloop_register_sock - Register handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event to wait for
+ * @handler: Callback function to be called when the event is triggered
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event notifier for the given socket's file descriptor. The
+ * handler function will be called whenever the that event is triggered for the
+ * socket. The handler function is responsible for clearing the event after
+ * having processed it in order to avoid eloop from calling the handler again
+ * for the same event.
+ */
+int eloop_register_sock(int sock, eloop_event_type type,
+                       eloop_sock_handler handler,
+                       void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_sock - Unregister handler for socket events
+ * @sock: File descriptor number for the socket
+ * @type: Type of event for which sock was registered
+ *
+ * Unregister a socket event notifier that was previously registered with
+ * eloop_register_sock().
+ */
+void eloop_unregister_sock(int sock, eloop_event_type type);
+
+/**
+ * eloop_register_event - Register handler for generic events
+ * @event: Event to wait (eloop implementation specific)
+ * @event_size: Size of event data
+ * @handler: Callback function to be called when event is triggered
+ * @eloop_data: Callback context data (eloop_data)
+ * @user_data: Callback context data (user_data)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register an event handler for the given event. This function is used to
+ * register eloop implementation specific events which are mainly targetted for
+ * operating system specific code (driver interface and l2_packet) since the
+ * portable code will not be able to use such an OS-specific call. The handler
+ * function will be called whenever the event is triggered. The handler
+ * function is responsible for clearing the event after having processed it in
+ * order to avoid eloop from calling the handler again for the same event.
+ *
+ * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE
+ * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable,
+ * and they would call this function with eloop_register_event(h, sizeof(h),
+ * ...).
+ */
+int eloop_register_event(void *event, size_t event_size,
+                        eloop_event_handler handler,
+                        void *eloop_data, void *user_data);
+
+/**
+ * eloop_unregister_event - Unregister handler for a generic event
+ * @event: Event to cancel (eloop implementation specific)
+ * @event_size: Size of event data
+ *
+ * Unregister a generic event notifier that was previously registered with
+ * eloop_register_event().
+ */
+void eloop_unregister_event(void *event, size_t event_size);
+
+/**
+ * eloop_register_timeout - Register timeout
+ * @secs: Number of seconds to the timeout
+ * @usecs: Number of microseconds to the timeout
+ * @handler: Callback function to be called when timeout occurs
+ * @eloop_data: Callback context data (eloop_ctx)
+ * @user_data: Callback context data (sock_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a timeout that will cause the handler function to be called after
+ * given time.
+ */
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+                          eloop_timeout_handler handler,
+                          void *eloop_data, void *user_data);
+
+/**
+ * eloop_cancel_timeout - Cancel timeouts
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all
+ * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all
+ * Returns: Number of cancelled timeouts
+ *
+ * Cancel matching <handler,eloop_data,user_data> timeouts registered with
+ * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for
+ * cancelling all timeouts regardless of eloop_data/user_data.
+ */
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+                        void *eloop_data, void *user_data);
+
+/**
+ * eloop_is_timeout_registered - Check if a timeout is already registered
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is registered, 0 if the timeout is not registered
+ *
+ * Determine if a matching <handler,eloop_data,user_data> timeout is registered
+ * with eloop_register_timeout().
+ */
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+                               void *eloop_data, void *user_data);
+
+/**
+ * eloop_register_signal - Register handler for signals
+ * @sig: Signal number (e.g., SIGHUP)
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a signal is received.
+ * The callback function is actually called only after the system signal
+ * handler has returned. This means that the normal limits for sighandlers
+ * (i.e., only "safe functions" allowed) do not apply for the registered
+ * callback.
+ */
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+                         void *user_data);
+
+/**
+ * eloop_register_signal_terminate - Register handler for terminate signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a process termination
+ * signal is received. The callback function is actually called only after the
+ * system signal handler has returned. This means that the normal limits for
+ * sighandlers (i.e., only "safe functions" allowed) do not apply for the
+ * registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers handlers for SIGINT and SIGTERM.
+ */
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+                                   void *user_data);
+
+/**
+ * eloop_register_signal_reconfig - Register handler for reconfig signals
+ * @handler: Callback function to be called when the signal is received
+ * @user_data: Callback context data (signal_ctx)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Register a callback function that will be called when a reconfiguration /
+ * hangup signal is received. The callback function is actually called only
+ * after the system signal handler has returned. This means that the normal
+ * limits for sighandlers (i.e., only "safe functions" allowed) do not apply
+ * for the registered callback.
+ *
+ * This function is a more portable version of eloop_register_signal() since
+ * the knowledge of exact details of the signals is hidden in eloop
+ * implementation. In case of operating systems using signal(), this function
+ * registers a handler for SIGHUP.
+ */
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+                                  void *user_data);
+
+/**
+ * eloop_run - Start the event loop
+ *
+ * Start the event loop and continue running as long as there are any
+ * registered event handlers. This function is run after event loop has been
+ * initialized with event_init() and one or more events have been registered.
+ */
+void eloop_run(void);
+
+/**
+ * eloop_terminate - Terminate event loop
+ *
+ * Terminate event loop even if there are registered events. This can be used
+ * to request the program to be terminated cleanly.
+ */
+void eloop_terminate(void);
+
+/**
+ * eloop_destroy - Free any resources allocated for the event loop
+ *
+ * After calling eloop_destroy(), other eloop_* functions must not be called
+ * before re-running eloop_init().
+ */
+void eloop_destroy(void);
+
+/**
+ * eloop_terminated - Check whether event loop has been terminated
+ * Returns: 1 = event loop terminate, 0 = event loop still running
+ *
+ * This function can be used to check whether eloop_terminate() has been called
+ * to request termination of the event loop. This is normally used to abort
+ * operations that may still be queued to be run when eloop_terminate() was
+ * called.
+ */
+int eloop_terminated(void);
+
+/**
+ * eloop_wait_for_read_sock - Wait for a single reader
+ * @sock: File descriptor number for the socket
+ *
+ * Do a blocking wait for a single read socket.
+ */
+void eloop_wait_for_read_sock(int sock);
+
+#endif /* ELOOP_H */
diff --git a/src/utils/eloop_none.c b/src/utils/eloop_none.c
new file mode 100644 (file)
index 0000000..18eae4e
--- /dev/null
@@ -0,0 +1,401 @@
+/*
+ * Event loop - empty template (basic structure, but no OS specific operations)
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+       int sock;
+       void *eloop_data;
+       void *user_data;
+       void (*handler)(int sock, void *eloop_ctx, void *sock_ctx);
+};
+
+struct eloop_timeout {
+       struct os_time time;
+       void *eloop_data;
+       void *user_data;
+       void (*handler)(void *eloop_ctx, void *sock_ctx);
+       struct eloop_timeout *next;
+};
+
+struct eloop_signal {
+       int sig;
+       void *user_data;
+       void (*handler)(int sig, void *eloop_ctx, void *signal_ctx);
+       int signaled;
+};
+
+struct eloop_data {
+       int max_sock, reader_count;
+       struct eloop_sock *readers;
+
+       struct eloop_timeout *timeout;
+
+       int signal_count;
+       struct eloop_signal *signals;
+       int signaled;
+       int pending_terminate;
+
+       int terminate;
+       int reader_table_changed;
+};
+
+static struct eloop_data eloop;
+
+
+int eloop_init(void)
+{
+       memset(&eloop, 0, sizeof(eloop));
+       return 0;
+}
+
+
+int eloop_register_read_sock(int sock,
+                            void (*handler)(int sock, void *eloop_ctx,
+                                            void *sock_ctx),
+                            void *eloop_data, void *user_data)
+{
+       struct eloop_sock *tmp;
+
+       tmp = (struct eloop_sock *)
+               realloc(eloop.readers,
+                       (eloop.reader_count + 1) * sizeof(struct eloop_sock));
+       if (tmp == NULL)
+               return -1;
+
+       tmp[eloop.reader_count].sock = sock;
+       tmp[eloop.reader_count].eloop_data = eloop_data;
+       tmp[eloop.reader_count].user_data = user_data;
+       tmp[eloop.reader_count].handler = handler;
+       eloop.reader_count++;
+       eloop.readers = tmp;
+       if (sock > eloop.max_sock)
+               eloop.max_sock = sock;
+       eloop.reader_table_changed = 1;
+
+       return 0;
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+       int i;
+
+       if (eloop.readers == NULL || eloop.reader_count == 0)
+               return;
+
+       for (i = 0; i < eloop.reader_count; i++) {
+               if (eloop.readers[i].sock == sock)
+                       break;
+       }
+       if (i == eloop.reader_count)
+               return;
+       if (i != eloop.reader_count - 1) {
+               memmove(&eloop.readers[i], &eloop.readers[i + 1],
+                       (eloop.reader_count - i - 1) *
+                       sizeof(struct eloop_sock));
+       }
+       eloop.reader_count--;
+       eloop.reader_table_changed = 1;
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+                          void (*handler)(void *eloop_ctx, void *timeout_ctx),
+                          void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *timeout, *tmp, *prev;
+
+       timeout = (struct eloop_timeout *) malloc(sizeof(*timeout));
+       if (timeout == NULL)
+               return -1;
+       os_get_time(&timeout->time);
+       timeout->time.sec += secs;
+       timeout->time.usec += usecs;
+       while (timeout->time.usec >= 1000000) {
+               timeout->time.sec++;
+               timeout->time.usec -= 1000000;
+       }
+       timeout->eloop_data = eloop_data;
+       timeout->user_data = user_data;
+       timeout->handler = handler;
+       timeout->next = NULL;
+
+       if (eloop.timeout == NULL) {
+               eloop.timeout = timeout;
+               return 0;
+       }
+
+       prev = NULL;
+       tmp = eloop.timeout;
+       while (tmp != NULL) {
+               if (os_time_before(&timeout->time, &tmp->time))
+                       break;
+               prev = tmp;
+               tmp = tmp->next;
+       }
+
+       if (prev == NULL) {
+               timeout->next = eloop.timeout;
+               eloop.timeout = timeout;
+       } else {
+               timeout->next = prev->next;
+               prev->next = timeout;
+       }
+
+       return 0;
+}
+
+
+int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx),
+                        void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *timeout, *prev, *next;
+       int removed = 0;
+
+       prev = NULL;
+       timeout = eloop.timeout;
+       while (timeout != NULL) {
+               next = timeout->next;
+
+               if (timeout->handler == handler &&
+                   (timeout->eloop_data == eloop_data ||
+                    eloop_data == ELOOP_ALL_CTX) &&
+                   (timeout->user_data == user_data ||
+                    user_data == ELOOP_ALL_CTX)) {
+                       if (prev == NULL)
+                               eloop.timeout = next;
+                       else
+                               prev->next = next;
+                       free(timeout);
+                       removed++;
+               } else
+                       prev = timeout;
+
+               timeout = next;
+       }
+
+       return removed;
+}
+
+
+int eloop_is_timeout_registered(void (*handler)(void *eloop_ctx,
+                                               void *timeout_ctx),
+                               void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *tmp;
+
+       tmp = eloop.timeout;
+       while (tmp != NULL) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data)
+                       return 1;
+
+               tmp = tmp->next;
+       }
+
+       return 0;
+}
+
+
+/* TODO: replace with suitable signal handler */
+#if 0
+static void eloop_handle_signal(int sig)
+{
+       int i;
+
+       eloop.signaled++;
+       for (i = 0; i < eloop.signal_count; i++) {
+               if (eloop.signals[i].sig == sig) {
+                       eloop.signals[i].signaled++;
+                       break;
+               }
+       }
+}
+#endif
+
+
+static void eloop_process_pending_signals(void)
+{
+       int i;
+
+       if (eloop.signaled == 0)
+               return;
+       eloop.signaled = 0;
+
+       if (eloop.pending_terminate) {
+               eloop.pending_terminate = 0;
+       }
+
+       for (i = 0; i < eloop.signal_count; i++) {
+               if (eloop.signals[i].signaled) {
+                       eloop.signals[i].signaled = 0;
+                       eloop.signals[i].handler(eloop.signals[i].sig,
+                                                eloop.user_data,
+                                                eloop.signals[i].user_data);
+               }
+       }
+}
+
+
+int eloop_register_signal(int sig,
+                         void (*handler)(int sig, void *eloop_ctx,
+                                         void *signal_ctx),
+                         void *user_data)
+{
+       struct eloop_signal *tmp;
+
+       tmp = (struct eloop_signal *)
+               realloc(eloop.signals,
+                       (eloop.signal_count + 1) *
+                       sizeof(struct eloop_signal));
+       if (tmp == NULL)
+               return -1;
+
+       tmp[eloop.signal_count].sig = sig;
+       tmp[eloop.signal_count].user_data = user_data;
+       tmp[eloop.signal_count].handler = handler;
+       tmp[eloop.signal_count].signaled = 0;
+       eloop.signal_count++;
+       eloop.signals = tmp;
+
+       /* TODO: register signal handler */
+
+       return 0;
+}
+
+
+int eloop_register_signal_terminate(void (*handler)(int sig, void *eloop_ctx,
+                                                   void *signal_ctx),
+                                   void *user_data)
+{
+#if 0
+       /* TODO: for example */
+       int ret = eloop_register_signal(SIGINT, handler, user_data);
+       if (ret == 0)
+               ret = eloop_register_signal(SIGTERM, handler, user_data);
+       return ret;
+#endif
+       return 0;
+}
+
+
+int eloop_register_signal_reconfig(void (*handler)(int sig, void *eloop_ctx,
+                                                  void *signal_ctx),
+                                  void *user_data)
+{
+#if 0
+       /* TODO: for example */
+       return eloop_register_signal(SIGHUP, handler, user_data);
+#endif
+       return 0;
+}
+
+
+void eloop_run(void)
+{
+       int i;
+       struct os_time tv, now;
+
+       while (!eloop.terminate &&
+               (eloop.timeout || eloop.reader_count > 0)) {
+               if (eloop.timeout) {
+                       os_get_time(&now);
+                       if (os_time_before(&now, &eloop.timeout->time))
+                               os_time_sub(&eloop.timeout->time, &now, &tv);
+                       else
+                               tv.sec = tv.usec = 0;
+               }
+
+               /*
+                * TODO: wait for any event (read socket ready, timeout (tv),
+                * signal
+                */
+               os_sleep(1, 0); /* just a dummy wait for testing */
+
+               eloop_process_pending_signals();
+
+               /* check if some registered timeouts have occurred */
+               if (eloop.timeout) {
+                       struct eloop_timeout *tmp;
+
+                       os_get_time(&now);
+                       if (!os_time_before(&now, &eloop.timeout->time)) {
+                               tmp = eloop.timeout;
+                               eloop.timeout = eloop.timeout->next;
+                               tmp->handler(tmp->eloop_data,
+                                            tmp->user_data);
+                               free(tmp);
+                       }
+
+               }
+
+               eloop.reader_table_changed = 0;
+               for (i = 0; i < eloop.reader_count; i++) {
+                       /*
+                        * TODO: call each handler that has pending data to
+                        * read
+                        */
+                       if (0 /* TODO: eloop.readers[i].sock ready */) {
+                               eloop.readers[i].handler(
+                                       eloop.readers[i].sock,
+                                       eloop.readers[i].eloop_data,
+                                       eloop.readers[i].user_data);
+                               if (eloop.reader_table_changed)
+                                       break;
+                       }
+               }
+       }
+}
+
+
+void eloop_terminate(void)
+{
+       eloop.terminate = 1;
+}
+
+
+void eloop_destroy(void)
+{
+       struct eloop_timeout *timeout, *prev;
+
+       timeout = eloop.timeout;
+       while (timeout != NULL) {
+               prev = timeout;
+               timeout = timeout->next;
+               free(prev);
+       }
+       free(eloop.readers);
+       free(eloop.signals);
+}
+
+
+int eloop_terminated(void)
+{
+       return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+       /*
+        * TODO: wait for the file descriptor to have something available for
+        * reading
+        */
+}
diff --git a/src/utils/eloop_win.c b/src/utils/eloop_win.c
new file mode 100644 (file)
index 0000000..94cc72d
--- /dev/null
@@ -0,0 +1,611 @@
+/*
+ * Event loop based on Windows events and WaitForMultipleObjects
+ * 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.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+
+#include "common.h"
+#include "eloop.h"
+
+
+struct eloop_sock {
+       int sock;
+       void *eloop_data;
+       void *user_data;
+       eloop_sock_handler handler;
+       WSAEVENT event;
+};
+
+struct eloop_event {
+       void *eloop_data;
+       void *user_data;
+       eloop_event_handler handler;
+       HANDLE event;
+};
+
+struct eloop_timeout {
+       struct os_time time;
+       void *eloop_data;
+       void *user_data;
+       eloop_timeout_handler handler;
+       struct eloop_timeout *next;
+};
+
+struct eloop_signal {
+       int sig;
+       void *user_data;
+       eloop_signal_handler handler;
+       int signaled;
+};
+
+struct eloop_data {
+       int max_sock;
+       size_t reader_count;
+       struct eloop_sock *readers;
+
+       size_t event_count;
+       struct eloop_event *events;
+
+       struct eloop_timeout *timeout;
+
+       int signal_count;
+       struct eloop_signal *signals;
+       int signaled;
+       int pending_terminate;
+
+       int terminate;
+       int reader_table_changed;
+
+       struct eloop_signal term_signal;
+       HANDLE term_event;
+
+       HANDLE *handles;
+       size_t num_handles;
+};
+
+static struct eloop_data eloop;
+
+
+int eloop_init(void)
+{
+       os_memset(&eloop, 0, sizeof(eloop));
+       eloop.num_handles = 1;
+       eloop.handles = os_malloc(eloop.num_handles *
+                                 sizeof(eloop.handles[0]));
+       if (eloop.handles == NULL)
+               return -1;
+
+       eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+       if (eloop.term_event == NULL) {
+               printf("CreateEvent() failed: %d\n",
+                      (int) GetLastError());
+               os_free(eloop.handles);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int eloop_prepare_handles(void)
+{
+       HANDLE *n;
+
+       if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8)
+               return 0;
+       n = os_realloc(eloop.handles,
+                      eloop.num_handles * 2 * sizeof(eloop.handles[0]));
+       if (n == NULL)
+               return -1;
+       eloop.handles = n;
+       eloop.num_handles *= 2;
+       return 0;
+}
+
+
+int eloop_register_read_sock(int sock, eloop_sock_handler handler,
+                            void *eloop_data, void *user_data)
+{
+       WSAEVENT event;
+       struct eloop_sock *tmp;
+
+       if (eloop_prepare_handles())
+               return -1;
+
+       event = WSACreateEvent();
+       if (event == WSA_INVALID_EVENT) {
+               printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+               return -1;
+       }
+
+       if (WSAEventSelect(sock, event, FD_READ)) {
+               printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+               WSACloseEvent(event);
+               return -1;
+       }
+       tmp = os_realloc(eloop.readers,
+                        (eloop.reader_count + 1) * sizeof(struct eloop_sock));
+       if (tmp == NULL) {
+               WSAEventSelect(sock, event, 0);
+               WSACloseEvent(event);
+               return -1;
+       }
+
+       tmp[eloop.reader_count].sock = sock;
+       tmp[eloop.reader_count].eloop_data = eloop_data;
+       tmp[eloop.reader_count].user_data = user_data;
+       tmp[eloop.reader_count].handler = handler;
+       tmp[eloop.reader_count].event = event;
+       eloop.reader_count++;
+       eloop.readers = tmp;
+       if (sock > eloop.max_sock)
+               eloop.max_sock = sock;
+       eloop.reader_table_changed = 1;
+
+       return 0;
+}
+
+
+void eloop_unregister_read_sock(int sock)
+{
+       size_t i;
+
+       if (eloop.readers == NULL || eloop.reader_count == 0)
+               return;
+
+       for (i = 0; i < eloop.reader_count; i++) {
+               if (eloop.readers[i].sock == sock)
+                       break;
+       }
+       if (i == eloop.reader_count)
+               return;
+
+       WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0);
+       WSACloseEvent(eloop.readers[i].event);
+
+       if (i != eloop.reader_count - 1) {
+               os_memmove(&eloop.readers[i], &eloop.readers[i + 1],
+                          (eloop.reader_count - i - 1) *
+                          sizeof(struct eloop_sock));
+       }
+       eloop.reader_count--;
+       eloop.reader_table_changed = 1;
+}
+
+
+int eloop_register_event(void *event, size_t event_size,
+                        eloop_event_handler handler,
+                        void *eloop_data, void *user_data)
+{
+       struct eloop_event *tmp;
+       HANDLE h = event;
+
+       if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE)
+               return -1;
+
+       if (eloop_prepare_handles())
+               return -1;
+
+       tmp = os_realloc(eloop.events,
+                        (eloop.event_count + 1) * sizeof(struct eloop_event));
+       if (tmp == NULL)
+               return -1;
+
+       tmp[eloop.event_count].eloop_data = eloop_data;
+       tmp[eloop.event_count].user_data = user_data;
+       tmp[eloop.event_count].handler = handler;
+       tmp[eloop.event_count].event = h;
+       eloop.event_count++;
+       eloop.events = tmp;
+
+       return 0;
+}
+
+
+void eloop_unregister_event(void *event, size_t event_size)
+{
+       size_t i;
+       HANDLE h = event;
+
+       if (eloop.events == NULL || eloop.event_count == 0 ||
+           event_size != sizeof(HANDLE))
+               return;
+
+       for (i = 0; i < eloop.event_count; i++) {
+               if (eloop.events[i].event == h)
+                       break;
+       }
+       if (i == eloop.event_count)
+               return;
+
+       if (i != eloop.event_count - 1) {
+               os_memmove(&eloop.events[i], &eloop.events[i + 1],
+                          (eloop.event_count - i - 1) *
+                          sizeof(struct eloop_event));
+       }
+       eloop.event_count--;
+}
+
+
+int eloop_register_timeout(unsigned int secs, unsigned int usecs,
+                          eloop_timeout_handler handler,
+                          void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *timeout, *tmp, *prev;
+
+       timeout = os_malloc(sizeof(*timeout));
+       if (timeout == NULL)
+               return -1;
+       os_get_time(&timeout->time);
+       timeout->time.sec += secs;
+       timeout->time.usec += usecs;
+       while (timeout->time.usec >= 1000000) {
+               timeout->time.sec++;
+               timeout->time.usec -= 1000000;
+       }
+       timeout->eloop_data = eloop_data;
+       timeout->user_data = user_data;
+       timeout->handler = handler;
+       timeout->next = NULL;
+
+       if (eloop.timeout == NULL) {
+               eloop.timeout = timeout;
+               return 0;
+       }
+
+       prev = NULL;
+       tmp = eloop.timeout;
+       while (tmp != NULL) {
+               if (os_time_before(&timeout->time, &tmp->time))
+                       break;
+               prev = tmp;
+               tmp = tmp->next;
+       }
+
+       if (prev == NULL) {
+               timeout->next = eloop.timeout;
+               eloop.timeout = timeout;
+       } else {
+               timeout->next = prev->next;
+               prev->next = timeout;
+       }
+
+       return 0;
+}
+
+
+int eloop_cancel_timeout(eloop_timeout_handler handler,
+                        void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *timeout, *prev, *next;
+       int removed = 0;
+
+       prev = NULL;
+       timeout = eloop.timeout;
+       while (timeout != NULL) {
+               next = timeout->next;
+
+               if (timeout->handler == handler &&
+                   (timeout->eloop_data == eloop_data ||
+                    eloop_data == ELOOP_ALL_CTX) &&
+                   (timeout->user_data == user_data ||
+                    user_data == ELOOP_ALL_CTX)) {
+                       if (prev == NULL)
+                               eloop.timeout = next;
+                       else
+                               prev->next = next;
+                       os_free(timeout);
+                       removed++;
+               } else
+                       prev = timeout;
+
+               timeout = next;
+       }
+
+       return removed;
+}
+
+
+int eloop_is_timeout_registered(eloop_timeout_handler handler,
+                               void *eloop_data, void *user_data)
+{
+       struct eloop_timeout *tmp;
+
+       tmp = eloop.timeout;
+       while (tmp != NULL) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data)
+                       return 1;
+
+               tmp = tmp->next;
+       }
+
+       return 0;
+}
+
+
+/* TODO: replace with suitable signal handler */
+#if 0
+static void eloop_handle_signal(int sig)
+{
+       int i;
+
+       eloop.signaled++;
+       for (i = 0; i < eloop.signal_count; i++) {
+               if (eloop.signals[i].sig == sig) {
+                       eloop.signals[i].signaled++;
+                       break;
+               }
+       }
+}
+#endif
+
+
+static void eloop_process_pending_signals(void)
+{
+       int i;
+
+       if (eloop.signaled == 0)
+               return;
+       eloop.signaled = 0;
+
+       if (eloop.pending_terminate) {
+               eloop.pending_terminate = 0;
+       }
+
+       for (i = 0; i < eloop.signal_count; i++) {
+               if (eloop.signals[i].signaled) {
+                       eloop.signals[i].signaled = 0;
+                       eloop.signals[i].handler(eloop.signals[i].sig,
+                                                eloop.signals[i].user_data);
+               }
+       }
+
+       if (eloop.term_signal.signaled) {
+               eloop.term_signal.signaled = 0;
+               eloop.term_signal.handler(eloop.term_signal.sig,
+                                         eloop.term_signal.user_data);
+       }
+}
+
+
+int eloop_register_signal(int sig, eloop_signal_handler handler,
+                         void *user_data)
+{
+       struct eloop_signal *tmp;
+
+       tmp = os_realloc(eloop.signals,
+                        (eloop.signal_count + 1) *
+                        sizeof(struct eloop_signal));
+       if (tmp == NULL)
+               return -1;
+
+       tmp[eloop.signal_count].sig = sig;
+       tmp[eloop.signal_count].user_data = user_data;
+       tmp[eloop.signal_count].handler = handler;
+       tmp[eloop.signal_count].signaled = 0;
+       eloop.signal_count++;
+       eloop.signals = tmp;
+
+       /* TODO: register signal handler */
+
+       return 0;
+}
+
+
+#ifndef _WIN32_WCE
+static BOOL eloop_handle_console_ctrl(DWORD type)
+{
+       switch (type) {
+       case CTRL_C_EVENT:
+       case CTRL_BREAK_EVENT:
+               eloop.signaled++;
+               eloop.term_signal.signaled++;
+               SetEvent(eloop.term_event);
+               return TRUE;
+       default:
+               return FALSE;
+       }
+}
+#endif /* _WIN32_WCE */
+
+
+int eloop_register_signal_terminate(eloop_signal_handler handler,
+                                   void *user_data)
+{
+#ifndef _WIN32_WCE
+       if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl,
+                                 TRUE) == 0) {
+               printf("SetConsoleCtrlHandler() failed: %d\n",
+                      (int) GetLastError());
+               return -1;
+       }
+#endif /* _WIN32_WCE */
+
+       eloop.term_signal.handler = handler;
+       eloop.term_signal.user_data = user_data;
+               
+       return 0;
+}
+
+
+int eloop_register_signal_reconfig(eloop_signal_handler handler,
+                                  void *user_data)
+{
+       /* TODO */
+       return 0;
+}
+
+
+void eloop_run(void)
+{
+       struct os_time tv, now;
+       DWORD count, ret, timeout, err;
+       size_t i;
+
+       while (!eloop.terminate &&
+              (eloop.timeout || eloop.reader_count > 0 ||
+               eloop.event_count > 0)) {
+               tv.sec = tv.usec = 0;
+               if (eloop.timeout) {
+                       os_get_time(&now);
+                       if (os_time_before(&now, &eloop.timeout->time))
+                               os_time_sub(&eloop.timeout->time, &now, &tv);
+               }
+
+               count = 0;
+               for (i = 0; i < eloop.event_count; i++)
+                       eloop.handles[count++] = eloop.events[i].event;
+
+               for (i = 0; i < eloop.reader_count; i++)
+                       eloop.handles[count++] = eloop.readers[i].event;
+
+               if (eloop.term_event)
+                       eloop.handles[count++] = eloop.term_event;
+
+               if (eloop.timeout)
+                       timeout = tv.sec * 1000 + tv.usec / 1000;
+               else
+                       timeout = INFINITE;
+
+               if (count > MAXIMUM_WAIT_OBJECTS) {
+                       printf("WaitForMultipleObjects: Too many events: "
+                              "%d > %d (ignoring extra events)\n",
+                              (int) count, MAXIMUM_WAIT_OBJECTS);
+                       count = MAXIMUM_WAIT_OBJECTS;
+               }
+#ifdef _WIN32_WCE
+               ret = WaitForMultipleObjects(count, eloop.handles, FALSE,
+                                            timeout);
+#else /* _WIN32_WCE */
+               ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE,
+                                              timeout, TRUE);
+#endif /* _WIN32_WCE */
+               err = GetLastError();
+
+               eloop_process_pending_signals();
+
+               /* check if some registered timeouts have occurred */
+               if (eloop.timeout) {
+                       struct eloop_timeout *tmp;
+
+                       os_get_time(&now);
+                       if (!os_time_before(&now, &eloop.timeout->time)) {
+                               tmp = eloop.timeout;
+                               eloop.timeout = eloop.timeout->next;
+                               tmp->handler(tmp->eloop_data,
+                                            tmp->user_data);
+                               os_free(tmp);
+                       }
+
+               }
+
+               if (ret == WAIT_FAILED) {
+                       printf("WaitForMultipleObjects(count=%d) failed: %d\n",
+                              (int) count, (int) err);
+                       os_sleep(1, 0);
+                       continue;
+               }
+
+#ifndef _WIN32_WCE
+               if (ret == WAIT_IO_COMPLETION)
+                       continue;
+#endif /* _WIN32_WCE */
+
+               if (ret == WAIT_TIMEOUT)
+                       continue;
+
+               while (ret >= WAIT_OBJECT_0 &&
+                      ret < WAIT_OBJECT_0 + eloop.event_count) {
+                       eloop.events[ret].handler(
+                               eloop.events[ret].eloop_data,
+                               eloop.events[ret].user_data);
+                       ret = WaitForMultipleObjects(eloop.event_count,
+                                                    eloop.handles, FALSE, 0);
+               }
+
+               eloop.reader_table_changed = 0;
+               for (i = 0; i < eloop.reader_count; i++) {
+                       WSANETWORKEVENTS events;
+                       if (WSAEnumNetworkEvents(eloop.readers[i].sock,
+                                                eloop.readers[i].event,
+                                                &events) == 0 &&
+                           (events.lNetworkEvents & FD_READ)) {
+                               eloop.readers[i].handler(
+                                       eloop.readers[i].sock,
+                                       eloop.readers[i].eloop_data,
+                                       eloop.readers[i].user_data);
+                               if (eloop.reader_table_changed)
+                                       break;
+                       }
+               }
+       }
+}
+
+
+void eloop_terminate(void)
+{
+       eloop.terminate = 1;
+       SetEvent(eloop.term_event);
+}
+
+
+void eloop_destroy(void)
+{
+       struct eloop_timeout *timeout, *prev;
+
+       timeout = eloop.timeout;
+       while (timeout != NULL) {
+               prev = timeout;
+               timeout = timeout->next;
+               os_free(prev);
+       }
+       os_free(eloop.readers);
+       os_free(eloop.signals);
+       if (eloop.term_event)
+               CloseHandle(eloop.term_event);
+       os_free(eloop.handles);
+       eloop.handles = NULL;
+       os_free(eloop.events);
+       eloop.events = NULL;
+}
+
+
+int eloop_terminated(void)
+{
+       return eloop.terminate;
+}
+
+
+void eloop_wait_for_read_sock(int sock)
+{
+       WSAEVENT event;
+
+       event = WSACreateEvent();
+       if (event == WSA_INVALID_EVENT) {
+               printf("WSACreateEvent() failed: %d\n", WSAGetLastError());
+               return;
+       }
+
+       if (WSAEventSelect(sock, event, FD_READ)) {
+               printf("WSAEventSelect() failed: %d\n", WSAGetLastError());
+               WSACloseEvent(event);
+               return ;
+       }
+
+       WaitForSingleObject(event, INFINITE);
+       WSAEventSelect(sock, event, 0);
+       WSACloseEvent(event);
+}
diff --git a/src/utils/includes.h b/src/utils/includes.h
new file mode 100644 (file)
index 0000000..63b5c23
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * wpa_supplicant/hostapd - Default include files
+ * Copyright (c) 2005-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.
+ *
+ * This header file is included into all C files so that commonly used header
+ * files can be selected with OS specific ifdef blocks in one place instead of
+ * having to have OS/C library specific selection in many files.
+ */
+
+#ifndef INCLUDES_H
+#define INCLUDES_H
+
+/* Include possible build time configuration before including anything else */
+#include "build_config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#ifndef _WIN32_WCE
+#ifndef CONFIG_TI_COMPILER
+#include <signal.h>
+#include <sys/types.h>
+#endif /* CONFIG_TI_COMPILER */
+#include <errno.h>
+#endif /* _WIN32_WCE */
+#include <ctype.h>
+#include <time.h>
+
+#ifndef CONFIG_TI_COMPILER
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif /* _MSC_VER */
+#endif /* CONFIG_TI_COMPILER */
+
+#ifndef CONFIG_NATIVE_WINDOWS
+#ifndef CONFIG_TI_COMPILER
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifndef __vxworks
+#ifndef __SYMBIAN32__
+#include <sys/uio.h>
+#endif /* __SYMBIAN32__ */
+#include <sys/time.h>
+#endif /* __vxworks */
+#endif /* CONFIG_TI_COMPILER */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#endif /* INCLUDES_H */
diff --git a/src/utils/ip_addr.c b/src/utils/ip_addr.c
new file mode 100644 (file)
index 0000000..158fd57
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "ip_addr.h"
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+                           size_t buflen)
+{
+       if (buflen == 0 || addr == NULL)
+               return NULL;
+
+       if (addr->af == AF_INET) {
+               os_strlcpy(buf, inet_ntoa(addr->u.v4), buflen);
+       } else {
+               buf[0] = '\0';
+       }
+#ifdef CONFIG_IPV6
+       if (addr->af == AF_INET6) {
+               if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL)
+                       buf[0] = '\0';
+       }
+#endif /* CONFIG_IPV6 */
+
+       return buf;
+}
+
+
+int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b)
+{
+       if (a == NULL && b == NULL)
+               return 0;
+       if (a == NULL || b == NULL)
+               return 1;
+
+       switch (a->af) {
+       case AF_INET:
+               if (a->u.v4.s_addr != b->u.v4.s_addr)
+                       return 1;
+               break;
+#ifdef CONFIG_IPV6
+       case AF_INET6:
+               if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0)
+                       return 1;
+               break;
+#endif /* CONFIG_IPV6 */
+       }
+
+       return 0;
+}
+
+
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr)
+{
+#ifndef CONFIG_NATIVE_WINDOWS
+       if (inet_aton(txt, &addr->u.v4)) {
+               addr->af = AF_INET;
+               return 0;
+       }
+
+#ifdef CONFIG_IPV6
+       if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) {
+               addr->af = AF_INET6;
+               return 0;
+       }
+#endif /* CONFIG_IPV6 */
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       return -1;
+}
diff --git a/src/utils/ip_addr.h b/src/utils/ip_addr.h
new file mode 100644 (file)
index 0000000..28ccaef
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * IP address processing
+ * Copyright (c) 2003-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 IP_ADDR_H
+#define IP_ADDR_H
+
+struct hostapd_ip_addr {
+       int af; /* AF_INET / AF_INET6 */
+       union {
+               struct in_addr v4;
+#ifdef CONFIG_IPV6
+               struct in6_addr v6;
+#endif /* CONFIG_IPV6 */
+               u8 max_len[16];
+       } u;
+};
+
+const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
+                           size_t buflen);
+int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b);
+int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
+
+#endif /* IP_ADDR_H */
diff --git a/src/utils/list.h b/src/utils/list.h
new file mode 100644 (file)
index 0000000..ed7c022
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Doubly-linked list
+ * Copyright (c) 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.
+ */
+
+#ifndef LIST_H
+#define LIST_H
+
+/**
+ * struct dl_list - Doubly-linked list
+ */
+struct dl_list {
+       struct dl_list *next;
+       struct dl_list *prev;
+};
+
+static inline void dl_list_init(struct dl_list *list)
+{
+       list->next = list;
+       list->prev = list;
+}
+
+static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
+{
+       item->next = list->next;
+       item->prev = list;
+       list->next->prev = item;
+       list->next = item;
+}
+
+static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
+{
+       dl_list_add(list->prev, item);
+}
+
+static inline void dl_list_del(struct dl_list *item)
+{
+       item->next->prev = item->prev;
+       item->prev->next = item->next;
+       item->next = NULL;
+       item->prev = NULL;
+}
+
+static inline int dl_list_empty(struct dl_list *list)
+{
+       return list->next == list;
+}
+
+static inline unsigned int dl_list_len(struct dl_list *list)
+{
+       struct dl_list *item;
+       int count = 0;
+       for (item = list->next; item != list; item = item->next)
+               count++;
+       return count;
+}
+
+#ifndef offsetof
+#define offsetof(type, member) ((long) &((type *) 0)->member)
+#endif
+
+#define dl_list_entry(item, type, member) \
+       ((type *) ((char *) item - offsetof(type, member)))
+
+#define dl_list_first(list, type, member) \
+       (dl_list_empty((list)) ? NULL : \
+        dl_list_entry((list)->next, type, member))
+
+#define dl_list_for_each(item, list, type, member) \
+       for (item = dl_list_entry((list)->next, type, member); \
+            &item->member != (list); \
+            item = dl_list_entry(item->member.next, type, member))
+
+#define dl_list_for_each_safe(item, n, list, type, member) \
+       for (item = dl_list_entry((list)->next, type, member), \
+                    n = dl_list_entry(item->member.next, type, member); \
+            &item->member != (list); \
+            item = n, n = dl_list_entry(n->member.next, type, member))
+
+#endif /* LIST_H */
diff --git a/src/utils/os.h b/src/utils/os.h
new file mode 100644 (file)
index 0000000..f4723d8
--- /dev/null
@@ -0,0 +1,508 @@
+/*
+ * OS specific functions
+ * Copyright (c) 2005-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.
+ */
+
+#ifndef OS_H
+#define OS_H
+
+typedef long os_time_t;
+
+/**
+ * os_sleep - Sleep (sec, usec)
+ * @sec: Number of seconds to sleep
+ * @usec: Number of microseconds to sleep
+ */
+void os_sleep(os_time_t sec, os_time_t usec);
+
+struct os_time {
+       os_time_t sec;
+       os_time_t usec;
+};
+
+/**
+ * os_get_time - Get current time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_time(struct os_time *t);
+
+
+/* Helper macros for handling struct os_time */
+
+#define os_time_before(a, b) \
+       ((a)->sec < (b)->sec || \
+        ((a)->sec == (b)->sec && (a)->usec < (b)->usec))
+
+#define os_time_sub(a, b, res) do { \
+       (res)->sec = (a)->sec - (b)->sec; \
+       (res)->usec = (a)->usec - (b)->usec; \
+       if ((res)->usec < 0) { \
+               (res)->sec--; \
+               (res)->usec += 1000000; \
+       } \
+} while (0)
+
+/**
+ * os_mktime - Convert broken-down time into seconds since 1970-01-01
+ * @year: Four digit year
+ * @month: Month (1 .. 12)
+ * @day: Day of month (1 .. 31)
+ * @hour: Hour (0 .. 23)
+ * @min: Minute (0 .. 59)
+ * @sec: Second (0 .. 60)
+ * @t: Buffer for returning calendar time representation (seconds since
+ * 1970-01-01 00:00:00)
+ * Returns: 0 on success, -1 on failure
+ *
+ * Note: The result is in seconds from Epoch, i.e., in UTC, not in local time
+ * which is used by POSIX mktime().
+ */
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+             os_time_t *t);
+
+
+/**
+ * os_daemonize - Run in the background (detach from the controlling terminal)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ * Returns: 0 on success, -1 on failure
+ */
+int os_daemonize(const char *pid_file);
+
+/**
+ * os_daemonize_terminate - Stop running in the background (remove pid file)
+ * @pid_file: File name to write the process ID to or %NULL to skip this
+ */
+void os_daemonize_terminate(const char *pid_file);
+
+/**
+ * os_get_random - Get cryptographically strong pseudo random data
+ * @buf: Buffer for pseudo random data
+ * @len: Length of the buffer
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_random(unsigned char *buf, size_t len);
+
+/**
+ * os_random - Get pseudo random value (not necessarily very strong)
+ * Returns: Pseudo random value
+ */
+unsigned long os_random(void);
+
+/**
+ * os_rel2abs_path - Get an absolute path for a file
+ * @rel_path: Relative path to a file
+ * Returns: Absolute path for the file or %NULL on failure
+ *
+ * This function tries to convert a relative path of a file to an absolute path
+ * in order for the file to be found even if current working directory has
+ * changed. The returned value is allocated and caller is responsible for
+ * freeing it. It is acceptable to just return the same path in an allocated
+ * buffer, e.g., return strdup(rel_path). This function is only used to find
+ * configuration files when os_daemonize() may have changed the current working
+ * directory and relative path would be pointing to a different location.
+ */
+char * os_rel2abs_path(const char *rel_path);
+
+/**
+ * os_program_init - Program initialization (called at start)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when a programs starts. If there are any OS specific
+ * processing that is needed, it can be placed here. It is also acceptable to
+ * just return 0 if not special processing is needed.
+ */
+int os_program_init(void);
+
+/**
+ * os_program_deinit - Program deinitialization (called just before exit)
+ *
+ * This function is called just before a program exists. If there are any OS
+ * specific processing, e.g., freeing resourced allocated in os_program_init(),
+ * it should be done here. It is also acceptable for this function to do
+ * nothing.
+ */
+void os_program_deinit(void);
+
+/**
+ * os_setenv - Set environment variable
+ * @name: Name of the variable
+ * @value: Value to set to the variable
+ * @overwrite: Whether existing variable should be overwritten
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_setenv(const char *name, const char *value, int overwrite);
+
+/**
+ * os_unsetenv - Delete environent variable
+ * @name: Name of the variable
+ * Returns: 0 on success, -1 on error
+ *
+ * This function is only used for wpa_cli action scripts. OS wrapper does not
+ * need to implement this if such functionality is not needed.
+ */
+int os_unsetenv(const char *name);
+
+/**
+ * os_readfile - Read a file to an allocated memory buffer
+ * @name: Name of the file to read
+ * @len: For returning the length of the allocated buffer
+ * Returns: Pointer to the allocated buffer or %NULL on failure
+ *
+ * This function allocates memory and reads the given file to this buffer. Both
+ * binary and text files can be read with this function. The caller is
+ * responsible for freeing the returned buffer with os_free().
+ */
+char * os_readfile(const char *name, size_t *len);
+
+/**
+ * os_zalloc - Allocate and zero memory
+ * @size: Number of bytes to allocate
+ * Returns: Pointer to allocated and zeroed memory or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_zalloc(size_t size);
+
+
+/*
+ * The following functions are wrapper for standard ANSI C or POSIX functions.
+ * By default, they are just defined to use the standard function name and no
+ * os_*.c implementation is needed for them. This avoids extra function calls
+ * by allowing the C pre-processor take care of the function name mapping.
+ *
+ * If the target system uses a C library that does not provide these functions,
+ * build_config.h can be used to define the wrappers to use a different
+ * function name. This can be done on function-by-function basis since the
+ * defines here are only used if build_config.h does not define the os_* name.
+ * If needed, os_*.c file can be used to implement the functions that are not
+ * included in the C library on the target system. Alternatively,
+ * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case
+ * these functions need to be implemented in os_*.c file for the target system.
+ */
+
+#ifdef OS_NO_C_LIB_DEFINES
+
+/**
+ * os_malloc - Allocate dynamic memory
+ * @size: Size of the buffer to allocate
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+void * os_malloc(size_t size);
+
+/**
+ * os_realloc - Re-allocate dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc()
+ * @size: Size of the new buffer
+ * Returns: Allocated buffer or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ * If re-allocation fails, %NULL is returned and the original buffer (ptr) is
+ * not freed and caller is still responsible for freeing it.
+ */
+void * os_realloc(void *ptr, size_t size);
+
+/**
+ * os_free - Free dynamic memory
+ * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL
+ */
+void os_free(void *ptr);
+
+/**
+ * os_memcpy - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst must not overlap. os_memmove() can be used with
+ * overlapping memory.
+ */
+void * os_memcpy(void *dest, const void *src, size_t n);
+
+/**
+ * os_memmove - Copy memory area
+ * @dest: Destination
+ * @src: Source
+ * @n: Number of bytes to copy
+ * Returns: dest
+ *
+ * The memory areas src and dst may overlap.
+ */
+void * os_memmove(void *dest, const void *src, size_t n);
+
+/**
+ * os_memset - Fill memory with a constant byte
+ * @s: Memory area to be filled
+ * @c: Constant byte
+ * @n: Number of bytes started from s to fill with c
+ * Returns: s
+ */
+void * os_memset(void *s, int c, size_t n);
+
+/**
+ * os_memcmp - Compare memory areas
+ * @s1: First buffer
+ * @s2: Second buffer
+ * @n: Maximum numbers of octets to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_memcmp(const void *s1, const void *s2, size_t n);
+
+/**
+ * os_strdup - Duplicate a string
+ * @s: Source string
+ * Returns: Allocated buffer with the string copied into it or %NULL on failure
+ *
+ * Caller is responsible for freeing the returned buffer with os_free().
+ */
+char * os_strdup(const char *s);
+
+/**
+ * os_strlen - Calculate the length of a string
+ * @s: '\0' terminated string
+ * Returns: Number of characters in s (not counting the '\0' terminator)
+ */
+size_t os_strlen(const char *s);
+
+/**
+ * os_strcasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcasecmp(const char *s1, const char *s2);
+
+/**
+ * os_strncasecmp - Compare two strings ignoring case
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncasecmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strchr - Locate the first occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strchr(const char *s, int c);
+
+/**
+ * os_strrchr - Locate the last occurrence of a character in string
+ * @s: String
+ * @c: Character to search for
+ * Returns: Pointer to the matched character or %NULL if not found
+ */
+char * os_strrchr(const char *s, int c);
+
+/**
+ * os_strcmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greatred than s2
+ */
+int os_strcmp(const char *s1, const char *s2);
+
+/**
+ * os_strncmp - Compare two strings
+ * @s1: First string
+ * @s2: Second string
+ * @n: Maximum numbers of characters to compare
+ * Returns: An integer less than, equal to, or greater than zero if s1 is
+ * found to be less than, to match, or be greater than s2. Only first n
+ * characters will be compared.
+ */
+int os_strncmp(const char *s1, const char *s2, size_t n);
+
+/**
+ * os_strncpy - Copy a string
+ * @dest: Destination
+ * @src: Source
+ * @n: Maximum number of characters to copy
+ * Returns: dest
+ */
+char * os_strncpy(char *dest, const char *src, size_t n);
+
+/**
+ * os_strstr - Locate a substring
+ * @haystack: String (haystack) to search from
+ * @needle: Needle to search from haystack
+ * Returns: Pointer to the beginning of the substring or %NULL if not found
+ */
+char * os_strstr(const char *haystack, const char *needle);
+
+/**
+ * os_snprintf - Print to a memory buffer
+ * @str: Memory buffer to print into
+ * @size: Maximum length of the str buffer
+ * @format: printf format
+ * Returns: Number of characters printed (not including trailing '\0').
+ *
+ * If the output buffer is truncated, number of characters which would have
+ * been written is returned. Since some C libraries return -1 in such a case,
+ * the caller must be prepared on that value, too, to indicate truncation.
+ *
+ * Note: Some C library implementations of snprintf() may not guarantee null
+ * termination in case the output is truncated. The OS wrapper function of
+ * os_snprintf() should provide this guarantee, i.e., to null terminate the
+ * output buffer if a C library version of the function is used and if that
+ * function does not guarantee null termination.
+ *
+ * If the target system does not include snprintf(), see, e.g.,
+ * http://www.ijs.si/software/snprintf/ for an example of a portable
+ * implementation of snprintf.
+ */
+int os_snprintf(char *str, size_t size, const char *format, ...);
+
+#else /* OS_NO_C_LIB_DEFINES */
+
+#ifdef WPA_TRACE
+void * os_malloc(size_t size);
+void * os_realloc(void *ptr, size_t size);
+void os_free(void *ptr);
+char * os_strdup(const char *s);
+#else /* WPA_TRACE */
+#ifndef os_malloc
+#define os_malloc(s) malloc((s))
+#endif
+#ifndef os_realloc
+#define os_realloc(p, s) realloc((p), (s))
+#endif
+#ifndef os_free
+#define os_free(p) free((p))
+#endif
+#ifndef os_strdup
+#ifdef _MSC_VER
+#define os_strdup(s) _strdup(s)
+#else
+#define os_strdup(s) strdup(s)
+#endif
+#endif
+#endif /* WPA_TRACE */
+
+#ifndef os_memcpy
+#define os_memcpy(d, s, n) memcpy((d), (s), (n))
+#endif
+#ifndef os_memmove
+#define os_memmove(d, s, n) memmove((d), (s), (n))
+#endif
+#ifndef os_memset
+#define os_memset(s, c, n) memset(s, c, n)
+#endif
+#ifndef os_memcmp
+#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n))
+#endif
+
+#ifndef os_strlen
+#define os_strlen(s) strlen(s)
+#endif
+#ifndef os_strcasecmp
+#ifdef _MSC_VER
+#define os_strcasecmp(s1, s2) _stricmp((s1), (s2))
+#else
+#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2))
+#endif
+#endif
+#ifndef os_strncasecmp
+#ifdef _MSC_VER
+#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n))
+#else
+#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n))
+#endif
+#endif
+#ifndef os_strchr
+#define os_strchr(s, c) strchr((s), (c))
+#endif
+#ifndef os_strcmp
+#define os_strcmp(s1, s2) strcmp((s1), (s2))
+#endif
+#ifndef os_strncmp
+#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
+#endif
+#ifndef os_strncpy
+#define os_strncpy(d, s, n) strncpy((d), (s), (n))
+#endif
+#ifndef os_strrchr
+#define os_strrchr(s, c) strrchr((s), (c))
+#endif
+#ifndef os_strstr
+#define os_strstr(h, n) strstr((h), (n))
+#endif
+
+#ifndef os_snprintf
+#ifdef _MSC_VER
+#define os_snprintf _snprintf
+#else
+#define os_snprintf snprintf
+#endif
+#endif
+
+#endif /* OS_NO_C_LIB_DEFINES */
+
+
+/**
+ * os_strlcpy - Copy a string with size bound and NUL-termination
+ * @dest: Destination
+ * @src: Source
+ * @siz: Size of the target buffer
+ * Returns: Total length of the target string (length of src) (not including
+ * NUL-termination)
+ *
+ * This function matches in behavior with the strlcpy(3) function in OpenBSD.
+ */
+size_t os_strlcpy(char *dest, const char *src, size_t siz);
+
+
+#ifdef OS_REJECT_C_LIB_FUNCTIONS
+#define malloc OS_DO_NOT_USE_malloc
+#define realloc OS_DO_NOT_USE_realloc
+#define free OS_DO_NOT_USE_free
+#define memcpy OS_DO_NOT_USE_memcpy
+#define memmove OS_DO_NOT_USE_memmove
+#define memset OS_DO_NOT_USE_memset
+#define memcmp OS_DO_NOT_USE_memcmp
+#undef strdup
+#define strdup OS_DO_NOT_USE_strdup
+#define strlen OS_DO_NOT_USE_strlen
+#define strcasecmp OS_DO_NOT_USE_strcasecmp
+#define strncasecmp OS_DO_NOT_USE_strncasecmp
+#undef strchr
+#define strchr OS_DO_NOT_USE_strchr
+#undef strcmp
+#define strcmp OS_DO_NOT_USE_strcmp
+#undef strncmp
+#define strncmp OS_DO_NOT_USE_strncmp
+#undef strncpy
+#define strncpy OS_DO_NOT_USE_strncpy
+#define strrchr OS_DO_NOT_USE_strrchr
+#define strstr OS_DO_NOT_USE_strstr
+#undef snprintf
+#define snprintf OS_DO_NOT_USE_snprintf
+
+#define strcpy OS_DO_NOT_USE_strcpy
+#endif /* OS_REJECT_C_LIB_FUNCTIONS */
+
+#endif /* OS_H */
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
new file mode 100644 (file)
index 0000000..5260e23
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * wpa_supplicant/hostapd / Internal implementation of OS specific functions
+ * Copyright (c) 2005-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.
+ *
+ * This file is an example of operating system specific  wrapper functions.
+ * This version implements many of the functions internally, so it can be used
+ * to fill in missing functions from the target system C libraries.
+ *
+ * Some of the functions are using standard C library calls in order to keep
+ * this file in working condition to allow the functions to be tested on a
+ * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for
+ * this file to work correctly. Note that these implementations are only
+ * examples and are not optimized for speed.
+ */
+
+#include "includes.h"
+
+#undef OS_REJECT_C_LIB_FUNCTIONS
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+       if (sec)
+               sleep(sec);
+       if (usec)
+               usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+       int res;
+       struct timeval tv;
+       res = gettimeofday(&tv, NULL);
+       t->sec = tv.tv_sec;
+       t->usec = tv.tv_usec;
+       return res;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+             os_time_t *t)
+{
+       struct tm tm;
+
+       if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+           hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+           sec > 60)
+               return -1;
+
+       os_memset(&tm, 0, sizeof(tm));
+       tm.tm_year = year - 1900;
+       tm.tm_mon = month - 1;
+       tm.tm_mday = day;
+       tm.tm_hour = hour;
+       tm.tm_min = min;
+       tm.tm_sec = sec;
+
+       *t = (os_time_t) mktime(&tm);
+       return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+       if (daemon(0, 0)) {
+               perror("daemon");
+               return -1;
+       }
+
+       if (pid_file) {
+               FILE *f = fopen(pid_file, "w");
+               if (f) {
+                       fprintf(f, "%u\n", getpid());
+                       fclose(f);
+               }
+       }
+
+       return -0;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+       if (pid_file)
+               unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+       FILE *f;
+       size_t rc;
+
+       f = fopen("/dev/urandom", "rb");
+       if (f == NULL) {
+               printf("Could not open /dev/urandom.\n");
+               return -1;
+       }
+
+       rc = fread(buf, 1, len, f);
+       fclose(f);
+
+       return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+       return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+       char *buf = NULL, *cwd, *ret;
+       size_t len = 128, cwd_len, rel_len, ret_len;
+
+       if (rel_path[0] == '/')
+               return os_strdup(rel_path);
+
+       for (;;) {
+               buf = os_malloc(len);
+               if (buf == NULL)
+                       return NULL;
+               cwd = getcwd(buf, len);
+               if (cwd == NULL) {
+                       os_free(buf);
+                       if (errno != ERANGE) {
+                               return NULL;
+                       }
+                       len *= 2;
+               } else {
+                       break;
+               }
+       }
+
+       cwd_len = strlen(cwd);
+       rel_len = strlen(rel_path);
+       ret_len = cwd_len + 1 + rel_len + 1;
+       ret = os_malloc(ret_len);
+       if (ret) {
+               os_memcpy(ret, cwd, cwd_len);
+               ret[cwd_len] = '/';
+               os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+               ret[ret_len - 1] = '\0';
+       }
+       os_free(buf);
+       return ret;
+}
+
+
+int os_program_init(void)
+{
+       return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+       return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__)
+       unsetenv(name);
+       return 0;
+#else
+       return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+       FILE *f;
+       char *buf;
+
+       f = fopen(name, "rb");
+       if (f == NULL)
+               return NULL;
+
+       fseek(f, 0, SEEK_END);
+       *len = ftell(f);
+       fseek(f, 0, SEEK_SET);
+
+       buf = os_malloc(*len);
+       if (buf == NULL) {
+               fclose(f);
+               return NULL;
+       }
+
+       if (fread(buf, 1, *len, f) != *len) {
+               fclose(f);
+               os_free(buf);
+               return NULL;
+       }
+
+       fclose(f);
+
+       return buf;
+}
+
+
+void * os_zalloc(size_t size)
+{
+       void *n = os_malloc(size);
+       if (n)
+               os_memset(n, 0, size);
+       return n;
+}
+
+
+void * os_malloc(size_t size)
+{
+       return malloc(size);
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+       return realloc(ptr, size);
+}
+
+
+void os_free(void *ptr)
+{
+       free(ptr);
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+       char *d = dest;
+       const char *s = src;
+       while (n--)
+               *d++ = *s++;
+       return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+       if (dest < src)
+               os_memcpy(dest, src, n);
+       else {
+               /* overlapping areas */
+               char *d = (char *) dest + n;
+               const char *s = (const char *) src + n;
+               while (n--)
+                       *--d = *--s;
+       }
+       return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+       char *p = s;
+       while (n--)
+               *p++ = c;
+       return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+       const unsigned char *p1 = s1, *p2 = s2;
+
+       if (n == 0)
+               return 0;
+
+       while (*p1 == *p2) {
+               p1++;
+               p2++;
+               n--;
+               if (n == 0)
+                       return 0;
+       }
+
+       return *p1 - *p2;
+}
+
+
+char * os_strdup(const char *s)
+{
+       char *res;
+       size_t len;
+       if (s == NULL)
+               return NULL;
+       len = os_strlen(s);
+       res = os_malloc(len + 1);
+       if (res)
+               os_memcpy(res, s, len + 1);
+       return res;
+}
+
+
+size_t os_strlen(const char *s)
+{
+       const char *p = s;
+       while (*p)
+               p++;
+       return p - s;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+       /*
+        * Ignoring case is not required for main functionality, so just use
+        * the case sensitive version of the function.
+        */
+       return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+       /*
+        * Ignoring case is not required for main functionality, so just use
+        * the case sensitive version of the function.
+        */
+       return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+       while (*s) {
+               if (*s == c)
+                       return (char *) s;
+               s++;
+       }
+       return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+       const char *p = s;
+       while (*p)
+               p++;
+       p--;
+       while (p >= s) {
+               if (*p == c)
+                       return (char *) p;
+               p--;
+       }
+       return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+       while (*s1 == *s2) {
+               if (*s1 == '\0')
+                       break;
+               s1++;
+               s2++;
+       }
+
+       return *s1 - *s2;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+       if (n == 0)
+               return 0;
+
+       while (*s1 == *s2) {
+               if (*s1 == '\0')
+                       break;
+               s1++;
+               s2++;
+               n--;
+               if (n == 0)
+                       return 0;
+       }
+
+       return *s1 - *s2;
+}
+
+
+char * os_strncpy(char *dest, const char *src, size_t n)
+{
+       char *d = dest;
+
+       while (n--) {
+               *d = *src;
+               if (*src == '\0')
+                       break;
+               d++;
+               src++;
+       }
+
+       return dest;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+       const char *s = src;
+       size_t left = siz;
+
+       if (left) {
+               /* Copy string up to the maximum size of the dest buffer */
+               while (--left != 0) {
+                       if ((*dest++ = *s++) == '\0')
+                               break;
+               }
+       }
+
+       if (left == 0) {
+               /* Not enough room for the string; force NUL-termination */
+               if (siz != 0)
+                       *dest = '\0';
+               while (*s++)
+                       ; /* determine total src string length */
+       }
+
+       return s - src - 1;
+}
+
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+       size_t len = os_strlen(needle);
+       while (*haystack) {
+               if (os_strncmp(haystack, needle, len) == 0)
+                       return (char *) haystack;
+               haystack++;
+       }
+
+       return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+       va_list ap;
+       int ret;
+
+       /* See http://www.ijs.si/software/snprintf/ for portable
+        * implementation of snprintf.
+        */
+
+       va_start(ap, format);
+       ret = vsnprintf(str, size, format, ap);
+       va_end(ap);
+       if (size > 0)
+               str[size - 1] = '\0';
+       return ret;
+}
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
new file mode 100644 (file)
index 0000000..bab8f17
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * wpa_supplicant/hostapd / Empty OS specific functions
+ * Copyright (c) 2005-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.
+ *
+ * This file can be used as a starting point when adding a new OS target. The
+ * functions here do not really work as-is since they are just empty or only
+ * return an error value. os_internal.c can be used as another starting point
+ * or reference since it has example implementation of many of these functions.
+ */
+
+#include "includes.h"
+
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+}
+
+
+int os_get_time(struct os_time *t)
+{
+       return -1;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+             os_time_t *t)
+{
+       return -1;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+       return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+       return -1;
+}
+
+
+unsigned long os_random(void)
+{
+       return 0;
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+       return NULL; /* strdup(rel_path) can be used here */
+}
+
+
+int os_program_init(void)
+{
+       return 0;
+}
+
+
+void os_program_deinit(void)
+{
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+       return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+       return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+       return NULL;
+}
+
+
+void * os_zalloc(size_t size)
+{
+       return NULL;
+}
+
+
+#ifdef OS_NO_C_LIB_DEFINES
+void * os_malloc(size_t size)
+{
+       return NULL;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+       return NULL;
+}
+
+
+void os_free(void *ptr)
+{
+}
+
+
+void * os_memcpy(void *dest, const void *src, size_t n)
+{
+       return dest;
+}
+
+
+void * os_memmove(void *dest, const void *src, size_t n)
+{
+       return dest;
+}
+
+
+void * os_memset(void *s, int c, size_t n)
+{
+       return s;
+}
+
+
+int os_memcmp(const void *s1, const void *s2, size_t n)
+{
+       return 0;
+}
+
+
+char * os_strdup(const char *s)
+{
+       return NULL;
+}
+
+
+size_t os_strlen(const char *s)
+{
+       return 0;
+}
+
+
+int os_strcasecmp(const char *s1, const char *s2)
+{
+       /*
+        * Ignoring case is not required for main functionality, so just use
+        * the case sensitive version of the function.
+        */
+       return os_strcmp(s1, s2);
+}
+
+
+int os_strncasecmp(const char *s1, const char *s2, size_t n)
+{
+       /*
+        * Ignoring case is not required for main functionality, so just use
+        * the case sensitive version of the function.
+        */
+       return os_strncmp(s1, s2, n);
+}
+
+
+char * os_strchr(const char *s, int c)
+{
+       return NULL;
+}
+
+
+char * os_strrchr(const char *s, int c)
+{
+       return NULL;
+}
+
+
+int os_strcmp(const char *s1, const char *s2)
+{
+       return 0;
+}
+
+
+int os_strncmp(const char *s1, const char *s2, size_t n)
+{
+       return 0;
+}
+
+
+char * os_strncpy(char *dest, const char *src, size_t n)
+{
+       return dest;
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t size)
+{
+       return 0;
+}
+
+
+char * os_strstr(const char *haystack, const char *needle)
+{
+       return NULL;
+}
+
+
+int os_snprintf(char *str, size_t size, const char *format, ...)
+{
+       return 0;
+}
+#endif /* OS_NO_C_LIB_DEFINES */
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
new file mode 100644 (file)
index 0000000..6f58fa4
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ * OS specific functions for UNIX/POSIX systems
+ * Copyright (c) 2005-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 "os.h"
+
+#ifdef WPA_TRACE
+
+#include "common.h"
+#include "list.h"
+#include "wpa_debug.h"
+#include "trace.h"
+
+static struct dl_list alloc_list;
+
+#define ALLOC_MAGIC 0xa84ef1b2
+#define FREED_MAGIC 0x67fd487a
+
+struct os_alloc_trace {
+       unsigned int magic;
+       struct dl_list list;
+       size_t len;
+       WPA_TRACE_INFO
+};
+
+#endif /* WPA_TRACE */
+
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+       if (sec)
+               sleep(sec);
+       if (usec)
+               usleep(usec);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+       int res;
+       struct timeval tv;
+       res = gettimeofday(&tv, NULL);
+       t->sec = tv.tv_sec;
+       t->usec = tv.tv_usec;
+       return res;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+             os_time_t *t)
+{
+       struct tm tm, *tm1;
+       time_t t_local, t1, t2;
+       os_time_t tz_offset;
+
+       if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+           hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+           sec > 60)
+               return -1;
+
+       memset(&tm, 0, sizeof(tm));
+       tm.tm_year = year - 1900;
+       tm.tm_mon = month - 1;
+       tm.tm_mday = day;
+       tm.tm_hour = hour;
+       tm.tm_min = min;
+       tm.tm_sec = sec;
+
+       t_local = mktime(&tm);
+
+       /* figure out offset to UTC */
+       tm1 = localtime(&t_local);
+       if (tm1) {
+               t1 = mktime(tm1);
+               tm1 = gmtime(&t_local);
+               if (tm1) {
+                       t2 = mktime(tm1);
+                       tz_offset = t2 - t1;
+               } else
+                       tz_offset = 0;
+       } else
+               tz_offset = 0;
+
+       *t = (os_time_t) t_local - tz_offset;
+       return 0;
+}
+
+
+#ifdef __APPLE__
+#include <fcntl.h>
+static int os_daemon(int nochdir, int noclose)
+{
+       int devnull;
+
+       if (chdir("/") < 0)
+               return -1;
+
+       devnull = open("/dev/null", O_RDWR);
+       if (devnull < 0)
+               return -1;
+
+       if (dup2(devnull, STDIN_FILENO) < 0) {
+               close(devnull);
+               return -1;
+       }
+
+       if (dup2(devnull, STDOUT_FILENO) < 0) {
+               close(devnull);
+               return -1;
+       }
+
+       if (dup2(devnull, STDERR_FILENO) < 0) {
+               close(devnull);
+               return -1;
+       }
+
+       return 0;
+}
+#else /* __APPLE__ */
+#define os_daemon daemon
+#endif /* __APPLE__ */
+
+
+int os_daemonize(const char *pid_file)
+{
+#ifdef __uClinux__
+       return -1;
+#else /* __uClinux__ */
+       if (os_daemon(0, 0)) {
+               perror("daemon");
+               return -1;
+       }
+
+       if (pid_file) {
+               FILE *f = fopen(pid_file, "w");
+               if (f) {
+                       fprintf(f, "%u\n", getpid());
+                       fclose(f);
+               }
+       }
+
+       return -0;
+#endif /* __uClinux__ */
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+       if (pid_file)
+               unlink(pid_file);
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+       FILE *f;
+       size_t rc;
+
+       f = fopen("/dev/urandom", "rb");
+       if (f == NULL) {
+               printf("Could not open /dev/urandom.\n");
+               return -1;
+       }
+
+       rc = fread(buf, 1, len, f);
+       fclose(f);
+
+       return rc != len ? -1 : 0;
+}
+
+
+unsigned long os_random(void)
+{
+       return random();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+       char *buf = NULL, *cwd, *ret;
+       size_t len = 128, cwd_len, rel_len, ret_len;
+       int last_errno;
+
+       if (rel_path[0] == '/')
+               return os_strdup(rel_path);
+
+       for (;;) {
+               buf = os_malloc(len);
+               if (buf == NULL)
+                       return NULL;
+               cwd = getcwd(buf, len);
+               if (cwd == NULL) {
+                       last_errno = errno;
+                       os_free(buf);
+                       if (last_errno != ERANGE)
+                               return NULL;
+                       len *= 2;
+                       if (len > 2000)
+                               return NULL;
+               } else {
+                       buf[len - 1] = '\0';
+                       break;
+               }
+       }
+
+       cwd_len = os_strlen(cwd);
+       rel_len = os_strlen(rel_path);
+       ret_len = cwd_len + 1 + rel_len + 1;
+       ret = os_malloc(ret_len);
+       if (ret) {
+               os_memcpy(ret, cwd, cwd_len);
+               ret[cwd_len] = '/';
+               os_memcpy(ret + cwd_len + 1, rel_path, rel_len);
+               ret[ret_len - 1] = '\0';
+       }
+       os_free(buf);
+       return ret;
+}
+
+
+int os_program_init(void)
+{
+#ifdef WPA_TRACE
+       dl_list_init(&alloc_list);
+#endif /* WPA_TRACE */
+       return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef WPA_TRACE
+       struct os_alloc_trace *a;
+       unsigned long total = 0;
+       dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) {
+               total += a->len;
+               if (a->magic != ALLOC_MAGIC) {
+                       wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x "
+                                  "len %lu",
+                                  a, a->magic, (unsigned long) a->len);
+                       continue;
+               }
+               wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu",
+                          a, (unsigned long) a->len);
+               wpa_trace_dump("memleak", a);
+       }
+       if (total)
+               wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes",
+                          (unsigned long) total);
+#endif /* WPA_TRACE */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+       return setenv(name, value, overwrite);
+}
+
+
+int os_unsetenv(const char *name)
+{
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) || \
+    defined(__OpenBSD__)
+       unsetenv(name);
+       return 0;
+#else
+       return unsetenv(name);
+#endif
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+       FILE *f;
+       char *buf;
+
+       f = fopen(name, "rb");
+       if (f == NULL)
+               return NULL;
+
+       fseek(f, 0, SEEK_END);
+       *len = ftell(f);
+       fseek(f, 0, SEEK_SET);
+
+       buf = os_malloc(*len);
+       if (buf == NULL) {
+               fclose(f);
+               return NULL;
+       }
+
+       if (fread(buf, 1, *len, f) != *len) {
+               fclose(f);
+               os_free(buf);
+               return NULL;
+       }
+
+       fclose(f);
+
+       return buf;
+}
+
+
+#ifndef WPA_TRACE
+void * os_zalloc(size_t size)
+{
+       return calloc(1, size);
+}
+#endif /* WPA_TRACE */
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+       const char *s = src;
+       size_t left = siz;
+
+       if (left) {
+               /* Copy string up to the maximum size of the dest buffer */
+               while (--left != 0) {
+                       if ((*dest++ = *s++) == '\0')
+                               break;
+               }
+       }
+
+       if (left == 0) {
+               /* Not enough room for the string; force NUL-termination */
+               if (siz != 0)
+                       *dest = '\0';
+               while (*s++)
+                       ; /* determine total src string length */
+       }
+
+       return s - src - 1;
+}
+
+
+#ifdef WPA_TRACE
+
+void * os_malloc(size_t size)
+{
+       struct os_alloc_trace *a;
+       a = malloc(sizeof(*a) + size);
+       if (a == NULL)
+               return NULL;
+       a->magic = ALLOC_MAGIC;
+       dl_list_add(&alloc_list, &a->list);
+       a->len = size;
+       wpa_trace_record(a);
+       return a + 1;
+}
+
+
+void * os_realloc(void *ptr, size_t size)
+{
+       struct os_alloc_trace *a;
+       size_t copy_len;
+       void *n;
+
+       if (ptr == NULL)
+               return os_malloc(size);
+
+       a = (struct os_alloc_trace *) ptr - 1;
+       if (a->magic != ALLOC_MAGIC) {
+               wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s",
+                          a, a->magic,
+                          a->magic == FREED_MAGIC ? " (already freed)" : "");
+               wpa_trace_show("Invalid os_realloc() call");
+               abort();
+       }
+       n = os_malloc(size);
+       if (n == NULL)
+               return NULL;
+       copy_len = a->len;
+       if (copy_len > size)
+               copy_len = size;
+       os_memcpy(n, a + 1, copy_len);
+       os_free(ptr);
+       return n;
+}
+
+
+void os_free(void *ptr)
+{
+       struct os_alloc_trace *a;
+
+       if (ptr == NULL)
+               return;
+       a = (struct os_alloc_trace *) ptr - 1;
+       if (a->magic != ALLOC_MAGIC) {
+               wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s",
+                          a, a->magic,
+                          a->magic == FREED_MAGIC ? " (already freed)" : "");
+               wpa_trace_show("Invalid os_free() call");
+               abort();
+       }
+       dl_list_del(&a->list);
+       a->magic = FREED_MAGIC;
+
+       wpa_trace_check_ref(ptr);
+       free(a);
+}
+
+
+void * os_zalloc(size_t size)
+{
+       void *ptr = os_malloc(size);
+       if (ptr)
+               os_memset(ptr, 0, size);
+       return ptr;
+}
+
+
+char * os_strdup(const char *s)
+{
+       size_t len;
+       char *d;
+       len = os_strlen(s);
+       d = os_malloc(len + 1);
+       if (d == NULL)
+               return NULL;
+       os_memcpy(d, s, len);
+       d[len] = '\0';
+       return d;
+}
+
+#endif /* WPA_TRACE */
diff --git a/src/utils/os_win32.c b/src/utils/os_win32.c
new file mode 100644 (file)
index 0000000..0740964
--- /dev/null
@@ -0,0 +1,222 @@
+/*
+ * wpa_supplicant/hostapd / OS specific functions for Win32 systems
+ * Copyright (c) 2005-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.
+ */
+
+#include "includes.h"
+#include <winsock2.h>
+#include <wincrypt.h>
+
+#include "os.h"
+
+void os_sleep(os_time_t sec, os_time_t usec)
+{
+       if (sec)
+               Sleep(sec * 1000);
+       if (usec)
+               Sleep(usec / 1000);
+}
+
+
+int os_get_time(struct os_time *t)
+{
+#define EPOCHFILETIME (116444736000000000ULL)
+       FILETIME ft;
+       LARGE_INTEGER li;
+       ULONGLONG tt;
+
+#ifdef _WIN32_WCE
+       SYSTEMTIME st;
+
+       GetSystemTime(&st);
+       SystemTimeToFileTime(&st, &ft);
+#else /* _WIN32_WCE */
+       GetSystemTimeAsFileTime(&ft);
+#endif /* _WIN32_WCE */
+       li.LowPart = ft.dwLowDateTime;
+       li.HighPart = ft.dwHighDateTime;
+       tt = (li.QuadPart - EPOCHFILETIME) / 10;
+       t->sec = (os_time_t) (tt / 1000000);
+       t->usec = (os_time_t) (tt % 1000000);
+
+       return 0;
+}
+
+
+int os_mktime(int year, int month, int day, int hour, int min, int sec,
+             os_time_t *t)
+{
+       struct tm tm, *tm1;
+       time_t t_local, t1, t2;
+       os_time_t tz_offset;
+
+       if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 ||
+           hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 ||
+           sec > 60)
+               return -1;
+
+       memset(&tm, 0, sizeof(tm));
+       tm.tm_year = year - 1900;
+       tm.tm_mon = month - 1;
+       tm.tm_mday = day;
+       tm.tm_hour = hour;
+       tm.tm_min = min;
+       tm.tm_sec = sec;
+
+       t_local = mktime(&tm);
+
+       /* figure out offset to UTC */
+       tm1 = localtime(&t_local);
+       if (tm1) {
+               t1 = mktime(tm1);
+               tm1 = gmtime(&t_local);
+               if (tm1) {
+                       t2 = mktime(tm1);
+                       tz_offset = t2 - t1;
+               } else
+                       tz_offset = 0;
+       } else
+               tz_offset = 0;
+
+       *t = (os_time_t) t_local - tz_offset;
+       return 0;
+}
+
+
+int os_daemonize(const char *pid_file)
+{
+       /* TODO */
+       return -1;
+}
+
+
+void os_daemonize_terminate(const char *pid_file)
+{
+}
+
+
+int os_get_random(unsigned char *buf, size_t len)
+{
+       HCRYPTPROV prov;
+       BOOL ret;
+
+       if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL,
+                                CRYPT_VERIFYCONTEXT))
+               return -1;
+
+       ret = CryptGenRandom(prov, len, buf);
+       CryptReleaseContext(prov, 0);
+
+       return ret ? 0 : -1;
+}
+
+
+unsigned long os_random(void)
+{
+       return rand();
+}
+
+
+char * os_rel2abs_path(const char *rel_path)
+{
+       return _strdup(rel_path);
+}
+
+
+int os_program_init(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+       WSADATA wsaData;
+       if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+               printf("Could not find a usable WinSock.dll\n");
+               return -1;
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+       return 0;
+}
+
+
+void os_program_deinit(void)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+       WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
+int os_setenv(const char *name, const char *value, int overwrite)
+{
+       return -1;
+}
+
+
+int os_unsetenv(const char *name)
+{
+       return -1;
+}
+
+
+char * os_readfile(const char *name, size_t *len)
+{
+       FILE *f;
+       char *buf;
+
+       f = fopen(name, "rb");
+       if (f == NULL)
+               return NULL;
+
+       fseek(f, 0, SEEK_END);
+       *len = ftell(f);
+       fseek(f, 0, SEEK_SET);
+
+       buf = malloc(*len);
+       if (buf == NULL) {
+               fclose(f);
+               return NULL;
+       }
+
+       fread(buf, 1, *len, f);
+       fclose(f);
+
+       return buf;
+}
+
+
+void * os_zalloc(size_t size)
+{
+       return calloc(1, size);
+}
+
+
+size_t os_strlcpy(char *dest, const char *src, size_t siz)
+{
+       const char *s = src;
+       size_t left = siz;
+
+       if (left) {
+               /* Copy string up to the maximum size of the dest buffer */
+               while (--left != 0) {
+                       if ((*dest++ = *s++) == '\0')
+                               break;
+               }
+       }
+
+       if (left == 0) {
+               /* Not enough room for the string; force NUL-termination */
+               if (siz != 0)
+                       *dest = '\0';
+               while (*s++)
+                       ; /* determine total src string length */
+       }
+
+       return s - src - 1;
+}
diff --git a/src/utils/pcsc_funcs.c b/src/utils/pcsc_funcs.c
new file mode 100644 (file)
index 0000000..bf9f04a
--- /dev/null
@@ -0,0 +1,1238 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * 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.
+ *
+ * This file implements wrapper functions for accessing GSM SIM and 3GPP USIM
+ * cards through PC/SC smartcard library. These functions are used to implement
+ * authentication routines for EAP-SIM and EAP-AKA.
+ */
+
+#include "includes.h"
+#include <winscard.h>
+
+#include "common.h"
+#include "pcsc_funcs.h"
+
+
+/* See ETSI GSM 11.11 and ETSI TS 102 221 for details.
+ * SIM commands:
+ * Command APDU: CLA INS P1 P2 P3 Data
+ *   CLA (class of instruction): A0 for GSM, 00 for USIM
+ *   INS (instruction)
+ *   P1 P2 P3 (parameters, P3 = length of Data)
+ * Response APDU: Data SW1 SW2
+ *   SW1 SW2 (Status words)
+ * Commands (INS P1 P2 P3):
+ *   SELECT: A4 00 00 02 <file_id, 2 bytes>
+ *   GET RESPONSE: C0 00 00 <len>
+ *   RUN GSM ALG: 88 00 00 00 <RAND len = 10>
+ *   RUN UMTS ALG: 88 00 81 <len=0x22> data: 0x10 | RAND | 0x10 | AUTN
+ *     P1 = ID of alg in card
+ *     P2 = ID of secret key
+ *   READ BINARY: B0 <offset high> <offset low> <len>
+ *   READ RECORD: B2 <record number> <mode> <len>
+ *     P2 (mode) = '02' (next record), '03' (previous record),
+ *                 '04' (absolute mode)
+ *   VERIFY CHV: 20 00 <CHV number> 08
+ *   CHANGE CHV: 24 00 <CHV number> 10
+ *   DISABLE CHV: 26 00 01 08
+ *   ENABLE CHV: 28 00 01 08
+ *   UNBLOCK CHV: 2C 00 <00=CHV1, 02=CHV2> 10
+ *   SLEEP: FA 00 00 00
+ */
+
+/* GSM SIM commands */
+#define SIM_CMD_SELECT                 0xa0, 0xa4, 0x00, 0x00, 0x02
+#define SIM_CMD_RUN_GSM_ALG            0xa0, 0x88, 0x00, 0x00, 0x10
+#define SIM_CMD_GET_RESPONSE           0xa0, 0xc0, 0x00, 0x00
+#define SIM_CMD_READ_BIN               0xa0, 0xb0, 0x00, 0x00
+#define SIM_CMD_READ_RECORD            0xa0, 0xb2, 0x00, 0x00
+#define SIM_CMD_VERIFY_CHV1            0xa0, 0x20, 0x00, 0x01, 0x08
+
+/* USIM commands */
+#define USIM_CLA                       0x00
+#define USIM_CMD_RUN_UMTS_ALG          0x00, 0x88, 0x00, 0x81, 0x22
+#define USIM_CMD_GET_RESPONSE          0x00, 0xc0, 0x00, 0x00
+
+#define SIM_RECORD_MODE_ABSOLUTE 0x04
+
+#define USIM_FSP_TEMPL_TAG             0x62
+
+#define USIM_TLV_FILE_DESC             0x82
+#define USIM_TLV_FILE_ID               0x83
+#define USIM_TLV_DF_NAME               0x84
+#define USIM_TLV_PROPR_INFO            0xA5
+#define USIM_TLV_LIFE_CYCLE_STATUS     0x8A
+#define USIM_TLV_FILE_SIZE             0x80
+#define USIM_TLV_TOTAL_FILE_SIZE       0x81
+#define USIM_TLV_PIN_STATUS_TEMPLATE   0xC6
+#define USIM_TLV_SHORT_FILE_ID         0x88
+
+#define USIM_PS_DO_TAG                 0x90
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+
+
+typedef enum { SCARD_GSM_SIM, SCARD_USIM } sim_types;
+
+struct scard_data {
+       SCARDCONTEXT ctx;
+       SCARDHANDLE card;
+       DWORD protocol;
+       sim_types sim_type;
+       int pin1_required;
+};
+
+#ifdef __MINGW32_VERSION
+/* MinGW does not yet support WinScard, so load the needed functions
+ * dynamically from winscard.dll for now. */
+
+static HINSTANCE dll = NULL; /* winscard.dll */
+
+static const SCARD_IO_REQUEST *dll_g_rgSCardT0Pci, *dll_g_rgSCardT1Pci;
+#undef SCARD_PCI_T0
+#define SCARD_PCI_T0 (dll_g_rgSCardT0Pci)
+#undef SCARD_PCI_T1
+#define SCARD_PCI_T1 (dll_g_rgSCardT1Pci)
+
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEstablishContext)(IN DWORD dwScope,
+                            IN LPCVOID pvReserved1,
+                            IN LPCVOID pvReserved2,
+                            OUT LPSCARDCONTEXT phContext);
+#define SCardEstablishContext dll_SCardEstablishContext
+
+static long (*dll_SCardReleaseContext)(long hContext);
+#define SCardReleaseContext dll_SCardReleaseContext
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardListReadersA)(IN SCARDCONTEXT hContext,
+                        IN LPCSTR mszGroups,
+                        OUT LPSTR mszReaders,
+                        IN OUT LPDWORD pcchReaders);
+#undef SCardListReaders
+#define SCardListReaders dll_SCardListReadersA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardConnectA)(IN SCARDCONTEXT hContext,
+                    IN LPCSTR szReader,
+                    IN DWORD dwShareMode,
+                    IN DWORD dwPreferredProtocols,
+                    OUT LPSCARDHANDLE phCard,
+                    OUT LPDWORD pdwActiveProtocol);
+#undef SCardConnect
+#define SCardConnect dll_SCardConnectA
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardDisconnect)(IN SCARDHANDLE hCard,
+                      IN DWORD dwDisposition);
+#define SCardDisconnect dll_SCardDisconnect
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardTransmit)(IN SCARDHANDLE hCard,
+                    IN LPCSCARD_IO_REQUEST pioSendPci,
+                    IN LPCBYTE pbSendBuffer,
+                    IN DWORD cbSendLength,
+                    IN OUT LPSCARD_IO_REQUEST pioRecvPci,
+                    OUT LPBYTE pbRecvBuffer,
+                    IN OUT LPDWORD pcbRecvLength);
+#define SCardTransmit dll_SCardTransmit
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardBeginTransaction)(IN SCARDHANDLE hCard);
+#define SCardBeginTransaction dll_SCardBeginTransaction
+
+static WINSCARDAPI LONG WINAPI
+(*dll_SCardEndTransaction)(IN SCARDHANDLE hCard, IN DWORD dwDisposition);
+#define SCardEndTransaction dll_SCardEndTransaction
+
+
+static int mingw_load_symbols(void)
+{
+       char *sym;
+
+       if (dll)
+               return 0;
+
+       dll = LoadLibrary("winscard");
+       if (dll == NULL) {
+               wpa_printf(MSG_DEBUG, "WinSCard: Could not load winscard.dll "
+                          "library");
+               return -1;
+       }
+
+#define LOADSYM(s) \
+       sym = #s; \
+       dll_ ## s = (void *) GetProcAddress(dll, sym); \
+       if (dll_ ## s == NULL) \
+               goto fail;
+
+       LOADSYM(SCardEstablishContext);
+       LOADSYM(SCardReleaseContext);
+       LOADSYM(SCardListReadersA);
+       LOADSYM(SCardConnectA);
+       LOADSYM(SCardDisconnect);
+       LOADSYM(SCardTransmit);
+       LOADSYM(SCardBeginTransaction);
+       LOADSYM(SCardEndTransaction);
+       LOADSYM(g_rgSCardT0Pci);
+       LOADSYM(g_rgSCardT1Pci);
+
+#undef LOADSYM
+
+       return 0;
+
+fail:
+       wpa_printf(MSG_DEBUG, "WinSCard: Could not get address for %s from "
+                  "winscard.dll", sym);
+       FreeLibrary(dll);
+       dll = NULL;
+       return -1;
+}
+
+
+static void mingw_unload_symbols(void)
+{
+       if (dll == NULL)
+               return;
+
+       FreeLibrary(dll);
+       dll = NULL;
+}
+
+#else /* __MINGW32_VERSION */
+
+#define mingw_load_symbols() 0
+#define mingw_unload_symbols() do { } while (0)
+
+#endif /* __MINGW32_VERSION */
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+                             unsigned char *buf, size_t *buf_len,
+                             sim_types sim_type, unsigned char *aid,
+                             size_t aidlen);
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+                            unsigned char *buf, size_t *buf_len);
+static int scard_verify_pin(struct scard_data *scard, const char *pin);
+static int scard_get_record_len(struct scard_data *scard,
+                               unsigned char recnum, unsigned char mode);
+static int scard_read_record(struct scard_data *scard,
+                            unsigned char *data, size_t len,
+                            unsigned char recnum, unsigned char mode);
+
+
+static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
+                                int *ps_do, int *file_len)
+{
+               unsigned char *pos, *end;
+
+               if (ps_do)
+                       *ps_do = -1;
+               if (file_len)
+                       *file_len = -1;
+
+               pos = buf;
+               end = pos + buf_len;
+               if (*pos != USIM_FSP_TEMPL_TAG) {
+                       wpa_printf(MSG_DEBUG, "SCARD: file header did not "
+                                  "start with FSP template tag");
+                       return -1;
+               }
+               pos++;
+               if (pos >= end)
+                       return -1;
+               if ((pos + pos[0]) < end)
+                       end = pos + 1 + pos[0];
+               pos++;
+               wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
+                           pos, end - pos);
+
+               while (pos + 1 < end) {
+                       wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV "
+                                  "0x%02x len=%d", pos[0], pos[1]);
+                       if (pos + 2 + pos[1] > end)
+                               break;
+
+                       if (pos[0] == USIM_TLV_FILE_SIZE &&
+                           (pos[1] == 1 || pos[1] == 2) && file_len) {
+                               if (pos[1] == 1)
+                                       *file_len = (int) pos[2];
+                               else
+                                       *file_len = ((int) pos[2] << 8) |
+                                               (int) pos[3];
+                               wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
+                                          *file_len);
+                       }
+
+                       if (pos[0] == USIM_TLV_PIN_STATUS_TEMPLATE &&
+                           pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
+                           pos[3] >= 1 && ps_do) {
+                               wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
+                                          pos[4]);
+                               *ps_do = (int) pos[4];
+                       }
+
+                       pos += 2 + pos[1];
+
+                       if (pos == end)
+                               return 0;
+               }
+               return -1;
+}
+
+
+static int scard_pin_needed(struct scard_data *scard,
+                           unsigned char *hdr, size_t hlen)
+{
+       if (scard->sim_type == SCARD_GSM_SIM) {
+               if (hlen > SCARD_CHV1_OFFSET &&
+                   !(hdr[SCARD_CHV1_OFFSET] & SCARD_CHV1_FLAG))
+                       return 1;
+               return 0;
+       }
+
+       if (scard->sim_type == SCARD_USIM) {
+               int ps_do;
+               if (scard_parse_fsp_templ(hdr, hlen, &ps_do, NULL))
+                       return -1;
+               /* TODO: there could be more than one PS_DO entry because of
+                * multiple PINs in key reference.. */
+               if (ps_do > 0 && (ps_do & 0x80))
+                       return 1;
+               return 0;
+       }
+
+       return -1;
+}
+
+
+static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
+                        size_t maxlen)
+{
+       int rlen, rec;
+       struct efdir {
+               unsigned char appl_template_tag; /* 0x61 */
+               unsigned char appl_template_len;
+               unsigned char appl_id_tag; /* 0x4f */
+               unsigned char aid_len;
+               unsigned char rid[5];
+               unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
+       } *efdir;
+       unsigned char buf[100];
+       size_t blen;
+
+       efdir = (struct efdir *) buf;
+       blen = sizeof(buf);
+       if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
+               wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR select", buf, blen);
+
+       for (rec = 1; rec < 10; rec++) {
+               rlen = scard_get_record_len(scard, rec,
+                                           SIM_RECORD_MODE_ABSOLUTE);
+               if (rlen < 0) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Failed to get EF_DIR "
+                                  "record length");
+                       return -1;
+               }
+               blen = sizeof(buf);
+               if (rlen > (int) blen) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Too long EF_DIR record");
+                       return -1;
+               }
+               if (scard_read_record(scard, buf, rlen, rec,
+                                     SIM_RECORD_MODE_ABSOLUTE) < 0) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Failed to read "
+                                  "EF_DIR record %d", rec);
+                       return -1;
+               }
+               wpa_hexdump(MSG_DEBUG, "SCARD: EF_DIR record", buf, rlen);
+
+               if (efdir->appl_template_tag != 0x61) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+                                  "template tag 0x%x",
+                                  efdir->appl_template_tag);
+                       continue;
+               }
+
+               if (efdir->appl_template_len > rlen - 2) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Too long application "
+                                  "template (len=%d rlen=%d)",
+                                  efdir->appl_template_len, rlen);
+                       continue;
+               }
+
+               if (efdir->appl_id_tag != 0x4f) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Unexpected application "
+                                  "identifier tag 0x%x", efdir->appl_id_tag);
+                       continue;
+               }
+
+               if (efdir->aid_len < 1 || efdir->aid_len > 16) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d",
+                                  efdir->aid_len);
+                       continue;
+               }
+
+               wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
+                           efdir->rid, efdir->aid_len);
+
+               if (efdir->appl_code[0] == 0x10 &&
+                   efdir->appl_code[1] == 0x02) {
+                       wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app found from "
+                                  "EF_DIR record %d", rec);
+                       break;
+               }
+       }
+
+       if (rec >= 10) {
+               wpa_printf(MSG_DEBUG, "SCARD: 3G USIM app not found "
+                          "from EF_DIR records");
+               return -1;
+       }
+
+       if (efdir->aid_len > maxlen) {
+               wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
+               return -1;
+       }
+
+       os_memcpy(aid, efdir->rid, efdir->aid_len);
+
+       return efdir->aid_len;
+}
+
+
+/**
+ * scard_init - Initialize SIM/USIM connection using PC/SC
+ * @sim_type: Allowed SIM types (SIM, USIM, or both)
+ * Returns: Pointer to private data structure, or %NULL on failure
+ *
+ * This function is used to initialize SIM/USIM connection. PC/SC is used to
+ * open connection to the SIM/USIM card and the card is verified to support the
+ * selected sim_type. In addition, local flag is set if a PIN is needed to
+ * access some of the card functions. Once the connection is not needed
+ * anymore, scard_deinit() can be used to close it.
+ */
+struct scard_data * scard_init(scard_sim_type sim_type)
+{
+       long ret;
+       unsigned long len;
+       struct scard_data *scard;
+#ifdef CONFIG_NATIVE_WINDOWS
+       TCHAR *readers = NULL;
+#else /* CONFIG_NATIVE_WINDOWS */
+       char *readers = NULL;
+#endif /* CONFIG_NATIVE_WINDOWS */
+       unsigned char buf[100];
+       size_t blen;
+       int transaction = 0;
+       int pin_needed;
+
+       wpa_printf(MSG_DEBUG, "SCARD: initializing smart card interface");
+       if (mingw_load_symbols())
+               return NULL;
+       scard = os_zalloc(sizeof(*scard));
+       if (scard == NULL)
+               return NULL;
+
+       ret = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL,
+                                   &scard->ctx);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SCARD: Could not establish smart card "
+                          "context (err=%ld)", ret);
+               goto failed;
+       }
+
+       ret = SCardListReaders(scard->ctx, NULL, NULL, &len);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed "
+                          "(err=%ld)", ret);
+               goto failed;
+       }
+
+#ifdef UNICODE
+       len *= 2;
+#endif /* UNICODE */
+       readers = os_malloc(len);
+       if (readers == NULL) {
+               wpa_printf(MSG_INFO, "SCARD: malloc failed\n");
+               goto failed;
+       }
+
+       ret = SCardListReaders(scard->ctx, NULL, readers, &len);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SCARD: SCardListReaders failed(2) "
+                          "(err=%ld)", ret);
+               goto failed;
+       }
+       if (len < 3) {
+               wpa_printf(MSG_WARNING, "SCARD: No smart card readers "
+                          "available.");
+               goto failed;
+       }
+       /* readers is a list of available reader. Last entry is terminated with
+        * double NUL.
+        * TODO: add support for selecting the reader; now just use the first
+        * one.. */
+#ifdef UNICODE
+       wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%S'", readers);
+#else /* UNICODE */
+       wpa_printf(MSG_DEBUG, "SCARD: Selected reader='%s'", readers);
+#endif /* UNICODE */
+
+       ret = SCardConnect(scard->ctx, readers, SCARD_SHARE_SHARED,
+                          SCARD_PROTOCOL_T0, &scard->card, &scard->protocol);
+       if (ret != SCARD_S_SUCCESS) {
+               if (ret == (long) SCARD_E_NO_SMARTCARD)
+                       wpa_printf(MSG_INFO, "No smart card inserted.");
+               else
+                       wpa_printf(MSG_WARNING, "SCardConnect err=%lx", ret);
+               goto failed;
+       }
+
+       os_free(readers);
+       readers = NULL;
+
+       wpa_printf(MSG_DEBUG, "SCARD: card=0x%x active_protocol=%lu (%s)",
+                  (unsigned int) scard->card, scard->protocol,
+                  scard->protocol == SCARD_PROTOCOL_T0 ? "T0" : "T1");
+
+       ret = SCardBeginTransaction(scard->card);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SCARD: Could not begin transaction: "
+                          "0x%x", (unsigned int) ret);
+               goto failed;
+       }
+       transaction = 1;
+
+       blen = sizeof(buf);
+
+       scard->sim_type = SCARD_GSM_SIM;
+       if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) {
+               wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
+               if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
+                                      SCARD_USIM, NULL, 0)) {
+                       wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported");
+                       if (sim_type == SCARD_USIM_ONLY)
+                               goto failed;
+                       wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM");
+                       scard->sim_type = SCARD_GSM_SIM;
+               } else {
+                       wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
+                       scard->sim_type = SCARD_USIM;
+               }
+       }
+
+       if (scard->sim_type == SCARD_GSM_SIM) {
+               blen = sizeof(buf);
+               if (scard_select_file(scard, SCARD_FILE_MF, buf, &blen)) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Failed to read MF");
+                       goto failed;
+               }
+
+               blen = sizeof(buf);
+               if (scard_select_file(scard, SCARD_FILE_GSM_DF, buf, &blen)) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Failed to read GSM DF");
+                       goto failed;
+               }
+       } else {
+               unsigned char aid[32];
+               int aid_len;
+
+               aid_len = scard_get_aid(scard, aid, sizeof(aid));
+               if (aid_len < 0) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Failed to find AID for "
+                                  "3G USIM app - try to use standard 3G RID");
+                       os_memcpy(aid, "\xa0\x00\x00\x00\x87", 5);
+                       aid_len = 5;
+               }
+               wpa_hexdump(MSG_DEBUG, "SCARD: 3G USIM AID", aid, aid_len);
+
+               /* Select based on AID = 3G RID from EF_DIR. This is usually
+                * starting with A0 00 00 00 87. */
+               blen = sizeof(buf);
+               if (_scard_select_file(scard, 0, buf, &blen, scard->sim_type,
+                                      aid, aid_len)) {
+                       wpa_printf(MSG_INFO, "SCARD: Failed to read 3G USIM "
+                                  "app");
+                       wpa_hexdump(MSG_INFO, "SCARD: 3G USIM AID",
+                                   aid, aid_len);
+                       goto failed;
+               }
+       }
+
+       /* Verify whether CHV1 (PIN1) is needed to access the card. */
+       pin_needed = scard_pin_needed(scard, buf, blen);
+       if (pin_needed < 0) {
+               wpa_printf(MSG_DEBUG, "SCARD: Failed to determine whether PIN "
+                          "is needed");
+               goto failed;
+       }
+       if (pin_needed) {
+               scard->pin1_required = 1;
+               wpa_printf(MSG_DEBUG, "PIN1 needed for SIM access");
+       }
+
+       ret = SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SCARD: Could not end transaction: "
+                          "0x%x", (unsigned int) ret);
+       }
+
+       return scard;
+
+failed:
+       if (transaction)
+               SCardEndTransaction(scard->card, SCARD_LEAVE_CARD);
+       os_free(readers);
+       scard_deinit(scard);
+       return NULL;
+}
+
+
+/**
+ * scard_set_pin - Set PIN (CHV1/PIN1) code for accessing SIM/USIM commands
+ * @scard: Pointer to private data from scard_init()
+ * @pin: PIN code as an ASCII string (e.g., "1234")
+ * Returns: 0 on success, -1 on failure
+ */
+int scard_set_pin(struct scard_data *scard, const char *pin)
+{
+       if (scard == NULL)
+               return -1;
+
+       /* Verify whether CHV1 (PIN1) is needed to access the card. */
+       if (scard->pin1_required) {
+               if (pin == NULL) {
+                       wpa_printf(MSG_DEBUG, "No PIN configured for SIM "
+                                  "access");
+                       return -1;
+               }
+               if (scard_verify_pin(scard, pin)) {
+                       wpa_printf(MSG_INFO, "PIN verification failed for "
+                               "SIM access");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * scard_deinit - Deinitialize SIM/USIM connection
+ * @scard: Pointer to private data from scard_init()
+ *
+ * This function closes the SIM/USIM connect opened with scard_init().
+ */
+void scard_deinit(struct scard_data *scard)
+{
+       long ret;
+
+       if (scard == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "SCARD: deinitializing smart card interface");
+       if (scard->card) {
+               ret = SCardDisconnect(scard->card, SCARD_UNPOWER_CARD);
+               if (ret != SCARD_S_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Failed to disconnect "
+                                  "smart card (err=%ld)", ret);
+               }
+       }
+
+       if (scard->ctx) {
+               ret = SCardReleaseContext(scard->ctx);
+               if (ret != SCARD_S_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "Failed to release smart card "
+                                  "context (err=%ld)", ret);
+               }
+       }
+       os_free(scard);
+       mingw_unload_symbols();
+}
+
+
+static long scard_transmit(struct scard_data *scard,
+                          unsigned char *_send, size_t send_len,
+                          unsigned char *_recv, size_t *recv_len)
+{
+       long ret;
+       unsigned long rlen;
+
+       wpa_hexdump_key(MSG_DEBUG, "SCARD: scard_transmit: send",
+                       _send, send_len);
+       rlen = *recv_len;
+       ret = SCardTransmit(scard->card,
+                           scard->protocol == SCARD_PROTOCOL_T1 ?
+                           SCARD_PCI_T1 : SCARD_PCI_T0,
+                           _send, (unsigned long) send_len,
+                           NULL, _recv, &rlen);
+       *recv_len = rlen;
+       if (ret == SCARD_S_SUCCESS) {
+               wpa_hexdump(MSG_DEBUG, "SCARD: scard_transmit: recv",
+                           _recv, rlen);
+       } else {
+               wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+                          "(err=0x%lx)", ret);
+       }
+       return ret;
+}
+
+
+static int _scard_select_file(struct scard_data *scard, unsigned short file_id,
+                             unsigned char *buf, size_t *buf_len,
+                             sim_types sim_type, unsigned char *aid,
+                             size_t aidlen)
+{
+       long ret;
+       unsigned char resp[3];
+       unsigned char cmd[50] = { SIM_CMD_SELECT };
+       int cmdlen;
+       unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+       size_t len, rlen;
+
+       if (sim_type == SCARD_USIM) {
+               cmd[0] = USIM_CLA;
+               cmd[3] = 0x04;
+               get_resp[0] = USIM_CLA;
+       }
+
+       wpa_printf(MSG_DEBUG, "SCARD: select file %04x", file_id);
+       if (aid) {
+               wpa_hexdump(MSG_DEBUG, "SCARD: select file by AID",
+                           aid, aidlen);
+               if (5 + aidlen > sizeof(cmd))
+                       return -1;
+               cmd[2] = 0x04; /* Select by AID */
+               cmd[4] = aidlen; /* len */
+               os_memcpy(cmd + 5, aid, aidlen);
+               cmdlen = 5 + aidlen;
+       } else {
+               cmd[5] = file_id >> 8;
+               cmd[6] = file_id & 0xff;
+               cmdlen = 7;
+       }
+       len = sizeof(resp);
+       ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_WARNING, "SCARD: SCardTransmit failed "
+                          "(err=0x%lx)", ret);
+               return -1;
+       }
+
+       if (len != 2) {
+               wpa_printf(MSG_WARNING, "SCARD: unexpected resp len "
+                          "%d (expected 2)", (int) len);
+               return -1;
+       }
+
+       if (resp[0] == 0x98 && resp[1] == 0x04) {
+               /* Security status not satisfied (PIN_WLAN) */
+               wpa_printf(MSG_WARNING, "SCARD: Security status not satisfied "
+                          "(PIN_WLAN)");
+               return -1;
+       }
+
+       if (resp[0] == 0x6e) {
+               wpa_printf(MSG_DEBUG, "SCARD: used CLA not supported");
+               return -1;
+       }
+
+       if (resp[0] != 0x6c && resp[0] != 0x9f && resp[0] != 0x61) {
+               wpa_printf(MSG_WARNING, "SCARD: unexpected response 0x%02x "
+                          "(expected 0x61, 0x6c, or 0x9f)", resp[0]);
+               return -1;
+       }
+       /* Normal ending of command; resp[1] bytes available */
+       get_resp[4] = resp[1];
+       wpa_printf(MSG_DEBUG, "SCARD: trying to get response (%d bytes)",
+                  resp[1]);
+
+       rlen = *buf_len;
+       ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &rlen);
+       if (ret == SCARD_S_SUCCESS) {
+               *buf_len = resp[1] < rlen ? resp[1] : rlen;
+               return 0;
+       }
+
+       wpa_printf(MSG_WARNING, "SCARD: SCardTransmit err=0x%lx\n", ret);
+       return -1;
+}
+
+
+static int scard_select_file(struct scard_data *scard, unsigned short file_id,
+                            unsigned char *buf, size_t *buf_len)
+{
+       return _scard_select_file(scard, file_id, buf, buf_len,
+                                 scard->sim_type, NULL, 0);
+}
+
+
+static int scard_get_record_len(struct scard_data *scard, unsigned char recnum,
+                               unsigned char mode)
+{
+       unsigned char buf[255];
+       unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+       size_t blen;
+       long ret;
+
+       if (scard->sim_type == SCARD_USIM)
+               cmd[0] = USIM_CLA;
+       cmd[2] = recnum;
+       cmd[3] = mode;
+       cmd[4] = sizeof(buf);
+
+       blen = sizeof(buf);
+       ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+       if (ret != SCARD_S_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SCARD: failed to determine file "
+                          "length for record %d", recnum);
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "SCARD: file length determination response",
+                   buf, blen);
+
+       if (blen < 2 || buf[0] != 0x6c) {
+               wpa_printf(MSG_DEBUG, "SCARD: unexpected response to file "
+                          "length determination");
+               return -1;
+       }
+
+       return buf[1];
+}
+
+
+static int scard_read_record(struct scard_data *scard,
+                            unsigned char *data, size_t len,
+                            unsigned char recnum, unsigned char mode)
+{
+       unsigned char cmd[5] = { SIM_CMD_READ_RECORD /* , len */ };
+       size_t blen = len + 3;
+       unsigned char *buf;
+       long ret;
+
+       if (scard->sim_type == SCARD_USIM)
+               cmd[0] = USIM_CLA;
+       cmd[2] = recnum;
+       cmd[3] = mode;
+       cmd[4] = len;
+
+       buf = os_malloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+       if (ret != SCARD_S_SUCCESS) {
+               os_free(buf);
+               return -2;
+       }
+       if (blen != len + 2) {
+               wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+                          "length %ld (expected %ld)",
+                          (long) blen, (long) len + 2);
+               os_free(buf);
+               return -3;
+       }
+
+       if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+               wpa_printf(MSG_DEBUG, "SCARD: record read returned unexpected "
+                          "status %02x %02x (expected 90 00)",
+                          buf[len], buf[len + 1]);
+               os_free(buf);
+               return -4;
+       }
+
+       os_memcpy(data, buf, len);
+       os_free(buf);
+
+       return 0;
+}
+
+
+static int scard_read_file(struct scard_data *scard,
+                          unsigned char *data, size_t len)
+{
+       unsigned char cmd[5] = { SIM_CMD_READ_BIN /* , len */ };
+       size_t blen = len + 3;
+       unsigned char *buf;
+       long ret;
+
+       cmd[4] = len;
+
+       buf = os_malloc(blen);
+       if (buf == NULL)
+               return -1;
+
+       if (scard->sim_type == SCARD_USIM)
+               cmd[0] = USIM_CLA;
+       ret = scard_transmit(scard, cmd, sizeof(cmd), buf, &blen);
+       if (ret != SCARD_S_SUCCESS) {
+               os_free(buf);
+               return -2;
+       }
+       if (blen != len + 2) {
+               wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+                          "length %ld (expected %ld)",
+                          (long) blen, (long) len + 2);
+               os_free(buf);
+               return -3;
+       }
+
+       if (buf[len] != 0x90 || buf[len + 1] != 0x00) {
+               wpa_printf(MSG_DEBUG, "SCARD: file read returned unexpected "
+                          "status %02x %02x (expected 90 00)",
+                          buf[len], buf[len + 1]);
+               os_free(buf);
+               return -4;
+       }
+
+       os_memcpy(data, buf, len);
+       os_free(buf);
+
+       return 0;
+}
+
+
+static int scard_verify_pin(struct scard_data *scard, const char *pin)
+{
+       long ret;
+       unsigned char resp[3];
+       unsigned char cmd[5 + 8] = { SIM_CMD_VERIFY_CHV1 };
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "SCARD: verifying PIN");
+
+       if (pin == NULL || os_strlen(pin) > 8)
+               return -1;
+
+       if (scard->sim_type == SCARD_USIM)
+               cmd[0] = USIM_CLA;
+       os_memcpy(cmd + 5, pin, os_strlen(pin));
+       os_memset(cmd + 5 + os_strlen(pin), 0xff, 8 - os_strlen(pin));
+
+       len = sizeof(resp);
+       ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+       if (ret != SCARD_S_SUCCESS)
+               return -2;
+
+       if (len != 2 || resp[0] != 0x90 || resp[1] != 0x00) {
+               wpa_printf(MSG_WARNING, "SCARD: PIN verification failed");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "SCARD: PIN verified successfully");
+       return 0;
+}
+
+
+/**
+ * scard_get_imsi - Read IMSI from SIM/USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @imsi: Buffer for IMSI
+ * @len: Length of imsi buffer; set to IMSI length on success
+ * Returns: 0 on success, -1 if IMSI file cannot be selected, -2 if IMSI file
+ * selection returns invalid result code, -3 if parsing FSP template file fails
+ * (USIM only), -4 if IMSI does not fit in the provided imsi buffer (len is set
+ * to needed length), -5 if reading IMSI file fails.
+ *
+ * This function can be used to read IMSI from the SIM/USIM card. If the IMSI
+ * file is PIN protected, scard_set_pin() must have been used to set the
+ * correct PIN code before calling scard_get_imsi().
+ */
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
+{
+       unsigned char buf[100];
+       size_t blen, imsilen, i;
+       char *pos;
+
+       wpa_printf(MSG_DEBUG, "SCARD: reading IMSI from (GSM) EF-IMSI");
+       blen = sizeof(buf);
+       if (scard_select_file(scard, SCARD_FILE_GSM_EF_IMSI, buf, &blen))
+               return -1;
+       if (blen < 4) {
+               wpa_printf(MSG_WARNING, "SCARD: too short (GSM) EF-IMSI "
+                          "header (len=%ld)", (long) blen);
+               return -2;
+       }
+
+       if (scard->sim_type == SCARD_GSM_SIM) {
+               blen = (buf[2] << 8) | buf[3];
+       } else {
+               int file_size;
+               if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
+                       return -3;
+               blen = file_size;
+       }
+       if (blen < 2 || blen > sizeof(buf)) {
+               wpa_printf(MSG_DEBUG, "SCARD: invalid IMSI file length=%ld",
+                          (long) blen);
+               return -3;
+       }
+
+       imsilen = (blen - 2) * 2 + 1;
+       wpa_printf(MSG_DEBUG, "SCARD: IMSI file length=%ld imsilen=%ld",
+                  (long) blen, (long) imsilen);
+       if (blen < 2 || imsilen > *len) {
+               *len = imsilen;
+               return -4;
+       }
+
+       if (scard_read_file(scard, buf, blen))
+               return -5;
+
+       pos = imsi;
+       *pos++ = '0' + (buf[1] >> 4 & 0x0f);
+       for (i = 2; i < blen; i++) {
+               unsigned char digit;
+
+               digit = buf[i] & 0x0f;
+               if (digit < 10)
+                       *pos++ = '0' + digit;
+               else
+                       imsilen--;
+
+               digit = buf[i] >> 4 & 0x0f;
+               if (digit < 10)
+                       *pos++ = '0' + digit;
+               else
+                       imsilen--;
+       }
+       *len = imsilen;
+
+       return 0;
+}
+
+
+/**
+ * scard_gsm_auth - Run GSM authentication command on SIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @sres: 4-byte buffer for SRES
+ * @kc: 8-byte buffer for Kc
+ * Returns: 0 on success, -1 if SIM/USIM connection has not been initialized,
+ * -2 if authentication command execution fails, -3 if unknown response code
+ * for authentication command is received, -4 if reading of response fails,
+ * -5 if if response data is of unexpected length
+ *
+ * This function performs GSM authentication using SIM/USIM card and the
+ * provided RAND value from HLR/AuC. If authentication command can be completed
+ * successfully, SRES and Kc values will be written into sres and kc buffers.
+ */
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+                  unsigned char *sres, unsigned char *kc)
+{
+       unsigned char cmd[5 + 1 + 16] = { SIM_CMD_RUN_GSM_ALG };
+       int cmdlen;
+       unsigned char get_resp[5] = { SIM_CMD_GET_RESPONSE };
+       unsigned char resp[3], buf[12 + 3 + 2];
+       size_t len;
+       long ret;
+
+       if (scard == NULL)
+               return -1;
+
+       wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - RAND", _rand, 16);
+       if (scard->sim_type == SCARD_GSM_SIM) {
+               cmdlen = 5 + 16;
+               os_memcpy(cmd + 5, _rand, 16);
+       } else {
+               cmdlen = 5 + 1 + 16;
+               cmd[0] = USIM_CLA;
+               cmd[3] = 0x80;
+               cmd[4] = 17;
+               cmd[5] = 16;
+               os_memcpy(cmd + 6, _rand, 16);
+       }
+       len = sizeof(resp);
+       ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
+       if (ret != SCARD_S_SUCCESS)
+               return -2;
+
+       if ((scard->sim_type == SCARD_GSM_SIM &&
+            (len != 2 || resp[0] != 0x9f || resp[1] != 0x0c)) ||
+           (scard->sim_type == SCARD_USIM &&
+            (len != 2 || resp[0] != 0x61 || resp[1] != 0x0e))) {
+               wpa_printf(MSG_WARNING, "SCARD: unexpected response for GSM "
+                          "auth request (len=%ld resp=%02x %02x)",
+                          (long) len, resp[0], resp[1]);
+               return -3;
+       }
+       get_resp[4] = resp[1];
+
+       len = sizeof(buf);
+       ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+       if (ret != SCARD_S_SUCCESS)
+               return -4;
+
+       if (scard->sim_type == SCARD_GSM_SIM) {
+               if (len != 4 + 8 + 2) {
+                       wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+                                  "length for GSM auth (len=%ld, expected 14)",
+                                  (long) len);
+                       return -5;
+               }
+               os_memcpy(sres, buf, 4);
+               os_memcpy(kc, buf + 4, 8);
+       } else {
+               if (len != 1 + 4 + 1 + 8 + 2) {
+                       wpa_printf(MSG_WARNING, "SCARD: unexpected data "
+                                  "length for USIM auth (len=%ld, "
+                                  "expected 16)", (long) len);
+                       return -5;
+               }
+               if (buf[0] != 4 || buf[5] != 8) {
+                       wpa_printf(MSG_WARNING, "SCARD: unexpected SREC/Kc "
+                                  "length (%d %d, expected 4 8)",
+                                  buf[0], buf[5]);
+               }
+               os_memcpy(sres, buf + 1, 4);
+               os_memcpy(kc, buf + 6, 8);
+       }
+
+       wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - SRES", sres, 4);
+       wpa_hexdump(MSG_DEBUG, "SCARD: GSM auth - Kc", kc, 8);
+
+       return 0;
+}
+
+
+/**
+ * scard_umts_auth - Run UMTS authentication command on USIM card
+ * @scard: Pointer to private data from scard_init()
+ * @_rand: 16-byte RAND value from HLR/AuC
+ * @autn: 16-byte AUTN value from HLR/AuC
+ * @res: 16-byte buffer for RES
+ * @res_len: Variable that will be set to RES length
+ * @ik: 16-byte buffer for IK
+ * @ck: 16-byte buffer for CK
+ * @auts: 14-byte buffer for AUTS
+ * Returns: 0 on success, -1 on failure, or -2 if USIM reports synchronization
+ * failure
+ *
+ * This function performs AKA authentication using USIM card and the provided
+ * RAND and AUTN values from HLR/AuC. If authentication command can be
+ * completed successfully, RES, IK, and CK values will be written into provided
+ * buffers and res_len is set to length of received RES value. If USIM reports
+ * synchronization failure, the received AUTS value will be written into auts
+ * buffer. In this case, RES, IK, and CK are not valid.
+ */
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+                   const unsigned char *autn,
+                   unsigned char *res, size_t *res_len,
+                   unsigned char *ik, unsigned char *ck, unsigned char *auts)
+{
+       unsigned char cmd[5 + 1 + AKA_RAND_LEN + 1 + AKA_AUTN_LEN] =
+               { USIM_CMD_RUN_UMTS_ALG };
+       unsigned char get_resp[5] = { USIM_CMD_GET_RESPONSE };
+       unsigned char resp[3], buf[64], *pos, *end;
+       size_t len;
+       long ret;
+
+       if (scard == NULL)
+               return -1;
+
+       if (scard->sim_type == SCARD_GSM_SIM) {
+               wpa_printf(MSG_ERROR, "SCARD: Non-USIM card - cannot do UMTS "
+                          "auth");
+               return -1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - RAND", _rand, AKA_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "SCARD: UMTS auth - AUTN", autn, AKA_AUTN_LEN);
+       cmd[5] = AKA_RAND_LEN;
+       os_memcpy(cmd + 6, _rand, AKA_RAND_LEN);
+       cmd[6 + AKA_RAND_LEN] = AKA_AUTN_LEN;
+       os_memcpy(cmd + 6 + AKA_RAND_LEN + 1, autn, AKA_AUTN_LEN);
+
+       len = sizeof(resp);
+       ret = scard_transmit(scard, cmd, sizeof(cmd), resp, &len);
+       if (ret != SCARD_S_SUCCESS)
+               return -1;
+
+       if (len <= sizeof(resp))
+               wpa_hexdump(MSG_DEBUG, "SCARD: UMTS alg response", resp, len);
+
+       if (len == 2 && resp[0] == 0x98 && resp[1] == 0x62) {
+               wpa_printf(MSG_WARNING, "SCARD: UMTS auth failed - "
+                          "MAC != XMAC");
+               return -1;
+       } else if (len != 2 || resp[0] != 0x61) {
+               wpa_printf(MSG_WARNING, "SCARD: unexpected response for UMTS "
+                          "auth request (len=%ld resp=%02x %02x)",
+                          (long) len, resp[0], resp[1]);
+               return -1;
+       }
+       get_resp[4] = resp[1];
+
+       len = sizeof(buf);
+       ret = scard_transmit(scard, get_resp, sizeof(get_resp), buf, &len);
+       if (ret != SCARD_S_SUCCESS || len > sizeof(buf))
+               return -1;
+
+       wpa_hexdump(MSG_DEBUG, "SCARD: UMTS get response result", buf, len);
+       if (len >= 2 + AKA_AUTS_LEN && buf[0] == 0xdc &&
+           buf[1] == AKA_AUTS_LEN) {
+               wpa_printf(MSG_DEBUG, "SCARD: UMTS Synchronization-Failure");
+               os_memcpy(auts, buf + 2, AKA_AUTS_LEN);
+               wpa_hexdump(MSG_DEBUG, "SCARD: AUTS", auts, AKA_AUTS_LEN);
+               return -2;
+       } else if (len >= 6 + IK_LEN + CK_LEN && buf[0] == 0xdb) {
+               pos = buf + 1;
+               end = buf + len;
+
+               /* RES */
+               if (pos[0] > RES_MAX_LEN || pos + pos[0] > end) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Invalid RES");
+                       return -1;
+               }
+               *res_len = *pos++;
+               os_memcpy(res, pos, *res_len);
+               pos += *res_len;
+               wpa_hexdump(MSG_DEBUG, "SCARD: RES", res, *res_len);
+
+               /* CK */
+               if (pos[0] != CK_LEN || pos + CK_LEN > end) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Invalid CK");
+                       return -1;
+               }
+               pos++;
+               os_memcpy(ck, pos, CK_LEN);
+               pos += CK_LEN;
+               wpa_hexdump(MSG_DEBUG, "SCARD: CK", ck, CK_LEN);
+
+               /* IK */
+               if (pos[0] != IK_LEN || pos + IK_LEN > end) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Invalid IK");
+                       return -1;
+               }
+               pos++;
+               os_memcpy(ik, pos, IK_LEN);
+               pos += IK_LEN;
+               wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
+
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "SCARD: Unrecognized response");
+       return -1;
+}
diff --git a/src/utils/pcsc_funcs.h b/src/utils/pcsc_funcs.h
new file mode 100644 (file)
index 0000000..543f7c5
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * WPA Supplicant / PC/SC smartcard interface for USIM, GSM SIM
+ * Copyright (c) 2004-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 PCSC_FUNCS_H
+#define PCSC_FUNCS_H
+
+/* GSM files
+ * File type in first octet:
+ * 3F = Master File
+ * 7F = Dedicated File
+ * 2F = Elementary File under the Master File
+ * 6F = Elementary File under a Dedicated File
+ */
+#define SCARD_FILE_MF          0x3F00
+#define SCARD_FILE_GSM_DF      0x7F20
+#define SCARD_FILE_UMTS_DF     0x7F50
+#define SCARD_FILE_GSM_EF_IMSI 0x6F07
+#define SCARD_FILE_EF_DIR      0x2F00
+#define SCARD_FILE_EF_ICCID    0x2FE2
+#define SCARD_FILE_EF_CK       0x6FE1
+#define SCARD_FILE_EF_IK       0x6FE2
+
+#define SCARD_CHV1_OFFSET      13
+#define SCARD_CHV1_FLAG                0x80
+
+typedef enum {
+       SCARD_GSM_SIM_ONLY,
+       SCARD_USIM_ONLY,
+       SCARD_TRY_BOTH
+} scard_sim_type;
+
+
+#ifdef PCSC_FUNCS
+struct scard_data * scard_init(scard_sim_type sim_type);
+void scard_deinit(struct scard_data *scard);
+
+int scard_set_pin(struct scard_data *scard, const char *pin);
+int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len);
+int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
+                  unsigned char *sres, unsigned char *kc);
+int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
+                   const unsigned char *autn,
+                   unsigned char *res, size_t *res_len,
+                   unsigned char *ik, unsigned char *ck, unsigned char *auts);
+
+#else /* PCSC_FUNCS */
+
+#define scard_init(s) NULL
+#define scard_deinit(s) do { } while (0)
+#define scard_set_pin(s, p) -1
+#define scard_get_imsi(s, i, l) -1
+#define scard_gsm_auth(s, r, s2, k) -1
+#define scard_umts_auth(s, r, a, r2, rl, i, c, a2) -1
+
+#endif /* PCSC_FUNCS */
+
+#endif /* PCSC_FUNCS_H */
diff --git a/src/utils/radiotap.c b/src/utils/radiotap.c
new file mode 100644 (file)
index 0000000..804473f
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * Radiotap parser
+ *
+ * Copyright 2007              Andy Green <andy@warmcat.com>
+ *
+ * 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.
+ *
+ *
+ * Modified for userspace by Johannes Berg <johannes@sipsolutions.net>
+ * I only modified some things on top to ease syncing should bugs be found.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "radiotap_iter.h"
+
+#define le16_to_cpu            le_to_host16
+#define le32_to_cpu            le_to_host32
+#define __le32                 uint32_t
+#define ulong                  unsigned long
+#define unlikely(cond)         (cond)
+#define get_unaligned(p)                                       \
+({                                                             \
+       struct packed_dummy_struct {                            \
+               typeof(*(p)) __val;                             \
+       } __attribute__((packed)) *__ptr = (void *) (p);        \
+                                                               \
+       __ptr->__val;                                           \
+})
+
+/* function prototypes and related defs are in radiotap_iter.h */
+
+/**
+ * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
+ * @iterator: radiotap_iterator to initialize
+ * @radiotap_header: radiotap header to parse
+ * @max_length: total length we can parse into (eg, whole packet length)
+ *
+ * Returns: 0 or a negative error code if there is a problem.
+ *
+ * This function initializes an opaque iterator struct which can then
+ * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap
+ * argument which is present in the header.  It knows about extended
+ * present headers and handles them.
+ *
+ * How to use:
+ * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator
+ * struct ieee80211_radiotap_iterator (no need to init the struct beforehand)
+ * checking for a good 0 return code.  Then loop calling
+ * __ieee80211_radiotap_iterator_next()... it returns either 0,
+ * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem.
+ * The iterator's @this_arg member points to the start of the argument
+ * associated with the current argument index that is present, which can be
+ * found in the iterator's @this_arg_index member.  This arg index corresponds
+ * to the IEEE80211_RADIOTAP_... defines.
+ *
+ * Radiotap header length:
+ * You can find the CPU-endian total radiotap header length in
+ * iterator->max_length after executing ieee80211_radiotap_iterator_init()
+ * successfully.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ *
+ * Example code:
+ * See Documentation/networking/radiotap-headers.txt
+ */
+
+int ieee80211_radiotap_iterator_init(
+    struct ieee80211_radiotap_iterator *iterator,
+    struct ieee80211_radiotap_header *radiotap_header,
+    int max_length)
+{
+       /* Linux only supports version 0 radiotap format */
+       if (radiotap_header->it_version)
+               return -EINVAL;
+
+       /* sanity check for allowed length and radiotap length field */
+       if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len)))
+               return -EINVAL;
+
+       iterator->rtheader = radiotap_header;
+       iterator->max_length = le16_to_cpu(get_unaligned(
+                                               &radiotap_header->it_len));
+       iterator->arg_index = 0;
+       iterator->bitmap_shifter = le32_to_cpu(get_unaligned(
+                                               &radiotap_header->it_present));
+       iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
+       iterator->this_arg = NULL;
+
+       /* find payload start allowing for extended bitmap(s) */
+
+       if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
+               while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
+                                  (1<<IEEE80211_RADIOTAP_EXT)) {
+                       iterator->arg += sizeof(u32);
+
+                       /*
+                        * check for insanity where the present bitmaps
+                        * keep claiming to extend up to or even beyond the
+                        * stated radiotap header length
+                        */
+
+                       if (((ulong)iterator->arg - (ulong)iterator->rtheader)
+                           > (ulong)iterator->max_length)
+                               return -EINVAL;
+               }
+
+               iterator->arg += sizeof(u32);
+
+               /*
+                * no need to check again for blowing past stated radiotap
+                * header length, because ieee80211_radiotap_iterator_next
+                * checks it before it is dereferenced
+                */
+       }
+
+       /* we are all initialized happily */
+
+       return 0;
+}
+
+
+/**
+ * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
+ * @iterator: radiotap_iterator to move to next arg (if any)
+ *
+ * Returns: 0 if there is an argument to handle,
+ * -ENOENT if there are no more args or -EINVAL
+ * if there is something else wrong.
+ *
+ * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*)
+ * in @this_arg_index and sets @this_arg to point to the
+ * payload for the field.  It takes care of alignment handling and extended
+ * present fields.  @this_arg can be changed by the caller (eg,
+ * incremented to move inside a compound argument like
+ * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in
+ * little-endian format whatever the endianess of your CPU.
+ *
+ * Alignment Gotcha:
+ * You must take care when dereferencing iterator.this_arg
+ * for multibyte types... the pointer is not aligned.  Use
+ * get_unaligned((type *)iterator.this_arg) to dereference
+ * iterator.this_arg for type "type" safely on all arches.
+ */
+
+int ieee80211_radiotap_iterator_next(
+    struct ieee80211_radiotap_iterator *iterator)
+{
+
+       /*
+        * small length lookup table for all radiotap types we heard of
+        * starting from b0 in the bitmap, so we can walk the payload
+        * area of the radiotap header
+        *
+        * There is a requirement to pad args, so that args
+        * of a given length must begin at a boundary of that length
+        * -- but note that compound args are allowed (eg, 2 x u16
+        * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
+        * a reliable indicator of alignment requirement.
+        *
+        * upper nybble: content alignment for arg
+        * lower nybble: content length for arg
+        */
+
+       static const u8 rt_sizes[] = {
+               [IEEE80211_RADIOTAP_TSFT] = 0x88,
+               [IEEE80211_RADIOTAP_FLAGS] = 0x11,
+               [IEEE80211_RADIOTAP_RATE] = 0x11,
+               [IEEE80211_RADIOTAP_CHANNEL] = 0x24,
+               [IEEE80211_RADIOTAP_FHSS] = 0x22,
+               [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
+               [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
+               [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
+               [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
+               [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
+               [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
+               [IEEE80211_RADIOTAP_ANTENNA] = 0x11,
+               [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
+               [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
+               [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
+               [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
+               [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
+               [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
+               /*
+                * add more here as they are defined in
+                * include/net/ieee80211_radiotap.h
+                */
+       };
+
+       /*
+        * for every radiotap entry we can at
+        * least skip (by knowing the length)...
+        */
+
+       while (iterator->arg_index < (int) sizeof(rt_sizes)) {
+               int hit = 0;
+               int pad;
+
+               if (!(iterator->bitmap_shifter & 1))
+                       goto next_entry; /* arg not present */
+
+               /*
+                * arg is present, account for alignment padding
+                *  8-bit args can be at any alignment
+                * 16-bit args must start on 16-bit boundary
+                * 32-bit args must start on 32-bit boundary
+                * 64-bit args must start on 64-bit boundary
+                *
+                * note that total arg size can differ from alignment of
+                * elements inside arg, so we use upper nybble of length
+                * table to base alignment on
+                *
+                * also note: these alignments are ** relative to the
+                * start of the radiotap header **.  There is no guarantee
+                * that the radiotap header itself is aligned on any
+                * kind of boundary.
+                *
+                * the above is why get_unaligned() is used to dereference
+                * multibyte elements from the radiotap area
+                */
+
+               pad = (((ulong)iterator->arg) -
+                       ((ulong)iterator->rtheader)) &
+                       ((rt_sizes[iterator->arg_index] >> 4) - 1);
+
+               if (pad)
+                       iterator->arg +=
+                               (rt_sizes[iterator->arg_index] >> 4) - pad;
+
+               /*
+                * this is what we will return to user, but we need to
+                * move on first so next call has something fresh to test
+                */
+               iterator->this_arg_index = iterator->arg_index;
+               iterator->this_arg = iterator->arg;
+               hit = 1;
+
+               /* internally move on the size of this arg */
+               iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+
+               /*
+                * check for insanity where we are given a bitmap that
+                * claims to have more arg content than the length of the
+                * radiotap section.  We will normally end up equalling this
+                * max_length on the last arg, never exceeding it.
+                */
+
+               if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
+                   (ulong) iterator->max_length)
+                       return -EINVAL;
+
+       next_entry:
+               iterator->arg_index++;
+               if (unlikely((iterator->arg_index & 31) == 0)) {
+                       /* completed current u32 bitmap */
+                       if (iterator->bitmap_shifter & 1) {
+                               /* b31 was set, there is more */
+                               /* move to next u32 bitmap */
+                               iterator->bitmap_shifter = le32_to_cpu(
+                                       get_unaligned(iterator->next_bitmap));
+                               iterator->next_bitmap++;
+                       } else
+                               /* no more bitmaps: end */
+                               iterator->arg_index = sizeof(rt_sizes);
+               } else /* just try the next bit */
+                       iterator->bitmap_shifter >>= 1;
+
+               /* if we found a valid arg earlier, return it now */
+               if (hit)
+                       return 0;
+       }
+
+       /* we don't know how to handle any more args, we're done */
+       return -ENOENT;
+}
diff --git a/src/utils/radiotap.h b/src/utils/radiotap.h
new file mode 100644 (file)
index 0000000..508264c
--- /dev/null
@@ -0,0 +1,242 @@
+/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */
+/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */
+
+/*-
+ * Copyright (c) 2003, 2004 David Young.  All rights reserved.
+ *
+ * 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. The name of David Young may not be used to endorse or promote
+ *    products derived from this software without specific prior
+ *    written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``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 DAVID
+ * YOUNG 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.
+ */
+
+/*
+ * Modifications to fit into the linux IEEE 802.11 stack,
+ * Mike Kershaw (dragorn@kismetwireless.net)
+ */
+
+#ifndef IEEE80211RADIOTAP_H
+#define IEEE80211RADIOTAP_H
+
+#include <stdint.h>
+
+/* Base version of the radiotap packet header data */
+#define PKTHDR_RADIOTAP_VERSION                0
+
+/* A generic radio capture format is desirable. There is one for
+ * Linux, but it is neither rigidly defined (there were not even
+ * units given for some fields) nor easily extensible.
+ *
+ * I suggest the following extensible radio capture format. It is
+ * based on a bitmap indicating which fields are present.
+ *
+ * I am trying to describe precisely what the application programmer
+ * should expect in the following, and for that reason I tell the
+ * units and origin of each measurement (where it applies), or else I
+ * use sufficiently weaselly language ("is a monotonically nondecreasing
+ * function of...") that I cannot set false expectations for lawyerly
+ * readers.
+ */
+
+/* The radio capture header precedes the 802.11 header.
+ * All data in the header is little endian on all platforms.
+ */
+struct ieee80211_radiotap_header {
+       uint8_t it_version;     /* Version 0. Only increases
+                                * for drastic changes,
+                                * introduction of compatible
+                                * new fields does not count.
+                                */
+       uint8_t it_pad;
+       uint16_t it_len;        /* length of the whole
+                                * header in bytes, including
+                                * it_version, it_pad,
+                                * it_len, and data fields.
+                                */
+       uint32_t it_present;    /* A bitmap telling which
+                                * fields are present. Set bit 31
+                                * (0x80000000) to extend the
+                                * bitmap by another 32 bits.
+                                * Additional extensions are made
+                                * by setting bit 31.
+                                */
+};
+
+/* Name                                 Data type    Units
+ * ----                                 ---------    -----
+ *
+ * IEEE80211_RADIOTAP_TSFT              __le64       microseconds
+ *
+ *      Value in microseconds of the MAC's 64-bit 802.11 Time
+ *      Synchronization Function timer when the first bit of the
+ *      MPDU arrived at the MAC. For received frames, only.
+ *
+ * IEEE80211_RADIOTAP_CHANNEL           2 x uint16_t   MHz, bitmap
+ *
+ *      Tx/Rx frequency in MHz, followed by flags (see below).
+ *
+ * IEEE80211_RADIOTAP_FHSS              uint16_t       see below
+ *
+ *      For frequency-hopping radios, the hop set (first byte)
+ *      and pattern (second byte).
+ *
+ * IEEE80211_RADIOTAP_RATE              u8           500kb/s
+ *
+ *      Tx/Rx data rate
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     s8           decibels from
+ *                                                   one milliwatt (dBm)
+ *
+ *      RF signal power at the antenna, decibel difference from
+ *      one milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DBM_ANTNOISE      s8           decibels from
+ *                                                   one milliwatt (dBm)
+ *
+ *      RF noise power at the antenna, decibel difference from one
+ *      milliwatt.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u8           decibel (dB)
+ *
+ *      RF signal power at the antenna, decibel difference from an
+ *      arbitrary, fixed reference.
+ *
+ * IEEE80211_RADIOTAP_DB_ANTNOISE       u8           decibel (dB)
+ *
+ *      RF noise power at the antenna, decibel difference from an
+ *      arbitrary, fixed reference point.
+ *
+ * IEEE80211_RADIOTAP_LOCK_QUALITY      uint16_t       unitless
+ *
+ *      Quality of Barker code lock. Unitless. Monotonically
+ *      nondecreasing with "better" lock strength. Called "Signal
+ *      Quality" in datasheets.  (Is there a standard way to measure
+ *      this?)
+ *
+ * IEEE80211_RADIOTAP_TX_ATTENUATION    uint16_t       unitless
+ *
+ *      Transmit power expressed as unitless distance from max
+ *      power set at factory calibration.  0 is max power.
+ *      Monotonically nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t       decibels (dB)
+ *
+ *      Transmit power expressed as decibel distance from max power
+ *      set at factory calibration.  0 is max power.  Monotonically
+ *      nondecreasing with lower power levels.
+ *
+ * IEEE80211_RADIOTAP_DBM_TX_POWER      s8           decibels from
+ *                                                   one milliwatt (dBm)
+ *
+ *      Transmit power expressed as dBm (decibels from a 1 milliwatt
+ *      reference). This is the absolute power level measured at
+ *      the antenna port.
+ *
+ * IEEE80211_RADIOTAP_FLAGS             u8           bitmap
+ *
+ *      Properties of transmitted and received frames. See flags
+ *      defined below.
+ *
+ * IEEE80211_RADIOTAP_ANTENNA           u8           antenna index
+ *
+ *      Unitless indication of the Rx/Tx antenna for this packet.
+ *      The first antenna is antenna 0.
+ *
+ * IEEE80211_RADIOTAP_RX_FLAGS          uint16_t       bitmap
+ *
+ *     Properties of received frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_TX_FLAGS          uint16_t       bitmap
+ *
+ *     Properties of transmitted frames. See flags defined below.
+ *
+ * IEEE80211_RADIOTAP_RTS_RETRIES       u8           data
+ *
+ *     Number of rts retries a transmitted frame used.
+ *
+ * IEEE80211_RADIOTAP_DATA_RETRIES      u8           data
+ *
+ *     Number of unicast retries a transmitted frame used.
+ *
+ */
+enum ieee80211_radiotap_type {
+       IEEE80211_RADIOTAP_TSFT = 0,
+       IEEE80211_RADIOTAP_FLAGS = 1,
+       IEEE80211_RADIOTAP_RATE = 2,
+       IEEE80211_RADIOTAP_CHANNEL = 3,
+       IEEE80211_RADIOTAP_FHSS = 4,
+       IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5,
+       IEEE80211_RADIOTAP_DBM_ANTNOISE = 6,
+       IEEE80211_RADIOTAP_LOCK_QUALITY = 7,
+       IEEE80211_RADIOTAP_TX_ATTENUATION = 8,
+       IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9,
+       IEEE80211_RADIOTAP_DBM_TX_POWER = 10,
+       IEEE80211_RADIOTAP_ANTENNA = 11,
+       IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12,
+       IEEE80211_RADIOTAP_DB_ANTNOISE = 13,
+       IEEE80211_RADIOTAP_RX_FLAGS = 14,
+       IEEE80211_RADIOTAP_TX_FLAGS = 15,
+       IEEE80211_RADIOTAP_RTS_RETRIES = 16,
+       IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+       IEEE80211_RADIOTAP_EXT = 31
+};
+
+/* Channel flags. */
+#define        IEEE80211_CHAN_TURBO    0x0010  /* Turbo channel */
+#define        IEEE80211_CHAN_CCK      0x0020  /* CCK channel */
+#define        IEEE80211_CHAN_OFDM     0x0040  /* OFDM channel */
+#define        IEEE80211_CHAN_2GHZ     0x0080  /* 2 GHz spectrum channel. */
+#define        IEEE80211_CHAN_5GHZ     0x0100  /* 5 GHz spectrum channel */
+#define        IEEE80211_CHAN_PASSIVE  0x0200  /* Only passive scan allowed */
+#define        IEEE80211_CHAN_DYN      0x0400  /* Dynamic CCK-OFDM channel */
+#define        IEEE80211_CHAN_GFSK     0x0800  /* GFSK channel (FHSS PHY) */
+
+/* For IEEE80211_RADIOTAP_FLAGS */
+#define        IEEE80211_RADIOTAP_F_CFP        0x01    /* sent/received
+                                                * during CFP
+                                                */
+#define        IEEE80211_RADIOTAP_F_SHORTPRE   0x02    /* sent/received
+                                                * with short
+                                                * preamble
+                                                */
+#define        IEEE80211_RADIOTAP_F_WEP        0x04    /* sent/received
+                                                * with WEP encryption
+                                                */
+#define        IEEE80211_RADIOTAP_F_FRAG       0x08    /* sent/received
+                                                * with fragmentation
+                                                */
+#define        IEEE80211_RADIOTAP_F_FCS        0x10    /* frame includes FCS */
+#define        IEEE80211_RADIOTAP_F_DATAPAD    0x20    /* frame has padding between
+                                                * 802.11 header and payload
+                                                * (to 32-bit boundary)
+                                                */
+/* For IEEE80211_RADIOTAP_RX_FLAGS */
+#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001  /* frame failed crc check */
+
+/* For IEEE80211_RADIOTAP_TX_FLAGS */
+#define IEEE80211_RADIOTAP_F_TX_FAIL   0x0001  /* failed due to excessive
+                                                * retries */
+#define IEEE80211_RADIOTAP_F_TX_CTS    0x0002  /* used cts 'protection' */
+#define IEEE80211_RADIOTAP_F_TX_RTS    0x0004  /* used rts/cts handshake */
+
+#endif                         /* IEEE80211_RADIOTAP_H */
diff --git a/src/utils/radiotap_iter.h b/src/utils/radiotap_iter.h
new file mode 100644 (file)
index 0000000..92a798a
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef __RADIOTAP_ITER_H
+#define __RADIOTAP_ITER_H
+
+#include "radiotap.h"
+
+/* Radiotap header iteration
+ *   implemented in radiotap.c
+ */
+/**
+ * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
+ * @rtheader: pointer to the radiotap header we are walking through
+ * @max_length: length of radiotap header in cpu byte ordering
+ * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
+ * @this_arg: pointer to current radiotap arg
+ * @arg_index: internal next argument index
+ * @arg: internal next argument pointer
+ * @next_bitmap: internal pointer to next present u32
+ * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ */
+
+struct ieee80211_radiotap_iterator {
+       struct ieee80211_radiotap_header *rtheader;
+       int max_length;
+       int this_arg_index;
+       unsigned char *this_arg;
+
+       int arg_index;
+       unsigned char *arg;
+       uint32_t *next_bitmap;
+       uint32_t bitmap_shifter;
+};
+
+extern int ieee80211_radiotap_iterator_init(
+   struct ieee80211_radiotap_iterator *iterator,
+   struct ieee80211_radiotap_header *radiotap_header,
+   int max_length);
+
+extern int ieee80211_radiotap_iterator_next(
+   struct ieee80211_radiotap_iterator *iterator);
+
+#endif /* __RADIOTAP_ITER_H */
diff --git a/src/utils/state_machine.h b/src/utils/state_machine.h
new file mode 100644 (file)
index 0000000..31f6672
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * wpa_supplicant/hostapd - State machine definitions
+ * 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.
+ *
+ * This file includes a set of pre-processor macros that can be used to
+ * implement a state machine. In addition to including this header file, each
+ * file implementing a state machine must define STATE_MACHINE_DATA to be the
+ * data structure including state variables (enum machine_state,
+ * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used
+ * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define
+ * a group of state machines with shared data structure, STATE_MACHINE_ADDR
+ * needs to be defined to point to the MAC address used in debug output.
+ * SM_ENTRY_M macro can be used to define similar group of state machines
+ * without this additional debug info.
+ */
+
+#ifndef STATE_MACHINE_H
+#define STATE_MACHINE_H
+
+/**
+ * SM_STATE - Declaration of a state machine function
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used to declare a state machine function. It is used in place
+ * of a C function definition to declare functions to be run when the state is
+ * entered by calling SM_ENTER or SM_ENTER_GLOBAL.
+ */
+#define SM_STATE(machine, state) \
+static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \
+       int global)
+
+/**
+ * SM_ENTRY - State machine function entry point
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is used inside each state machine function declared with
+ * SM_STATE. SM_ENTRY should be in the beginning of the function body, but
+ * after declaration of possible local variables. This macro prints debug
+ * information about state transition and update the state machine state.
+ */
+#define SM_ENTRY(machine, state) \
+if (!global || sm->machine ## _state != machine ## _ ## state) { \
+       sm->changed = TRUE; \
+       wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \
+                  " entering state " #state); \
+} \
+sm->machine ## _state = machine ## _ ## state;
+
+/**
+ * SM_ENTRY_M - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY, but for state machine groups that use a shared
+ * data structure for more than one state machine. Both machine and prefix
+ * parameters are set to "sub-state machine" name. prefix is used to allow more
+ * than one state variable to be stored in the same data structure.
+ */
+#define SM_ENTRY_M(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+       sm->changed = TRUE; \
+       wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \
+                  #machine " entering state " #_state); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTRY_MA - State machine function entry point for state machine group
+ * @machine: State machine name
+ * @_state: State machine state
+ * @data: State variable prefix (full variable: prefix_state)
+ *
+ * This macro is like SM_ENTRY_M, but a MAC address is included in debug
+ * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to
+ * be included in debug.
+ */
+#define SM_ENTRY_MA(machine, _state, data) \
+if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \
+       sm->changed = TRUE; \
+       wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \
+                  #machine " entering state " #_state, \
+                  MAC2STR(STATE_MACHINE_ADDR)); \
+} \
+sm->data ## _ ## state = machine ## _ ## _state;
+
+/**
+ * SM_ENTER - Enter a new state machine state
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro expands to a function call to a state machine function defined
+ * with SM_STATE macro. SM_ENTER is used in a state machine step function to
+ * move the state machine to a new state.
+ */
+#define SM_ENTER(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 0)
+
+/**
+ * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule
+ * @machine: State machine name
+ * @state: State machine state
+ *
+ * This macro is like SM_ENTER, but this is used when entering a new state
+ * based on a global (not specific to any particular state) rule. A separate
+ * macro is used to avoid unwanted debug message floods when the same global
+ * rule is forcing a state machine to remain in on state.
+ */
+#define SM_ENTER_GLOBAL(machine, state) \
+sm_ ## machine ## _ ## state ## _Enter(sm, 1)
+
+/**
+ * SM_STEP - Declaration of a state machine step function
+ * @machine: State machine name
+ *
+ * This macro is used to declare a state machine step function. It is used in
+ * place of a C function definition to declare a function that is used to move
+ * state machine to a new state based on state variables. This function uses
+ * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state.
+ */
+#define SM_STEP(machine) \
+static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm)
+
+/**
+ * SM_STEP_RUN - Call the state machine step function
+ * @machine: State machine name
+ *
+ * This macro expands to a function call to a state machine step function
+ * defined with SM_STEP macro.
+ */
+#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm)
+
+#endif /* STATE_MACHINE_H */
diff --git a/src/utils/trace.c b/src/utils/trace.c
new file mode 100644 (file)
index 0000000..bb3eb24
--- /dev/null
@@ -0,0 +1,329 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 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 "common.h"
+#include "trace.h"
+
+#ifdef WPA_TRACE
+
+static struct dl_list active_references =
+{ &active_references, &active_references };
+
+#ifdef WPA_TRACE_BFD
+#include <bfd.h>
+#ifdef __linux__
+#include <demangle.h>
+#else /* __linux__ */
+#include <libiberty/demangle.h>
+#endif /* __linux__ */
+
+static char *prg_fname = NULL;
+static bfd *cached_abfd = NULL;
+static asymbol **syms = NULL;
+
+static void get_prg_fname(void)
+{
+       char exe[50], fname[512];
+       int len;
+       os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
+       len = readlink(exe, fname, sizeof(fname) - 1);
+       if (len < 0 || len >= (int) sizeof(fname)) {
+               perror("readlink");
+               return;
+       }
+       fname[len] = '\0';
+       prg_fname = strdup(fname);
+}
+
+
+static bfd * open_bfd(const char *fname)
+{
+       bfd *abfd;
+       char **matching;
+
+       abfd = bfd_openr(prg_fname, NULL);
+       if (abfd == NULL) {
+               wpa_printf(MSG_INFO, "bfd_openr failed");
+               return NULL;
+       }
+
+       if (bfd_check_format(abfd, bfd_archive)) {
+               wpa_printf(MSG_INFO, "bfd_check_format failed");
+               bfd_close(abfd);
+               return NULL;
+       }
+
+       if (!bfd_check_format_matches(abfd, bfd_object, &matching)) {
+               wpa_printf(MSG_INFO, "bfd_check_format_matches failed");
+               free(matching);
+               bfd_close(abfd);
+               return NULL;
+       }
+
+       return abfd;
+}
+
+
+static void read_syms(bfd *abfd)
+{
+       long storage, symcount;
+       bfd_boolean dynamic = FALSE;
+
+       if (syms)
+               return;
+
+       if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) {
+               wpa_printf(MSG_INFO, "No symbols");
+               return;
+       }
+
+       storage = bfd_get_symtab_upper_bound(abfd);
+       if (storage == 0) {
+               storage = bfd_get_dynamic_symtab_upper_bound(abfd);
+               dynamic = TRUE;
+       }
+       if (storage < 0) {
+               wpa_printf(MSG_INFO, "Unknown symtab upper bound");
+               return;
+       }
+
+       syms = malloc(storage);
+       if (syms == NULL) {
+               wpa_printf(MSG_INFO, "Failed to allocate memory for symtab "
+                          "(%ld bytes)", storage);
+               return;
+       }
+       if (dynamic)
+               symcount = bfd_canonicalize_dynamic_symtab(abfd, syms);
+       else
+               symcount = bfd_canonicalize_symtab(abfd, syms);
+       if (symcount < 0) {
+               wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab",
+                          dynamic ? "dynamic " : "");
+               free(syms);
+               syms = NULL;
+               return;
+       }
+}
+
+
+struct bfd_data {
+       bfd_vma pc;
+       bfd_boolean found;
+       const char *filename;
+       const char *function;
+       unsigned int line;
+};
+
+
+static void find_addr_sect(bfd *abfd, asection *section, void *obj)
+{
+       struct bfd_data *data = obj;
+       bfd_vma vma;
+       bfd_size_type size;
+
+       if (data->found)
+               return;
+
+       if (!(bfd_get_section_vma(abfd, section)))
+               return;
+
+       vma = bfd_get_section_vma(abfd, section);
+       if (data->pc < vma)
+               return;
+
+       size = bfd_get_section_size(section);
+       if (data->pc >= vma + size)
+               return;
+
+       data->found = bfd_find_nearest_line(abfd, section, syms,
+                                           data->pc - vma,
+                                           &data->filename,
+                                           &data->function,
+                                           &data->line);
+}
+
+
+static void wpa_trace_bfd_addr(void *pc)
+{
+       bfd *abfd = cached_abfd;
+       struct bfd_data data;
+       const char *name;
+       char *aname = NULL;
+       const char *filename;
+
+       if (abfd == NULL)
+               return;
+
+       data.pc = (bfd_vma) pc;
+       data.found = FALSE;
+       bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+       if (!data.found)
+               return;
+
+       do {
+               if (data.function)
+                       aname = bfd_demangle(abfd, data.function,
+                                            DMGL_ANSI | DMGL_PARAMS);
+               name = aname ? aname : data.function;
+               filename = data.filename;
+               if (filename) {
+                       char *end = os_strrchr(filename, '/');
+                       int i = 0;
+                       while (*filename && *filename == prg_fname[i] &&
+                              filename <= end) {
+                               filename++;
+                               i++;
+                       }
+               }
+               wpa_printf(MSG_INFO, "     %s() %s:%u",
+                          name, filename, data.line);
+               free(aname);
+
+               data.found = bfd_find_inliner_info(abfd, &data.filename,
+                                                  &data.function, &data.line);
+       } while (data.found);
+}
+
+
+static const char * wpa_trace_bfd_addr2func(void *pc)
+{
+       bfd *abfd = cached_abfd;
+       struct bfd_data data;
+
+       if (abfd == NULL)
+               return NULL;
+
+       data.pc = (bfd_vma) pc;
+       data.found = FALSE;
+       bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+       if (!data.found)
+               return NULL;
+
+       return data.function;
+}
+
+
+static void wpa_trace_bfd_init(void)
+{
+       if (!prg_fname) {
+               get_prg_fname();
+               if (!prg_fname)
+                       return;
+       }
+
+       if (!cached_abfd) {
+               cached_abfd = open_bfd(prg_fname);
+               if (!cached_abfd) {
+                       wpa_printf(MSG_INFO, "Failed to open bfd");
+                       return;
+               }
+       }
+
+       read_syms(cached_abfd);
+       if (!syms) {
+               wpa_printf(MSG_INFO, "Failed to read symbols");
+               return;
+       }
+}
+
+
+void wpa_trace_dump_funcname(const char *title, void *pc)
+{
+       wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc);
+       wpa_trace_bfd_init();
+       wpa_trace_bfd_addr(pc);
+}
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_bfd_init() do { } while (0)
+#define wpa_trace_bfd_addr(pc) do { } while (0)
+#define wpa_trace_bfd_addr2func(pc) NULL
+
+#endif /* WPA_TRACE_BFD */
+
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num)
+{
+       char **sym;
+       int i;
+       enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state;
+
+       wpa_trace_bfd_init();
+       wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title);
+       sym = backtrace_symbols(btrace, btrace_num);
+       state = TRACE_HEAD;
+       for (i = 0; i < btrace_num; i++) {
+               const char *func = wpa_trace_bfd_addr2func(btrace[i]);
+               if (state == TRACE_HEAD && func &&
+                   (os_strcmp(func, "wpa_trace_add_ref_func") == 0 ||
+                    os_strcmp(func, "wpa_trace_check_ref") == 0 ||
+                    os_strcmp(func, "wpa_trace_show") == 0))
+                       continue;
+               if (state == TRACE_TAIL && sym && sym[i] &&
+                   os_strstr(sym[i], "__libc_start_main"))
+                       break;
+               if (state == TRACE_HEAD)
+                       state = TRACE_RELEVANT;
+               if (sym)
+                       wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]);
+               else
+                       wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]);
+               wpa_trace_bfd_addr(btrace[i]);
+               if (state == TRACE_RELEVANT && func &&
+                   os_strcmp(func, "main") == 0)
+                       state = TRACE_TAIL;
+       }
+       free(sym);
+       wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title);
+}
+
+
+void wpa_trace_show(const char *title)
+{
+       struct info {
+               WPA_TRACE_INFO
+       } info;
+       wpa_trace_record(&info);
+       wpa_trace_dump(title, &info);
+}
+
+
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr)
+{
+       if (addr == NULL)
+               return;
+       ref->addr = addr;
+       wpa_trace_record(ref);
+       dl_list_add(&active_references, &ref->list);
+}
+
+
+void wpa_trace_check_ref(const void *addr)
+{
+       struct wpa_trace_ref *ref;
+       dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) {
+               if (addr != ref->addr)
+                       continue;
+               wpa_trace_show("Freeing referenced memory");
+               wpa_trace_dump("Reference registration", ref);
+               abort();
+       }
+}
+
+#endif /* WPA_TRACE */
diff --git a/src/utils/trace.h b/src/utils/trace.h
new file mode 100644 (file)
index 0000000..22d3de0
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Backtrace debugging
+ * Copyright (c) 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.
+ */
+
+#ifndef TRACE_H
+#define TRACE_H
+
+#define WPA_TRACE_LEN 16
+
+#ifdef WPA_TRACE
+#include <execinfo.h>
+
+#include "list.h"
+
+#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num;
+
+struct wpa_trace_ref {
+       struct dl_list list;
+       const void *addr;
+       WPA_TRACE_INFO
+};
+#define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name
+
+#define wpa_trace_dump(title, ptr) \
+       wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num)
+void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num);
+#define wpa_trace_record(ptr) \
+       (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN)
+void wpa_trace_show(const char *title);
+#define wpa_trace_add_ref(ptr, name, addr) \
+       wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr))
+void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
+#define wpa_trace_remove_ref(ptr, name, addr)  \
+       do { \
+               if ((addr)) \
+                       dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
+       } while (0)
+void wpa_trace_check_ref(const void *addr);
+
+#else /* WPA_TRACE */
+
+#define WPA_TRACE_INFO
+#define WPA_TRACE_REF(n)
+#define wpa_trace_dump(title, ptr) do { } while (0)
+#define wpa_trace_record(ptr) do { } while (0)
+#define wpa_trace_show(title) do { } while (0)
+#define wpa_trace_add_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_remove_ref(ptr, name, addr) do { } while (0)
+#define wpa_trace_check_ref(addr) do { } while (0)
+
+#endif /* WPA_TRACE */
+
+
+#ifdef WPA_TRACE_BFD
+
+void wpa_trace_dump_funcname(const char *title, void *pc);
+
+#else /* WPA_TRACE_BFD */
+
+#define wpa_trace_dump_funcname(title, pc) do { } while (0)
+
+#endif /* WPA_TRACE_BFD */
+
+#endif /* TRACE_H */
diff --git a/src/utils/uuid.c b/src/utils/uuid.c
new file mode 100644 (file)
index 0000000..d8cc267
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * 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 "common.h"
+#include "uuid.h"
+
+int uuid_str2bin(const char *str, u8 *bin)
+{
+       const char *pos;
+       u8 *opos;
+
+       pos = str;
+       opos = bin;
+
+       if (hexstr2bin(pos, opos, 4))
+               return -1;
+       pos += 8;
+       opos += 4;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+               return -1;
+       pos += 4;
+       opos += 2;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+               return -1;
+       pos += 4;
+       opos += 2;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 2))
+               return -1;
+       pos += 4;
+       opos += 2;
+
+       if (*pos++ != '-' || hexstr2bin(pos, opos, 6))
+               return -1;
+
+       return 0;
+}
+
+
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
+{
+       int len;
+       len = os_snprintf(str, max_len, "%02x%02x%02x%02x-%02x%02x-%02x%02x-"
+                         "%02x%02x-%02x%02x%02x%02x%02x%02x",
+                         bin[0], bin[1], bin[2], bin[3],
+                         bin[4], bin[5], bin[6], bin[7],
+                         bin[8], bin[9], bin[10], bin[11],
+                         bin[12], bin[13], bin[14], bin[15]);
+       if (len < 0 || (size_t) len >= max_len)
+               return -1;
+       return 0;
+}
+
+
+int is_nil_uuid(const u8 *uuid)
+{
+       int i;
+       for (i = 0; i < UUID_LEN; i++)
+               if (uuid[i])
+                       return 0;
+       return 1;
+}
diff --git a/src/utils/uuid.h b/src/utils/uuid.h
new file mode 100644 (file)
index 0000000..0759165
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Universally Unique IDentifier (UUID)
+ * 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 UUID_H
+#define UUID_H
+
+#define UUID_LEN 16
+
+int uuid_str2bin(const char *str, u8 *bin);
+int uuid_bin2str(const u8 *bin, char *str, size_t max_len);
+int is_nil_uuid(const u8 *uuid);
+
+#endif /* UUID_H */
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
new file mode 100644 (file)
index 0000000..6f6fc69
--- /dev/null
@@ -0,0 +1,400 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+#ifdef CONFIG_DEBUG_SYSLOG
+#include <syslog.h>
+
+static int wpa_debug_syslog = 0;
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+#ifdef CONFIG_DEBUG_FILE
+static FILE *out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+int wpa_debug_level = MSG_INFO;
+int wpa_debug_show_keys = 0;
+int wpa_debug_timestamp = 0;
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+
+void wpa_debug_print_timestamp(void)
+{
+       struct os_time tv;
+
+       if (!wpa_debug_timestamp)
+               return;
+
+       os_get_time(&tv);
+#ifdef CONFIG_DEBUG_FILE
+       if (out_file) {
+               fprintf(out_file, "%ld.%06u: ", (long) tv.sec,
+                       (unsigned int) tv.usec);
+       } else
+#endif /* CONFIG_DEBUG_FILE */
+       printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec);
+}
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+void wpa_debug_open_syslog(void)
+{
+       openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_DAEMON);
+       wpa_debug_syslog++;
+}
+
+
+void wpa_debug_close_syslog(void)
+{
+       if (wpa_debug_syslog)
+               closelog();
+}
+
+
+static int syslog_priority(int level)
+{
+       switch (level) {
+       case MSG_MSGDUMP:
+       case MSG_DEBUG:
+               return LOG_DEBUG;
+       case MSG_INFO:
+               return LOG_NOTICE;
+       case MSG_WARNING:
+               return LOG_WARNING;
+       case MSG_ERROR:
+               return LOG_ERR;
+       }
+       return LOG_INFO;
+}
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+{
+       va_list ap;
+
+       va_start(ap, fmt);
+       if (level >= wpa_debug_level) {
+#ifdef CONFIG_DEBUG_SYSLOG
+               if (wpa_debug_syslog) {
+                       vsyslog(syslog_priority(level), fmt, ap);
+               } else {
+#endif /* CONFIG_DEBUG_SYSLOG */
+               wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+               if (out_file) {
+                       vfprintf(out_file, fmt, ap);
+                       fprintf(out_file, "\n");
+               } else {
+#endif /* CONFIG_DEBUG_FILE */
+               vprintf(fmt, ap);
+               printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+               }
+#endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_SYSLOG
+               }
+#endif /* CONFIG_DEBUG_SYSLOG */
+       }
+       va_end(ap);
+}
+
+
+static void _wpa_hexdump(int level, const char *title, const u8 *buf,
+                        size_t len, int show)
+{
+       size_t i;
+       if (level < wpa_debug_level)
+               return;
+       wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+       if (out_file) {
+               fprintf(out_file, "%s - hexdump(len=%lu):",
+                       title, (unsigned long) len);
+               if (buf == NULL) {
+                       fprintf(out_file, " [NULL]");
+               } else if (show) {
+                       for (i = 0; i < len; i++)
+                               fprintf(out_file, " %02x", buf[i]);
+               } else {
+                       fprintf(out_file, " [REMOVED]");
+               }
+               fprintf(out_file, "\n");
+       } else {
+#endif /* CONFIG_DEBUG_FILE */
+       printf("%s - hexdump(len=%lu):", title, (unsigned long) len);
+       if (buf == NULL) {
+               printf(" [NULL]");
+       } else if (show) {
+               for (i = 0; i < len; i++)
+                       printf(" %02x", buf[i]);
+       } else {
+               printf(" [REMOVED]");
+       }
+       printf("\n");
+#ifdef CONFIG_DEBUG_FILE
+       }
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
+{
+       _wpa_hexdump(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
+{
+       _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+                              size_t len, int show)
+{
+       size_t i, llen;
+       const u8 *pos = buf;
+       const size_t line_len = 16;
+
+       if (level < wpa_debug_level)
+               return;
+       wpa_debug_print_timestamp();
+#ifdef CONFIG_DEBUG_FILE
+       if (out_file) {
+               if (!show) {
+                       fprintf(out_file,
+                               "%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+                               title, (unsigned long) len);
+                       return;
+               }
+               if (buf == NULL) {
+                       fprintf(out_file,
+                               "%s - hexdump_ascii(len=%lu): [NULL]\n",
+                               title, (unsigned long) len);
+                       return;
+               }
+               fprintf(out_file, "%s - hexdump_ascii(len=%lu):\n",
+                       title, (unsigned long) len);
+               while (len) {
+                       llen = len > line_len ? line_len : len;
+                       fprintf(out_file, "    ");
+                       for (i = 0; i < llen; i++)
+                               fprintf(out_file, " %02x", pos[i]);
+                       for (i = llen; i < line_len; i++)
+                               fprintf(out_file, "   ");
+                       fprintf(out_file, "   ");
+                       for (i = 0; i < llen; i++) {
+                               if (isprint(pos[i]))
+                                       fprintf(out_file, "%c", pos[i]);
+                               else
+                                       fprintf(out_file, "_");
+                       }
+                       for (i = llen; i < line_len; i++)
+                               fprintf(out_file, " ");
+                       fprintf(out_file, "\n");
+                       pos += llen;
+                       len -= llen;
+               }
+       } else {
+#endif /* CONFIG_DEBUG_FILE */
+       if (!show) {
+               printf("%s - hexdump_ascii(len=%lu): [REMOVED]\n",
+                      title, (unsigned long) len);
+               return;
+       }
+       if (buf == NULL) {
+               printf("%s - hexdump_ascii(len=%lu): [NULL]\n",
+                      title, (unsigned long) len);
+               return;
+       }
+       printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len);
+       while (len) {
+               llen = len > line_len ? line_len : len;
+               printf("    ");
+               for (i = 0; i < llen; i++)
+                       printf(" %02x", pos[i]);
+               for (i = llen; i < line_len; i++)
+                       printf("   ");
+               printf("   ");
+               for (i = 0; i < llen; i++) {
+                       if (isprint(pos[i]))
+                               printf("%c", pos[i]);
+                       else
+                               printf("_");
+               }
+               for (i = llen; i < line_len; i++)
+                       printf(" ");
+               printf("\n");
+               pos += llen;
+               len -= llen;
+       }
+#ifdef CONFIG_DEBUG_FILE
+       }
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+
+void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
+{
+       _wpa_hexdump_ascii(level, title, buf, len, 1);
+}
+
+
+void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+                          size_t len)
+{
+       _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
+}
+
+
+int wpa_debug_open_file(const char *path)
+{
+#ifdef CONFIG_DEBUG_FILE
+       if (!path)
+               return 0;
+       out_file = fopen(path, "a");
+       if (out_file == NULL) {
+               wpa_printf(MSG_ERROR, "wpa_debug_open_file: Failed to open "
+                          "output file, using standard output");
+               return -1;
+       }
+#ifndef _WIN32
+       setvbuf(out_file, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+#endif /* CONFIG_DEBUG_FILE */
+       return 0;
+}
+
+
+void wpa_debug_close_file(void)
+{
+#ifdef CONFIG_DEBUG_FILE
+       if (!out_file)
+               return;
+       fclose(out_file);
+       out_file = NULL;
+#endif /* CONFIG_DEBUG_FILE */
+}
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifndef CONFIG_NO_WPA_MSG
+static wpa_msg_cb_func wpa_msg_cb = NULL;
+
+void wpa_msg_register_cb(wpa_msg_cb_func func)
+{
+       wpa_msg_cb = func;
+}
+
+
+void wpa_msg(void *ctx, int level, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       const int buflen = 2048;
+       int len;
+
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
+                          "buffer");
+               return;
+       }
+       va_start(ap, fmt);
+       len = vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+       wpa_printf(level, "%s", buf);
+       if (wpa_msg_cb)
+               wpa_msg_cb(ctx, level, buf, len);
+       os_free(buf);
+}
+
+
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       const int buflen = 2048;
+       int len;
+
+       if (!wpa_msg_cb)
+               return;
+
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
+                          "message buffer");
+               return;
+       }
+       va_start(ap, fmt);
+       len = vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+       wpa_msg_cb(ctx, level, buf, len);
+       os_free(buf);
+}
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifndef CONFIG_NO_HOSTAPD_LOGGER
+static hostapd_logger_cb_func hostapd_logger_cb = NULL;
+
+void hostapd_logger_register_cb(hostapd_logger_cb_func func)
+{
+       hostapd_logger_cb = func;
+}
+
+
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+                   const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       const int buflen = 2048;
+       int len;
+
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
+                          "message buffer");
+               return;
+       }
+       va_start(ap, fmt);
+       len = vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+       if (hostapd_logger_cb)
+               hostapd_logger_cb(ctx, addr, module, level, buf, len);
+       else if (addr)
+               wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s",
+                          MAC2STR(addr), buf);
+       else
+               wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf);
+       os_free(buf);
+}
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
diff --git a/src/utils/wpa_debug.h b/src/utils/wpa_debug.h
new file mode 100644 (file)
index 0000000..6e5e79e
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * wpa_supplicant/hostapd / Debug prints
+ * 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 WPA_DEBUG_H
+#define WPA_DEBUG_H
+
+#include "wpabuf.h"
+
+/* Debugging function - conditional printf and hex dump. Driver wrappers can
+ * use these for debugging purposes. */
+
+enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR };
+
+#ifdef CONFIG_NO_STDOUT_DEBUG
+
+#define wpa_debug_print_timestamp() do { } while (0)
+#define wpa_printf(args...) do { } while (0)
+#define wpa_hexdump(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf(l,t,b) do { } while (0)
+#define wpa_hexdump_key(l,t,b,le) do { } while (0)
+#define wpa_hexdump_buf_key(l,t,b) do { } while (0)
+#define wpa_hexdump_ascii(l,t,b,le) do { } while (0)
+#define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
+#define wpa_debug_open_file(p) do { } while (0)
+#define wpa_debug_close_file() do { } while (0)
+
+#else /* CONFIG_NO_STDOUT_DEBUG */
+
+int wpa_debug_open_file(const char *path);
+void wpa_debug_close_file(void);
+
+/**
+ * wpa_debug_printf_timestamp - Print timestamp for debug output
+ *
+ * This function prints a timestamp in seconds_from_1970.microsoconds
+ * format if debug output has been configured to include timestamps in debug
+ * messages.
+ */
+void wpa_debug_print_timestamp(void);
+
+/**
+ * wpa_printf - conditional printf
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_printf(int level, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+/**
+ * wpa_hexdump - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump.
+ */
+void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len);
+
+static inline void wpa_hexdump_buf(int level, const char *title,
+                                  const struct wpabuf *buf)
+{
+       wpa_hexdump(level, title, wpabuf_head(buf), wpabuf_len(buf));
+}
+
+/**
+ * wpa_hexdump_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump. This works
+ * like wpa_hexdump(), but by default, does not include secret keys (passwords,
+ * etc.) in debug output.
+ */
+void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len);
+
+static inline void wpa_hexdump_buf_key(int level, const char *title,
+                                      const struct wpabuf *buf)
+{
+       wpa_hexdump_key(level, title, wpabuf_head(buf), wpabuf_len(buf));
+}
+
+/**
+ * wpa_hexdump_ascii - conditional hex dump
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown.
+ */
+void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+                      size_t len);
+
+/**
+ * wpa_hexdump_ascii_key - conditional hex dump, hide keys
+ * @level: priority level (MSG_*) of the message
+ * @title: title of for the message
+ * @buf: data buffer to be dumped
+ * @len: length of the buf
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. The contents of buf is printed out has hex dump with both
+ * the hex numbers and ASCII characters (for printable range) are shown. 16
+ * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
+ * default, does not include secret keys (passwords, etc.) in debug output.
+ */
+void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+                          size_t len);
+
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+#ifdef CONFIG_NO_WPA_MSG
+#define wpa_msg(args...) do { } while (0)
+#define wpa_msg_ctrl(args...) do { } while (0)
+#define wpa_msg_register_cb(f) do { } while (0)
+#else /* CONFIG_NO_WPA_MSG */
+/**
+ * wpa_msg - Conditional printf for default target and ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *     with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages. The
+ * output may be directed to stdout, stderr, and/or syslog based on
+ * configuration. This function is like wpa_printf(), but it also sends the
+ * same message to all attached ctrl_iface monitors.
+ *
+ * Note: New line '\n' is added to the end of the text when printing to stdout.
+ */
+void wpa_msg(void *ctx, int level, const char *fmt, ...) PRINTF_FORMAT(3, 4);
+
+/**
+ * wpa_msg_ctrl - Conditional printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *     with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg(), but it sends the output only to the
+ * attached ctrl_iface monitors. In other words, it can be used for frequent
+ * events that do not need to be sent to syslog.
+ */
+void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+typedef void (*wpa_msg_cb_func)(void *ctx, int level, const char *txt,
+                               size_t len);
+
+/**
+ * wpa_msg_register_cb - Register callback function for wpa_msg() messages
+ * @func: Callback function (%NULL to unregister)
+ */
+void wpa_msg_register_cb(wpa_msg_cb_func func);
+#endif /* CONFIG_NO_WPA_MSG */
+
+
+#ifdef CONFIG_NO_HOSTAPD_LOGGER
+#define hostapd_logger(args...) do { } while (0)
+#define hostapd_logger_register_cb(f) do { } while (0)
+#else /* CONFIG_NO_HOSTAPD_LOGGER */
+void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
+                   const char *fmt, ...) PRINTF_FORMAT(5, 6);
+
+typedef void (*hostapd_logger_cb_func)(void *ctx, const u8 *addr,
+                                      unsigned int module, int level,
+                                      const char *txt, size_t len);
+
+/**
+ * hostapd_logger_register_cb - Register callback function for hostapd_logger()
+ * @func: Callback function (%NULL to unregister)
+ */
+void hostapd_logger_register_cb(hostapd_logger_cb_func func);
+#endif /* CONFIG_NO_HOSTAPD_LOGGER */
+
+#define HOSTAPD_MODULE_IEEE80211       0x00000001
+#define HOSTAPD_MODULE_IEEE8021X       0x00000002
+#define HOSTAPD_MODULE_RADIUS          0x00000004
+#define HOSTAPD_MODULE_WPA             0x00000008
+#define HOSTAPD_MODULE_DRIVER          0x00000010
+#define HOSTAPD_MODULE_IAPP            0x00000020
+#define HOSTAPD_MODULE_MLME            0x00000040
+
+enum hostapd_logger_level {
+       HOSTAPD_LEVEL_DEBUG_VERBOSE = 0,
+       HOSTAPD_LEVEL_DEBUG = 1,
+       HOSTAPD_LEVEL_INFO = 2,
+       HOSTAPD_LEVEL_NOTICE = 3,
+       HOSTAPD_LEVEL_WARNING = 4
+};
+
+
+#ifdef CONFIG_DEBUG_SYSLOG
+
+void wpa_debug_open_syslog(void);
+void wpa_debug_close_syslog(void);
+
+#else /* CONFIG_DEBUG_SYSLOG */
+
+static inline void wpa_debug_open_syslog(void)
+{
+}
+
+static inline void wpa_debug_close_syslog(void)
+{
+}
+
+#endif /* CONFIG_DEBUG_SYSLOG */
+
+
+#ifdef EAPOL_TEST
+#define WPA_ASSERT(a)                                                 \
+       do {                                                           \
+               if (!(a)) {                                            \
+                       printf("WPA_ASSERT FAILED '" #a "' "           \
+                              "%s %s:%d\n",                           \
+                              __FUNCTION__, __FILE__, __LINE__);      \
+                       exit(1);                                       \
+               }                                                      \
+       } while (0)
+#else
+#define WPA_ASSERT(a) do { } while (0)
+#endif
+
+#endif /* WPA_DEBUG_H */
diff --git a/src/utils/wpabuf.c b/src/utils/wpabuf.c
new file mode 100644 (file)
index 0000000..eda779e
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-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 "common.h"
+#include "trace.h"
+#include "wpabuf.h"
+
+#ifdef WPA_TRACE
+#define WPABUF_MAGIC 0x51a974e3
+
+struct wpabuf_trace {
+       unsigned int magic;
+};
+
+static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf)
+{
+       return (struct wpabuf_trace *)
+               ((const u8 *) buf - sizeof(struct wpabuf_trace));
+}
+#endif /* WPA_TRACE */
+
+
+static void wpabuf_overflow(const struct wpabuf *buf, size_t len)
+{
+#ifdef WPA_TRACE
+       struct wpabuf_trace *trace = wpabuf_get_trace(buf);
+       if (trace->magic != WPABUF_MAGIC) {
+               wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+                          trace->magic);
+       }
+#endif /* WPA_TRACE */
+       wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu",
+                  buf, (unsigned long) buf->size, (unsigned long) buf->used,
+                  (unsigned long) len);
+       wpa_trace_show("wpabuf overflow");
+       abort();
+}
+
+
+int wpabuf_resize(struct wpabuf **_buf, size_t add_len)
+{
+       struct wpabuf *buf = *_buf;
+#ifdef WPA_TRACE
+       struct wpabuf_trace *trace;
+#endif /* WPA_TRACE */
+
+       if (buf == NULL) {
+               *_buf = wpabuf_alloc(add_len);
+               return *_buf == NULL ? -1 : 0;
+       }
+
+#ifdef WPA_TRACE
+       trace = wpabuf_get_trace(buf);
+       if (trace->magic != WPABUF_MAGIC) {
+               wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x",
+                          trace->magic);
+               wpa_trace_show("wpabuf_resize invalid magic");
+               abort();
+       }
+#endif /* WPA_TRACE */
+
+       if (buf->used + add_len > buf->size) {
+               unsigned char *nbuf;
+               if (buf->ext_data) {
+                       nbuf = os_realloc(buf->ext_data, buf->used + add_len);
+                       if (nbuf == NULL)
+                               return -1;
+                       os_memset(nbuf + buf->used, 0, add_len);
+                       buf->ext_data = nbuf;
+               } else {
+#ifdef WPA_TRACE
+                       nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) +
+                                         sizeof(struct wpabuf) +
+                                         buf->used + add_len);
+                       if (nbuf == NULL)
+                               return -1;
+                       trace = (struct wpabuf_trace *) nbuf;
+                       buf = (struct wpabuf *) (trace + 1);
+                       os_memset(nbuf + sizeof(struct wpabuf_trace) +
+                                 sizeof(struct wpabuf) + buf->used, 0,
+                                 add_len);
+#else /* WPA_TRACE */
+                       nbuf = os_realloc(buf, sizeof(struct wpabuf) +
+                                         buf->used + add_len);
+                       if (nbuf == NULL)
+                               return -1;
+                       buf = (struct wpabuf *) nbuf;
+                       os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0,
+                                 add_len);
+#endif /* WPA_TRACE */
+                       *_buf = buf;
+               }
+               buf->size = buf->used + add_len;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpabuf_alloc - Allocate a wpabuf of the given size
+ * @len: Length for the allocated buffer
+ * Returns: Buffer to the allocated wpabuf or %NULL on failure
+ */
+struct wpabuf * wpabuf_alloc(size_t len)
+{
+#ifdef WPA_TRACE
+       struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+                                              sizeof(struct wpabuf) + len);
+       struct wpabuf *buf;
+       if (trace == NULL)
+               return NULL;
+       trace->magic = WPABUF_MAGIC;
+       buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+       struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len);
+       if (buf == NULL)
+               return NULL;
+#endif /* WPA_TRACE */
+
+       buf->size = len;
+       return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len)
+{
+#ifdef WPA_TRACE
+       struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) +
+                                              sizeof(struct wpabuf));
+       struct wpabuf *buf;
+       if (trace == NULL)
+               return NULL;
+       trace->magic = WPABUF_MAGIC;
+       buf = (struct wpabuf *) (trace + 1);
+#else /* WPA_TRACE */
+       struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf));
+       if (buf == NULL)
+               return NULL;
+#endif /* WPA_TRACE */
+
+       buf->size = len;
+       buf->used = len;
+       buf->ext_data = data;
+
+       return buf;
+}
+
+
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len)
+{
+       struct wpabuf *buf = wpabuf_alloc(len);
+       if (buf)
+               wpabuf_put_data(buf, data, len);
+       return buf;
+}
+
+
+struct wpabuf * wpabuf_dup(const struct wpabuf *src)
+{
+       struct wpabuf *buf = wpabuf_alloc(wpabuf_len(src));
+       if (buf)
+               wpabuf_put_data(buf, wpabuf_head(src), wpabuf_len(src));
+       return buf;
+}
+
+
+/**
+ * wpabuf_free - Free a wpabuf
+ * @buf: wpabuf buffer
+ */
+void wpabuf_free(struct wpabuf *buf)
+{
+#ifdef WPA_TRACE
+       struct wpabuf_trace *trace;
+       if (buf == NULL)
+               return;
+       trace = wpabuf_get_trace(buf);
+       if (trace->magic != WPABUF_MAGIC) {
+               wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x",
+                          trace->magic);
+               wpa_trace_show("wpabuf_free magic mismatch");
+               abort();
+       }
+       os_free(buf->ext_data);
+       os_free(trace);
+#else /* WPA_TRACE */
+       if (buf == NULL)
+               return;
+       os_free(buf->ext_data);
+       os_free(buf);
+#endif /* WPA_TRACE */
+}
+
+
+void * wpabuf_put(struct wpabuf *buf, size_t len)
+{
+       void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+       buf->used += len;
+       if (buf->used > buf->size) {
+               wpabuf_overflow(buf, len);
+       }
+       return tmp;
+}
+
+
+/**
+ * wpabuf_concat - Concatenate two buffers into a newly allocated one
+ * @a: First buffer
+ * @b: Second buffer
+ * Returns: wpabuf with concatenated a + b data or %NULL on failure
+ *
+ * Both buffers a and b will be freed regardless of the return value. Input
+ * buffers can be %NULL which is interpreted as an empty buffer.
+ */
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b)
+{
+       struct wpabuf *n = NULL;
+       size_t len = 0;
+
+       if (b == NULL)
+               return a;
+
+       if (a)
+               len += wpabuf_len(a);
+       if (b)
+               len += wpabuf_len(b);
+
+       n = wpabuf_alloc(len);
+       if (n) {
+               if (a)
+                       wpabuf_put_buf(n, a);
+               if (b)
+                       wpabuf_put_buf(n, b);
+       }
+
+       wpabuf_free(a);
+       wpabuf_free(b);
+
+       return n;
+}
+
+
+/**
+ * wpabuf_zeropad - Pad buffer with 0x00 octets (prefix) to specified length
+ * @buf: Buffer to be padded
+ * @len: Length for the padded buffer
+ * Returns: wpabuf padded to len octets or %NULL on failure
+ *
+ * If buf is longer than len octets or of same size, it will be returned as-is.
+ * Otherwise a new buffer is allocated and prefixed with 0x00 octets followed
+ * by the source data. The source buffer will be freed on error, i.e., caller
+ * will only be responsible on freeing the returned buffer. If buf is %NULL,
+ * %NULL will be returned.
+ */
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len)
+{
+       struct wpabuf *ret;
+       size_t blen;
+
+       if (buf == NULL)
+               return NULL;
+
+       blen = wpabuf_len(buf);
+       if (blen >= len)
+               return buf;
+
+       ret = wpabuf_alloc(len);
+       if (ret) {
+               os_memset(wpabuf_put(ret, len - blen), 0, len - blen);
+               wpabuf_put_buf(ret, buf);
+       }
+       wpabuf_free(buf);
+
+       return ret;
+}
+
+
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...)
+{
+       va_list ap;
+       void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
+       int res;
+
+       va_start(ap, fmt);
+       res = vsnprintf(tmp, buf->size - buf->used, fmt, ap);
+       va_end(ap);
+       if (res < 0 || (size_t) res >= buf->size - buf->used)
+               wpabuf_overflow(buf, res);
+       buf->used += res;
+}
diff --git a/src/utils/wpabuf.h b/src/utils/wpabuf.h
new file mode 100644 (file)
index 0000000..a150455
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Dynamic data buffer
+ * Copyright (c) 2007-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.
+ */
+
+#ifndef WPABUF_H
+#define WPABUF_H
+
+/*
+ * Internal data structure for wpabuf. Please do not touch this directly from
+ * elsewhere. This is only defined in header file to allow inline functions
+ * from this file to access data.
+ */
+struct wpabuf {
+       size_t size; /* total size of the allocated buffer */
+       size_t used; /* length of data in the buffer */
+       u8 *ext_data; /* pointer to external data; NULL if data follows
+                      * struct wpabuf */
+       /* optionally followed by the allocated buffer */
+};
+
+
+int wpabuf_resize(struct wpabuf **buf, size_t add_len);
+struct wpabuf * wpabuf_alloc(size_t len);
+struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
+struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
+struct wpabuf * wpabuf_dup(const struct wpabuf *src);
+void wpabuf_free(struct wpabuf *buf);
+void * wpabuf_put(struct wpabuf *buf, size_t len);
+struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
+struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
+void wpabuf_printf(struct wpabuf *buf, char *fmt, ...) PRINTF_FORMAT(2, 3);
+
+
+/**
+ * wpabuf_size - Get the currently allocated size of a wpabuf buffer
+ * @buf: wpabuf buffer
+ * Returns: Currently allocated size of the buffer
+ */
+static inline size_t wpabuf_size(const struct wpabuf *buf)
+{
+       return buf->size;
+}
+
+/**
+ * wpabuf_len - Get the current length of a wpabuf buffer data
+ * @buf: wpabuf buffer
+ * Returns: Currently used length of the buffer
+ */
+static inline size_t wpabuf_len(const struct wpabuf *buf)
+{
+       return buf->used;
+}
+
+/**
+ * wpabuf_tailroom - Get size of available tail room in the end of the buffer
+ * @buf: wpabuf buffer
+ * Returns: Tail room (in bytes) of available space in the end of the buffer
+ */
+static inline size_t wpabuf_tailroom(const struct wpabuf *buf)
+{
+       return buf->size - buf->used;
+}
+
+/**
+ * wpabuf_head - Get pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline const void * wpabuf_head(const struct wpabuf *buf)
+{
+       if (buf->ext_data)
+               return buf->ext_data;
+       return buf + 1;
+}
+
+static inline const u8 * wpabuf_head_u8(const struct wpabuf *buf)
+{
+       return wpabuf_head(buf);
+}
+
+/**
+ * wpabuf_mhead - Get modifiable pointer to the head of the buffer data
+ * @buf: wpabuf buffer
+ * Returns: Pointer to the head of the buffer data
+ */
+static inline void * wpabuf_mhead(struct wpabuf *buf)
+{
+       if (buf->ext_data)
+               return buf->ext_data;
+       return buf + 1;
+}
+
+static inline u8 * wpabuf_mhead_u8(struct wpabuf *buf)
+{
+       return wpabuf_mhead(buf);
+}
+
+static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data)
+{
+       u8 *pos = wpabuf_put(buf, 1);
+       *pos = data;
+}
+
+static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data)
+{
+       u8 *pos = wpabuf_put(buf, 2);
+       WPA_PUT_LE16(pos, data);
+}
+
+static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data)
+{
+       u8 *pos = wpabuf_put(buf, 2);
+       WPA_PUT_BE16(pos, data);
+}
+
+static inline void wpabuf_put_be24(struct wpabuf *buf, u32 data)
+{
+       u8 *pos = wpabuf_put(buf, 3);
+       WPA_PUT_BE24(pos, data);
+}
+
+static inline void wpabuf_put_be32(struct wpabuf *buf, u32 data)
+{
+       u8 *pos = wpabuf_put(buf, 4);
+       WPA_PUT_BE32(pos, data);
+}
+
+static inline void wpabuf_put_data(struct wpabuf *buf, const void *data,
+                                  size_t len)
+{
+       if (data)
+               os_memcpy(wpabuf_put(buf, len), data, len);
+}
+
+static inline void wpabuf_put_buf(struct wpabuf *dst,
+                                 const struct wpabuf *src)
+{
+       wpabuf_put_data(dst, wpabuf_head(src), wpabuf_len(src));
+}
+
+static inline void wpabuf_set(struct wpabuf *buf, const void *data, size_t len)
+{
+       buf->ext_data = (u8 *) data;
+       buf->size = buf->used = len;
+}
+
+static inline void wpabuf_put_str(struct wpabuf *dst, const char *str)
+{
+       wpabuf_put_data(dst, str, os_strlen(str));
+}
+
+#endif /* WPABUF_H */
diff --git a/src/wps/Makefile b/src/wps/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/wps/http.h b/src/wps/http.h
new file mode 100644 (file)
index 0000000..2fee3a8
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * HTTP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef HTTP_H
+#define HTTP_H
+
+enum http_reply_code {
+       HTTP_OK = 200,
+       HTTP_BAD_REQUEST = 400,
+       UPNP_INVALID_ACTION = 401,
+       UPNP_INVALID_ARGS = 402,
+       HTTP_NOT_FOUND = 404,
+       HTTP_PRECONDITION_FAILED = 412,
+       HTTP_INTERNAL_SERVER_ERROR = 500,
+       HTTP_UNIMPLEMENTED = 501,
+       UPNP_ACTION_FAILED = 501,
+       UPNP_ARG_VALUE_INVALID = 600,
+       UPNP_ARG_VALUE_OUT_OF_RANGE = 601,
+       UPNP_OUT_OF_MEMORY = 603
+};
+
+#endif /* HTTP_H */
diff --git a/src/wps/http_client.c b/src/wps/http_client.c
new file mode 100644 (file)
index 0000000..fea2a04
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * http_client - HTTP client
+ * Copyright (c) 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 <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+
+
+#define HTTP_CLIENT_TIMEOUT 30
+
+
+struct http_client {
+       struct sockaddr_in dst;
+       int sd;
+       struct wpabuf *req;
+       size_t req_pos;
+       size_t max_response;
+
+       void (*cb)(void *ctx, struct http_client *c,
+                  enum http_client_event event);
+       void *cb_ctx;
+       struct httpread *hread;
+       struct wpabuf body;
+};
+
+
+static void http_client_timeout(void *eloop_data, void *user_ctx)
+{
+       struct http_client *c = eloop_data;
+       wpa_printf(MSG_DEBUG, "HTTP: Timeout");
+       c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
+}
+
+
+static void http_client_got_response(struct httpread *handle, void *cookie,
+                                    enum httpread_event e)
+{
+       struct http_client *c = cookie;
+
+       eloop_cancel_timeout(http_client_timeout, c, NULL);
+       switch (e) {
+       case HTTPREAD_EVENT_FILE_READY:
+               if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY)
+               {
+                       int reply_code = httpread_reply_code_get(c->hread);
+                       if (reply_code == 200 /* OK */) {
+                               wpa_printf(MSG_DEBUG, "HTTP: Response OK from "
+                                          "%s:%d",
+                                          inet_ntoa(c->dst.sin_addr),
+                                          ntohs(c->dst.sin_port));
+                               c->cb(c->cb_ctx, c, HTTP_CLIENT_OK);
+                       } else {
+                               wpa_printf(MSG_DEBUG, "HTTP: Error %d from "
+                                          "%s:%d", reply_code,
+                                          inet_ntoa(c->dst.sin_addr),
+                                          ntohs(c->dst.sin_port));
+                               c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
+                       }
+               } else
+                       c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY);
+               break;
+       case HTTPREAD_EVENT_TIMEOUT:
+               c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT);
+               break;
+       case HTTPREAD_EVENT_ERROR:
+               c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+               break;
+       }
+}
+
+
+static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct http_client *c = eloop_ctx;
+       int res;
+
+       wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu "
+                  "bytes remaining)",
+                  inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port),
+                  (unsigned long) wpabuf_len(c->req),
+                  (unsigned long) wpabuf_len(c->req) - c->req_pos);
+
+       res = send(c->sd, wpabuf_head(c->req) + c->req_pos,
+                  wpabuf_len(c->req) - c->req_pos, 0);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s",
+                          strerror(errno));
+               eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+               c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+               return;
+       }
+
+       if ((size_t) res < wpabuf_len(c->req) - c->req_pos) {
+               wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes "
+                          "remaining",
+                          res, (unsigned long) wpabuf_len(c->req),
+                          (unsigned long) wpabuf_len(c->req) - c->req_pos -
+                          res);
+               c->req_pos += res;
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d",
+                  inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port));
+       eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+       wpabuf_free(c->req);
+       c->req = NULL;
+
+       c->hread = httpread_create(c->sd, http_client_got_response, c,
+                                  c->max_response, HTTP_CLIENT_TIMEOUT);
+       if (c->hread == NULL) {
+               c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED);
+               return;
+       }
+}
+
+
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+                                     struct wpabuf *req, size_t max_response,
+                                     void (*cb)(void *ctx,
+                                                struct http_client *c,
+                                                enum http_client_event event),
+                                     void *cb_ctx)
+{
+       struct http_client *c;
+
+       c = os_zalloc(sizeof(*c));
+       if (c == NULL)
+               return NULL;
+       c->sd = -1;
+       c->dst = *dst;
+       c->max_response = max_response;
+       c->cb = cb;
+       c->cb_ctx = cb_ctx;
+
+       c->sd = socket(AF_INET, SOCK_STREAM, 0);
+       if (c->sd < 0) {
+               http_client_free(c);
+               return NULL;
+       }
+
+       if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) {
+               wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s",
+                          strerror(errno));
+               http_client_free(c);
+               return NULL;
+       }
+
+       if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) {
+               if (errno != EINPROGRESS) {
+                       wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s",
+                                  strerror(errno));
+                       http_client_free(c);
+                       return NULL;
+               }
+
+               /*
+                * Continue connecting in the background; eloop will call us
+                * once the connection is ready (or failed).
+                */
+       }
+
+       if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready,
+                               c, NULL)) {
+               http_client_free(c);
+               return NULL;
+       }
+
+       if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout,
+                                  c, NULL)) {
+               http_client_free(c);
+               return NULL;
+       }
+
+       c->req = req;
+
+       return c;
+}
+
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+                            char **ret_path)
+{
+       char *u, *addr, *port, *path;
+
+       u = os_strdup(url);
+       if (u == NULL)
+               return NULL;
+
+       os_memset(dst, 0, sizeof(*dst));
+       dst->sin_family = AF_INET;
+       addr = u + 7;
+       path = os_strchr(addr, '/');
+       port = os_strchr(addr, ':');
+       if (path == NULL) {
+               path = "/";
+       } else {
+               *path = '\0'; /* temporary nul termination for address */
+               if (port > path)
+                       port = NULL;
+       }
+       if (port)
+               *port++ = '\0';
+
+       if (inet_aton(addr, &dst->sin_addr) == 0) {
+               /* TODO: name lookup */
+               wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' "
+                          "(addr='%s' port='%s')",
+                          url, addr, port);
+               os_free(u);
+               return NULL;
+       }
+
+       if (port)
+               dst->sin_port = htons(atoi(port));
+       else
+               dst->sin_port = htons(80);
+
+       if (*path == '\0') {
+               /* remove temporary nul termination for address */
+               *path = '/';
+       }
+
+       *ret_path = path;
+
+       return u;
+}
+
+
+struct http_client * http_client_url(const char *url,
+                                    struct wpabuf *req, size_t max_response,
+                                    void (*cb)(void *ctx,
+                                               struct http_client *c,
+                                               enum http_client_event event),
+                                    void *cb_ctx)
+{
+       struct sockaddr_in dst;
+       struct http_client *c;
+       char *u, *path;
+       struct wpabuf *req_buf = NULL;
+
+       if (os_strncmp(url, "http://", 7) != 0)
+               return NULL;
+       u = http_client_url_parse(url, &dst, &path);
+       if (u == NULL)
+               return NULL;
+
+       if (req == NULL) {
+               req_buf = wpabuf_alloc(os_strlen(url) + 1000);
+               if (req_buf == NULL) {
+                       os_free(u);
+                       return NULL;
+               }
+               req = req_buf;
+               wpabuf_printf(req,
+                             "GET %s HTTP/1.1\r\n"
+                             "Cache-Control: no-cache\r\n"
+                             "Pragma: no-cache\r\n"
+                             "Accept: text/xml, application/xml\r\n"
+                             "User-Agent: wpa_supplicant\r\n"
+                             "Host: %s:%d\r\n"
+                             "\r\n",
+                             path, inet_ntoa(dst.sin_addr),
+                             ntohs(dst.sin_port));
+       }
+       os_free(u);
+
+       c = http_client_addr(&dst, req, max_response, cb, cb_ctx);
+       if (c == NULL) {
+               wpabuf_free(req_buf);
+               return NULL;
+       }
+
+       return c;
+}
+
+
+void http_client_free(struct http_client *c)
+{
+       if (c == NULL)
+               return;
+       httpread_destroy(c->hread);
+       wpabuf_free(c->req);
+       if (c->sd >= 0) {
+               eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE);
+               close(c->sd);
+       }
+       eloop_cancel_timeout(http_client_timeout, c, NULL);
+       os_free(c);
+}
+
+
+struct wpabuf * http_client_get_body(struct http_client *c)
+{
+       if (c->hread == NULL)
+               return NULL;
+       wpabuf_set(&c->body, httpread_data_get(c->hread),
+                  httpread_length_get(c->hread));
+       return &c->body;
+}
+
+
+char * http_client_get_hdr_line(struct http_client *c, const char *tag)
+{
+       if (c->hread == NULL)
+               return NULL;
+       return httpread_hdr_line_get(c->hread, tag);
+}
+
+
+char * http_link_update(char *url, const char *base)
+{
+       char *n;
+       size_t len;
+       const char *pos;
+
+       /* RFC 2396, Chapter 5.2 */
+       /* TODO: consider adding all cases described in RFC 2396 */
+
+       if (url == NULL)
+               return NULL;
+
+       if (os_strncmp(url, "http://", 7) == 0)
+               return url; /* absolute link */
+
+       if (os_strncmp(base, "http://", 7) != 0)
+               return url; /* unable to handle base URL */
+
+       len = os_strlen(url) + 1 + os_strlen(base) + 1;
+       n = os_malloc(len);
+       if (n == NULL)
+               return url; /* failed */
+
+       if (url[0] == '/') {
+               pos = os_strchr(base + 7, '/');
+               if (pos == NULL) {
+                       os_snprintf(n, len, "%s%s", base, url);
+               } else {
+                       os_memcpy(n, base, pos - base);
+                       os_memcpy(n + (pos - base), url, os_strlen(url) + 1);
+               }
+       } else {
+               pos = os_strrchr(base + 7, '/');
+               if (pos == NULL) {
+                       os_snprintf(n, len, "%s/%s", base, url);
+               } else {
+                       os_memcpy(n, base, pos - base + 1);
+                       os_memcpy(n + (pos - base) + 1, url, os_strlen(url) +
+                                 1);
+               }
+       }
+
+       os_free(url);
+
+       return n;
+}
diff --git a/src/wps/http_client.h b/src/wps/http_client.h
new file mode 100644 (file)
index 0000000..924d6ab
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * http_client - HTTP client
+ * Copyright (c) 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.
+ */
+
+#ifndef HTTP_CLIENT_H
+#define HTTP_CLIENT_H
+
+struct http_client;
+
+enum http_client_event {
+       HTTP_CLIENT_FAILED,
+       HTTP_CLIENT_TIMEOUT,
+       HTTP_CLIENT_OK,
+       HTTP_CLIENT_INVALID_REPLY,
+};
+
+char * http_client_url_parse(const char *url, struct sockaddr_in *dst,
+                            char **path);
+struct http_client * http_client_addr(struct sockaddr_in *dst,
+                                     struct wpabuf *req, size_t max_response,
+                                     void (*cb)(void *ctx,
+                                                struct http_client *c,
+                                                enum http_client_event event),
+                                     void *cb_ctx);
+struct http_client * http_client_url(const char *url,
+                                    struct wpabuf *req, size_t max_response,
+                                    void (*cb)(void *ctx,
+                                               struct http_client *c,
+                                               enum http_client_event event),
+                                    void *cb_ctx);
+void http_client_free(struct http_client *c);
+struct wpabuf * http_client_get_body(struct http_client *c);
+char * http_client_get_hdr_line(struct http_client *c, const char *tag);
+char * http_link_update(char *url, const char *base);
+
+#endif /* HTTP_CLIENT_H */
diff --git a/src/wps/http_server.c b/src/wps/http_server.c
new file mode 100644 (file)
index 0000000..356f599
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * http_server - HTTP server
+ * Copyright (c) 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 <fcntl.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_server.h"
+
+#define HTTP_SERVER_TIMEOUT 30
+#define HTTP_SERVER_MAX_REQ_LEN 8000
+#define HTTP_SERVER_MAX_CONNECTIONS 10
+
+struct http_request {
+       struct http_request *next;
+       struct http_server *srv;
+       int fd;
+       struct sockaddr_in cli;
+       struct httpread *hread;
+};
+
+struct http_server {
+       void (*cb)(void *ctx, struct http_request *req);
+       void *cb_ctx;
+
+       int fd;
+       int port;
+
+       struct http_request *requests;
+       unsigned int request_count;
+};
+
+
+static void http_request_cb(struct httpread *handle, void *cookie,
+                           enum httpread_event en)
+{
+       struct http_request *req = cookie;
+       struct http_server *srv = req->srv;
+
+       if (en == HTTPREAD_EVENT_FILE_READY) {
+               wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received",
+                          inet_ntoa(req->cli.sin_addr),
+                          ntohs(req->cli.sin_port));
+               srv->cb(srv->cb_ctx, req);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received "
+                  "completely", inet_ntoa(req->cli.sin_addr),
+                  ntohs(req->cli.sin_port));
+       http_request_deinit(req);
+}
+
+
+static struct http_request * http_request_init(struct http_server *srv, int fd,
+                                              struct sockaddr_in *cli)
+{
+       struct http_request *req;
+
+       if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) {
+               wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests");
+               return NULL;
+       }
+
+       req = os_zalloc(sizeof(*req));
+       if (req == NULL)
+               return NULL;
+
+       req->srv = srv;
+       req->fd = fd;
+       req->cli = *cli;
+
+       req->hread = httpread_create(req->fd, http_request_cb, req,
+                                    HTTP_SERVER_MAX_REQ_LEN,
+                                    HTTP_SERVER_TIMEOUT);
+       if (req->hread == NULL) {
+               http_request_deinit(req);
+               return NULL;
+       }
+
+       return req;
+}
+
+
+void http_request_deinit(struct http_request *req)
+{
+       struct http_request *r, *p;
+       struct http_server *srv;
+
+       if (req == NULL)
+               return;
+
+       srv = req->srv;
+       p = NULL;
+       r = srv->requests;
+       while (r) {
+               if (r == req) {
+                       if (p)
+                               p->next = r->next;
+                       else
+                               srv->requests = r->next;
+                       srv->request_count--;
+                       break;
+               }
+               p = r;
+               r = r->next;
+       }
+
+       httpread_destroy(req->hread);
+       close(req->fd);
+       os_free(req);
+}
+
+
+static void http_request_free_all(struct http_request *req)
+{
+       struct http_request *prev;
+       while (req) {
+               prev = req;
+               req = req->next;
+               http_request_deinit(prev);
+       }
+}
+
+
+void http_request_send(struct http_request *req, struct wpabuf *resp)
+{
+       int res;
+
+       wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d",
+                  (unsigned long) wpabuf_len(resp),
+                  inet_ntoa(req->cli.sin_addr),
+                  ntohs(req->cli.sin_port));
+
+       res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s",
+                          strerror(errno));
+       } else if ((size_t) res < wpabuf_len(resp)) {
+               wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes",
+                          res, (unsigned long) wpabuf_len(resp));
+               /* TODO: add eloop handler for sending rest of the data */
+       }
+
+       wpabuf_free(resp);
+}
+
+
+void http_request_send_and_deinit(struct http_request *req,
+                                 struct wpabuf *resp)
+{
+       http_request_send(req, resp);
+       http_request_deinit(req);
+}
+
+
+enum httpread_hdr_type http_request_get_type(struct http_request *req)
+{
+       return httpread_hdr_type_get(req->hread);
+}
+
+
+char * http_request_get_uri(struct http_request *req)
+{
+       return httpread_uri_get(req->hread);
+}
+
+
+char * http_request_get_hdr(struct http_request *req)
+{
+       return httpread_hdr_get(req->hread);
+}
+
+
+char * http_request_get_data(struct http_request *req)
+{
+       return httpread_data_get(req->hread);
+}
+
+
+char * http_request_get_hdr_line(struct http_request *req, const char *tag)
+{
+       return httpread_hdr_line_get(req->hread, tag);
+}
+
+
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req)
+{
+       return &req->cli;
+}
+
+
+static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+       struct sockaddr_in addr;
+       socklen_t addr_len = sizeof(addr);
+       struct http_server *srv = eloop_ctx;
+       int conn;
+       struct http_request *req;
+
+       conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len);
+       if (conn < 0) {
+               wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: "
+                          "%s", strerror(errno));
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d",
+                  inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+       req = http_request_init(srv, conn, &addr);
+       if (req == NULL) {
+               close(conn);
+               return;
+       }
+
+       req->next = srv->requests;
+       srv->requests = req;
+       srv->request_count++;
+}
+
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+                                     void (*cb)(void *ctx,
+                                                struct http_request *req),
+                                     void *cb_ctx)
+{
+       struct sockaddr_in sin;
+       struct http_server *srv;
+
+       srv = os_zalloc(sizeof(*srv));
+       if (srv == NULL)
+               return NULL;
+       srv->cb = cb;
+       srv->cb_ctx = cb_ctx;
+
+       srv->fd = socket(AF_INET, SOCK_STREAM, 0);
+       if (srv->fd < 0)
+               goto fail;
+       if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
+               goto fail;
+       if (port < 0)
+               srv->port = 49152;
+       else
+               srv->port = port;
+
+       os_memset(&sin, 0, sizeof(sin));
+       sin.sin_family = AF_INET;
+       sin.sin_addr.s_addr = addr->s_addr;
+
+       for (;;) {
+               sin.sin_port = htons(srv->port);
+               if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0)
+                       break;
+               if (errno == EADDRINUSE) {
+                       /* search for unused port */
+                       if (++srv->port == 65535 || port >= 0)
+                               goto fail;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: "
+                          "%s", srv->port, strerror(errno));
+               goto fail;
+       }
+       if (listen(srv->fd, 10 /* max backlog */) < 0)
+               goto fail;
+       if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
+               goto fail;
+       if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb,
+                               srv, NULL))
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d",
+                  inet_ntoa(*addr), srv->port);
+
+       return srv;
+
+fail:
+       http_server_deinit(srv);
+       return NULL;
+}
+
+
+void http_server_deinit(struct http_server *srv)
+{
+       if (srv == NULL)
+               return;
+       if (srv->fd >= 0) {
+               eloop_unregister_sock(srv->fd, EVENT_TYPE_READ);
+               close(srv->fd);
+       }
+       http_request_free_all(srv->requests);
+
+       os_free(srv);
+}
+
+
+int http_server_get_port(struct http_server *srv)
+{
+       return srv->port;
+}
diff --git a/src/wps/http_server.h b/src/wps/http_server.h
new file mode 100644 (file)
index 0000000..219941c
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * http_server - HTTP server
+ * Copyright (c) 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.
+ */
+
+#ifndef HTTP_SERVER_H
+#define HTTP_SERVER_H
+
+struct http_server;
+struct http_request;
+
+void http_request_deinit(struct http_request *req);
+void http_request_send(struct http_request *req, struct wpabuf *resp);
+void http_request_send_and_deinit(struct http_request *req,
+                                 struct wpabuf *resp);
+enum httpread_hdr_type http_request_get_type(struct http_request *req);
+char * http_request_get_uri(struct http_request *req);
+char * http_request_get_hdr(struct http_request *req);
+char * http_request_get_data(struct http_request *req);
+char * http_request_get_hdr_line(struct http_request *req, const char *tag);
+struct sockaddr_in * http_request_get_cli_addr(struct http_request *req);
+
+struct http_server * http_server_init(struct in_addr *addr, int port,
+                                     void (*cb)(void *ctx,
+                                                struct http_request *req),
+                                     void *cb_ctx);
+void http_server_deinit(struct http_server *srv);
+int http_server_get_port(struct http_server *srv);
+
+#endif /* HTTP_SERVER_H */
diff --git a/src/wps/httpread.c b/src/wps/httpread.c
new file mode 100644 (file)
index 0000000..40422e4
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * 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.
+ *
+ * The files are buffered via internal callbacks from eloop, then presented to
+ * an application callback routine when completely read into memory. May also
+ * be used if no file is expected but just to get the header, including HTTP
+ * replies (e.g. HTTP/1.1 200 OK etc.).
+ *
+ * This does not attempt to be an optimally efficient implementation, but does
+ * attempt to be of reasonably small size and memory consumption; assuming that
+ * only small files are to be read. A maximum file size is provided by
+ * application and enforced.
+ *
+ * It is assumed that the application does not expect any of the following:
+ * -- transfer encoding other than chunked
+ * -- trailer fields
+ * It is assumed that, even if the other side requested that the connection be
+ * kept open, that we will close it (thus HTTP messages sent by application
+ * should have the connection closed field); this is allowed by HTTP/1.1 and
+ * simplifies things for us.
+ *
+ * Other limitations:
+ * -- HTTP header may not exceed a hard-coded size.
+ *
+ * Notes:
+ * This code would be massively simpler without some of the new features of
+ * HTTP/1.1, especially chunked data.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "httpread.h"
+
+
+/* Tunable parameters */
+#define HTTPREAD_READBUF_SIZE 1024      /* read in chunks of this size */
+#define HTTPREAD_HEADER_MAX_SIZE 4096   /* max allowed for headers */
+#define HTTPREAD_BODYBUF_DELTA 4096     /* increase allocation by this */
+
+#if 0
+/* httpread_debug -- set this global variable > 0 e.g. from debugger
+ * to enable debugs (larger numbers for more debugs)
+ * Make this a #define of 0 to eliminate the debugging code.
+ */
+int httpread_debug = 99;
+#else
+#define httpread_debug 0        /* eliminates even the debugging code */
+#endif
+
+
+/* control instance -- actual definition (opaque to application)
+ */
+struct httpread {
+       /* information from creation */
+       int sd;         /* descriptor of TCP socket to read from */
+       void (*cb)(struct httpread *handle, void *cookie,
+                   enum httpread_event e);  /* call on event */
+       void *cookie;   /* pass to callback */
+       int max_bytes;          /* maximum file size else abort it */
+       int timeout_seconds;            /* 0 or total duration timeout period */
+
+       /* dynamically used information follows */
+       int sd_registered;      /* nonzero if we need to unregister socket */
+       int to_registered;      /* nonzero if we need to unregister timeout */
+
+       int got_hdr;            /* nonzero when header is finalized */
+       char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
+       int hdr_nbytes;
+
+       enum httpread_hdr_type hdr_type;
+       int version;            /* 1 if we've seen 1.1 */
+       int reply_code;         /* for type REPLY, e.g. 200 for HTTP/1.1 200 OK */
+       int got_content_length; /* true if we know content length for sure */
+       int content_length;     /* body length,  iff got_content_length */
+       int chunked;            /* nonzero for chunked data */
+       char *uri;
+
+       int got_body;           /* nonzero when body is finalized */
+       char *body;
+       int body_nbytes;
+       int body_alloc_nbytes;  /* amount allocated */
+
+       int got_file;           /* here when we are done */
+
+       /* The following apply if data is chunked: */
+       int in_chunk_data;      /* 0=in/at header, 1=in the data or tail*/
+       int chunk_start;        /* offset in body of chunk hdr or data */
+       int chunk_size;         /* data of chunk (not hdr or ending CRLF)*/
+       int in_trailer;         /* in header fields after data (chunked only)*/
+       enum trailer_state {
+               trailer_line_begin = 0,
+               trailer_empty_cr,       /* empty line + CR */
+               trailer_nonempty,
+               trailer_nonempty_cr,
+       } trailer_state;
+};
+
+
+/* Check words for equality, where words consist of graphical characters
+ * delimited by whitespace
+ * Returns nonzero if "equal" doing case insensitive comparison.
+ */
+static int word_eq(char *s1, char *s2)
+{
+       int c1;
+       int c2;
+       int end1 = 0;
+       int end2 = 0;
+       for (;;) {
+               c1 = *s1++;
+               c2 = *s2++;
+               if (isalpha(c1) && isupper(c1))
+                       c1 = tolower(c1);
+               if (isalpha(c2) && isupper(c2))
+                       c2 = tolower(c2);
+               end1 = !isgraph(c1);
+               end2 = !isgraph(c2);
+               if (end1 || end2 || c1 != c2)
+                       break;
+       }
+       return end1 && end2;  /* reached end of both words? */
+}
+
+
+/* convert hex to binary
+ * Requires that c have been previously tested true with isxdigit().
+ */
+static int hex_value(int c)
+{
+       if (isdigit(c))
+               return c - '0';
+       if (islower(c))
+               return 10 + c - 'a';
+       return 10 + c - 'A';
+}
+
+
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h)
+{
+       if (httpread_debug >= 10)
+               wpa_printf(MSG_DEBUG, "ENTER httpread_destroy(%p)", h);
+       if (!h)
+               return;
+
+       if (h->to_registered)
+               eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+       h->to_registered = 0;
+       if (h->sd_registered)
+               eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+       h->sd_registered = 0;
+       os_free(h->body);
+       os_free(h->uri);
+       os_memset(h, 0, sizeof(*h));  /* aid debugging */
+       h->sd = -1;     /* aid debugging */
+       os_free(h);
+}
+
+
+/* httpread_timeout_handler -- called on excessive total duration
+ */
+static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
+{
+       struct httpread *h = user_ctx;
+       wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
+       h->to_registered = 0;   /* is self-cancelling */
+       (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
+}
+
+
+/* Analyze options only so far as is needed to correctly obtain the file.
+ * The application can look at the raw header to find other options.
+ */
+static int httpread_hdr_option_analyze(
+       struct httpread *h,
+       char *hbp       /* pointer to current line in header buffer */
+       )
+{
+       if (word_eq(hbp, "CONTENT-LENGTH:")) {
+               while (isgraph(*hbp))
+                       hbp++;
+               while (*hbp == ' ' || *hbp == '\t')
+                       hbp++;
+               if (!isdigit(*hbp))
+                       return -1;
+               h->content_length = atol(hbp);
+               h->got_content_length = 1;
+               return 0;
+       }
+       if (word_eq(hbp, "TRANSFER_ENCODING:") ||
+           word_eq(hbp, "TRANSFER-ENCODING:")) {
+               while (isgraph(*hbp))
+                       hbp++;
+               while (*hbp == ' ' || *hbp == '\t')
+                       hbp++;
+               /* There should (?) be no encodings of interest
+                * other than chunked...
+                */
+               if (word_eq(hbp, "CHUNKED")) {
+                       h->chunked = 1;
+                       h->in_chunk_data = 0;
+                       /* ignore possible ;<parameters> */
+               }
+               return 0;
+       }
+       /* skip anything we don't know, which is a lot */
+       return 0;
+}
+
+
+static int httpread_hdr_analyze(struct httpread *h)
+{
+       char *hbp = h->hdr;      /* pointer into h->hdr */
+       int standard_first_line = 1;
+
+       /* First line is special */
+       h->hdr_type = HTTPREAD_HDR_TYPE_UNKNOWN;
+       if (!isgraph(*hbp))
+               goto bad;
+       if (os_strncmp(hbp, "HTTP/", 5) == 0) {
+               h->hdr_type = HTTPREAD_HDR_TYPE_REPLY;
+               standard_first_line = 0;
+               hbp += 5;
+               if (hbp[0] == '1' && hbp[1] == '.' &&
+                   isdigit(hbp[2]) && hbp[2] != '0')
+                       h->version = 1;
+               while (isgraph(*hbp))
+                       hbp++;
+               while (*hbp == ' ' || *hbp == '\t')
+                       hbp++;
+               if (!isdigit(*hbp))
+                       goto bad;
+               h->reply_code = atol(hbp);
+       } else if (word_eq(hbp, "GET"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_GET;
+       else if (word_eq(hbp, "HEAD"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_HEAD;
+       else if (word_eq(hbp, "POST"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_POST;
+       else if (word_eq(hbp, "PUT"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_PUT;
+       else if (word_eq(hbp, "DELETE"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_DELETE;
+       else if (word_eq(hbp, "TRACE"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_TRACE;
+       else if (word_eq(hbp, "CONNECT"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_CONNECT;
+       else if (word_eq(hbp, "NOTIFY"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_NOTIFY;
+       else if (word_eq(hbp, "M-SEARCH"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_M_SEARCH;
+       else if (word_eq(hbp, "M-POST"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_M_POST;
+       else if (word_eq(hbp, "SUBSCRIBE"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_SUBSCRIBE;
+       else if (word_eq(hbp, "UNSUBSCRIBE"))
+               h->hdr_type = HTTPREAD_HDR_TYPE_UNSUBSCRIBE;
+       else {
+       }
+
+       if (standard_first_line) {
+               char *rawuri;
+               char *uri;
+               /* skip type */
+               while (isgraph(*hbp))
+                       hbp++;
+               while (*hbp == ' ' || *hbp == '\t')
+                       hbp++;
+               /* parse uri.
+                * Find length, allocate memory for translated
+                * copy, then translate by changing %<hex><hex>
+                * into represented value.
+                */
+               rawuri = hbp;
+               while (isgraph(*hbp))
+                       hbp++;
+               h->uri = os_malloc((hbp - rawuri) + 1);
+               if (h->uri == NULL)
+                       goto bad;
+               uri = h->uri;
+               while (rawuri < hbp) {
+                       int c = *rawuri;
+                       if (c == '%' &&
+                           isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
+                               *uri++ = (hex_value(rawuri[1]) << 4) |
+                                       hex_value(rawuri[2]);
+                               rawuri += 3;
+                       } else {
+                               *uri++ = c;
+                               rawuri++;
+                       }
+               }
+               *uri = 0;       /* null terminate */
+               while (isgraph(*hbp))
+                       hbp++;
+               while (*hbp == ' ' || *hbp == '\t')
+                       hbp++;
+               /* get version */
+               if (0 == strncmp(hbp, "HTTP/", 5)) {
+                       hbp += 5;
+                       if (hbp[0] == '1' && hbp[1] == '.' &&
+                           isdigit(hbp[2]) && hbp[2] != '0')
+                               h->version = 1;
+               }
+       }
+       /* skip rest of line */
+       while (*hbp)
+               if (*hbp++ == '\n')
+                       break;
+
+       /* Remainder of lines are options, in any order;
+        * or empty line to terminate
+        */
+       for (;;) {
+               /* Empty line to terminate */
+               if (hbp[0] == '\n' ||
+                   (hbp[0] == '\r' && hbp[1] == '\n'))
+                       break;
+               if (!isgraph(*hbp))
+                       goto bad;
+               if (httpread_hdr_option_analyze(h, hbp))
+                       goto bad;
+               /* skip line */
+               while (*hbp)
+                       if (*hbp++ == '\n')
+                               break;
+       }
+
+       /* chunked overrides content-length always */
+       if (h->chunked)
+               h->got_content_length = 0;
+
+       /* For some types, we should not try to read a body
+        * This is in addition to the application determining
+        * that we should not read a body.
+        */
+       switch (h->hdr_type) {
+       case HTTPREAD_HDR_TYPE_REPLY:
+               /* Some codes can have a body and some not.
+                * For now, just assume that any other than 200
+                * do not...
+                */
+               if (h->reply_code != 200)
+                       h->max_bytes = 0;
+               break;
+       case HTTPREAD_HDR_TYPE_GET:
+       case HTTPREAD_HDR_TYPE_HEAD:
+               /* in practice it appears that it is assumed
+                * that GETs have a body length of 0... ?
+                */
+               if (h->chunked == 0 && h->got_content_length == 0)
+                       h->max_bytes = 0;
+               break;
+       case HTTPREAD_HDR_TYPE_POST:
+       case HTTPREAD_HDR_TYPE_PUT:
+       case HTTPREAD_HDR_TYPE_DELETE:
+       case HTTPREAD_HDR_TYPE_TRACE:
+       case HTTPREAD_HDR_TYPE_CONNECT:
+       case HTTPREAD_HDR_TYPE_NOTIFY:
+       case HTTPREAD_HDR_TYPE_M_SEARCH:
+       case HTTPREAD_HDR_TYPE_M_POST:
+       case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+       case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+       default:
+               break;
+       }
+
+       return 0;
+
+bad:
+       /* Error */
+       return -1;
+}
+
+
+/* httpread_read_handler -- called when socket ready to read
+ *
+ * Note: any extra data we read past end of transmitted file is ignored;
+ * if we were to support keeping connections open for multiple files then
+ * this would have to be addressed.
+ */
+static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+       struct httpread *h = sock_ctx;
+       int nread;
+       char *rbp;      /* pointer into read buffer */
+       char *hbp;      /* pointer into header buffer */
+       char *bbp;      /* pointer into body buffer */
+       char readbuf[HTTPREAD_READBUF_SIZE];  /* temp use to read into */
+
+       if (httpread_debug >= 20)
+               wpa_printf(MSG_DEBUG, "ENTER httpread_read_handler(%p)", h);
+
+       /* read some at a time, then search for the interal
+        * boundaries between header and data and etc.
+        */
+       nread = read(h->sd, readbuf, sizeof(readbuf));
+       if (nread < 0)
+               goto bad;
+       if (nread == 0) {
+               /* end of transmission... this may be normal
+                * or may be an error... in some cases we can't
+                * tell which so we must assume it is normal then.
+                */
+               if (!h->got_hdr) {
+                       /* Must at least have completed header */
+                       wpa_printf(MSG_DEBUG, "httpread premature eof(%p)", h);
+                       goto bad;
+               }
+               if (h->chunked || h->got_content_length) {
+                       /* Premature EOF; e.g. dropped connection */
+                       wpa_printf(MSG_DEBUG,
+                                  "httpread premature eof(%p) %d/%d",
+                                  h, h->body_nbytes,
+                                  h->content_length);
+                       goto bad;
+               }
+               /* No explicit length, hopefully we have all the data
+                * although dropped connections can cause false
+                * end
+                */
+               if (httpread_debug >= 10)
+                       wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
+                       h->got_body = 1;
+                       goto got_file;
+       }
+       rbp = readbuf;
+
+       /* Header consists of text lines (terminated by both CR and LF)
+        * and an empty line (CR LF only).
+        */
+       if (!h->got_hdr) {
+               hbp = h->hdr + h->hdr_nbytes;
+               /* add to headers until:
+                *      -- we run out of data in read buffer
+                *      -- or, we run out of header buffer room
+                *      -- or, we get double CRLF in headers
+                */
+               for (;;) {
+                       if (nread == 0)
+                               goto get_more;
+                       if (h->hdr_nbytes == HTTPREAD_HEADER_MAX_SIZE) {
+                               goto bad;
+                       }
+                       *hbp++ = *rbp++;
+                       nread--;
+                       h->hdr_nbytes++;
+                       if (h->hdr_nbytes >= 4 &&
+                           hbp[-1] == '\n' &&
+                           hbp[-2] == '\r' &&
+                           hbp[-3] == '\n' &&
+                           hbp[-4] == '\r' ) {
+                               h->got_hdr = 1;
+                               *hbp = 0;       /* null terminate */
+                               break;
+                       }
+               }
+               /* here we've just finished reading the header */
+               if (httpread_hdr_analyze(h)) {
+                       wpa_printf(MSG_DEBUG, "httpread bad hdr(%p)", h);
+                       goto bad;
+               }
+               if (h->max_bytes == 0) {
+                       if (httpread_debug >= 10)
+                               wpa_printf(MSG_DEBUG,
+                                          "httpread no body hdr end(%p)", h);
+                       goto got_file;
+               }
+               if (h->got_content_length && h->content_length == 0) {
+                       if (httpread_debug >= 10)
+                               wpa_printf(MSG_DEBUG,
+                                          "httpread zero content length(%p)",
+                                          h);
+                       goto got_file;
+               }
+       }
+
+       /* Certain types of requests never have data and so
+        * must be specially recognized.
+        */
+       if (!os_strncasecmp(h->hdr, "SUBSCRIBE", 9) ||
+           !os_strncasecmp(h->hdr, "UNSUBSCRIBE", 11) ||
+           !os_strncasecmp(h->hdr, "HEAD", 4) ||
+           !os_strncasecmp(h->hdr, "GET", 3)) {
+               if (!h->got_body) {
+                       if (httpread_debug >= 10)
+                               wpa_printf(MSG_DEBUG,
+                                          "httpread NO BODY for sp. type");
+               }
+               h->got_body = 1;
+               goto got_file;
+       }
+
+       /* Data can be just plain binary data, or if "chunked"
+        * consists of chunks each with a header, ending with
+        * an ending header.
+        */
+       if (nread == 0)
+               goto get_more;
+       if (!h->got_body) {
+               /* Here to get (more of) body */
+               /* ensure we have enough room for worst case for body
+                * plus a null termination character
+                */
+               if (h->body_alloc_nbytes < (h->body_nbytes + nread + 1)) {
+                       char *new_body;
+                       int new_alloc_nbytes;
+
+                       if (h->body_nbytes >= h->max_bytes)
+                               goto bad;
+                       new_alloc_nbytes = h->body_alloc_nbytes +
+                               HTTPREAD_BODYBUF_DELTA;
+                       /* For content-length case, the first time
+                        * through we allocate the whole amount
+                        * we need.
+                        */
+                       if (h->got_content_length &&
+                           new_alloc_nbytes < (h->content_length + 1))
+                               new_alloc_nbytes = h->content_length + 1;
+                       if ((new_body = os_realloc(h->body, new_alloc_nbytes))
+                           == NULL)
+                               goto bad;
+
+                       h->body = new_body;
+                       h->body_alloc_nbytes = new_alloc_nbytes;
+               }
+               /* add bytes */
+               bbp = h->body + h->body_nbytes;
+               for (;;) {
+                       int ncopy;
+                       /* See if we need to stop */
+                       if (h->chunked && h->in_chunk_data == 0) {
+                               /* in chunk header */
+                               char *cbp = h->body + h->chunk_start;
+                               if (bbp-cbp >= 2 && bbp[-2] == '\r' &&
+                                   bbp[-1] == '\n') {
+                                       /* end of chunk hdr line */
+                                       /* hdr line consists solely
+                                        * of a hex numeral and CFLF
+                                        */
+                                       if (!isxdigit(*cbp))
+                                               goto bad;
+                                       h->chunk_size = strtoul(cbp, NULL, 16);
+                                       /* throw away chunk header
+                                        * so we have only real data
+                                        */
+                                       h->body_nbytes = h->chunk_start;
+                                       bbp = cbp;
+                                       if (h->chunk_size == 0) {
+                                               /* end of chunking */
+                                               /* trailer follows */
+                                               h->in_trailer = 1;
+                                               if (httpread_debug >= 20)
+                                                       wpa_printf(
+                                                               MSG_DEBUG,
+                                                               "httpread end chunks(%p)", h);
+                                               break;
+                                       }
+                                       h->in_chunk_data = 1;
+                                       /* leave chunk_start alone */
+                               }
+                       } else if (h->chunked) {
+                               /* in chunk data */
+                               if ((h->body_nbytes - h->chunk_start) ==
+                                   (h->chunk_size + 2)) {
+                                       /* end of chunk reached,
+                                        * new chunk starts
+                                        */
+                                       /* check chunk ended w/ CRLF
+                                        * which we'll throw away
+                                        */
+                                       if (bbp[-1] == '\n' &&
+                                           bbp[-2] == '\r') {
+                                       } else
+                                               goto bad;
+                                       h->body_nbytes -= 2;
+                                       bbp -= 2;
+                                       h->chunk_start = h->body_nbytes;
+                                       h->in_chunk_data = 0;
+                                       h->chunk_size = 0; /* just in case */
+                               }
+                       } else if (h->got_content_length &&
+                                  h->body_nbytes >= h->content_length) {
+                               h->got_body = 1;
+                               if (httpread_debug >= 10)
+                                       wpa_printf(
+                                               MSG_DEBUG,
+                                               "httpread got content(%p)", h);
+                               goto got_file;
+                       }
+                       if (nread <= 0)
+                               break;
+                       /* Now transfer. Optimize using memcpy where we can. */
+                       if (h->chunked && h->in_chunk_data) {
+                               /* copy up to remainder of chunk data
+                                * plus the required CR+LF at end
+                                */
+                               ncopy = (h->chunk_start + h->chunk_size + 2) -
+                                       h->body_nbytes;
+                       } else if (h->chunked) {
+                               /*in chunk header -- don't optimize */
+                               *bbp++ = *rbp++;
+                               nread--;
+                               h->body_nbytes++;
+                               continue;
+                       } else if (h->got_content_length) {
+                               ncopy = h->content_length - h->body_nbytes;
+                       } else {
+                               ncopy = nread;
+                       }
+                       /* Note: should never be 0 */
+                       if (ncopy > nread)
+                               ncopy = nread;
+                       os_memcpy(bbp, rbp, ncopy);
+                       bbp += ncopy;
+                       h->body_nbytes += ncopy;
+                       rbp += ncopy;
+                       nread -= ncopy;
+               }       /* body copy loop */
+       }       /* !got_body */
+       if (h->chunked && h->in_trailer) {
+               /* If "chunked" then there is always a trailer,
+                * consisting of zero or more non-empty lines
+                * ending with CR LF and then an empty line w/ CR LF.
+                * We do NOT support trailers except to skip them --
+                * this is supported (generally) by the http spec.
+                */
+               bbp = h->body + h->body_nbytes;
+               for (;;) {
+                       int c;
+                       if (nread <= 0)
+                               break;
+                       c = *rbp++;
+                       nread--;
+                       switch (h->trailer_state) {
+                       case trailer_line_begin:
+                               if (c == '\r')
+                                       h->trailer_state = trailer_empty_cr;
+                               else
+                                       h->trailer_state = trailer_nonempty;
+                               break;
+                       case trailer_empty_cr:
+                               /* end empty line */
+                               if (c == '\n') {
+                                       h->trailer_state = trailer_line_begin;
+                                       h->in_trailer = 0;
+                                       if (httpread_debug >= 10)
+                                               wpa_printf(
+                                                       MSG_DEBUG,
+                                                       "httpread got content(%p)", h);
+                                       h->got_body = 1;
+                                       goto got_file;
+                               }
+                               h->trailer_state = trailer_nonempty;
+                               break;
+                       case trailer_nonempty:
+                               if (c == '\r')
+                                       h->trailer_state = trailer_nonempty_cr;
+                               break;
+                       case trailer_nonempty_cr:
+                               if (c == '\n')
+                                       h->trailer_state = trailer_line_begin;
+                               else
+                                       h->trailer_state = trailer_nonempty;
+                               break;
+                       }
+               }
+       }
+       goto get_more;
+
+bad:
+       /* Error */
+       wpa_printf(MSG_DEBUG, "httpread read/parse failure (%p)", h);
+       (*h->cb)(h, h->cookie, HTTPREAD_EVENT_ERROR);
+       return;
+
+get_more:
+       return;
+
+got_file:
+       if (httpread_debug >= 10)
+               wpa_printf(MSG_DEBUG,
+                          "httpread got file %d bytes type %d",
+                          h->body_nbytes, h->hdr_type);
+       /* Null terminate for convenience of some applications */
+       if (h->body)
+               h->body[h->body_nbytes] = 0; /* null terminate */
+       h->got_file = 1;
+       /* Assume that we do NOT support keeping connection alive,
+        * and just in case somehow we don't get destroyed right away,
+        * unregister now.
+        */
+       if (h->sd_registered)
+               eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
+       h->sd_registered = 0;
+       /* The application can destroy us whenever they feel like...
+        * cancel timeout.
+        */
+       if (h->to_registered)
+               eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+       h->to_registered = 0;
+       (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
+}
+
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+       int sd,  /* descriptor of TCP socket to read from */
+       void (*cb)(struct httpread *handle, void *cookie,
+                  enum httpread_event e),  /* call on event */
+       void *cookie,    /* pass to callback */
+       int max_bytes,    /* maximum body size else abort it */
+       int timeout_seconds     /* 0; or total duration timeout period */
+       )
+{
+       struct httpread *h = NULL;
+
+       h = os_zalloc(sizeof(*h));
+       if (h == NULL)
+               goto fail;
+       h->sd = sd;
+       h->cb = cb;
+       h->cookie = cookie;
+       h->max_bytes = max_bytes;
+       h->timeout_seconds = timeout_seconds;
+
+       if (timeout_seconds > 0) {
+               if (eloop_register_timeout(timeout_seconds, 0,
+                                          httpread_timeout_handler,
+                                          NULL, h)) {
+                       /* No way to recover (from malloc failure) */
+                       goto fail;
+               }
+               h->to_registered = 1;
+       }
+       if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
+                               NULL, h)) {
+               /* No way to recover (from malloc failure) */
+               goto fail;
+       }
+       h->sd_registered = 1;
+       return h;
+
+fail:
+
+       /* Error */
+       httpread_destroy(h);
+       return NULL;
+}
+
+
+/* httpread_hdr_type_get -- When file is ready, returns header type. */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h)
+{
+       return h->hdr_type;
+}
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char * httpread_uri_get(struct httpread *h)
+{
+       return h->uri;
+}
+
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h)
+{
+       return h->reply_code;
+}
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h)
+{
+       return h->body_nbytes;
+}
+
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h)
+{
+       return h->body ? h->body : "";
+}
+
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h)
+{
+       return h->hdr;
+}
+
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag)
+{
+       int tag_len = os_strlen(tag);
+       char *hdr = h->hdr;
+       hdr = os_strchr(hdr, '\n');
+       if (hdr == NULL)
+               return NULL;
+       hdr++;
+       for (;;) {
+               if (!os_strncasecmp(hdr, tag, tag_len)) {
+                       hdr += tag_len;
+                       while (*hdr == ' ' || *hdr == '\t')
+                               hdr++;
+                       return hdr;
+               }
+               hdr = os_strchr(hdr, '\n');
+               if (hdr == NULL)
+                       return NULL;
+               hdr++;
+       }
+}
diff --git a/src/wps/httpread.h b/src/wps/httpread.h
new file mode 100644 (file)
index 0000000..51aa214
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * httpread - Manage reading file(s) from HTTP/TCP socket
+ * Author: Ted Merrill
+ * Copyright 2008 Atheros Communications
+ *
+ * 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 HTTPREAD_H
+#define HTTPREAD_H
+
+/* event types (passed to callback) */
+enum httpread_event {
+       HTTPREAD_EVENT_FILE_READY = 1,        /* including reply ready */
+       HTTPREAD_EVENT_TIMEOUT = 2,
+       HTTPREAD_EVENT_ERROR = 3      /* misc. error, esp malloc error */
+};
+
+
+/* header type detected
+ * available to callback via call to httpread_reply_code_get()
+ */
+enum httpread_hdr_type {
+       HTTPREAD_HDR_TYPE_UNKNOWN = 0,      /* none of the following */
+       HTTPREAD_HDR_TYPE_REPLY = 1,        /* hdr begins w/ HTTP/ */
+       HTTPREAD_HDR_TYPE_GET = 2,          /* hdr begins with GET<sp> */
+       HTTPREAD_HDR_TYPE_HEAD = 3,         /* hdr begins with HEAD<sp> */
+       HTTPREAD_HDR_TYPE_POST = 4,         /* hdr begins with POST<sp> */
+       HTTPREAD_HDR_TYPE_PUT = 5,          /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_DELETE = 6,       /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_TRACE = 7,        /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_CONNECT = 8,      /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_NOTIFY = 9,       /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_M_SEARCH = 10,    /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_M_POST = 11,      /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_SUBSCRIBE = 12,   /* hdr begins with ... */
+       HTTPREAD_HDR_TYPE_UNSUBSCRIBE = 13, /* hdr begins with ... */
+
+       HTTPREAD_N_HDR_TYPES    /* keep last */
+};
+
+
+/* control instance -- opaque struct declaration
+ */
+struct httpread;
+
+
+/* httpread_destroy -- if h is non-NULL, clean up
+ * This must eventually be called by the application following
+ * call of the application's callback and may be called
+ * earlier if desired.
+ */
+void httpread_destroy(struct httpread *h);
+
+/* httpread_create -- start a new reading session making use of eloop.
+ * The new instance will use the socket descriptor for reading (until
+ * it gets a file and not after) but will not close the socket, even
+ * when the instance is destroyed (the application must do that).
+ * Return NULL on error.
+ *
+ * Provided that httpread_create successfully returns a handle,
+ * the callback fnc is called to handle httpread_event events.
+ * The caller should do destroy on any errors or unknown events.
+ *
+ * Pass max_bytes == 0 to not read body at all (required for e.g.
+ * reply to HEAD request).
+ */
+struct httpread * httpread_create(
+       int sd,         /* descriptor of TCP socket to read from */
+       void (*cb)(struct httpread *handle, void *cookie,
+                   enum httpread_event e),  /* call on event */
+       void *cookie,    /* pass to callback */
+       int max_bytes,          /* maximum file size else abort it */
+       int timeout_seconds     /* 0; or total duration timeout period */
+       );
+
+/* httpread_hdr_type_get -- When file is ready, returns header type.
+ */
+enum httpread_hdr_type httpread_hdr_type_get(struct httpread *h);
+
+
+/* httpread_uri_get -- When file is ready, uri_get returns (translated) URI
+ * or possibly NULL (which would be an error).
+ */
+char *httpread_uri_get(struct httpread *h);
+
+/* httpread_reply_code_get -- When reply is ready, returns reply code */
+int httpread_reply_code_get(struct httpread *h);
+
+
+/* httpread_length_get -- When file is ready, returns file length. */
+int httpread_length_get(struct httpread *h);
+
+/* httpread_data_get -- When file is ready, returns file content
+ * with null byte appened.
+ * Might return NULL in some error condition.
+ */
+void * httpread_data_get(struct httpread *h);
+
+/* httpread_hdr_get -- When file is ready, returns header content
+ * with null byte appended.
+ * Might return NULL in some error condition.
+ */
+char * httpread_hdr_get(struct httpread *h);
+
+/* httpread_hdr_line_get -- When file is ready, returns pointer
+ * to line within header content matching the given tag
+ * (after the tag itself and any spaces/tabs).
+ *
+ * The tag should end with a colon for reliable matching.
+ *
+ * If not found, returns NULL;
+ */
+char * httpread_hdr_line_get(struct httpread *h, const char *tag);
+
+#endif /* HTTPREAD_H */
diff --git a/src/wps/ndef.c b/src/wps/ndef.c
new file mode 100644 (file)
index 0000000..9baec7f
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup
+ *   Reference is "NFCForum-TS-NDEF_1.0 2006-07-24".
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * 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 "wps/wps.h"
+#include "wps/wps_i.h"
+
+#define FLAG_MESSAGE_BEGIN (1 << 7)
+#define FLAG_MESSAGE_END (1 << 6)
+#define FLAG_CHUNK (1 << 5)
+#define FLAG_SHORT_RECORD (1 << 4)
+#define FLAG_ID_LENGTH_PRESENT (1 << 3)
+#define FLAG_TNF_RFC2046 (0x02)
+
+struct ndef_record {
+       u8 *type;
+       u8 *id;
+       u8 *payload;
+       u8 type_length;
+       u8 id_length;
+       u32 payload_length;
+       u32 total_length;
+};
+
+static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+
+static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record)
+{
+       u8 *pos = data + 1;
+
+       if (size < 2)
+               return -1;
+       record->type_length = *pos++;
+       if (data[0] & FLAG_SHORT_RECORD) {
+               if (size < 3)
+                       return -1;
+               record->payload_length = *pos++;
+       } else {
+               if (size < 6)
+                       return -1;
+               record->payload_length = ntohl(*(u32 *)pos);
+               pos += sizeof(u32);
+       }
+
+       if (data[0] & FLAG_ID_LENGTH_PRESENT) {
+               if ((int) size < pos - data + 1)
+                       return -1;
+               record->id_length = *pos++;
+       } else
+               record->id_length = 0;
+
+       record->type = record->type_length == 0 ? NULL : pos;
+       pos += record->type_length;
+
+       record->id = record->id_length == 0 ? NULL : pos;
+       pos += record->id_length;
+
+       record->payload = record->payload_length == 0 ? NULL : pos;
+       pos += record->payload_length;
+
+       record->total_length = pos - data;
+       if (record->total_length > size)
+               return -1;
+       return 0;
+}
+
+
+static struct wpabuf * ndef_parse_records(struct wpabuf *buf,
+                                         int (*filter)(struct ndef_record *))
+{
+       struct ndef_record record;
+       int len = wpabuf_len(buf);
+       u8 *data = wpabuf_mhead(buf);
+
+       while (len > 0) {
+               if (ndef_parse_record(data, len, &record) < 0) {
+                       wpa_printf(MSG_ERROR, "NDEF : Failed to parse");
+                       return NULL;
+               }
+               if (filter == NULL || filter(&record))
+                       return wpabuf_alloc_copy(record.payload,
+                                                record.payload_length);
+               data += record.total_length;
+               len -= record.total_length;
+       }
+       wpa_printf(MSG_ERROR, "NDEF : Record not found");
+       return NULL;
+}
+
+
+static struct wpabuf * ndef_build_record(u8 flags, void *type,
+                                        u8 type_length, void *id,
+                                        u8 id_length, void *payload,
+                                        u32 payload_length)
+{
+       struct wpabuf *record;
+       size_t total_len;
+       int short_record;
+       u8 local_flag;
+
+       short_record = payload_length < 256 ? 1 : 0;
+
+       total_len = 2; /* flag + type length */
+       /* payload length */
+       total_len += short_record ? sizeof(u8) : sizeof(u32);
+       if (id_length > 0)
+               total_len += 1;
+       total_len += type_length + id_length + payload_length;
+       record = wpabuf_alloc(total_len);
+       if (record == NULL) {
+               wpa_printf(MSG_ERROR, "NDEF : Failed to allocate "
+                          "record for build");
+               return NULL;
+       }
+
+       local_flag = flags;
+       if (id_length > 0)
+               local_flag |= FLAG_ID_LENGTH_PRESENT;
+       if (short_record)
+               local_flag |= FLAG_SHORT_RECORD;
+       wpabuf_put_u8(record, local_flag);
+
+       wpabuf_put_u8(record, type_length);
+
+       if (short_record)
+               wpabuf_put_u8(record, payload_length);
+       else
+               wpabuf_put_be32(record, payload_length);
+
+       if (id_length > 0)
+               wpabuf_put_u8(record, id_length);
+       wpabuf_put_data(record, type, type_length);
+       wpabuf_put_data(record, id, id_length);
+       wpabuf_put_data(record, payload, payload_length);
+       return record;
+}
+
+
+static int wifi_filter(struct ndef_record *record)
+{
+       if (record->type_length != os_strlen(wifi_handover_type))
+               return 0;
+       if (os_memcmp(record->type, wifi_handover_type,
+                     os_strlen(wifi_handover_type)) != 0)
+               return 0;
+       return 1;
+}
+
+
+struct wpabuf * ndef_parse_wifi(struct wpabuf *buf)
+{
+       return ndef_parse_records(buf, wifi_filter);
+}
+
+
+struct wpabuf * ndef_build_wifi(struct wpabuf *buf)
+{
+       return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+                                FLAG_TNF_RFC2046, wifi_handover_type,
+                                os_strlen(wifi_handover_type), NULL, 0,
+                                wpabuf_mhead(buf), wpabuf_len(buf));
+}
diff --git a/src/wps/upnp_xml.c b/src/wps/upnp_xml.c
new file mode 100644 (file)
index 0000000..b1b1e2b
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "http.h"
+#include "upnp_xml.h"
+
+
+/*
+ * XML parsing and formatting
+ *
+ * XML is a markup language based on unicode; usually (and in our case,
+ * always!) based on utf-8. utf-8 uses a variable number of bytes per
+ * character. utf-8 has the advantage that all non-ASCII unicode characters are
+ * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII
+ * characters are single ascii bytes, thus we can use typical text processing.
+ *
+ * (One other interesting thing about utf-8 is that it is possible to look at
+ * any random byte and determine if it is the first byte of a character as
+ * versus a continuation byte).
+ *
+ * The base syntax of XML uses a few ASCII punctionation characters; any
+ * characters that would appear in the payload data are rewritten using
+ * sequences, e.g., &amp; for ampersand(&) and &lt for left angle bracket (<).
+ * Five such escapes total (more can be defined but that does not apply to our
+ * case). Thus we can safely parse for angle brackets etc.
+ *
+ * XML describes tree structures of tagged data, with each element beginning
+ * with an opening tag <label> and ending with a closing tag </label> with
+ * matching label. (There is also a self-closing tag <label/> which is supposed
+ * to be equivalent to <label></label>, i.e., no payload, but we are unlikely
+ * to see it for our purpose).
+ *
+ * Actually the opening tags are a little more complicated because they can
+ * contain "attributes" after the label (delimited by ascii space or tab chars)
+ * of the form attribute_label="value" or attribute_label='value'; as it turns
+ * out we do not have to read any of these attributes, just ignore them.
+ *
+ * Labels are any sequence of chars other than space, tab, right angle bracket
+ * (and ?), but may have an inner structure of <namespace><colon><plain_label>.
+ * As it turns out, we can ignore the namespaces, in fact we can ignore the
+ * entire tree hierarchy, because the plain labels we are looking for will be
+ * unique (not in general, but for this application). We do however have to be
+ * careful to skip over the namespaces.
+ *
+ * In generating XML we have to be more careful, but that is easy because
+ * everything we do is pretty canned. The only real care to take is to escape
+ * any special chars in our payload.
+ */
+
+/**
+ * xml_next_tag - Advance to next tag
+ * @in: Input
+ * @out: OUT: start of tag just after '<'
+ * @out_tagname: OUT: start of name of tag, skipping namespace
+ * @end: OUT: one after tag
+ * Returns: 0 on success, 1 on failure
+ *
+ * A tag has form:
+ *     <left angle bracket><...><right angle bracket>
+ * Within the angle brackets, there is an optional leading forward slash (which
+ * makes the tag an ending tag), then an optional leading label (followed by
+ * colon) and then the tag name itself.
+ *
+ * Note that angle brackets present in the original data must have been encoded
+ * as &lt; and &gt; so they will not trouble us.
+ */
+static int xml_next_tag(const char *in, const char **out,
+                       const char **out_tagname, const char **end)
+{
+       while (*in && *in != '<')
+               in++;
+       if (*in != '<')
+               return 1;
+       *out = ++in;
+       if (*in == '/')
+               in++;
+       *out_tagname = in; /* maybe */
+       while (isalnum(*in) || *in == '-')
+               in++;
+       if (*in == ':')
+               *out_tagname = ++in;
+       while (*in && *in != '>')
+               in++;
+       if (*in != '>')
+               return 1;
+       *end = ++in;
+       return 0;
+}
+
+
+/* xml_data_encode -- format data for xml file, escaping special characters.
+ *
+ * Note that we assume we are using utf8 both as input and as output!
+ * In utf8, characters may be classed as follows:
+ *     0xxxxxxx(2) -- 1 byte ascii char
+ *     11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80
+ *         110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here)
+ *         1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here)
+ *         11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here)
+ *      10xxxxxx(2) -- extension byte (6 payload bits per byte)
+ *      Some values implied by the above are however illegal because they
+ *      do not represent unicode chars or are not the shortest encoding.
+ * Actually, we can almost entirely ignore the above and just do
+ * text processing same as for ascii text.
+ *
+ * XML is written with arbitrary unicode characters, except that five
+ * characters have special meaning and so must be escaped where they
+ * appear in payload data... which we do here.
+ */
+void xml_data_encode(struct wpabuf *buf, const char *data, int len)
+{
+       int i;
+       for (i = 0; i < len; i++) {
+               u8 c = ((u8 *) data)[i];
+               if (c == '<') {
+                       wpabuf_put_str(buf, "&lt;");
+                       continue;
+               }
+               if (c == '>') {
+                       wpabuf_put_str(buf, "&gt;");
+                       continue;
+               }
+               if (c == '&') {
+                       wpabuf_put_str(buf, "&amp;");
+                       continue;
+               }
+               if (c == '\'') {
+                       wpabuf_put_str(buf, "&apos;");
+                       continue;
+               }
+               if (c == '"') {
+                       wpabuf_put_str(buf, "&quot;");
+                       continue;
+               }
+               /*
+                * We could try to represent control characters using the
+                * sequence: &#x; where x is replaced by a hex numeral, but not
+                * clear why we would do this.
+                */
+               wpabuf_put_u8(buf, c);
+       }
+}
+
+
+/* xml_add_tagged_data -- format tagged data as a new xml line.
+ *
+ * tag must not have any special chars.
+ * data may have special chars, which are escaped.
+ */
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data)
+{
+       wpabuf_printf(buf, "<%s>", tag);
+       xml_data_encode(buf, data, os_strlen(data));
+       wpabuf_printf(buf, "</%s>\n", tag);
+}
+
+
+/* A POST body looks something like (per upnp spec):
+ * <?xml version="1.0"?>
+ * <s:Envelope
+ *     xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
+ *     s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+ *   <s:Body>
+ *     <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v">
+ *       <argumentName>in arg value</argumentName>
+ *       other in args and their values go here, if any
+ *     </u:actionName>
+ *   </s:Body>
+ * </s:Envelope>
+ *
+ * where :
+ *      s: might be some other namespace name followed by colon
+ *      u: might be some other namespace name followed by colon
+ *      actionName will be replaced according to action requested
+ *      schema following actionName will be WFA scheme instead
+ *      argumentName will be actual argument name
+ *      (in arg value) will be actual argument value
+ */
+char * xml_get_first_item(const char *doc, const char *item)
+{
+       const char *match = item;
+       int match_len = os_strlen(item);
+       const char *tag, *tagname, *end;
+       char *value;
+
+       /*
+        * This is crude: ignore any possible tag name conflicts and go right
+        * to the first tag of this name. This should be ok for the limited
+        * domain of UPnP messages.
+        */
+       for (;;) {
+               if (xml_next_tag(doc, &tag, &tagname, &end))
+                       return NULL;
+               doc = end;
+               if (!os_strncasecmp(tagname, match, match_len) &&
+                   *tag != '/' &&
+                   (tagname[match_len] == '>' ||
+                    !isgraph(tagname[match_len]))) {
+                       break;
+               }
+       }
+       end = doc;
+       while (*end && *end != '<')
+               end++;
+       value = os_zalloc(1 + (end - doc));
+       if (value == NULL)
+               return NULL;
+       os_memcpy(value, doc, end - doc);
+       return value;
+}
+
+
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+                                   enum http_reply_code *ret)
+{
+       char *msg;
+       struct wpabuf *buf;
+       unsigned char *decoded;
+       size_t len;
+
+       msg = xml_get_first_item(data, name);
+       if (msg == NULL) {
+               *ret = UPNP_ARG_VALUE_INVALID;
+               return NULL;
+       }
+
+       decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len);
+       os_free(msg);
+       if (decoded == NULL) {
+               *ret = UPNP_OUT_OF_MEMORY;
+               return NULL;
+       }
+
+       buf = wpabuf_alloc_ext_data(decoded, len);
+       if (buf == NULL) {
+               os_free(decoded);
+               *ret = UPNP_OUT_OF_MEMORY;
+               return NULL;
+       }
+       return buf;
+}
diff --git a/src/wps/upnp_xml.h b/src/wps/upnp_xml.h
new file mode 100644 (file)
index 0000000..62dbe60
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * UPnP XML helper routines
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef UPNP_XML_H
+#define UPNP_XML_H
+
+#include "http.h"
+
+void xml_data_encode(struct wpabuf *buf, const char *data, int len);
+void xml_add_tagged_data(struct wpabuf *buf, const char *tag,
+                        const char *data);
+char * xml_get_first_item(const char *doc, const char *item);
+struct wpabuf * xml_get_base64_item(const char *data, const char *name,
+                                   enum http_reply_code *ret);
+
+#endif /* UPNP_XML_H */
diff --git a/src/wps/wps.c b/src/wps/wps.c
new file mode 100644 (file)
index 0000000..619af15
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-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 "common.h"
+#include "crypto/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+/**
+ * wps_init - Initialize WPS Registration protocol data
+ * @cfg: WPS configuration
+ * Returns: Pointer to allocated data or %NULL on failure
+ *
+ * This function is used to initialize WPS data for a registration protocol
+ * instance (i.e., each run of registration protocol as a Registrar of
+ * Enrollee. The caller is responsible for freeing this data after the
+ * registration run has been completed by calling wps_deinit().
+ */
+struct wps_data * wps_init(const struct wps_config *cfg)
+{
+       struct wps_data *data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->wps = cfg->wps;
+       data->registrar = cfg->registrar;
+       if (cfg->registrar) {
+               os_memcpy(data->uuid_r, cfg->wps->uuid, WPS_UUID_LEN);
+       } else {
+               os_memcpy(data->mac_addr_e, cfg->wps->dev.mac_addr, ETH_ALEN);
+               os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN);
+       }
+       if (cfg->pin) {
+               data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ?
+                       DEV_PW_DEFAULT : data->wps->oob_dev_pw_id;
+               data->dev_password = os_malloc(cfg->pin_len);
+               if (data->dev_password == NULL) {
+                       os_free(data);
+                       return NULL;
+               }
+               os_memcpy(data->dev_password, cfg->pin, cfg->pin_len);
+               data->dev_password_len = cfg->pin_len;
+       }
+
+       data->pbc = cfg->pbc;
+       if (cfg->pbc) {
+               /* Use special PIN '00000000' for PBC */
+               data->dev_pw_id = DEV_PW_PUSHBUTTON;
+               os_free(data->dev_password);
+               data->dev_password = os_malloc(8);
+               if (data->dev_password == NULL) {
+                       os_free(data);
+                       return NULL;
+               }
+               os_memset(data->dev_password, '0', 8);
+               data->dev_password_len = 8;
+       }
+
+       data->state = data->registrar ? RECV_M1 : SEND_M1;
+
+       if (cfg->assoc_wps_ie) {
+               struct wps_parse_attr attr;
+               wpa_hexdump_buf(MSG_DEBUG, "WPS: WPS IE from (Re)AssocReq",
+                               cfg->assoc_wps_ie);
+               if (wps_parse_msg(cfg->assoc_wps_ie, &attr) < 0) {
+                       wpa_printf(MSG_DEBUG, "WPS: Failed to parse WPS IE "
+                                  "from (Re)AssocReq");
+               } else if (attr.request_type == NULL) {
+                       wpa_printf(MSG_DEBUG, "WPS: No Request Type attribute "
+                                  "in (Re)AssocReq WPS IE");
+               } else {
+                       wpa_printf(MSG_DEBUG, "WPS: Request Type (from WPS IE "
+                                  "in (Re)AssocReq WPS IE): %d",
+                                  *attr.request_type);
+                       data->request_type = *attr.request_type;
+               }
+       }
+
+       if (cfg->new_ap_settings) {
+               data->new_ap_settings =
+                       os_malloc(sizeof(*data->new_ap_settings));
+               if (data->new_ap_settings == NULL) {
+                       os_free(data);
+                       return NULL;
+               }
+               os_memcpy(data->new_ap_settings, cfg->new_ap_settings,
+                         sizeof(*data->new_ap_settings));
+       }
+
+       if (cfg->peer_addr)
+               os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN);
+
+       data->use_psk_key = cfg->use_psk_key;
+
+       return data;
+}
+
+
+/**
+ * wps_deinit - Deinitialize WPS Registration protocol data
+ * @data: WPS Registration protocol data from wps_init()
+ */
+void wps_deinit(struct wps_data *data)
+{
+       if (data->wps_pin_revealed) {
+               wpa_printf(MSG_DEBUG, "WPS: Full PIN information revealed and "
+                          "negotiation failed");
+               if (data->registrar)
+                       wps_registrar_invalidate_pin(data->wps->registrar,
+                                                    data->uuid_e);
+       } else if (data->registrar)
+               wps_registrar_unlock_pin(data->wps->registrar, data->uuid_e);
+
+       wpabuf_free(data->dh_privkey);
+       wpabuf_free(data->dh_pubkey_e);
+       wpabuf_free(data->dh_pubkey_r);
+       wpabuf_free(data->last_msg);
+       os_free(data->dev_password);
+       os_free(data->new_psk);
+       wps_device_data_free(&data->peer_dev);
+       os_free(data->new_ap_settings);
+       dh5_free(data->dh_ctx);
+       os_free(data);
+}
+
+
+/**
+ * wps_process_msg - Process a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Message OP Code
+ * @msg: Message data
+ * Returns: Processing result
+ *
+ * This function is used to process WPS messages with OP Codes WSC_ACK,
+ * WSC_NACK, WSC_MSG, and WSC_Done. The caller (e.g., EAP server/peer) is
+ * responsible for reassembling the messages before calling this function.
+ * Response to this message is built by calling wps_get_msg().
+ */
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+                                    enum wsc_op_code op_code,
+                                    const struct wpabuf *msg)
+{
+       if (wps->registrar)
+               return wps_registrar_process_msg(wps, op_code, msg);
+       else
+               return wps_enrollee_process_msg(wps, op_code, msg);
+}
+
+
+/**
+ * wps_get_msg - Build a WPS message
+ * @wps: WPS Registration protocol data from wps_init()
+ * @op_code: Buffer for returning message OP Code
+ * Returns: The generated WPS message or %NULL on failure
+ *
+ * This function is used to build a response to a message processed by calling
+ * wps_process_msg(). The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code)
+{
+       if (wps->registrar)
+               return wps_registrar_get_msg(wps, op_code);
+       else
+               return wps_enrollee_get_msg(wps, op_code);
+}
+
+
+/**
+ * wps_is_selected_pbc_registrar - Check whether WPS IE indicates active PBC
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PBC Registrar is active, 0 if not
+ */
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       /*
+        * In theory, this could also verify that attr.sel_reg_config_methods
+        * includes WPS_CONFIG_PUSHBUTTON, but some deployed AP implementations
+        * do not set Selected Registrar Config Methods attribute properly, so
+        * it is safer to just use Device Password ID here.
+        */
+
+       if (wps_parse_msg(msg, &attr) < 0 ||
+           !attr.selected_registrar || *attr.selected_registrar == 0 ||
+           !attr.dev_password_id ||
+           WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+               return 0;
+
+       return 1;
+}
+
+
+/**
+ * wps_is_selected_pin_registrar - Check whether WPS IE indicates active PIN
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: 1 if PIN Registrar is active, 0 if not
+ */
+int wps_is_selected_pin_registrar(const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       /*
+        * In theory, this could also verify that attr.sel_reg_config_methods
+        * includes WPS_CONFIG_LABEL, WPS_CONFIG_DISPLAY, or WPS_CONFIG_KEYPAD,
+        * but some deployed AP implementations do not set Selected Registrar
+        * Config Methods attribute properly, so it is safer to just use
+        * Device Password ID here.
+        */
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return 0;
+
+       if (!attr.selected_registrar || *attr.selected_registrar == 0)
+               return 0;
+
+       if (attr.dev_password_id != NULL &&
+           WPA_GET_BE16(attr.dev_password_id) == DEV_PW_PUSHBUTTON)
+               return 0;
+
+       return 1;
+}
+
+
+/**
+ * wps_get_uuid_e - Get UUID-E from WPS IE
+ * @msg: WPS IE contents from Beacon or Probe Response frame
+ * Returns: Pointer to UUID-E or %NULL if not included
+ *
+ * The returned pointer is to the msg contents and it remains valid only as
+ * long as the msg buffer is valid.
+ */
+const u8 * wps_get_uuid_e(const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return NULL;
+       return attr.uuid_e;
+}
+
+
+/**
+ * wps_build_assoc_req_ie - Build WPS IE for (Re)Association Request
+ * @req_type: Value for Request Type attribute
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
+{
+       struct wpabuf *ie;
+       u8 *len;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+                  "Request");
+       ie = wpabuf_alloc(100);
+       if (ie == NULL)
+               return NULL;
+
+       wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+       len = wpabuf_put(ie, 1);
+       wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+       if (wps_build_version(ie) ||
+           wps_build_req_type(ie, req_type)) {
+               wpabuf_free(ie);
+               return NULL;
+       }
+
+       *len = wpabuf_len(ie) - 2;
+
+       return ie;
+}
+
+
+/**
+ * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_assoc_resp_ie(void)
+{
+       struct wpabuf *ie;
+       u8 *len;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association "
+                  "Response");
+       ie = wpabuf_alloc(100);
+       if (ie == NULL)
+               return NULL;
+
+       wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+       len = wpabuf_put(ie, 1);
+       wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+       if (wps_build_version(ie) ||
+           wps_build_resp_type(ie, WPS_RESP_AP)) {
+               wpabuf_free(ie);
+               return NULL;
+       }
+
+       *len = wpabuf_len(ie) - 2;
+
+       return ie;
+}
+
+
+/**
+ * wps_build_probe_req_ie - Build WPS IE for Probe Request
+ * @pbc: Whether searching for PBC mode APs
+ * @dev: Device attributes
+ * @uuid: Own UUID
+ * @req_type: Value for Request Type attribute
+ * Returns: WPS IE or %NULL on failure
+ *
+ * The caller is responsible for freeing the buffer.
+ */
+struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+                                      const u8 *uuid,
+                                      enum wps_request_type req_type)
+{
+       struct wpabuf *ie;
+       u8 *len;
+       u16 methods;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for Probe Request");
+
+       ie = wpabuf_alloc(200);
+       if (ie == NULL)
+               return NULL;
+
+       wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+       len = wpabuf_put(ie, 1);
+       wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+
+       if (pbc)
+               methods = WPS_CONFIG_PUSHBUTTON;
+       else {
+               methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY |
+                       WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS_UFD
+               methods |= WPS_CONFIG_USBA;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+               methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+       }
+
+       if (wps_build_version(ie) ||
+           wps_build_req_type(ie, req_type) ||
+           wps_build_config_methods(ie, methods) ||
+           wps_build_uuid_e(ie, uuid) ||
+           wps_build_primary_dev_type(dev, ie) ||
+           wps_build_rf_bands(dev, ie) ||
+           wps_build_assoc_state(NULL, ie) ||
+           wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
+           wps_build_dev_password_id(ie, pbc ? DEV_PW_PUSHBUTTON :
+                                     DEV_PW_DEFAULT)) {
+               wpabuf_free(ie);
+               return NULL;
+       }
+
+       *len = wpabuf_len(ie) - 2;
+
+       return ie;
+}
+
+
+void wps_free_pending_msgs(struct upnp_pending_message *msgs)
+{
+       struct upnp_pending_message *p, *prev;
+       p = msgs;
+       while (p) {
+               prev = p;
+               p = p->next;
+               wpabuf_free(prev->msg);
+               os_free(prev);
+       }
+}
+
+
+int wps_attr_text(struct wpabuf *data, char *buf, char *end)
+{
+       struct wps_parse_attr attr;
+       char *pos = buf;
+       int ret;
+
+       if (wps_parse_msg(data, &attr) < 0)
+               return -1;
+
+       if (attr.wps_state) {
+               if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED)
+                       ret = os_snprintf(pos, end - pos,
+                                         "wps_state=unconfigured\n");
+               else if (*attr.wps_state == WPS_STATE_CONFIGURED)
+                       ret = os_snprintf(pos, end - pos,
+                                         "wps_state=configured\n");
+               else
+                       ret = 0;
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.ap_setup_locked && *attr.ap_setup_locked) {
+               ret = os_snprintf(pos, end - pos,
+                                 "wps_ap_setup_locked=1\n");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.selected_registrar && *attr.selected_registrar) {
+               ret = os_snprintf(pos, end - pos,
+                                 "wps_selected_registrar=1\n");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.dev_password_id) {
+               ret = os_snprintf(pos, end - pos,
+                                 "wps_device_password_id=%u\n",
+                                 WPA_GET_BE16(attr.dev_password_id));
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.sel_reg_config_methods) {
+               ret = os_snprintf(pos, end - pos,
+                                 "wps_selected_registrar_config_methods="
+                                 "0x%04x\n",
+                                 WPA_GET_BE16(attr.sel_reg_config_methods));
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.primary_dev_type) {
+               char devtype[WPS_DEV_TYPE_BUFSIZE];
+               ret = os_snprintf(pos, end - pos,
+                                 "wps_primary_device_type=%s\n",
+                                 wps_dev_type_bin2str(attr.primary_dev_type,
+                                                      devtype,
+                                                      sizeof(devtype)));
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.dev_name) {
+               char *str = os_malloc(attr.dev_name_len + 1);
+               size_t i;
+               if (str == NULL)
+                       return pos - buf;
+               for (i = 0; i < attr.dev_name_len; i++) {
+                       if (attr.dev_name[i] < 32)
+                               str[i] = '_';
+                       else
+                               str[i] = attr.dev_name[i];
+               }
+               str[i] = '\0';
+               ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
+               os_free(str);
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (attr.config_methods) {
+               ret = os_snprintf(pos, end - pos,
+                                 "wps_config_methods=0x%04x\n",
+                                 WPA_GET_BE16(attr.config_methods));
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
diff --git a/src/wps/wps.h b/src/wps/wps.h
new file mode 100644 (file)
index 0000000..1fd1e52
--- /dev/null
@@ -0,0 +1,736 @@
+/*
+ * Wi-Fi Protected Setup
+ * Copyright (c) 2007-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.
+ */
+
+#ifndef WPS_H
+#define WPS_H
+
+#include "wps_defs.h"
+
+/**
+ * enum wsc_op_code - EAP-WSC OP-Code values
+ */
+enum wsc_op_code {
+       WSC_UPnP = 0 /* No OP Code in UPnP transport */,
+       WSC_Start = 0x01,
+       WSC_ACK = 0x02,
+       WSC_NACK = 0x03,
+       WSC_MSG = 0x04,
+       WSC_Done = 0x05,
+       WSC_FRAG_ACK = 0x06
+};
+
+struct wps_registrar;
+struct upnp_wps_device_sm;
+struct wps_er;
+
+/**
+ * struct wps_credential - WPS Credential
+ * @ssid: SSID
+ * @ssid_len: Length of SSID
+ * @auth_type: Authentication Type (WPS_AUTH_OPEN, .. flags)
+ * @encr_type: Encryption Type (WPS_ENCR_NONE, .. flags)
+ * @key_idx: Key index
+ * @key: Key
+ * @key_len: Key length in octets
+ * @mac_addr: MAC address of the Credential receiver
+ * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
+ *     this may be %NULL, if not used
+ * @cred_attr_len: Length of cred_attr in octets
+ */
+struct wps_credential {
+       u8 ssid[32];
+       size_t ssid_len;
+       u16 auth_type;
+       u16 encr_type;
+       u8 key_idx;
+       u8 key[64];
+       size_t key_len;
+       u8 mac_addr[ETH_ALEN];
+       const u8 *cred_attr;
+       size_t cred_attr_len;
+};
+
+#define WPS_DEV_TYPE_LEN 8
+#define WPS_DEV_TYPE_BUFSIZE 21
+
+/**
+ * struct wps_device_data - WPS Device Data
+ * @mac_addr: Device MAC address
+ * @device_name: Device Name (0..32 octets encoded in UTF-8)
+ * @manufacturer: Manufacturer (0..64 octets encoded in UTF-8)
+ * @model_name: Model Name (0..32 octets encoded in UTF-8)
+ * @model_number: Model Number (0..32 octets encoded in UTF-8)
+ * @serial_number: Serial Number (0..32 octets encoded in UTF-8)
+ * @pri_dev_type: Primary Device Type
+ * @os_version: OS Version
+ * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags)
+ */
+struct wps_device_data {
+       u8 mac_addr[ETH_ALEN];
+       char *device_name;
+       char *manufacturer;
+       char *model_name;
+       char *model_number;
+       char *serial_number;
+       u8 pri_dev_type[WPS_DEV_TYPE_LEN];
+       u32 os_version;
+       u8 rf_bands;
+};
+
+struct oob_conf_data {
+       enum {
+               OOB_METHOD_UNKNOWN = 0,
+               OOB_METHOD_DEV_PWD_E,
+               OOB_METHOD_DEV_PWD_R,
+               OOB_METHOD_CRED,
+       } oob_method;
+       struct wpabuf *dev_password;
+       struct wpabuf *pubkey_hash;
+};
+
+/**
+ * struct wps_config - WPS configuration for a single registration protocol run
+ */
+struct wps_config {
+       /**
+        * wps - Pointer to long term WPS context
+        */
+       struct wps_context *wps;
+
+       /**
+        * registrar - Whether this end is a Registrar
+        */
+       int registrar;
+
+       /**
+        * pin - Enrollee Device Password (%NULL for Registrar or PBC)
+        */
+       const u8 *pin;
+
+       /**
+        * pin_len - Length on pin in octets
+        */
+       size_t pin_len;
+
+       /**
+        * pbc - Whether this is protocol run uses PBC
+        */
+       int pbc;
+
+       /**
+        * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP)
+        */
+       const struct wpabuf *assoc_wps_ie;
+
+       /**
+        * new_ap_settings - New AP settings (%NULL if not used)
+        *
+        * This parameter provides new AP settings when using a wireless
+        * stations as a Registrar to configure the AP. %NULL means that AP
+        * will not be reconfigured, i.e., the station will only learn the
+        * current AP settings by using AP PIN.
+        */
+       const struct wps_credential *new_ap_settings;
+
+       /**
+        * peer_addr: MAC address of the peer in AP; %NULL if not AP
+        */
+       const u8 *peer_addr;
+
+       /**
+        * use_psk_key - Use PSK format key in Credential
+        *
+        * Force PSK format to be used instead of ASCII passphrase when
+        * building Credential for an Enrollee. The PSK value is set in
+        * struct wpa_context::psk.
+        */
+       int use_psk_key;
+};
+
+struct wps_data * wps_init(const struct wps_config *cfg);
+
+void wps_deinit(struct wps_data *data);
+
+/**
+ * enum wps_process_res - WPS message processing result
+ */
+enum wps_process_res {
+       /**
+        * WPS_DONE - Processing done
+        */
+       WPS_DONE,
+
+       /**
+        * WPS_CONTINUE - Processing continues
+        */
+       WPS_CONTINUE,
+
+       /**
+        * WPS_FAILURE - Processing failed
+        */
+       WPS_FAILURE,
+
+       /**
+        * WPS_PENDING - Processing continues, but waiting for an external
+        *      event (e.g., UPnP message from an external Registrar)
+        */
+       WPS_PENDING
+};
+enum wps_process_res wps_process_msg(struct wps_data *wps,
+                                    enum wsc_op_code op_code,
+                                    const struct wpabuf *msg);
+
+struct wpabuf * wps_get_msg(struct wps_data *wps, enum wsc_op_code *op_code);
+
+int wps_is_selected_pbc_registrar(const struct wpabuf *msg);
+int wps_is_selected_pin_registrar(const struct wpabuf *msg);
+const u8 * wps_get_uuid_e(const struct wpabuf *msg);
+
+struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type);
+struct wpabuf * wps_build_assoc_resp_ie(void);
+struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev,
+                                      const u8 *uuid,
+                                      enum wps_request_type req_type);
+
+
+/**
+ * struct wps_registrar_config - WPS Registrar configuration
+ */
+struct wps_registrar_config {
+       /**
+        * new_psk_cb - Callback for new PSK
+        * @ctx: Higher layer context data (cb_ctx)
+        * @mac_addr: MAC address of the Enrollee
+        * @psk: The new PSK
+        * @psk_len: The length of psk in octets
+        * Returns: 0 on success, -1 on failure
+        *
+        * This callback is called when a new per-device PSK is provisioned.
+        */
+       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+                         size_t psk_len);
+
+       /**
+        * set_ie_cb - Callback for WPS IE changes
+        * @ctx: Higher layer context data (cb_ctx)
+        * @beacon_ie: WPS IE for Beacon
+        * @probe_resp_ie: WPS IE for Probe Response
+        * Returns: 0 on success, -1 on failure
+        *
+        * This callback is called whenever the WPS IE in Beacon or Probe
+        * Response frames needs to be changed (AP only). Callee is responsible
+        * for freeing the buffers.
+        */
+       int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+                        struct wpabuf *probe_resp_ie);
+
+       /**
+        * pin_needed_cb - Callback for requesting a PIN
+        * @ctx: Higher layer context data (cb_ctx)
+        * @uuid_e: UUID-E of the unknown Enrollee
+        * @dev: Device Data from the unknown Enrollee
+        *
+        * This callback is called whenever an unknown Enrollee requests to use
+        * PIN method and a matching PIN (Device Password) is not found in
+        * Registrar data.
+        */
+       void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+                             const struct wps_device_data *dev);
+
+       /**
+        * reg_success_cb - Callback for reporting successful registration
+        * @ctx: Higher layer context data (cb_ctx)
+        * @mac_addr: MAC address of the Enrollee
+        * @uuid_e: UUID-E of the Enrollee
+        *
+        * This callback is called whenever an Enrollee completes registration
+        * successfully.
+        */
+       void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+                              const u8 *uuid_e);
+
+       /**
+        * set_sel_reg_cb - Callback for reporting selected registrar changes
+        * @ctx: Higher layer context data (cb_ctx)
+        * @sel_reg: Whether the Registrar is selected
+        * @dev_passwd_id: Device Password ID to indicate with method or
+        *      specific password the Registrar intends to use
+        * @sel_reg_config_methods: Bit field of active config methods
+        *
+        * This callback is called whenever the Selected Registrar state
+        * changes (e.g., a new PIN becomes available or PBC is invoked). This
+        * callback is only used by External Registrar implementation;
+        * set_ie_cb() is used by AP implementation in similar caes, but it
+        * provides the full WPS IE data instead of just the minimal Registrar
+        * state information.
+        */
+       void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+                              u16 sel_reg_config_methods);
+
+       /**
+        * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq
+        * @ctx: Higher layer context data (cb_ctx)
+        * @addr: MAC address of the Enrollee
+        * @uuid_e: UUID of the Enrollee
+        * @pri_dev_type: Primary device type
+        * @config_methods: Config Methods
+        * @dev_password_id: Device Password ID
+        * @request_type: Request Type
+        * @dev_name: Device Name (if available)
+        */
+       void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+                                const u8 *pri_dev_type, u16 config_methods,
+                                u16 dev_password_id, u8 request_type,
+                                const char *dev_name);
+
+       /**
+        * cb_ctx: Higher layer context data for Registrar callbacks
+        */
+       void *cb_ctx;
+
+       /**
+        * skip_cred_build: Do not build credential
+        *
+        * This option can be used to disable internal code that builds
+        * Credential attribute into M8 based on the current network
+        * configuration and Enrollee capabilities. The extra_cred data will
+        * then be used as the Credential(s).
+        */
+       int skip_cred_build;
+
+       /**
+        * extra_cred: Additional Credential attribute(s)
+        *
+        * This optional data (set to %NULL to disable) can be used to add
+        * Credential attribute(s) for other networks into M8. If
+        * skip_cred_build is set, this will also override the automatically
+        * generated Credential attribute.
+        */
+       const u8 *extra_cred;
+
+       /**
+        * extra_cred_len: Length of extra_cred in octets
+        */
+       size_t extra_cred_len;
+
+       /**
+        * disable_auto_conf - Disable auto-configuration on first registration
+        *
+        * By default, the AP that is started in not configured state will
+        * generate a random PSK and move to configured state when the first
+        * registration protocol run is completed successfully. This option can
+        * be used to disable this functionality and leave it up to an external
+        * program to take care of configuration. This requires the extra_cred
+        * to be set with a suitable Credential and skip_cred_build being used.
+        */
+       int disable_auto_conf;
+
+       /**
+        * static_wep_only - Whether the BSS supports only static WEP
+        */
+       int static_wep_only;
+};
+
+
+/**
+ * enum wps_event - WPS event types
+ */
+enum wps_event {
+       /**
+        * WPS_EV_M2D - M2D received (Registrar did not know us)
+        */
+       WPS_EV_M2D,
+
+       /**
+        * WPS_EV_FAIL - Registration failed
+        */
+       WPS_EV_FAIL,
+
+       /**
+        * WPS_EV_SUCCESS - Registration succeeded
+        */
+       WPS_EV_SUCCESS,
+
+       /**
+        * WPS_EV_PWD_AUTH_FAIL - Password authentication failed
+        */
+       WPS_EV_PWD_AUTH_FAIL,
+
+       /**
+        * WPS_EV_PBC_OVERLAP - PBC session overlap detected
+        */
+       WPS_EV_PBC_OVERLAP,
+
+       /**
+        * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start
+        */
+       WPS_EV_PBC_TIMEOUT,
+
+       /**
+        * WPS_EV_ER_AP_ADD - ER: AP added
+        */
+       WPS_EV_ER_AP_ADD,
+
+       /**
+        * WPS_EV_ER_AP_REMOVE - ER: AP removed
+        */
+       WPS_EV_ER_AP_REMOVE,
+
+       /**
+        * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added
+        */
+       WPS_EV_ER_ENROLLEE_ADD,
+
+       /**
+        * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed
+        */
+       WPS_EV_ER_ENROLLEE_REMOVE
+};
+
+/**
+ * union wps_event_data - WPS event data
+ */
+union wps_event_data {
+       /**
+        * struct wps_event_m2d - M2D event data
+        */
+       struct wps_event_m2d {
+               u16 config_methods;
+               const u8 *manufacturer;
+               size_t manufacturer_len;
+               const u8 *model_name;
+               size_t model_name_len;
+               const u8 *model_number;
+               size_t model_number_len;
+               const u8 *serial_number;
+               size_t serial_number_len;
+               const u8 *dev_name;
+               size_t dev_name_len;
+               const u8 *primary_dev_type; /* 8 octets */
+               u16 config_error;
+               u16 dev_password_id;
+       } m2d;
+
+       /**
+        * struct wps_event_fail - Registration failure information
+        * @msg: enum wps_msg_type
+        */
+       struct wps_event_fail {
+               int msg;
+       } fail;
+
+       struct wps_event_pwd_auth_fail {
+               int enrollee;
+               int part;
+       } pwd_auth_fail;
+
+       struct wps_event_er_ap {
+               const u8 *uuid;
+               const u8 *mac_addr;
+               const char *friendly_name;
+               const char *manufacturer;
+               const char *manufacturer_url;
+               const char *model_description;
+               const char *model_name;
+               const char *model_number;
+               const char *model_url;
+               const char *serial_number;
+               const char *upc;
+               const u8 *pri_dev_type;
+               u8 wps_state;
+       } ap;
+
+       struct wps_event_er_enrollee {
+               const u8 *uuid;
+               const u8 *mac_addr;
+               int m1_received;
+               u16 config_methods;
+               u16 dev_passwd_id;
+               const u8 *pri_dev_type;
+               const char *dev_name;
+               const char *manufacturer;
+               const char *model_name;
+               const char *model_number;
+               const char *serial_number;
+       } enrollee;
+};
+
+/**
+ * struct upnp_pending_message - Pending PutWLANResponse messages
+ * @next: Pointer to next pending message or %NULL
+ * @addr: NewWLANEventMAC
+ * @msg: NewMessage
+ * @type: Message Type
+ */
+struct upnp_pending_message {
+       struct upnp_pending_message *next;
+       u8 addr[ETH_ALEN];
+       struct wpabuf *msg;
+       enum wps_msg_type type;
+};
+
+/**
+ * struct wps_context - Long term WPS context data
+ *
+ * This data is stored at the higher layer Authenticator or Supplicant data
+ * structures and it is maintained over multiple registration protocol runs.
+ */
+struct wps_context {
+       /**
+        * ap - Whether the local end is an access point
+        */
+       int ap;
+
+       /**
+        * registrar - Pointer to WPS registrar data from wps_registrar_init()
+        */
+       struct wps_registrar *registrar;
+
+       /**
+        * wps_state - Current WPS state
+        */
+       enum wps_state wps_state;
+
+       /**
+        * ap_setup_locked - Whether AP setup is locked (only used at AP)
+        */
+       int ap_setup_locked;
+
+       /**
+        * uuid - Own UUID
+        */
+       u8 uuid[16];
+
+       /**
+        * ssid - SSID
+        *
+        * This SSID is used by the Registrar to fill in information for
+        * Credentials. In addition, AP uses it when acting as an Enrollee to
+        * notify Registrar of the current configuration.
+        */
+       u8 ssid[32];
+
+       /**
+        * ssid_len - Length of ssid in octets
+        */
+       size_t ssid_len;
+
+       /**
+        * dev - Own WPS device data
+        */
+       struct wps_device_data dev;
+
+       /**
+        * oob_conf - OOB Config data
+        */
+       struct oob_conf_data oob_conf;
+
+       /**
+        * oob_dev_pw_id - OOB Device password id
+        */
+       u16 oob_dev_pw_id;
+
+       /**
+        * dh_ctx - Context data for Diffie-Hellman operation
+        */
+       void *dh_ctx;
+
+       /**
+        * dh_privkey - Diffie-Hellman private key
+        */
+       struct wpabuf *dh_privkey;
+
+       /**
+        * dh_pubkey_oob - Diffie-Hellman public key
+        */
+       struct wpabuf *dh_pubkey;
+
+       /**
+        * config_methods - Enabled configuration methods
+        *
+        * Bit field of WPS_CONFIG_*
+        */
+       u16 config_methods;
+
+       /**
+        * encr_types - Enabled encryption types (bit field of WPS_ENCR_*)
+        */
+       u16 encr_types;
+
+       /**
+        * auth_types - Authentication types (bit field of WPS_AUTH_*)
+        */
+       u16 auth_types;
+
+       /**
+        * network_key - The current Network Key (PSK) or %NULL to generate new
+        *
+        * If %NULL, Registrar will generate per-device PSK. In addition, AP
+        * uses this when acting as an Enrollee to notify Registrar of the
+        * current configuration.
+        *
+        * When using WPA/WPA2-Person, this key can be either the ASCII
+        * passphrase (8..63 characters) or the 32-octet PSK (64 hex
+        * characters). When this is set to the ASCII passphrase, the PSK can
+        * be provided in the psk buffer and used per-Enrollee to control which
+        * key type is included in the Credential (e.g., to reduce calculation
+        * need on low-powered devices by provisioning PSK while still allowing
+        * other devices to get the passphrase).
+        */
+       u8 *network_key;
+
+       /**
+        * network_key_len - Length of network_key in octets
+        */
+       size_t network_key_len;
+
+       /**
+        * psk - The current network PSK
+        *
+        * This optional value can be used to provide the current PSK if
+        * network_key is set to the ASCII passphrase.
+        */
+       u8 psk[32];
+
+       /**
+        * psk_set - Whether psk value is set
+        */
+       int psk_set;
+
+       /**
+        * ap_settings - AP Settings override for M7 (only used at AP)
+        *
+        * If %NULL, AP Settings attributes will be generated based on the
+        * current network configuration.
+        */
+       u8 *ap_settings;
+
+       /**
+        * ap_settings_len - Length of ap_settings in octets
+        */
+       size_t ap_settings_len;
+
+       /**
+        * friendly_name - Friendly Name (required for UPnP)
+        */
+       char *friendly_name;
+
+       /**
+        * manufacturer_url - Manufacturer URL (optional for UPnP)
+        */
+       char *manufacturer_url;
+
+       /**
+        * model_description - Model Description (recommended for UPnP)
+        */
+       char *model_description;
+
+       /**
+        * model_url - Model URL (optional for UPnP)
+        */
+       char *model_url;
+
+       /**
+        * upc - Universal Product Code (optional for UPnP)
+        */
+       char *upc;
+
+       /**
+        * cred_cb - Callback to notify that new Credentials were received
+        * @ctx: Higher layer context data (cb_ctx)
+        * @cred: The received Credential
+        * Return: 0 on success, -1 on failure
+        */
+       int (*cred_cb)(void *ctx, const struct wps_credential *cred);
+
+       /**
+        * event_cb - Event callback (state information about progress)
+        * @ctx: Higher layer context data (cb_ctx)
+        * @event: Event type
+        * @data: Event data
+        */
+       void (*event_cb)(void *ctx, enum wps_event event,
+                        union wps_event_data *data);
+
+       /**
+        * cb_ctx: Higher layer context data for callbacks
+        */
+       void *cb_ctx;
+
+       struct upnp_wps_device_sm *wps_upnp;
+
+       /* Pending messages from UPnP PutWLANResponse */
+       struct upnp_pending_message *upnp_msgs;
+};
+
+struct oob_device_data {
+       char *device_name;
+       char *device_path;
+       void * (*init_func)(struct wps_context *, struct oob_device_data *,
+                           int);
+       struct wpabuf * (*read_func)(void *);
+       int (*write_func)(void *, struct wpabuf *);
+       void (*deinit_func)(void *);
+};
+
+struct oob_nfc_device_data {
+       int (*init_func)(char *);
+       void * (*read_func)(size_t *);
+       int (*write_func)(void *, size_t);
+       void (*deinit_func)(void);
+};
+
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+                  const struct wps_registrar_config *cfg);
+void wps_registrar_deinit(struct wps_registrar *reg);
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
+                         const u8 *pin, size_t pin_len, int timeout);
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid);
+int wps_registrar_button_pushed(struct wps_registrar *reg);
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+                               const struct wpabuf *wps_data);
+int wps_registrar_update_ie(struct wps_registrar *reg);
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+                          char *buf, size_t buflen);
+
+unsigned int wps_pin_checksum(unsigned int pin);
+unsigned int wps_pin_valid(unsigned int pin);
+unsigned int wps_generate_pin(void);
+void wps_free_pending_msgs(struct upnp_pending_message *msgs);
+
+struct oob_device_data * wps_get_oob_device(char *device_type);
+struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name);
+int wps_get_oob_method(char *method);
+int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
+                   int registrar);
+int wps_attr_text(struct wpabuf *data, char *buf, char *end);
+
+struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname);
+void wps_er_refresh(struct wps_er *er);
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx);
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+                       u16 sel_reg_config_methods);
+int wps_er_pbc(struct wps_er *er, const u8 *uuid);
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
+                size_t pin_len);
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+                           size_t buf_len);
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid);
+u16 wps_config_methods_str2bin(const char *str);
+
+#endif /* WPS_H */
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
new file mode 100644 (file)
index 0000000..9da556a
--- /dev/null
@@ -0,0 +1,322 @@
+/*
+ * Wi-Fi Protected Setup - attribute building
+ * 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 "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha256.h"
+#include "wps_i.h"
+
+
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
+{
+       struct wpabuf *pubkey;
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Public Key");
+       wpabuf_free(wps->dh_privkey);
+       if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) {
+               wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys");
+               wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey);
+               wps->dh_ctx = wps->wps->dh_ctx;
+               wps->wps->dh_ctx = NULL;
+               pubkey = wpabuf_dup(wps->wps->dh_pubkey);
+       } else {
+               wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
+               wps->dh_privkey = NULL;
+               dh5_free(wps->dh_ctx);
+               wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey);
+               pubkey = wpabuf_zeropad(pubkey, 192);
+       }
+       if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to initialize "
+                          "Diffie-Hellman handshake");
+               wpabuf_free(pubkey);
+               return -1;
+       }
+
+       wpabuf_put_be16(msg, ATTR_PUBLIC_KEY);
+       wpabuf_put_be16(msg, wpabuf_len(pubkey));
+       wpabuf_put_buf(msg, pubkey);
+
+       if (wps->registrar) {
+               wpabuf_free(wps->dh_pubkey_r);
+               wps->dh_pubkey_r = pubkey;
+       } else {
+               wpabuf_free(wps->dh_pubkey_e);
+               wps->dh_pubkey_e = pubkey;
+       }
+
+       return 0;
+}
+
+
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Request Type");
+       wpabuf_put_be16(msg, ATTR_REQUEST_TYPE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, type);
+       return 0;
+}
+
+
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Response Type (%d)", type);
+       wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, type);
+       return 0;
+}
+
+
+int wps_build_config_methods(struct wpabuf *msg, u16 methods)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
+       wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, methods);
+       return 0;
+}
+
+
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * UUID-E");
+       wpabuf_put_be16(msg, ATTR_UUID_E);
+       wpabuf_put_be16(msg, WPS_UUID_LEN);
+       wpabuf_put_data(msg, uuid, WPS_UUID_LEN);
+       return 0;
+}
+
+
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
+       wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, id);
+       return 0;
+}
+
+
+int wps_build_config_error(struct wpabuf *msg, u16 err)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Configuration Error (%d)", err);
+       wpabuf_put_be16(msg, ATTR_CONFIG_ERROR);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, err);
+       return 0;
+}
+
+
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       if (wps->last_msg == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+                          "building authenticator");
+               return -1;
+       }
+
+       /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+        * (M_curr* is M_curr without the Authenticator attribute)
+        */
+       addr[0] = wpabuf_head(wps->last_msg);
+       len[0] = wpabuf_len(wps->last_msg);
+       addr[1] = wpabuf_head(msg);
+       len[1] = wpabuf_len(msg);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Authenticator");
+       wpabuf_put_be16(msg, ATTR_AUTHENTICATOR);
+       wpabuf_put_be16(msg, WPS_AUTHENTICATOR_LEN);
+       wpabuf_put_data(msg, hash, WPS_AUTHENTICATOR_LEN);
+
+       return 0;
+}
+
+
+int wps_build_version(struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Version");
+       wpabuf_put_be16(msg, ATTR_VERSION);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, WPS_VERSION);
+       return 0;
+}
+
+
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Message Type (%d)", msg_type);
+       wpabuf_put_be16(msg, ATTR_MSG_TYPE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, msg_type);
+       return 0;
+}
+
+
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Enrollee Nonce");
+       wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+       wpabuf_put_be16(msg, WPS_NONCE_LEN);
+       wpabuf_put_data(msg, wps->nonce_e, WPS_NONCE_LEN);
+       return 0;
+}
+
+
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Registrar Nonce");
+       wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+       wpabuf_put_be16(msg, WPS_NONCE_LEN);
+       wpabuf_put_data(msg, wps->nonce_r, WPS_NONCE_LEN);
+       return 0;
+}
+
+
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
+       wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, WPS_AUTH_TYPES);
+       return 0;
+}
+
+
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
+       wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, WPS_ENCR_TYPES);
+       return 0;
+}
+
+
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Connection Type Flags");
+       wpabuf_put_be16(msg, ATTR_CONN_TYPE_FLAGS);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, WPS_CONN_ESS);
+       return 0;
+}
+
+
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Association State");
+       wpabuf_put_be16(msg, ATTR_ASSOC_STATE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, WPS_ASSOC_NOT_ASSOC);
+       return 0;
+}
+
+
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 hash[SHA256_MAC_LEN];
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Key Wrap Authenticator");
+       hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, wpabuf_head(msg),
+                   wpabuf_len(msg), hash);
+
+       wpabuf_put_be16(msg, ATTR_KEY_WRAP_AUTH);
+       wpabuf_put_be16(msg, WPS_KWA_LEN);
+       wpabuf_put_data(msg, hash, WPS_KWA_LEN);
+       return 0;
+}
+
+
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+                           struct wpabuf *plain)
+{
+       size_t pad_len;
+       const size_t block_size = 16;
+       u8 *iv, *data;
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Encrypted Settings");
+
+       /* PKCS#5 v2.0 pad */
+       pad_len = block_size - wpabuf_len(plain) % block_size;
+       os_memset(wpabuf_put(plain, pad_len), pad_len, pad_len);
+
+       wpabuf_put_be16(msg, ATTR_ENCR_SETTINGS);
+       wpabuf_put_be16(msg, block_size + wpabuf_len(plain));
+
+       iv = wpabuf_put(msg, block_size);
+       if (os_get_random(iv, block_size) < 0)
+               return -1;
+
+       data = wpabuf_put(msg, 0);
+       wpabuf_put_buf(msg, plain);
+       if (aes_128_cbc_encrypt(wps->keywrapkey, iv, data, wpabuf_len(plain)))
+               return -1;
+
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps)
+{
+       size_t hash_len;
+       const u8 *addr[1];
+       u8 pubkey_hash[WPS_HASH_LEN];
+       u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN];
+
+       wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password");
+
+       addr[0] = wpabuf_head(wps->dh_pubkey);
+       hash_len = wpabuf_len(wps->dh_pubkey);
+       sha256_vector(1, addr, &hash_len, pubkey_hash);
+
+       if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) {
+               wpa_printf(MSG_ERROR, "WPS: device password id "
+                          "generation error");
+               return -1;
+       }
+       wps->oob_dev_pw_id |= 0x0010;
+
+       if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) {
+               wpa_printf(MSG_ERROR, "WPS: OOB device password "
+                          "generation error");
+               return -1;
+       }
+
+       wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
+       wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+       wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+       wpabuf_put_be16(msg, wps->oob_dev_pw_id);
+       wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+       wpa_snprintf_hex_uppercase(
+               wpabuf_put(wps->oob_conf.dev_password,
+                          wpabuf_size(wps->oob_conf.dev_password)),
+               wpabuf_size(wps->oob_conf.dev_password),
+               dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN);
+
+       return 0;
+}
+#endif /* CONFIG_WPS_OOB */
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
new file mode 100644 (file)
index 0000000..30b0e79
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Wi-Fi Protected Setup - attribute parsing
+ * 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 "common.h"
+#include "wps_i.h"
+
+#define WPS_WORKAROUNDS
+
+
+static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
+                       const u8 *pos, u16 len)
+{
+       switch (type) {
+       case ATTR_VERSION:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Version length %u",
+                                  len);
+                       return -1;
+               }
+               attr->version = pos;
+               break;
+       case ATTR_MSG_TYPE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->msg_type = pos;
+               break;
+       case ATTR_ENROLLEE_NONCE:
+               if (len != WPS_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->enrollee_nonce = pos;
+               break;
+       case ATTR_REGISTRAR_NONCE:
+               if (len != WPS_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->registrar_nonce = pos;
+               break;
+       case ATTR_UUID_E:
+               if (len != WPS_UUID_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-E length %u",
+                                  len);
+                       return -1;
+               }
+               attr->uuid_e = pos;
+               break;
+       case ATTR_UUID_R:
+               if (len != WPS_UUID_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid UUID-R length %u",
+                                  len);
+                       return -1;
+               }
+               attr->uuid_r = pos;
+               break;
+       case ATTR_AUTH_TYPE_FLAGS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+                                  "Type Flags length %u", len);
+                       return -1;
+               }
+               attr->auth_type_flags = pos;
+               break;
+       case ATTR_ENCR_TYPE_FLAGS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption Type "
+                                  "Flags length %u", len);
+                       return -1;
+               }
+               attr->encr_type_flags = pos;
+               break;
+       case ATTR_CONN_TYPE_FLAGS:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Connection Type "
+                                  "Flags length %u", len);
+                       return -1;
+               }
+               attr->conn_type_flags = pos;
+               break;
+       case ATTR_CONFIG_METHODS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Config Methods "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->config_methods = pos;
+               break;
+       case ATTR_SELECTED_REGISTRAR_CONFIG_METHODS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Selected "
+                                  "Registrar Config Methods length %u", len);
+                       return -1;
+               }
+               attr->sel_reg_config_methods = pos;
+               break;
+       case ATTR_PRIMARY_DEV_TYPE:
+               if (len != WPS_DEV_TYPE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->primary_dev_type = pos;
+               break;
+       case ATTR_RF_BANDS:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid RF Bands length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->rf_bands = pos;
+               break;
+       case ATTR_ASSOC_STATE:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Association State "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->assoc_state = pos;
+               break;
+       case ATTR_CONFIG_ERROR:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Configuration "
+                                  "Error length %u", len);
+                       return -1;
+               }
+               attr->config_error = pos;
+               break;
+       case ATTR_DEV_PASSWORD_ID:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Device Password "
+                                  "ID length %u", len);
+                       return -1;
+               }
+               attr->dev_password_id = pos;
+               break;
+       case ATTR_OOB_DEVICE_PASSWORD:
+               if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
+                                  "Password length %u", len);
+                       return -1;
+               }
+               attr->oob_dev_password = pos;
+               break;
+       case ATTR_OS_VERSION:
+               if (len != 4) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->os_version = pos;
+               break;
+       case ATTR_WPS_STATE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Wi-Fi Protected "
+                                  "Setup State length %u", len);
+                       return -1;
+               }
+               attr->wps_state = pos;
+               break;
+       case ATTR_AUTHENTICATOR:
+               if (len != WPS_AUTHENTICATOR_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Authenticator "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->authenticator = pos;
+               break;
+       case ATTR_R_HASH1:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash1 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->r_hash1 = pos;
+               break;
+       case ATTR_R_HASH2:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-Hash2 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->r_hash2 = pos;
+               break;
+       case ATTR_E_HASH1:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash1 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->e_hash1 = pos;
+               break;
+       case ATTR_E_HASH2:
+               if (len != WPS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-Hash2 length %u",
+                                  len);
+                       return -1;
+               }
+               attr->e_hash2 = pos;
+               break;
+       case ATTR_R_SNONCE1:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce1 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->r_snonce1 = pos;
+               break;
+       case ATTR_R_SNONCE2:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid R-SNonce2 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->r_snonce2 = pos;
+               break;
+       case ATTR_E_SNONCE1:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce1 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->e_snonce1 = pos;
+               break;
+       case ATTR_E_SNONCE2:
+               if (len != WPS_SECRET_NONCE_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid E-SNonce2 length "
+                                  "%u", len);
+                       return -1;
+               }
+               attr->e_snonce2 = pos;
+               break;
+       case ATTR_KEY_WRAP_AUTH:
+               if (len != WPS_KWA_LEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Key Wrap "
+                                  "Authenticator length %u", len);
+                       return -1;
+               }
+               attr->key_wrap_auth = pos;
+               break;
+       case ATTR_AUTH_TYPE:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Authentication "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->auth_type = pos;
+               break;
+       case ATTR_ENCR_TYPE:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Encryption "
+                                  "Type length %u", len);
+                       return -1;
+               }
+               attr->encr_type = pos;
+               break;
+       case ATTR_NETWORK_INDEX:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Network Index "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->network_idx = pos;
+               break;
+       case ATTR_NETWORK_KEY_INDEX:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Network Key Index "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->network_key_idx = pos;
+               break;
+       case ATTR_MAC_ADDR:
+               if (len != ETH_ALEN) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid MAC Address "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->mac_addr = pos;
+               break;
+       case ATTR_KEY_PROVIDED_AUTO:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
+                                  "Automatically length %u", len);
+                       return -1;
+               }
+               attr->key_prov_auto = pos;
+               break;
+       case ATTR_802_1X_ENABLED:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->dot1x_enabled = pos;
+               break;
+       case ATTR_SELECTED_REGISTRAR:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
+                                  " length %u", len);
+                       return -1;
+               }
+               attr->selected_registrar = pos;
+               break;
+       case ATTR_REQUEST_TYPE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Request Type "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->request_type = pos;
+               break;
+       case ATTR_RESPONSE_TYPE:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Response Type "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->response_type = pos;
+               break;
+       case ATTR_MANUFACTURER:
+               attr->manufacturer = pos;
+               attr->manufacturer_len = len;
+               break;
+       case ATTR_MODEL_NAME:
+               attr->model_name = pos;
+               attr->model_name_len = len;
+               break;
+       case ATTR_MODEL_NUMBER:
+               attr->model_number = pos;
+               attr->model_number_len = len;
+               break;
+       case ATTR_SERIAL_NUMBER:
+               attr->serial_number = pos;
+               attr->serial_number_len = len;
+               break;
+       case ATTR_DEV_NAME:
+               attr->dev_name = pos;
+               attr->dev_name_len = len;
+               break;
+       case ATTR_PUBLIC_KEY:
+               attr->public_key = pos;
+               attr->public_key_len = len;
+               break;
+       case ATTR_ENCR_SETTINGS:
+               attr->encr_settings = pos;
+               attr->encr_settings_len = len;
+               break;
+       case ATTR_CRED:
+               if (attr->num_cred >= MAX_CRED_COUNT) {
+                       wpa_printf(MSG_DEBUG, "WPS: Skipped Credential "
+                                  "attribute (max %d credentials)",
+                                  MAX_CRED_COUNT);
+                       break;
+               }
+               attr->cred[attr->num_cred] = pos;
+               attr->cred_len[attr->num_cred] = len;
+               attr->num_cred++;
+               break;
+       case ATTR_SSID:
+               attr->ssid = pos;
+               attr->ssid_len = len;
+               break;
+       case ATTR_NETWORK_KEY:
+               attr->network_key = pos;
+               attr->network_key_len = len;
+               break;
+       case ATTR_EAP_TYPE:
+               attr->eap_type = pos;
+               attr->eap_type_len = len;
+               break;
+       case ATTR_EAP_IDENTITY:
+               attr->eap_identity = pos;
+               attr->eap_identity_len = len;
+               break;
+       case ATTR_AP_SETUP_LOCKED:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
+                                  "length %u", len);
+                       return -1;
+               }
+               attr->ap_setup_locked = pos;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported attribute type 0x%x "
+                          "len=%u", type, len);
+               break;
+       }
+
+       return 0;
+}
+
+
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr)
+{
+       const u8 *pos, *end;
+       u16 type, len;
+
+       os_memset(attr, 0, sizeof(*attr));
+       pos = wpabuf_head(msg);
+       end = pos + wpabuf_len(msg);
+
+       while (pos < end) {
+               if (end - pos < 4) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid message - "
+                                  "%lu bytes remaining",
+                                  (unsigned long) (end - pos));
+                       return -1;
+               }
+
+               type = WPA_GET_BE16(pos);
+               pos += 2;
+               len = WPA_GET_BE16(pos);
+               pos += 2;
+               wpa_printf(MSG_MSGDUMP, "WPS: attr type=0x%x len=%u",
+                          type, len);
+               if (len > end - pos) {
+                       wpa_printf(MSG_DEBUG, "WPS: Attribute overflow");
+                       return -1;
+               }
+
+#ifdef WPS_WORKAROUNDS
+               if (type == 0 && len == 0) {
+                       /*
+                        * Mac OS X 10.6 seems to be adding 0x00 padding to the
+                        * end of M1. Skip those to avoid interop issues.
+                        */
+                       int i;
+                       for (i = 0; i < end - pos; i++) {
+                               if (pos[i])
+                                       break;
+                       }
+                       if (i == end - pos) {
+                               wpa_printf(MSG_DEBUG, "WPS: Workaround - skip "
+                                          "unexpected message padding");
+                               break;
+                       }
+               }
+#endif /* WPS_WORKAROUNDS */
+
+               if (wps_set_attr(attr, type, pos, len) < 0)
+                       return -1;
+
+               pos += len;
+       }
+
+       return 0;
+}
diff --git a/src/wps/wps_attr_process.c b/src/wps/wps_attr_process.c
new file mode 100644 (file)
index 0000000..4751bbc
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Wi-Fi Protected Setup - attribute processing
+ * 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 "common.h"
+#include "crypto/sha256.h"
+#include "wps_i.h"
+
+
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+                             const struct wpabuf *msg)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[2];
+       size_t len[2];
+
+       if (authenticator == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Authenticator attribute "
+                          "included");
+               return -1;
+       }
+
+       if (wps->last_msg == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Last message not available for "
+                          "validating authenticator");
+               return -1;
+       }
+
+       /* Authenticator = HMAC-SHA256_AuthKey(M_prev || M_curr*)
+        * (M_curr* is M_curr without the Authenticator attribute)
+        */
+       addr[0] = wpabuf_head(wps->last_msg);
+       len[0] = wpabuf_len(wps->last_msg);
+       addr[1] = wpabuf_head(msg);
+       len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
+
+       if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+                             const u8 *key_wrap_auth)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *head;
+       size_t len;
+
+       if (key_wrap_auth == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No KWA in decrypted attribute");
+               return -1;
+       }
+
+       head = wpabuf_head(msg);
+       len = wpabuf_len(msg) - 4 - WPS_KWA_LEN;
+       if (head + len != key_wrap_auth - 4) {
+               wpa_printf(MSG_DEBUG, "WPS: KWA not in the end of the "
+                          "decrypted attribute");
+               return -1;
+       }
+
+       hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
+       if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_cred_network_idx(struct wps_credential *cred,
+                                       const u8 *idx)
+{
+       if (idx == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+                          "Network Index");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Network Index: %d", *idx);
+
+       return 0;
+}
+
+
+static int wps_process_cred_ssid(struct wps_credential *cred, const u8 *ssid,
+                                size_t ssid_len)
+{
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Credential did not include SSID");
+               return -1;
+       }
+
+       /* Remove zero-padding since some Registrar implementations seem to use
+        * hardcoded 32-octet length for this attribute */
+       while (ssid_len > 0 && ssid[ssid_len - 1] == 0)
+               ssid_len--;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", ssid, ssid_len);
+       if (ssid_len <= sizeof(cred->ssid)) {
+               os_memcpy(cred->ssid, ssid, ssid_len);
+               cred->ssid_len = ssid_len;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_cred_auth_type(struct wps_credential *cred,
+                                     const u8 *auth_type)
+{
+       if (auth_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+                          "Authentication Type");
+               return -1;
+       }
+
+       cred->auth_type = WPA_GET_BE16(auth_type);
+       wpa_printf(MSG_DEBUG, "WPS: Authentication Type: 0x%x",
+                  cred->auth_type);
+
+       return 0;
+}
+
+
+static int wps_process_cred_encr_type(struct wps_credential *cred,
+                                     const u8 *encr_type)
+{
+       if (encr_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+                          "Encryption Type");
+               return -1;
+       }
+
+       cred->encr_type = WPA_GET_BE16(encr_type);
+       wpa_printf(MSG_DEBUG, "WPS: Encryption Type: 0x%x",
+                  cred->encr_type);
+
+       return 0;
+}
+
+
+static int wps_process_cred_network_key_idx(struct wps_credential *cred,
+                                           const u8 *key_idx)
+{
+       if (key_idx == NULL)
+               return 0; /* optional attribute */
+
+       wpa_printf(MSG_DEBUG, "WPS: Network Key Index: %d", *key_idx);
+       cred->key_idx = *key_idx;
+
+       return 0;
+}
+
+
+static int wps_process_cred_network_key(struct wps_credential *cred,
+                                       const u8 *key, size_t key_len)
+{
+       if (key == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+                          "Network Key");
+               if (cred->auth_type == WPS_AUTH_OPEN &&
+                   cred->encr_type == WPS_ENCR_NONE) {
+                       wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow "
+                                  "missing mandatory Network Key attribute "
+                                  "for open network");
+                       return 0;
+               }
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", key, key_len);
+       if (key_len <= sizeof(cred->key)) {
+               os_memcpy(cred->key, key, key_len);
+               cred->key_len = key_len;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_cred_mac_addr(struct wps_credential *cred,
+                                    const u8 *mac_addr)
+{
+       if (mac_addr == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Credential did not include "
+                          "MAC Address");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, MAC2STR(mac_addr));
+       os_memcpy(cred->mac_addr, mac_addr, ETH_ALEN);
+
+       return 0;
+}
+
+
+static int wps_process_cred_eap_type(struct wps_credential *cred,
+                                    const u8 *eap_type, size_t eap_type_len)
+{
+       if (eap_type == NULL)
+               return 0; /* optional attribute */
+
+       wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
+
+       return 0;
+}
+
+
+static int wps_process_cred_eap_identity(struct wps_credential *cred,
+                                        const u8 *identity,
+                                        size_t identity_len)
+{
+       if (identity == NULL)
+               return 0; /* optional attribute */
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
+                         identity, identity_len);
+
+       return 0;
+}
+
+
+static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
+                                         const u8 *key_prov_auto)
+{
+       if (key_prov_auto == NULL)
+               return 0; /* optional attribute */
+
+       wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
+                  *key_prov_auto);
+
+       return 0;
+}
+
+
+static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
+                                          const u8 *dot1x_enabled)
+{
+       if (dot1x_enabled == NULL)
+               return 0; /* optional attribute */
+
+       wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
+
+       return 0;
+}
+
+
+static void wps_workaround_cred_key(struct wps_credential *cred)
+{
+       if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
+           cred->key_len > 8 && cred->key_len < 64 &&
+           cred->key[cred->key_len - 1] == 0) {
+               /*
+                * A deployed external registrar is known to encode ASCII
+                * passphrases incorrectly. Remove the extra NULL termination
+                * to fix the encoding.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Workaround - remove NULL "
+                          "termination from ASCII passphrase");
+               cred->key_len--;
+       }
+}
+
+
+int wps_process_cred(struct wps_parse_attr *attr,
+                    struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Process Credential");
+
+       /* TODO: support multiple Network Keys */
+       if (wps_process_cred_network_idx(cred, attr->network_idx) ||
+           wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+           wps_process_cred_auth_type(cred, attr->auth_type) ||
+           wps_process_cred_encr_type(cred, attr->encr_type) ||
+           wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+           wps_process_cred_network_key(cred, attr->network_key,
+                                        attr->network_key_len) ||
+           wps_process_cred_mac_addr(cred, attr->mac_addr) ||
+           wps_process_cred_eap_type(cred, attr->eap_type,
+                                     attr->eap_type_len) ||
+           wps_process_cred_eap_identity(cred, attr->eap_identity,
+                                         attr->eap_identity_len) ||
+           wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
+           wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled))
+               return -1;
+
+       wps_workaround_cred_key(cred);
+
+       return 0;
+}
+
+
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+                           struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Processing AP Settings");
+       os_memset(cred, 0, sizeof(*cred));
+       /* TODO: optional attributes New Password and Device Password ID */
+       if (wps_process_cred_ssid(cred, attr->ssid, attr->ssid_len) ||
+           wps_process_cred_auth_type(cred, attr->auth_type) ||
+           wps_process_cred_encr_type(cred, attr->encr_type) ||
+           wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
+           wps_process_cred_network_key(cred, attr->network_key,
+                                        attr->network_key_len) ||
+           wps_process_cred_mac_addr(cred, attr->mac_addr))
+               return -1;
+
+       wps_workaround_cred_key(cred);
+
+       return 0;
+}
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
new file mode 100644 (file)
index 0000000..6ef14db
--- /dev/null
@@ -0,0 +1,635 @@
+/*
+ * Wi-Fi Protected Setup - common functionality
+ * Copyright (c) 2008-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 "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_group5.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+            const char *label, u8 *res, size_t res_len)
+{
+       u8 i_buf[4], key_bits[4];
+       const u8 *addr[4];
+       size_t len[4];
+       int i, iter;
+       u8 hash[SHA256_MAC_LEN], *opos;
+       size_t left;
+
+       WPA_PUT_BE32(key_bits, res_len * 8);
+
+       addr[0] = i_buf;
+       len[0] = sizeof(i_buf);
+       addr[1] = label_prefix;
+       len[1] = label_prefix_len;
+       addr[2] = (const u8 *) label;
+       len[2] = os_strlen(label);
+       addr[3] = key_bits;
+       len[3] = sizeof(key_bits);
+
+       iter = (res_len + SHA256_MAC_LEN - 1) / SHA256_MAC_LEN;
+       opos = res;
+       left = res_len;
+
+       for (i = 1; i <= iter; i++) {
+               WPA_PUT_BE32(i_buf, i);
+               hmac_sha256_vector(key, SHA256_MAC_LEN, 4, addr, len, hash);
+               if (i < iter) {
+                       os_memcpy(opos, hash, SHA256_MAC_LEN);
+                       opos += SHA256_MAC_LEN;
+                       left -= SHA256_MAC_LEN;
+               } else
+                       os_memcpy(opos, hash, left);
+       }
+}
+
+
+int wps_derive_keys(struct wps_data *wps)
+{
+       struct wpabuf *pubkey, *dh_shared;
+       u8 dhkey[SHA256_MAC_LEN], kdk[SHA256_MAC_LEN];
+       const u8 *addr[3];
+       size_t len[3];
+       u8 keys[WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN + WPS_EMSK_LEN];
+
+       if (wps->dh_privkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Own DH private key not available");
+               return -1;
+       }
+
+       pubkey = wps->registrar ? wps->dh_pubkey_e : wps->dh_pubkey_r;
+       if (pubkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Peer DH public key not available");
+               return -1;
+       }
+
+       dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey);
+       dh5_free(wps->dh_ctx);
+       wps->dh_ctx = NULL;
+       dh_shared = wpabuf_zeropad(dh_shared, 192);
+       if (dh_shared == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key");
+               return -1;
+       }
+
+       /* Own DH private key is not needed anymore */
+       wpabuf_free(wps->dh_privkey);
+       wps->dh_privkey = NULL;
+
+       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: DH shared key", dh_shared);
+
+       /* DHKey = SHA-256(g^AB mod p) */
+       addr[0] = wpabuf_head(dh_shared);
+       len[0] = wpabuf_len(dh_shared);
+       sha256_vector(1, addr, len, dhkey);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: DHKey", dhkey, sizeof(dhkey));
+       wpabuf_free(dh_shared);
+
+       /* KDK = HMAC-SHA-256_DHKey(N1 || EnrolleeMAC || N2) */
+       addr[0] = wps->nonce_e;
+       len[0] = WPS_NONCE_LEN;
+       addr[1] = wps->mac_addr_e;
+       len[1] = ETH_ALEN;
+       addr[2] = wps->nonce_r;
+       len[2] = WPS_NONCE_LEN;
+       hmac_sha256_vector(dhkey, sizeof(dhkey), 3, addr, len, kdk);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: KDK", kdk, sizeof(kdk));
+
+       wps_kdf(kdk, NULL, 0, "Wi-Fi Easy and Secure Key Derivation",
+               keys, sizeof(keys));
+       os_memcpy(wps->authkey, keys, WPS_AUTHKEY_LEN);
+       os_memcpy(wps->keywrapkey, keys + WPS_AUTHKEY_LEN, WPS_KEYWRAPKEY_LEN);
+       os_memcpy(wps->emsk, keys + WPS_AUTHKEY_LEN + WPS_KEYWRAPKEY_LEN,
+                 WPS_EMSK_LEN);
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: AuthKey",
+                       wps->authkey, WPS_AUTHKEY_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: KeyWrapKey",
+                       wps->keywrapkey, WPS_KEYWRAPKEY_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: EMSK", wps->emsk, WPS_EMSK_LEN);
+
+       return 0;
+}
+
+
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+                   size_t dev_passwd_len)
+{
+       u8 hash[SHA256_MAC_LEN];
+
+       hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, dev_passwd,
+                   (dev_passwd_len + 1) / 2, hash);
+       os_memcpy(wps->psk1, hash, WPS_PSK_LEN);
+       hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN,
+                   dev_passwd + (dev_passwd_len + 1) / 2,
+                   dev_passwd_len / 2, hash);
+       os_memcpy(wps->psk2, hash, WPS_PSK_LEN);
+
+       wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Device Password",
+                             dev_passwd, dev_passwd_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: PSK1", wps->psk1, WPS_PSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "WPS: PSK2", wps->psk2, WPS_PSK_LEN);
+}
+
+
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+                                         size_t encr_len)
+{
+       struct wpabuf *decrypted;
+       const size_t block_size = 16;
+       size_t i;
+       u8 pad;
+       const u8 *pos;
+
+       /* AES-128-CBC */
+       if (encr == NULL || encr_len < 2 * block_size || encr_len % block_size)
+       {
+               wpa_printf(MSG_DEBUG, "WPS: No Encrypted Settings received");
+               return NULL;
+       }
+
+       decrypted = wpabuf_alloc(encr_len - block_size);
+       if (decrypted == NULL)
+               return NULL;
+
+       wpa_hexdump(MSG_MSGDUMP, "WPS: Encrypted Settings", encr, encr_len);
+       wpabuf_put_data(decrypted, encr + block_size, encr_len - block_size);
+       if (aes_128_cbc_decrypt(wps->keywrapkey, encr, wpabuf_mhead(decrypted),
+                               wpabuf_len(decrypted))) {
+               wpabuf_free(decrypted);
+               return NULL;
+       }
+
+       wpa_hexdump_buf_key(MSG_MSGDUMP, "WPS: Decrypted Encrypted Settings",
+                           decrypted);
+
+       pos = wpabuf_head_u8(decrypted) + wpabuf_len(decrypted) - 1;
+       pad = *pos;
+       if (pad > wpabuf_len(decrypted)) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad value");
+               wpabuf_free(decrypted);
+               return NULL;
+       }
+       for (i = 0; i < pad; i++) {
+               if (*pos-- != pad) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid PKCS#5 v2.0 pad "
+                                  "string");
+                       wpabuf_free(decrypted);
+                       return NULL;
+               }
+       }
+       decrypted->used -= pad;
+
+       return decrypted;
+}
+
+
+/**
+ * wps_pin_checksum - Compute PIN checksum
+ * @pin: Seven digit PIN (i.e., eight digit PIN without the checksum digit)
+ * Returns: Checksum digit
+ */
+unsigned int wps_pin_checksum(unsigned int pin)
+{
+       unsigned int accum = 0;
+       while (pin) {
+               accum += 3 * (pin % 10);
+               pin /= 10;
+               accum += pin % 10;
+               pin /= 10;
+       }
+
+       return (10 - accum % 10) % 10;
+}
+
+
+/**
+ * wps_pin_valid - Check whether a PIN has a valid checksum
+ * @pin: Eight digit PIN (i.e., including the checksum digit)
+ * Returns: 1 if checksum digit is valid, or 0 if not
+ */
+unsigned int wps_pin_valid(unsigned int pin)
+{
+       return wps_pin_checksum(pin / 10) == (pin % 10);
+}
+
+
+/**
+ * wps_generate_pin - Generate a random PIN
+ * Returns: Eight digit PIN (i.e., including the checksum digit)
+ */
+unsigned int wps_generate_pin(void)
+{
+       unsigned int val;
+
+       /* Generate seven random digits for the PIN */
+       if (os_get_random((unsigned char *) &val, sizeof(val)) < 0) {
+               struct os_time now;
+               os_get_time(&now);
+               val = os_random() ^ now.sec ^ now.usec;
+       }
+       val %= 10000000;
+
+       /* Append checksum digit */
+       return val * 10 + wps_pin_checksum(val);
+}
+
+
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg)
+{
+       union wps_event_data data;
+
+       if (wps->event_cb == NULL)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       data.fail.msg = msg;
+       wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
+}
+
+
+void wps_success_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
+}
+
+
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
+{
+       union wps_event_data data;
+
+       if (wps->event_cb == NULL)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       data.pwd_auth_fail.enrollee = enrollee;
+       data.pwd_auth_fail.part = part;
+       wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
+}
+
+
+void wps_pbc_overlap_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_OVERLAP, NULL);
+}
+
+
+void wps_pbc_timeout_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+
+static struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+{
+       struct wps_data data;
+       struct wpabuf *plain;
+
+       plain = wpabuf_alloc(500);
+       if (plain == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "credential");
+               return NULL;
+       }
+
+       os_memset(&data, 0, sizeof(data));
+       data.wps = wps;
+       data.auth_type = wps->auth_types;
+       data.encr_type = wps->encr_types;
+       if (wps_build_version(plain) || wps_build_cred(&data, plain)) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       return plain;
+}
+
+
+static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps)
+{
+       struct wpabuf *data;
+
+       data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN);
+       if (data == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "device password attribute");
+               return NULL;
+       }
+
+       wpabuf_free(wps->oob_conf.dev_password);
+       wps->oob_conf.dev_password =
+               wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+       if (wps->oob_conf.dev_password == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "device password");
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       if (wps_build_version(data) ||
+           wps_build_oob_dev_password(data, wps)) {
+               wpa_printf(MSG_ERROR, "WPS: Build OOB device password "
+                          "attribute error");
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       return data;
+}
+
+
+static int wps_parse_oob_dev_pwd(struct wps_context *wps,
+                                struct wpabuf *data)
+{
+       struct oob_conf_data *oob_conf = &wps->oob_conf;
+       struct wps_parse_attr attr;
+       const u8 *pos;
+
+       if (wps_parse_msg(data, &attr) < 0 ||
+           attr.oob_dev_password == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: OOB device password not found");
+               return -1;
+       }
+
+       pos = attr.oob_dev_password;
+
+       oob_conf->pubkey_hash =
+               wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN);
+       if (oob_conf->pubkey_hash == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "public key hash");
+               return -1;
+       }
+       pos += WPS_OOB_PUBKEY_HASH_LEN;
+
+       wps->oob_dev_pw_id = WPA_GET_BE16(pos);
+       pos += sizeof(wps->oob_dev_pw_id);
+
+       oob_conf->dev_password =
+               wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1);
+       if (oob_conf->dev_password == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB "
+                          "device password");
+               return -1;
+       }
+       wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password,
+                                  wpabuf_size(oob_conf->dev_password)),
+                                  wpabuf_size(oob_conf->dev_password), pos,
+                                  WPS_OOB_DEVICE_PASSWORD_LEN);
+
+       return 0;
+}
+
+
+static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data)
+{
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       size_t i;
+
+       if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) {
+               wpa_printf(MSG_ERROR, "WPS: OOB credential not found");
+               return -1;
+       }
+
+       for (i = 0; i < attr.num_cred; i++) {
+               struct wps_credential local_cred;
+               struct wps_parse_attr cattr;
+
+               os_memset(&local_cred, 0, sizeof(local_cred));
+               wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]);
+               if (wps_parse_msg(&msg, &cattr) < 0 ||
+                   wps_process_cred(&cattr, &local_cred)) {
+                       wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB "
+                                  "credential");
+                       return -1;
+               }
+               wps->cred_cb(wps->cb_ctx, &local_cred);
+       }
+
+       return 0;
+}
+
+
+int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev,
+                   int registrar)
+{
+       struct wpabuf *data;
+       int ret, write_f, oob_method = wps->oob_conf.oob_method;
+       void *oob_priv;
+
+       write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar;
+
+       oob_priv = oob_dev->init_func(wps, oob_dev, registrar);
+       if (oob_priv == NULL) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device");
+               return -1;
+       }
+
+       if (write_f) {
+               if (oob_method == OOB_METHOD_CRED)
+                       data = wps_get_oob_cred(wps);
+               else
+                       data = wps_get_oob_dev_pwd(wps);
+
+               ret = 0;
+               if (data == NULL || oob_dev->write_func(oob_priv, data) < 0)
+                       ret = -1;
+       } else {
+               data = oob_dev->read_func(oob_priv);
+               if (data == NULL)
+                       ret = -1;
+               else {
+                       if (oob_method == OOB_METHOD_CRED)
+                               ret = wps_parse_oob_cred(wps, data);
+                       else
+                               ret = wps_parse_oob_dev_pwd(wps, data);
+               }
+       }
+       wpabuf_free(data);
+       oob_dev->deinit_func(oob_priv);
+
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+struct oob_device_data * wps_get_oob_device(char *device_type)
+{
+#ifdef CONFIG_WPS_UFD
+       if (os_strstr(device_type, "ufd") != NULL)
+               return &oob_ufd_device_data;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+       if (os_strstr(device_type, "nfc") != NULL)
+               return &oob_nfc_device_data;
+#endif /* CONFIG_WPS_NFC */
+
+       return NULL;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name)
+{
+       if (device_name == NULL)
+               return NULL;
+#ifdef CONFIG_WPS_NFC_PN531
+       if (os_strstr(device_name, "pn531") != NULL)
+               return &oob_nfc_pn531_device_data;
+#endif /* CONFIG_WPS_NFC_PN531 */
+
+       return NULL;
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+int wps_get_oob_method(char *method)
+{
+       if (os_strstr(method, "pin-e") != NULL)
+               return OOB_METHOD_DEV_PWD_E;
+       if (os_strstr(method, "pin-r") != NULL)
+               return OOB_METHOD_DEV_PWD_R;
+       if (os_strstr(method, "cred") != NULL)
+               return OOB_METHOD_CRED;
+       return OOB_METHOD_UNKNOWN;
+}
+
+#endif /* CONFIG_WPS_OOB */
+
+
+int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN])
+{
+       const char *pos;
+
+       /* <categ>-<OUI>-<subcateg> */
+       WPA_PUT_BE16(dev_type, atoi(str));
+       pos = os_strchr(str, '-');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       if (hexstr2bin(pos, &dev_type[2], 4))
+               return -1;
+       pos = os_strchr(pos, '-');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       WPA_PUT_BE16(&dev_type[6], atoi(pos));
+
+
+       return 0;
+}
+
+
+char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
+                           size_t buf_len)
+{
+       int ret;
+
+       ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
+                         WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
+                         WPA_GET_BE16(&dev_type[6]));
+       if (ret < 0 || (unsigned int) ret >= buf_len)
+               return NULL;
+
+       return buf;
+}
+
+
+void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid)
+{
+       const u8 *addr[2];
+       size_t len[2];
+       u8 hash[SHA1_MAC_LEN];
+       u8 nsid[16] = {
+               0x52, 0x64, 0x80, 0xf8,
+               0xc9, 0x9b,
+               0x4b, 0xe5,
+               0xa6, 0x55,
+               0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84
+       };
+
+       addr[0] = nsid;
+       len[0] = sizeof(nsid);
+       addr[1] = mac_addr;
+       len[1] = 6;
+       sha1_vector(2, addr, len, hash);
+       os_memcpy(uuid, hash, 16);
+
+       /* Version: 5 = named-based version using SHA-1 */
+       uuid[6] = (5 << 4) | (uuid[6] & 0x0f);
+
+       /* Variant specified in RFC 4122 */
+       uuid[8] = 0x80 | (uuid[8] & 0x3f);
+}
+
+
+u16 wps_config_methods_str2bin(const char *str)
+{
+       u16 methods = 0;
+
+       if (str == NULL) {
+               /* Default to enabling methods based on build configuration */
+               methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+#ifdef CONFIG_WPS_UFD
+               methods |= WPS_CONFIG_USBA;
+#endif /* CONFIG_WPS_UFD */
+#ifdef CONFIG_WPS_NFC
+               methods |= WPS_CONFIG_NFC_INTERFACE;
+#endif /* CONFIG_WPS_NFC */
+       } else {
+               if (os_strstr(str, "usba"))
+                       methods |= WPS_CONFIG_USBA;
+               if (os_strstr(str, "ethernet"))
+                       methods |= WPS_CONFIG_ETHERNET;
+               if (os_strstr(str, "label"))
+                       methods |= WPS_CONFIG_LABEL;
+               if (os_strstr(str, "display"))
+                       methods |= WPS_CONFIG_DISPLAY;
+               if (os_strstr(str, "ext_nfc_token"))
+                       methods |= WPS_CONFIG_EXT_NFC_TOKEN;
+               if (os_strstr(str, "int_nfc_token"))
+                       methods |= WPS_CONFIG_INT_NFC_TOKEN;
+               if (os_strstr(str, "nfc_interface"))
+                       methods |= WPS_CONFIG_NFC_INTERFACE;
+               if (os_strstr(str, "push_button"))
+                       methods |= WPS_CONFIG_PUSHBUTTON;
+               if (os_strstr(str, "keypad"))
+                       methods |= WPS_CONFIG_KEYPAD;
+       }
+
+       return methods;
+}
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
new file mode 100644 (file)
index 0000000..750ca41
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ * Wi-Fi Protected Setup - message definitions
+ * 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_DEFS_H
+#define WPS_DEFS_H
+
+#define WPS_VERSION 0x10
+
+/* Diffie-Hellman 1536-bit MODP Group; RFC 3526, Group 5 */
+#define WPS_DH_GROUP 5
+
+#define WPS_UUID_LEN 16
+#define WPS_NONCE_LEN 16
+#define WPS_AUTHENTICATOR_LEN 8
+#define WPS_AUTHKEY_LEN 32
+#define WPS_KEYWRAPKEY_LEN 16
+#define WPS_EMSK_LEN 32
+#define WPS_PSK_LEN 16
+#define WPS_SECRET_NONCE_LEN 16
+#define WPS_HASH_LEN 32
+#define WPS_KWA_LEN 8
+#define WPS_MGMTAUTHKEY_LEN 32
+#define WPS_MGMTENCKEY_LEN 16
+#define WPS_MGMT_KEY_ID_LEN 16
+#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54
+#define WPS_OOB_DEVICE_PASSWORD_LEN 32
+#define WPS_OOB_PUBKEY_HASH_LEN 20
+
+/* Attribute Types */
+enum wps_attribute {
+       ATTR_AP_CHANNEL = 0x1001,
+       ATTR_ASSOC_STATE = 0x1002,
+       ATTR_AUTH_TYPE = 0x1003,
+       ATTR_AUTH_TYPE_FLAGS = 0x1004,
+       ATTR_AUTHENTICATOR = 0x1005,
+       ATTR_CONFIG_METHODS = 0x1008,
+       ATTR_CONFIG_ERROR = 0x1009,
+       ATTR_CONFIRM_URL4 = 0x100a,
+       ATTR_CONFIRM_URL6 = 0x100b,
+       ATTR_CONN_TYPE = 0x100c,
+       ATTR_CONN_TYPE_FLAGS = 0x100d,
+       ATTR_CRED = 0x100e,
+       ATTR_ENCR_TYPE = 0x100f,
+       ATTR_ENCR_TYPE_FLAGS = 0x1010,
+       ATTR_DEV_NAME = 0x1011,
+       ATTR_DEV_PASSWORD_ID = 0x1012,
+       ATTR_E_HASH1 = 0x1014,
+       ATTR_E_HASH2 = 0x1015,
+       ATTR_E_SNONCE1 = 0x1016,
+       ATTR_E_SNONCE2 = 0x1017,
+       ATTR_ENCR_SETTINGS = 0x1018,
+       ATTR_ENROLLEE_NONCE = 0x101a,
+       ATTR_FEATURE_ID = 0x101b,
+       ATTR_IDENTITY = 0x101c,
+       ATTR_IDENTITY_PROOF = 0x101d,
+       ATTR_KEY_WRAP_AUTH = 0x101e,
+       ATTR_KEY_ID = 0x101f,
+       ATTR_MAC_ADDR = 0x1020,
+       ATTR_MANUFACTURER = 0x1021,
+       ATTR_MSG_TYPE = 0x1022,
+       ATTR_MODEL_NAME = 0x1023,
+       ATTR_MODEL_NUMBER = 0x1024,
+       ATTR_NETWORK_INDEX = 0x1026,
+       ATTR_NETWORK_KEY = 0x1027,
+       ATTR_NETWORK_KEY_INDEX = 0x1028,
+       ATTR_NEW_DEVICE_NAME = 0x1029,
+       ATTR_NEW_PASSWORD = 0x102a,
+       ATTR_OOB_DEVICE_PASSWORD = 0x102c,
+       ATTR_OS_VERSION = 0x102d,
+       ATTR_POWER_LEVEL = 0x102f,
+       ATTR_PSK_CURRENT = 0x1030,
+       ATTR_PSK_MAX = 0x1031,
+       ATTR_PUBLIC_KEY = 0x1032,
+       ATTR_RADIO_ENABLE = 0x1033,
+       ATTR_REBOOT = 0x1034,
+       ATTR_REGISTRAR_CURRENT = 0x1035,
+       ATTR_REGISTRAR_ESTABLISHED = 0x1036,
+       ATTR_REGISTRAR_LIST = 0x1037,
+       ATTR_REGISTRAR_MAX = 0x1038,
+       ATTR_REGISTRAR_NONCE = 0x1039,
+       ATTR_REQUEST_TYPE = 0x103a,
+       ATTR_RESPONSE_TYPE = 0x103b,
+       ATTR_RF_BANDS = 0x103c,
+       ATTR_R_HASH1 = 0x103d,
+       ATTR_R_HASH2 = 0x103e,
+       ATTR_R_SNONCE1 = 0x103f,
+       ATTR_R_SNONCE2 = 0x1040,
+       ATTR_SELECTED_REGISTRAR = 0x1041,
+       ATTR_SERIAL_NUMBER = 0x1042,
+       ATTR_WPS_STATE = 0x1044,
+       ATTR_SSID = 0x1045,
+       ATTR_TOTAL_NETWORKS = 0x1046,
+       ATTR_UUID_E = 0x1047,
+       ATTR_UUID_R = 0x1048,
+       ATTR_VENDOR_EXT = 0x1049,
+       ATTR_VERSION = 0x104a,
+       ATTR_X509_CERT_REQ = 0x104b,
+       ATTR_X509_CERT = 0x104c,
+       ATTR_EAP_IDENTITY = 0x104d,
+       ATTR_MSG_COUNTER = 0x104e,
+       ATTR_PUBKEY_HASH = 0x104f,
+       ATTR_REKEY_KEY = 0x1050,
+       ATTR_KEY_LIFETIME = 0x1051,
+       ATTR_PERMITTED_CFG_METHODS = 0x1052,
+       ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053,
+       ATTR_PRIMARY_DEV_TYPE = 0x1054,
+       ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055,
+       ATTR_PORTABLE_DEV = 0x1056,
+       ATTR_AP_SETUP_LOCKED = 0x1057,
+       ATTR_APPLICATION_EXT = 0x1058,
+       ATTR_EAP_TYPE = 0x1059,
+       ATTR_IV = 0x1060,
+       ATTR_KEY_PROVIDED_AUTO = 0x1061,
+       ATTR_802_1X_ENABLED = 0x1062,
+       ATTR_APPSESSIONKEY = 0x1063,
+       ATTR_WEPTRANSMITKEY = 0x1064
+};
+
+/* Device Password ID */
+enum wps_dev_password_id {
+       DEV_PW_DEFAULT = 0x0000,
+       DEV_PW_USER_SPECIFIED = 0x0001,
+       DEV_PW_MACHINE_SPECIFIED = 0x0002,
+       DEV_PW_REKEY = 0x0003,
+       DEV_PW_PUSHBUTTON = 0x0004,
+       DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+};
+
+/* Message Type */
+enum wps_msg_type {
+       WPS_Beacon = 0x01,
+       WPS_ProbeRequest = 0x02,
+       WPS_ProbeResponse = 0x03,
+       WPS_M1 = 0x04,
+       WPS_M2 = 0x05,
+       WPS_M2D = 0x06,
+       WPS_M3 = 0x07,
+       WPS_M4 = 0x08,
+       WPS_M5 = 0x09,
+       WPS_M6 = 0x0a,
+       WPS_M7 = 0x0b,
+       WPS_M8 = 0x0c,
+       WPS_WSC_ACK = 0x0d,
+       WPS_WSC_NACK = 0x0e,
+       WPS_WSC_DONE = 0x0f
+};
+
+/* Authentication Type Flags */
+#define WPS_AUTH_OPEN 0x0001
+#define WPS_AUTH_WPAPSK 0x0002
+#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_WPA 0x0008
+#define WPS_AUTH_WPA2 0x0010
+#define WPS_AUTH_WPA2PSK 0x0020
+#define WPS_AUTH_TYPES (WPS_AUTH_OPEN | WPS_AUTH_WPAPSK | WPS_AUTH_SHARED | \
+                       WPS_AUTH_WPA | WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)
+
+/* Encryption Type Flags */
+#define WPS_ENCR_NONE 0x0001
+#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_TKIP 0x0004
+#define WPS_ENCR_AES 0x0008
+#define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
+                       WPS_ENCR_AES)
+
+/* Configuration Error */
+enum wps_config_error {
+       WPS_CFG_NO_ERROR = 0,
+       WPS_CFG_OOB_IFACE_READ_ERROR = 1,
+       WPS_CFG_DECRYPTION_CRC_FAILURE = 2,
+       WPS_CFG_24_CHAN_NOT_SUPPORTED = 3,
+       WPS_CFG_50_CHAN_NOT_SUPPORTED = 4,
+       WPS_CFG_SIGNAL_TOO_WEAK = 5,
+       WPS_CFG_NETWORK_AUTH_FAILURE = 6,
+       WPS_CFG_NETWORK_ASSOC_FAILURE = 7,
+       WPS_CFG_NO_DHCP_RESPONSE = 8,
+       WPS_CFG_FAILED_DHCP_CONFIG = 9,
+       WPS_CFG_IP_ADDR_CONFLICT = 10,
+       WPS_CFG_NO_CONN_TO_REGISTRAR = 11,
+       WPS_CFG_MULTIPLE_PBC_DETECTED = 12,
+       WPS_CFG_ROGUE_SUSPECTED = 13,
+       WPS_CFG_DEVICE_BUSY = 14,
+       WPS_CFG_SETUP_LOCKED = 15,
+       WPS_CFG_MSG_TIMEOUT = 16,
+       WPS_CFG_REG_SESS_TIMEOUT = 17,
+       WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+};
+
+/* RF Bands */
+#define WPS_RF_24GHZ 0x01
+#define WPS_RF_50GHZ 0x02
+
+/* Config Methods */
+#define WPS_CONFIG_USBA 0x0001
+#define WPS_CONFIG_ETHERNET 0x0002
+#define WPS_CONFIG_LABEL 0x0004
+#define WPS_CONFIG_DISPLAY 0x0008
+#define WPS_CONFIG_EXT_NFC_TOKEN 0x0010
+#define WPS_CONFIG_INT_NFC_TOKEN 0x0020
+#define WPS_CONFIG_NFC_INTERFACE 0x0040
+#define WPS_CONFIG_PUSHBUTTON 0x0080
+#define WPS_CONFIG_KEYPAD 0x0100
+
+/* Connection Type Flags */
+#define WPS_CONN_ESS 0x01
+#define WPS_CONN_IBSS 0x02
+
+/* Wi-Fi Protected Setup State */
+enum wps_state {
+       WPS_STATE_NOT_CONFIGURED = 1,
+       WPS_STATE_CONFIGURED = 2
+};
+
+/* Association State */
+enum wps_assoc_state {
+       WPS_ASSOC_NOT_ASSOC = 0,
+       WPS_ASSOC_CONN_SUCCESS = 1,
+       WPS_ASSOC_CFG_FAILURE = 2,
+       WPS_ASSOC_FAILURE = 3,
+       WPS_ASSOC_IP_FAILURE = 4
+};
+
+
+#define WPS_DEV_OUI_WFA 0x0050f204
+
+enum wps_dev_categ {
+       WPS_DEV_COMPUTER = 1,
+       WPS_DEV_INPUT = 2,
+       WPS_DEV_PRINTER = 3,
+       WPS_DEV_CAMERA = 4,
+       WPS_DEV_STORAGE = 5,
+       WPS_DEV_NETWORK_INFRA = 6,
+       WPS_DEV_DISPLAY = 7,
+       WPS_DEV_MULTIMEDIA = 8,
+       WPS_DEV_GAMING = 9,
+       WPS_DEV_PHONE = 10
+};
+
+enum wps_dev_subcateg {
+       WPS_DEV_COMPUTER_PC = 1,
+       WPS_DEV_COMPUTER_SERVER = 2,
+       WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+       WPS_DEV_PRINTER_PRINTER = 1,
+       WPS_DEV_PRINTER_SCANNER = 2,
+       WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+       WPS_DEV_STORAGE_NAS = 1,
+       WPS_DEV_NETWORK_INFRA_AP = 1,
+       WPS_DEV_NETWORK_INFRA_ROUTER = 2,
+       WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+       WPS_DEV_DISPLAY_TV = 1,
+       WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
+       WPS_DEV_DISPLAY_PROJECTOR = 3,
+       WPS_DEV_MULTIMEDIA_DAR = 1,
+       WPS_DEV_MULTIMEDIA_PVR = 2,
+       WPS_DEV_MULTIMEDIA_MCX = 3,
+       WPS_DEV_GAMING_XBOX = 1,
+       WPS_DEV_GAMING_XBOX360 = 2,
+       WPS_DEV_GAMING_PLAYSTATION = 3,
+       WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+};
+
+
+/* Request Type */
+enum wps_request_type {
+       WPS_REQ_ENROLLEE_INFO = 0,
+       WPS_REQ_ENROLLEE = 1,
+       WPS_REQ_REGISTRAR = 2,
+       WPS_REQ_WLAN_MANAGER_REGISTRAR = 3
+};
+
+/* Response Type */
+enum wps_response_type {
+       WPS_RESP_ENROLLEE_INFO = 0,
+       WPS_RESP_ENROLLEE = 1,
+       WPS_RESP_REGISTRAR = 2,
+       WPS_RESP_AP = 3
+};
+
+/* Walk Time for push button configuration (in seconds) */
+#define WPS_PBC_WALK_TIME 120
+
+#endif /* WPS_DEFS_H */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
new file mode 100644 (file)
index 0000000..090bfa2
--- /dev/null
@@ -0,0 +1,382 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * 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 "common.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+static int wps_build_manufacturer(struct wps_device_data *dev,
+                                 struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG, "WPS:  * Manufacturer");
+       wpabuf_put_be16(msg, ATTR_MANUFACTURER);
+       len = dev->manufacturer ? os_strlen(dev->manufacturer) : 0;
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a null character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, '\0');
+       } else {
+               wpabuf_put_be16(msg, len);
+               wpabuf_put_data(msg, dev->manufacturer, len);
+       }
+       return 0;
+}
+
+
+static int wps_build_model_name(struct wps_device_data *dev,
+                               struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG, "WPS:  * Model Name");
+       wpabuf_put_be16(msg, ATTR_MODEL_NAME);
+       len = dev->model_name ? os_strlen(dev->model_name) : 0;
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a null character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, '\0');
+       } else {
+               wpabuf_put_be16(msg, len);
+               wpabuf_put_data(msg, dev->model_name, len);
+       }
+       return 0;
+}
+
+
+static int wps_build_model_number(struct wps_device_data *dev,
+                                 struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG, "WPS:  * Model Number");
+       wpabuf_put_be16(msg, ATTR_MODEL_NUMBER);
+       len = dev->model_number ? os_strlen(dev->model_number) : 0;
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a null character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, '\0');
+       } else {
+               wpabuf_put_be16(msg, len);
+               wpabuf_put_data(msg, dev->model_number, len);
+       }
+       return 0;
+}
+
+
+static int wps_build_serial_number(struct wps_device_data *dev,
+                                  struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
+       wpabuf_put_be16(msg, ATTR_SERIAL_NUMBER);
+       len = dev->serial_number ? os_strlen(dev->serial_number) : 0;
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a null character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, '\0');
+       } else {
+               wpabuf_put_be16(msg, len);
+               wpabuf_put_data(msg, dev->serial_number, len);
+       }
+       return 0;
+}
+
+
+int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Primary Device Type");
+       wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE);
+       wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN);
+       wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN);
+       return 0;
+}
+
+
+static int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       size_t len;
+       wpa_printf(MSG_DEBUG, "WPS:  * Device Name");
+       wpabuf_put_be16(msg, ATTR_DEV_NAME);
+       len = dev->device_name ? os_strlen(dev->device_name) : 0;
+       if (len == 0) {
+               /*
+                * Some deployed WPS implementations fail to parse zero-length
+                * attributes. As a workaround, send a null character if the
+                * device attribute string is empty.
+                */
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, '\0');
+       } else {
+               wpabuf_put_be16(msg, len);
+               wpabuf_put_data(msg, dev->device_name, len);
+       }
+       return 0;
+}
+
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       if (wps_build_manufacturer(dev, msg) ||
+           wps_build_model_name(dev, msg) ||
+           wps_build_model_number(dev, msg) ||
+           wps_build_serial_number(dev, msg) ||
+           wps_build_primary_dev_type(dev, msg) ||
+           wps_build_dev_name(dev, msg))
+               return -1;
+       return 0;
+}
+
+
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * OS Version");
+       wpabuf_put_be16(msg, ATTR_OS_VERSION);
+       wpabuf_put_be16(msg, 4);
+       wpabuf_put_be32(msg, 0x80000000 | dev->os_version);
+       return 0;
+}
+
+
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
+       wpabuf_put_be16(msg, ATTR_RF_BANDS);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, dev->rf_bands);
+       return 0;
+}
+
+
+static int wps_process_manufacturer(struct wps_device_data *dev, const u8 *str,
+                                   size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Manufacturer received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer", str, str_len);
+
+       os_free(dev->manufacturer);
+       dev->manufacturer = os_malloc(str_len + 1);
+       if (dev->manufacturer == NULL)
+               return -1;
+       os_memcpy(dev->manufacturer, str, str_len);
+       dev->manufacturer[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_model_name(struct wps_device_data *dev, const u8 *str,
+                                 size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Model Name received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name", str, str_len);
+
+       os_free(dev->model_name);
+       dev->model_name = os_malloc(str_len + 1);
+       if (dev->model_name == NULL)
+               return -1;
+       os_memcpy(dev->model_name, str, str_len);
+       dev->model_name[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_model_number(struct wps_device_data *dev, const u8 *str,
+                                   size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Model Number received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number", str, str_len);
+
+       os_free(dev->model_number);
+       dev->model_number = os_malloc(str_len + 1);
+       if (dev->model_number == NULL)
+               return -1;
+       os_memcpy(dev->model_number, str, str_len);
+       dev->model_number[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_serial_number(struct wps_device_data *dev,
+                                    const u8 *str, size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Serial Number received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number", str, str_len);
+
+       os_free(dev->serial_number);
+       dev->serial_number = os_malloc(str_len + 1);
+       if (dev->serial_number == NULL)
+               return -1;
+       os_memcpy(dev->serial_number, str, str_len);
+       dev->serial_number[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str,
+                               size_t str_len)
+{
+       if (str == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Device Name received");
+               return -1;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name", str, str_len);
+
+       os_free(dev->device_name);
+       dev->device_name = os_malloc(str_len + 1);
+       if (dev->device_name == NULL)
+               return -1;
+       os_memcpy(dev->device_name, str, str_len);
+       dev->device_name[str_len] = '\0';
+
+       return 0;
+}
+
+
+static int wps_process_primary_dev_type(struct wps_device_data *dev,
+                                       const u8 *dev_type)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+       if (dev_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received");
+               return -1;
+       }
+
+       os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN);
+       wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s",
+                  wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+                                       sizeof(devtype)));
+
+       return 0;
+}
+
+
+int wps_process_device_attrs(struct wps_device_data *dev,
+                            struct wps_parse_attr *attr)
+{
+       if (wps_process_manufacturer(dev, attr->manufacturer,
+                                    attr->manufacturer_len) ||
+           wps_process_model_name(dev, attr->model_name,
+                                  attr->model_name_len) ||
+           wps_process_model_number(dev, attr->model_number,
+                                    attr->model_number_len) ||
+           wps_process_serial_number(dev, attr->serial_number,
+                                     attr->serial_number_len) ||
+           wps_process_primary_dev_type(dev, attr->primary_dev_type) ||
+           wps_process_dev_name(dev, attr->dev_name, attr->dev_name_len))
+               return -1;
+       return 0;
+}
+
+
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
+{
+       if (ver == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No OS Version received");
+               return -1;
+       }
+
+       dev->os_version = WPA_GET_BE32(ver);
+       wpa_printf(MSG_DEBUG, "WPS: OS Version %08x", dev->os_version);
+
+       return 0;
+}
+
+
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
+{
+       if (bands == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No RF Bands received");
+               return -1;
+       }
+
+       dev->rf_bands = *bands;
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee RF Bands 0x%x", dev->rf_bands);
+
+       return 0;
+}
+
+
+void wps_device_data_dup(struct wps_device_data *dst,
+                        const struct wps_device_data *src)
+{
+       if (src->device_name)
+               dst->device_name = os_strdup(src->device_name);
+       if (src->manufacturer)
+               dst->manufacturer = os_strdup(src->manufacturer);
+       if (src->model_name)
+               dst->model_name = os_strdup(src->model_name);
+       if (src->model_number)
+               dst->model_number = os_strdup(src->model_number);
+       if (src->serial_number)
+               dst->serial_number = os_strdup(src->serial_number);
+       os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+       dst->os_version = src->os_version;
+       dst->rf_bands = src->rf_bands;
+}
+
+
+void wps_device_data_free(struct wps_device_data *dev)
+{
+       os_free(dev->device_name);
+       dev->device_name = NULL;
+       os_free(dev->manufacturer);
+       dev->manufacturer = NULL;
+       os_free(dev->model_name);
+       dev->model_name = NULL;
+       os_free(dev->model_number);
+       dev->model_number = NULL;
+       os_free(dev->serial_number);
+       dev->serial_number = NULL;
+}
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
new file mode 100644 (file)
index 0000000..a9c16ea
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Wi-Fi Protected Setup - device attributes
+ * 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_DEV_ATTR_H
+#define WPS_DEV_ATTR_H
+
+struct wps_parse_attr;
+
+int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_primary_dev_type(struct wps_device_data *dev,
+                              struct wpabuf *msg);
+int wps_process_device_attrs(struct wps_device_data *dev,
+                            struct wps_parse_attr *attr);
+int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
+void wps_device_data_dup(struct wps_device_data *dst,
+                        const struct wps_device_data *src);
+void wps_device_data_free(struct wps_device_data *dev);
+
+#endif /* WPS_DEV_ATTR_H */
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
new file mode 100644 (file)
index 0000000..dff24d4
--- /dev/null
@@ -0,0 +1,1240 @@
+/*
+ * Wi-Fi Protected Setup - Enrollee
+ * 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 "common.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+
+
+static int wps_build_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * MAC Address");
+       wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+       wpabuf_put_be16(msg, ETH_ALEN);
+       wpabuf_put_data(msg, wps->mac_addr_e, ETH_ALEN);
+       return 0;
+}
+
+
+static int wps_build_wps_state(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 state;
+       if (wps->wps->ap)
+               state = wps->wps->wps_state;
+       else
+               state = WPS_STATE_NOT_CONFIGURED;
+       wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
+                  state);
+       wpabuf_put_be16(msg, ATTR_WPS_STATE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, state);
+       return 0;
+}
+
+
+static int wps_build_e_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 *hash;
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+               return -1;
+       wpa_hexdump(MSG_DEBUG, "WPS: E-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-S2",
+                   wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+       if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+                          "E-Hash derivation");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS:  * E-Hash1");
+       wpabuf_put_be16(msg, ATTR_E_HASH1);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = wps->snonce;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", hash, SHA256_MAC_LEN);
+
+       wpa_printf(MSG_DEBUG, "WPS:  * E-Hash2");
+       wpabuf_put_be16(msg, ATTR_E_HASH2);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", hash, SHA256_MAC_LEN);
+
+       return 0;
+}
+
+
+static int wps_build_e_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce1");
+       wpabuf_put_be16(msg, ATTR_E_SNONCE1);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * E-SNonce2");
+       wpabuf_put_be16(msg, ATTR_E_SNONCE2);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+                       WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static struct wpabuf * wps_build_m1(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0)
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+                   wps->nonce_e, WPS_NONCE_LEN);
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M1");
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M1) ||
+           wps_build_uuid_e(msg, wps->uuid_e) ||
+           wps_build_mac_addr(wps, msg) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_public_key(wps, msg) ||
+           wps_build_auth_type_flags(wps, msg) ||
+           wps_build_encr_type_flags(wps, msg) ||
+           wps_build_conn_type_flags(wps, msg) ||
+           wps_build_config_methods(msg, wps->wps->config_methods) ||
+           wps_build_wps_state(wps, msg) ||
+           wps_build_device_attrs(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_assoc_state(wps, msg) ||
+           wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+           wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+           wps_build_os_version(&wps->wps->dev, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->state = RECV_M2;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m3(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M3");
+
+       if (wps->dev_password == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Device Password available");
+               return NULL;
+       }
+       wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M3) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_e_hash(wps, msg) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->state = RECV_M4;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m5(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M5");
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M5) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_e_snonce1(wps, plain) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->state = RECV_M6;
+       return msg;
+}
+
+
+static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+       wpabuf_put_be16(msg, ATTR_SSID);
+       wpabuf_put_be16(msg, wps->wps->ssid_len);
+       wpabuf_put_data(msg, wps->wps->ssid, wps->wps->ssid_len);
+       return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type");
+       wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, wps->wps->auth_types);
+       return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type");
+       wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, wps->wps->encr_types);
+       return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
+       wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+       wpabuf_put_be16(msg, wps->wps->network_key_len);
+       wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
+       return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (AP BSSID)");
+       wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+       wpabuf_put_be16(msg, ETH_ALEN);
+       wpabuf_put_data(msg, wps->wps->dev.mac_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
+{
+       if (wps->wps->ap_settings) {
+               wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
+               wpabuf_put_data(plain, wps->wps->ap_settings,
+                               wps->wps->ap_settings_len);
+               return 0;
+       }
+
+       return wps_build_cred_ssid(wps, plain) ||
+               wps_build_cred_mac_addr(wps, plain) ||
+               wps_build_cred_auth_type(wps, plain) ||
+               wps_build_cred_encr_type(wps, plain) ||
+               wps_build_cred_network_key(wps, plain);
+}
+
+
+static struct wpabuf * wps_build_m7(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M7");
+
+       plain = wpabuf_alloc(500 + wps->wps->ap_settings_len);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000 + wps->wps->ap_settings_len);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M7) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_e_snonce2(wps, plain) ||
+           (wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       if (wps->wps->ap && wps->wps->registrar) {
+               /*
+                * If the Registrar is only learning our current configuration,
+                * it may not continue protocol run to successful completion.
+                * Store information here to make sure it remains available.
+                */
+               wps_device_store(wps->wps->registrar, &wps->peer_dev,
+                                wps->uuid_r);
+       }
+
+       wps->state = RECV_M8;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_Done");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_DONE) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       if (wps->wps->ap)
+               wps->state = RECV_ACK;
+       else {
+               wps_success_event(wps->wps);
+               wps->state = WPS_FINISHED;
+       }
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_ACK) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_NACK) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_config_error(msg, wps->config_error)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+                                    enum wsc_op_code *op_code)
+{
+       struct wpabuf *msg;
+
+       switch (wps->state) {
+       case SEND_M1:
+               msg = wps_build_m1(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M3:
+               msg = wps_build_m3(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M5:
+               msg = wps_build_m5(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M7:
+               msg = wps_build_m7(wps);
+               *op_code = WSC_MSG;
+               break;
+       case RECEIVED_M2D:
+               if (wps->wps->ap) {
+                       msg = wps_build_wsc_nack(wps);
+                       *op_code = WSC_NACK;
+                       break;
+               }
+               msg = wps_build_wsc_ack(wps);
+               *op_code = WSC_ACK;
+               if (msg) {
+                       /* Another M2/M2D may be received */
+                       wps->state = RECV_M2;
+               }
+               break;
+       case SEND_WSC_NACK:
+               msg = wps_build_wsc_nack(wps);
+               *op_code = WSC_NACK;
+               break;
+       case WPS_MSG_DONE:
+               msg = wps_build_wsc_done(wps);
+               *op_code = WSC_Done;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+                          "a message", wps->state);
+               msg = NULL;
+               break;
+       }
+
+       if (*op_code == WSC_MSG && msg) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return msg;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+       if (r_nonce == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+               return -1;
+       }
+
+       os_memcpy(wps->nonce_r, r_nonce, WPS_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+                   wps->nonce_r, WPS_NONCE_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+       if (e_nonce == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+               return -1;
+       }
+
+       if (os_memcmp(wps->nonce_e, e_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Enrollee Nonce received");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_uuid_r(struct wps_data *wps, const u8 *uuid_r)
+{
+       if (uuid_r == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No UUID-R received");
+               return -1;
+       }
+
+       os_memcpy(wps->uuid_r, uuid_r, WPS_UUID_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+                             size_t pk_len)
+{
+       if (pk == NULL || pk_len == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+               return -1;
+       }
+
+#ifdef CONFIG_WPS_OOB
+       if (wps->dev_pw_id != DEV_PW_DEFAULT &&
+           wps->wps->oob_conf.pubkey_hash) {
+               const u8 *addr[1];
+               u8 hash[WPS_HASH_LEN];
+
+               addr[0] = pk;
+               sha256_vector(1, addr, &pk_len, hash);
+               if (os_memcmp(hash,
+                             wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+                             WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+                       wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_WPS_OOB */
+
+       wpabuf_free(wps->dh_pubkey_r);
+       wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
+       if (wps->dh_pubkey_r == NULL)
+               return -1;
+
+       if (wps_derive_keys(wps) < 0)
+               return -1;
+
+       return 0;
+}
+
+
+static int wps_process_r_hash1(struct wps_data *wps, const u8 *r_hash1)
+{
+       if (r_hash1 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No R-Hash1 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash1, r_hash1, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_r_hash2(struct wps_data *wps, const u8 *r_hash2)
+{
+       if (r_hash2 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No R-Hash2 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash2, r_hash2, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (r_snonce1 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No R-SNonce1 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce1", r_snonce1,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = r_snonce1;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+       if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
+                          "not match with the pre-committed value");
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 1, 1);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the first "
+                  "half of the device password");
+
+       return 0;
+}
+
+
+static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (r_snonce2 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No R-SNonce2 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: R-SNonce2", r_snonce2,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = r_snonce2;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+       if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
+                          "not match with the pre-committed value");
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 1, 2);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Registrar proved knowledge of the second "
+                  "half of the device password");
+
+       return 0;
+}
+
+
+static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
+                             size_t cred_len)
+{
+       struct wps_parse_attr attr;
+       struct wpabuf msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received Credential");
+       os_memset(&wps->cred, 0, sizeof(wps->cred));
+       wpabuf_set(&msg, cred, cred_len);
+       if (wps_parse_msg(&msg, &attr) < 0 ||
+           wps_process_cred(&attr, &wps->cred))
+               return -1;
+
+       if (os_memcmp(wps->cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+           0) {
+               wpa_printf(MSG_DEBUG, "WPS: MAC Address in the Credential ("
+                          MACSTR ") does not match with own address (" MACSTR
+                          ")", MAC2STR(wps->cred.mac_addr),
+                          MAC2STR(wps->wps->dev.mac_addr));
+               /*
+                * In theory, this could be consider fatal error, but there are
+                * number of deployed implementations using other address here
+                * due to unclarity in the specification. For interoperability
+                * reasons, allow this to be processed since we do not really
+                * use the MAC Address information for anything.
+                */
+       }
+
+       if (wps->wps->cred_cb) {
+               wps->cred.cred_attr = cred - 4;
+               wps->cred.cred_attr_len = cred_len + 4;
+               wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+               wps->cred.cred_attr = NULL;
+               wps->cred.cred_attr_len = 0;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_creds(struct wps_data *wps, const u8 *cred[],
+                            size_t cred_len[], size_t num_cred)
+{
+       size_t i;
+
+       if (wps->wps->ap)
+               return 0;
+
+       if (num_cred == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: No Credential attributes "
+                          "received");
+               return -1;
+       }
+
+       for (i = 0; i < num_cred; i++) {
+               if (wps_process_cred_e(wps, cred[i], cred_len[i]))
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_ap_settings_e(struct wps_data *wps,
+                                    struct wps_parse_attr *attr,
+                                    struct wpabuf *attrs)
+{
+       struct wps_credential cred;
+
+       if (!wps->wps->ap)
+               return 0;
+
+       if (wps_process_ap_settings(attr, &cred) < 0)
+               return -1;
+
+       wpa_printf(MSG_INFO, "WPS: Received new AP configuration from "
+                  "Registrar");
+
+       if (os_memcmp(cred.mac_addr, wps->wps->dev.mac_addr, ETH_ALEN) !=
+           0) {
+               wpa_printf(MSG_DEBUG, "WPS: MAC Address in the AP Settings ("
+                          MACSTR ") does not match with own address (" MACSTR
+                          ")", MAC2STR(cred.mac_addr),
+                          MAC2STR(wps->wps->dev.mac_addr));
+               /*
+                * In theory, this could be consider fatal error, but there are
+                * number of deployed implementations using other address here
+                * due to unclarity in the specification. For interoperability
+                * reasons, allow this to be processed since we do not really
+                * use the MAC Address information for anything.
+                */
+       }
+
+       if (wps->wps->cred_cb) {
+               cred.cred_attr = wpabuf_head(attrs);
+               cred.cred_attr_len = wpabuf_len(attrs);
+               wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+       }
+
+       return 0;
+}
+
+
+static enum wps_process_res wps_process_m2(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Received M2");
+
+       if (wps->state != RECV_M2) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M2", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_uuid_r(wps, attr->uuid_r)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->wps->ap &&
+           (wps->wps->ap_setup_locked || wps->dev_password == NULL)) {
+               wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse "
+                          "registration of a new Registrar");
+               wps->config_error = WPS_CFG_SETUP_LOCKED;
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+           wps_process_authenticator(wps, attr->authenticator, msg) ||
+           wps_process_device_attrs(&wps->peer_dev, attr)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wps->state = SEND_M3;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m2d(struct wps_data *wps,
+                                           struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Received M2D");
+
+       if (wps->state != RECV_M2) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M2D", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Manufacturer",
+                         attr->manufacturer, attr->manufacturer_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Name",
+                         attr->model_name, attr->model_name_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Model Number",
+                         attr->model_number, attr->model_number_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Serial Number",
+                         attr->serial_number, attr->serial_number_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Device Name",
+                         attr->dev_name, attr->dev_name_len);
+
+       if (wps->wps->event_cb) {
+               union wps_event_data data;
+               struct wps_event_m2d *m2d = &data.m2d;
+               os_memset(&data, 0, sizeof(data));
+               if (attr->config_methods)
+                       m2d->config_methods =
+                               WPA_GET_BE16(attr->config_methods);
+               m2d->manufacturer = attr->manufacturer;
+               m2d->manufacturer_len = attr->manufacturer_len;
+               m2d->model_name = attr->model_name;
+               m2d->model_name_len = attr->model_name_len;
+               m2d->model_number = attr->model_number;
+               m2d->model_number_len = attr->model_number_len;
+               m2d->serial_number = attr->serial_number;
+               m2d->serial_number_len = attr->serial_number_len;
+               m2d->dev_name = attr->dev_name;
+               m2d->dev_name_len = attr->dev_name_len;
+               m2d->primary_dev_type = attr->primary_dev_type;
+               if (attr->config_error)
+                       m2d->config_error =
+                               WPA_GET_BE16(attr->config_error);
+               if (attr->dev_password_id)
+                       m2d->dev_password_id =
+                               WPA_GET_BE16(attr->dev_password_id);
+               wps->wps->event_cb(wps->wps->cb_ctx, WPS_EV_M2D, &data);
+       }
+
+       wps->state = RECEIVED_M2D;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m4(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received M4");
+
+       if (wps->state != RECV_M4) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M4", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg) ||
+           wps_process_r_hash1(wps, attr->r_hash1) ||
+           wps_process_r_hash2(wps, attr->r_hash2)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_r_snonce1(wps, eattr.r_snonce1)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M5;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m6(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received M6");
+
+       if (wps->state != RECV_M6) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M6", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_r_snonce2(wps, eattr.r_snonce2)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M7;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m8(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received M8");
+
+       if (wps->state != RECV_M8) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M8", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_creds(wps, eattr.cred, eattr.cred_len,
+                             eattr.num_cred) ||
+           wps_process_ap_settings_e(wps, &eattr, decrypted)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = WPS_MSG_DONE;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       enum wps_process_res ret = WPS_CONTINUE;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       switch (*attr.msg_type) {
+       case WPS_M2:
+               ret = wps_process_m2(wps, msg, &attr);
+               break;
+       case WPS_M2D:
+               ret = wps_process_m2d(wps, &attr);
+               break;
+       case WPS_M4:
+               ret = wps_process_m4(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M4);
+               break;
+       case WPS_M6:
+               ret = wps_process_m6(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M6);
+               break;
+       case WPS_M8:
+               ret = wps_process_m8(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M8);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+       /*
+        * Save a copy of the last message for Authenticator derivation if we
+        * are continuing. However, skip M2D since it is not authenticated and
+        * neither is the ACK/NACK response frame. This allows the possibly
+        * following M2 to be processed correctly by using the previously sent
+        * M1 in Authenticator derivation.
+        */
+       if (ret == WPS_CONTINUE && *attr.msg_type != WPS_M2D) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_ACK) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+       {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       if (wps->state == RECV_ACK && wps->wps->ap) {
+               wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
+                          "completed successfully");
+               wps_success_event(wps->wps);
+               wps->state = WPS_FINISHED;
+               return WPS_DONE;
+       }
+
+       return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+                                                const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_NACK) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+       {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+               wpa_hexdump(MSG_DEBUG, "WPS: Received Registrar Nonce",
+                           attr.registrar_nonce, WPS_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "WPS: Expected Registrar Nonce",
+                           wps->nonce_r, WPS_NONCE_LEN);
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+               wpa_hexdump(MSG_DEBUG, "WPS: Received Enrollee Nonce",
+                           attr.enrollee_nonce, WPS_NONCE_LEN);
+               wpa_hexdump(MSG_DEBUG, "WPS: Expected Enrollee Nonce",
+                           wps->nonce_e, WPS_NONCE_LEN);
+               return WPS_FAILURE;
+       }
+
+       if (attr.config_error == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+                          "in WSC_NACK");
+               return WPS_FAILURE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Registrar terminated negotiation with "
+                  "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+
+       switch (wps->state) {
+       case RECV_M4:
+               wps_fail_event(wps->wps, WPS_M3);
+               break;
+       case RECV_M6:
+               wps_fail_event(wps->wps, WPS_M5);
+               break;
+       case RECV_M8:
+               wps_fail_event(wps->wps, WPS_M7);
+               break;
+       default:
+               break;
+       }
+
+       /* Followed by NACK if Enrollee is Supplicant or EAP-Failure if
+        * Enrollee is Authenticator */
+       wps->state = SEND_WSC_NACK;
+
+       return WPS_FAILURE;
+}
+
+
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+                                             enum wsc_op_code op_code,
+                                             const struct wpabuf *msg)
+{
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+                  "op_code=%d)",
+                  (unsigned long) wpabuf_len(msg), op_code);
+
+       if (op_code == WSC_UPnP) {
+               /* Determine the OpCode based on message type attribute */
+               struct wps_parse_attr attr;
+               if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type) {
+                       if (*attr.msg_type == WPS_WSC_ACK)
+                               op_code = WSC_ACK;
+                       else if (*attr.msg_type == WPS_WSC_NACK)
+                               op_code = WSC_NACK;
+               }
+       }
+
+       switch (op_code) {
+       case WSC_MSG:
+       case WSC_UPnP:
+               return wps_process_wsc_msg(wps, msg);
+       case WSC_ACK:
+               return wps_process_wsc_ack(wps, msg);
+       case WSC_NACK:
+               return wps_process_wsc_nack(wps, msg);
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+               return WPS_FAILURE;
+       }
+}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
new file mode 100644 (file)
index 0000000..e0cdd1d
--- /dev/null
@@ -0,0 +1,1663 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * Copyright (c) 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 "common.h"
+#include "base64.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "httpread.h"
+#include "http_client.h"
+#include "http_server.h"
+#include "upnp_xml.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "wps_er.h"
+
+
+static void wps_er_deinit_finish(void *eloop_data, void *user_ctx);
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx);
+static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg);
+static int wps_er_send_get_device_info(struct wps_er_ap *ap,
+                                      void (*m1_handler)(struct wps_er_ap *ap,
+                                                         struct wpabuf *m1));
+
+
+static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta,
+                            enum wps_event event)
+{
+       union wps_event_data data;
+       struct wps_event_er_enrollee *ev = &data.enrollee;
+
+       if (wps->event_cb == NULL)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       ev->uuid = sta->uuid;
+       ev->mac_addr = sta->addr;
+       ev->m1_received = sta->m1_received;
+       ev->config_methods = sta->config_methods;
+       ev->dev_passwd_id = sta->dev_passwd_id;
+       ev->pri_dev_type = sta->pri_dev_type;
+       ev->dev_name = sta->dev_name;
+       ev->manufacturer = sta->manufacturer;
+       ev->model_name = sta->model_name;
+       ev->model_number = sta->model_number;
+       ev->serial_number = sta->serial_number;
+       wps->event_cb(wps->cb_ctx, event, &data);
+}
+
+
+static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr)
+{
+       struct wps_er_sta *sta;
+       dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) {
+               if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+                       return sta;
+       }
+       return NULL;
+}
+
+
+static void wps_er_sta_free(struct wps_er_sta *sta)
+{
+       wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE);
+       if (sta->wps)
+               wps_deinit(sta->wps);
+       os_free(sta->manufacturer);
+       os_free(sta->model_name);
+       os_free(sta->model_number);
+       os_free(sta->serial_number);
+       os_free(sta->dev_name);
+       http_client_free(sta->http);
+       eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+       os_free(sta->cred);
+       os_free(sta);
+}
+
+
+static void wps_er_sta_remove_all(struct wps_er_ap *ap)
+{
+       struct wps_er_sta *prev, *sta;
+       dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list)
+               wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_ap * wps_er_ap_get(struct wps_er *er,
+                                       struct in_addr *addr, const u8 *uuid)
+{
+       struct wps_er_ap *ap;
+       dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+               if ((addr == NULL || ap->addr.s_addr == addr->s_addr) &&
+                   (uuid == NULL ||
+                    os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0))
+                       return ap;
+       }
+       return NULL;
+}
+
+
+static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id)
+{
+       struct wps_er_ap *ap;
+       dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+               if (ap->id == id)
+                       return ap;
+       }
+       return NULL;
+}
+
+
+static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap,
+                           enum wps_event event)
+{
+       union wps_event_data data;
+       struct wps_event_er_ap *evap = &data.ap;
+
+       if (wps->event_cb == NULL)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       evap->uuid = ap->uuid;
+       evap->friendly_name = ap->friendly_name;
+       evap->manufacturer = ap->manufacturer;
+       evap->manufacturer_url = ap->manufacturer_url;
+       evap->model_description = ap->model_description;
+       evap->model_name = ap->model_name;
+       evap->model_number = ap->model_number;
+       evap->model_url = ap->model_url;
+       evap->serial_number = ap->serial_number;
+       evap->upc = ap->upc;
+       evap->pri_dev_type = ap->pri_dev_type;
+       evap->wps_state = ap->wps_state;
+       evap->mac_addr = ap->mac_addr;
+       wps->event_cb(wps->cb_ctx, event, &data);
+}
+
+
+static void wps_er_ap_free(struct wps_er_ap *ap)
+{
+       http_client_free(ap->http);
+       ap->http = NULL;
+
+       os_free(ap->location);
+       os_free(ap->friendly_name);
+       os_free(ap->manufacturer);
+       os_free(ap->manufacturer_url);
+       os_free(ap->model_description);
+       os_free(ap->model_name);
+       os_free(ap->model_number);
+       os_free(ap->model_url);
+       os_free(ap->serial_number);
+       os_free(ap->udn);
+       os_free(ap->upc);
+
+       os_free(ap->scpd_url);
+       os_free(ap->control_url);
+       os_free(ap->event_sub_url);
+
+       os_free(ap->ap_settings);
+
+       os_free(ap);
+}
+
+
+static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap)
+{
+       wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)",
+                  inet_ntoa(ap->addr), ap->location);
+       dl_list_del(&ap->list);
+       wps_er_ap_free(ap);
+
+       if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) {
+               eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
+               wps_er_deinit_finish(er, NULL);
+       }
+}
+
+
+static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c,
+                                      enum http_client_event event)
+{
+       struct wps_er_ap *ap = ctx;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events");
+               ap->subscribed = 0;
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from "
+                          "events");
+               break;
+       }
+       http_client_free(ap->http);
+       ap->http = NULL;
+
+       /*
+        * Need to get rid of the AP entry regardless of whether we managed to
+        * unsubscribe cleanly or not.
+        */
+       wps_er_ap_unsubscribed(ap->er, ap);
+}
+
+
+static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap)
+{
+       struct wpabuf *req;
+       struct sockaddr_in dst;
+       char *url, *path;
+       char sid[100];
+
+       if (ap->event_sub_url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
+                          "subscribe");
+               goto fail;
+       }
+       if (ap->http) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
+                          "send subscribe request");
+               goto fail;
+       }
+
+       url = http_client_url_parse(ap->event_sub_url, &dst, &path);
+       if (url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
+               goto fail;
+       }
+
+       req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
+       if (req == NULL) {
+               os_free(url);
+               goto fail;
+       }
+       uuid_bin2str(ap->sid, sid, sizeof(sid));
+       wpabuf_printf(req,
+                     "UNSUBSCRIBE %s HTTP/1.1\r\n"
+                     "HOST: %s:%d\r\n"
+                     "SID: uuid:%s\r\n"
+                     "\r\n",
+                     path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid);
+       os_free(url);
+       wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request",
+                         wpabuf_head(req), wpabuf_len(req));
+
+       ap->http = http_client_addr(&dst, req, 1000,
+                                   wps_er_http_unsubscribe_cb, ap);
+       if (ap->http == NULL) {
+               wpabuf_free(req);
+               goto fail;
+       }
+       return;
+
+fail:
+       /*
+        * Need to get rid of the AP entry even when we fail to unsubscribe
+        * cleanly.
+        */
+       wps_er_ap_unsubscribed(ap->er, ap);
+}
+
+static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap)
+{
+       wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)",
+                  inet_ntoa(ap->addr), ap->location);
+       eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+       wps_er_sta_remove_all(ap);
+       wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE);
+       http_client_free(ap->http);
+       ap->http = NULL;
+       if (ap->wps) {
+               wps_deinit(ap->wps);
+               ap->wps = NULL;
+       }
+
+       dl_list_del(&ap->list);
+       if (ap->subscribed) {
+               dl_list_add(&er->ap_unsubscribing, &ap->list);
+               wps_er_ap_unsubscribe(er, ap);
+       } else
+               wps_er_ap_free(ap);
+}
+
+
+static void wps_er_ap_timeout(void *eloop_data, void *user_ctx)
+{
+       struct wps_er *er = eloop_data;
+       struct wps_er_ap *ap = user_ctx;
+       wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out");
+       wps_er_ap_remove_entry(er, ap);
+}
+
+
+static int wps_er_get_sid(struct wps_er_ap *ap, char *sid)
+{
+       char *pos;
+       char txt[100];
+
+       if (!sid) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)",
+                          inet_ntoa(ap->addr), ap->location);
+               return -1;
+       }
+
+       pos = os_strstr(sid, "uuid:");
+       if (!pos) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
+                          "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
+                          sid);
+               return -1;
+       }
+
+       pos += 5;
+       if (uuid_str2bin(pos, ap->sid) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from "
+                          "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location,
+                          sid);
+               return -1;
+       }
+
+       uuid_bin2str(ap->sid, txt, sizeof(txt));
+       wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s",
+                  inet_ntoa(ap->addr), ap->location, txt);
+
+       return 0;
+}
+
+
+static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c,
+                                    enum http_client_event event)
+{
+       struct wps_er_ap *ap = ctx;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events");
+               ap->subscribed = 1;
+               wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID"));
+               wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD);
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events");
+               break;
+       }
+       http_client_free(ap->http);
+       ap->http = NULL;
+}
+
+
+static void wps_er_subscribe(struct wps_er_ap *ap)
+{
+       struct wpabuf *req;
+       struct sockaddr_in dst;
+       char *url, *path;
+
+       if (ap->event_sub_url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot "
+                          "subscribe");
+               return;
+       }
+       if (ap->http) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot "
+                          "send subscribe request");
+               return;
+       }
+
+       url = http_client_url_parse(ap->event_sub_url, &dst, &path);
+       if (url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL");
+               return;
+       }
+
+       req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000);
+       if (req == NULL) {
+               os_free(url);
+               return;
+       }
+       wpabuf_printf(req,
+                     "SUBSCRIBE %s HTTP/1.1\r\n"
+                     "HOST: %s:%d\r\n"
+                     "CALLBACK: <http://%s:%d/event/%u/%u>\r\n"
+                     "NT: upnp:event\r\n"
+                     "TIMEOUT: Second-%d\r\n"
+                     "\r\n",
+                     path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port),
+                     ap->er->ip_addr_text, ap->er->http_port,
+                     ap->er->event_id, ap->id, 1800);
+       os_free(url);
+       wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request",
+                         wpabuf_head(req), wpabuf_len(req));
+
+       ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb,
+                                   ap);
+       if (ap->http == NULL)
+               wpabuf_free(req);
+}
+
+
+static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+       struct wps_parse_attr attr;
+
+       if (wps_parse_msg(m1, &attr) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1");
+               return;
+       }
+       if (attr.primary_dev_type)
+               os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8);
+       if (attr.wps_state)
+               ap->wps_state = *attr.wps_state;
+       if (attr.mac_addr)
+               os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN);
+
+       wps_er_subscribe(ap);
+}
+
+
+static void wps_er_get_device_info(struct wps_er_ap *ap)
+{
+       wps_er_send_get_device_info(ap, wps_er_ap_get_m1);
+}
+
+
+static void wps_er_parse_device_description(struct wps_er_ap *ap,
+                                           struct wpabuf *reply)
+{
+       /* Note: reply includes null termination after the buffer data */
+       const char *data = wpabuf_head(reply);
+       char *pos;
+
+       wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info",
+                         wpabuf_head(reply), wpabuf_len(reply));
+
+       ap->friendly_name = xml_get_first_item(data, "friendlyName");
+       wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name);
+
+       ap->manufacturer = xml_get_first_item(data, "manufacturer");
+       wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer);
+
+       ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL");
+       wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'",
+                  ap->manufacturer_url);
+
+       ap->model_description = xml_get_first_item(data, "modelDescription");
+       wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'",
+                  ap->model_description);
+
+       ap->model_name = xml_get_first_item(data, "modelName");
+       wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name);
+
+       ap->model_number = xml_get_first_item(data, "modelNumber");
+       wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number);
+
+       ap->model_url = xml_get_first_item(data, "modelURL");
+       wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url);
+
+       ap->serial_number = xml_get_first_item(data, "serialNumber");
+       wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
+
+       ap->udn = xml_get_first_item(data, "UDN");
+       wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
+       pos = os_strstr(ap->udn, "uuid:");
+       if (pos) {
+               pos += 5;
+               if (uuid_str2bin(pos, ap->uuid) < 0)
+                       wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN");
+       }
+
+       ap->upc = xml_get_first_item(data, "UPC");
+       wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc);
+
+       ap->scpd_url = http_link_update(
+               xml_get_first_item(data, "SCPDURL"), ap->location);
+       wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url);
+
+       ap->control_url = http_link_update(
+               xml_get_first_item(data, "controlURL"), ap->location);
+       wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url);
+
+       ap->event_sub_url = http_link_update(
+               xml_get_first_item(data, "eventSubURL"), ap->location);
+       wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url);
+}
+
+
+static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c,
+                                   enum http_client_event event)
+{
+       struct wps_er_ap *ap = ctx;
+       struct wpabuf *reply;
+       int ok = 0;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               reply = http_client_get_body(c);
+               if (reply == NULL)
+                       break;
+               wps_er_parse_device_description(ap, reply);
+               ok = 1;
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info");
+               break;
+       }
+       http_client_free(ap->http);
+       ap->http = NULL;
+       if (ok)
+               wps_er_get_device_info(ap);
+}
+
+
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+                  const char *location, int max_age)
+{
+       struct wps_er_ap *ap;
+
+       ap = wps_er_ap_get(er, addr, uuid);
+       if (ap) {
+               /* Update advertisement timeout */
+               eloop_cancel_timeout(wps_er_ap_timeout, er, ap);
+               eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+               return;
+       }
+
+       ap = os_zalloc(sizeof(*ap));
+       if (ap == NULL)
+               return;
+       dl_list_init(&ap->sta);
+       ap->er = er;
+       ap->id = ++er->next_ap_id;
+       ap->location = os_strdup(location);
+       if (ap->location == NULL) {
+               os_free(ap);
+               return;
+       }
+       dl_list_add(&er->ap, &ap->list);
+
+       ap->addr.s_addr = addr->s_addr;
+       os_memcpy(ap->uuid, uuid, WPS_UUID_LEN);
+       eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap);
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)",
+                  inet_ntoa(ap->addr), ap->location);
+
+       /* Fetch device description */
+       ap->http = http_client_url(ap->location, NULL, 10000,
+                                  wps_er_http_dev_desc_cb, ap);
+}
+
+
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr)
+{
+       struct wps_er_ap *ap;
+       dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+               if (ap->addr.s_addr == addr->s_addr) {
+                       wps_er_ap_remove_entry(er, ap);
+                       return;
+               }
+       }
+}
+
+
+static void wps_er_ap_remove_all(struct wps_er *er)
+{
+       struct wps_er_ap *prev, *ap;
+       dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list)
+               wps_er_ap_remove_entry(er, ap);
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+       wpabuf_put_str(buf, "Date: ");
+       format_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void wps_er_http_resp_not_found(struct http_request *req)
+{
+       struct wpabuf *buf;
+       buf = wpabuf_alloc(200);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+
+       wpabuf_put_str(buf,
+                      "HTTP/1.1 404 Not Found\r\n"
+                      "Server: unspecified, UPnP/1.0, unspecified\r\n"
+                      "Connection: close\r\n");
+       http_put_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+       http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_http_resp_ok(struct http_request *req)
+{
+       struct wpabuf *buf;
+       buf = wpabuf_alloc(200);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+
+       wpabuf_put_str(buf,
+                      "HTTP/1.1 200 OK\r\n"
+                      "Server: unspecified, UPnP/1.0, unspecified\r\n"
+                      "Connection: close\r\n"
+                      "Content-Length: 0\r\n");
+       http_put_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+       http_request_send_and_deinit(req, buf);
+}
+
+
+static void wps_er_sta_timeout(void *eloop_data, void *user_ctx)
+{
+       struct wps_er_sta *sta = eloop_data;
+       wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out");
+       dl_list_del(&sta->list);
+       wps_er_sta_free(sta);
+}
+
+
+static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap,
+                                              const u8 *addr,
+                                              struct wps_parse_attr *attr,
+                                              int probe_req)
+{
+       struct wps_er_sta *sta = wps_er_sta_get(ap, addr);
+       int new_sta = 0;
+       int m1;
+
+       m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1;
+
+       if (sta == NULL) {
+               /*
+                * Only allow new STA entry to be added based on Probe Request
+                * or M1. This will filter out bogus events and anything that
+                * may have been ongoing at the time ER subscribed for events.
+                */
+               if (!probe_req && !m1)
+                       return NULL;
+
+               sta = os_zalloc(sizeof(*sta));
+               if (sta == NULL)
+                       return NULL;
+               os_memcpy(sta->addr, addr, ETH_ALEN);
+               sta->ap = ap;
+               dl_list_add(&ap->sta, &sta->list);
+               new_sta = 1;
+       }
+
+       if (m1)
+               sta->m1_received = 1;
+
+       if (attr->config_methods && (!probe_req || !sta->m1_received))
+               sta->config_methods = WPA_GET_BE16(attr->config_methods);
+       if (attr->uuid_e && (!probe_req || !sta->m1_received))
+               os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN);
+       if (attr->primary_dev_type && (!probe_req || !sta->m1_received))
+               os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8);
+       if (attr->dev_password_id && (!probe_req || !sta->m1_received))
+               sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id);
+
+       if (attr->manufacturer) {
+               os_free(sta->manufacturer);
+               sta->manufacturer = os_malloc(attr->manufacturer_len + 1);
+               if (sta->manufacturer) {
+                       os_memcpy(sta->manufacturer, attr->manufacturer,
+                                 attr->manufacturer_len);
+                       sta->manufacturer[attr->manufacturer_len] = '\0';
+               }
+       }
+
+       if (attr->model_name) {
+               os_free(sta->model_name);
+               sta->model_name = os_malloc(attr->model_name_len + 1);
+               if (sta->model_name) {
+                       os_memcpy(sta->model_name, attr->model_name,
+                                 attr->model_name_len);
+                       sta->model_name[attr->model_name_len] = '\0';
+               }
+       }
+
+       if (attr->model_number) {
+               os_free(sta->model_number);
+               sta->model_number = os_malloc(attr->model_number_len + 1);
+               if (sta->model_number) {
+                       os_memcpy(sta->model_number, attr->model_number,
+                                 attr->model_number_len);
+                       sta->model_number[attr->model_number_len] = '\0';
+               }
+       }
+
+       if (attr->serial_number) {
+               os_free(sta->serial_number);
+               sta->serial_number = os_malloc(attr->serial_number_len + 1);
+               if (sta->serial_number) {
+                       os_memcpy(sta->serial_number, attr->serial_number,
+                                 attr->serial_number_len);
+                       sta->serial_number[attr->serial_number_len] = '\0';
+               }
+       }
+
+       if (attr->dev_name) {
+               os_free(sta->dev_name);
+               sta->dev_name = os_malloc(attr->dev_name_len + 1);
+               if (sta->dev_name) {
+                       os_memcpy(sta->dev_name, attr->dev_name,
+                                 attr->dev_name_len);
+                       sta->dev_name[attr->dev_name_len] = '\0';
+               }
+       }
+
+       eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+       eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL);
+
+       if (m1 || new_sta)
+               wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
+
+       return sta;
+}
+
+
+static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap,
+                                              const u8 *addr,
+                                              struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from "
+                  MACSTR, MAC2STR(addr));
+       wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+                       "(TLVs from Probe Request)", msg);
+
+       if (wps_parse_msg(msg, &attr) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+                          "WLANEvent message");
+               return;
+       }
+
+       wps_er_add_sta_data(ap, addr, &attr, 1);
+}
+
+
+static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c,
+                                            enum http_client_event event)
+{
+       struct wps_er_sta *sta = ctx;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK");
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed");
+               break;
+       }
+       http_client_free(sta->http);
+       sta->http = NULL;
+}
+
+
+static const char *soap_prefix =
+       "<?xml version=\"1.0\"?>\n"
+       "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+       "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+       "<s:Body>\n";
+static const char *soap_postfix =
+       "</s:Body>\n</s:Envelope>\n";
+static const char *urn_wfawlanconfig =
+       "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+
+static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg,
+                                      const char *name, const char *arg_name,
+                                      const char *path,
+                                      const struct sockaddr_in *dst,
+                                      char **len_ptr, char **body_ptr)
+{
+       unsigned char *encoded;
+       size_t encoded_len;
+       struct wpabuf *buf;
+
+       if (msg) {
+               encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg),
+                                       &encoded_len);
+               if (encoded == NULL)
+                       return NULL;
+       } else {
+               encoded = NULL;
+               encoded_len = 0;
+       }
+
+       buf = wpabuf_alloc(1000 + encoded_len);
+       if (buf == NULL) {
+               os_free(encoded);
+               return NULL;
+       }
+
+       wpabuf_printf(buf,
+                     "POST %s HTTP/1.1\r\n"
+                     "Host: %s:%d\r\n"
+                     "Content-Type: text/xml; charset=\"utf-8\"\r\n"
+                     "Content-Length: ",
+                     path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port));
+
+       *len_ptr = wpabuf_put(buf, 0);
+       wpabuf_printf(buf,
+                     "        \r\n"
+                     "SOAPACTION: \"%s#%s\"\r\n"
+                     "\r\n",
+                     urn_wfawlanconfig, name);
+
+       *body_ptr = wpabuf_put(buf, 0);
+
+       wpabuf_put_str(buf, soap_prefix);
+       wpabuf_printf(buf, "<u:%s xmlns:u=\"", name);
+       wpabuf_put_str(buf, urn_wfawlanconfig);
+       wpabuf_put_str(buf, "\">\n");
+       if (encoded) {
+               wpabuf_printf(buf, "<%s>%s</%s>\n",
+                             arg_name, (char *) encoded, arg_name);
+               os_free(encoded);
+       }
+
+       return buf;
+}
+
+
+static void wps_er_soap_end(struct wpabuf *buf, const char *name,
+                           char *len_ptr, char *body_ptr)
+{
+       char len_buf[10];
+       wpabuf_printf(buf, "</u:%s>\n", name);
+       wpabuf_put_str(buf, soap_postfix);
+       os_snprintf(len_buf, sizeof(len_buf), "%d",
+                   (int) ((char *) wpabuf_put(buf, 0) - body_ptr));
+       os_memcpy(len_ptr, len_buf, os_strlen(len_buf));
+}
+
+
+static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+       struct wpabuf *buf;
+       char *len_ptr, *body_ptr;
+       struct sockaddr_in dst;
+       char *url, *path;
+
+       if (sta->http) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - "
+                          "ignore new request");
+               wpabuf_free(msg);
+               return;
+       }
+
+       if (sta->ap->control_url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+               wpabuf_free(msg);
+               return;
+       }
+
+       url = http_client_url_parse(sta->ap->control_url, &dst, &path);
+       if (url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+               wpabuf_free(msg);
+               return;
+       }
+
+       buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst,
+                             &len_ptr, &body_ptr);
+       wpabuf_free(msg);
+       os_free(url);
+       if (buf == NULL)
+               return;
+       wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n",
+                     UPNP_WPS_WLANEVENT_TYPE_EAP);
+       wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n",
+                     MAC2STR(sta->addr));
+
+       wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr);
+
+       sta->http = http_client_addr(&dst, buf, 1000,
+                                    wps_er_http_put_wlan_response_cb, sta);
+       if (sta->http == NULL)
+               wpabuf_free(buf);
+}
+
+
+static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg,
+                              enum wsc_op_code op_code)
+{
+       enum wps_process_res res;
+
+       res = wps_process_msg(sta->wps, op_code, msg);
+       if (res == WPS_CONTINUE) {
+               struct wpabuf *next = wps_get_msg(sta->wps, &op_code);
+               if (next)
+                       wps_er_sta_send_msg(sta, next);
+       } else {
+               wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the "
+                          "enrollee (res=%d)",
+                          res == WPS_DONE ? "succeeded" : "failed", res);
+               wps_deinit(sta->wps);
+               sta->wps = NULL;
+               if (res == WPS_DONE) {
+                       /* Remove the STA entry after short timeout */
+                       eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL);
+                       eloop_register_timeout(10, 0, wps_er_sta_timeout, sta,
+                                              NULL);
+               }
+       }
+}
+
+
+static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg)
+{
+       struct wps_config cfg;
+
+       if (sta->wps)
+               wps_deinit(sta->wps);
+
+       os_memset(&cfg, 0, sizeof(cfg));
+       cfg.wps = sta->ap->er->wps;
+       cfg.registrar = 1;
+       cfg.peer_addr = sta->addr;
+
+       sta->wps = wps_init(&cfg);
+       if (sta->wps == NULL)
+               return;
+       sta->wps->er = 1;
+       sta->wps->use_cred = sta->ap->ap_settings;
+       if (sta->ap->ap_settings) {
+               os_free(sta->cred);
+               sta->cred = os_malloc(sizeof(*sta->cred));
+               if (sta->cred) {
+                       os_memcpy(sta->cred, sta->ap->ap_settings,
+                                 sizeof(*sta->cred));
+                       sta->cred->cred_attr = NULL;
+                       os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN);
+                       sta->wps->use_cred = sta->cred;
+               }
+       }
+
+       wps_er_sta_process(sta, msg, WSC_MSG);
+}
+
+
+static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr,
+                                        struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       struct wps_er_sta *sta;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR,
+                  MAC2STR(addr));
+       wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message "
+                       "(TLVs from EAP-WSC)", msg);
+
+       if (wps_parse_msg(msg, &attr) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in "
+                          "WLANEvent message");
+               return;
+       }
+
+       sta = wps_er_add_sta_data(ap, addr, &attr, 0);
+       if (sta == NULL)
+               return;
+
+       if (attr.msg_type && *attr.msg_type == WPS_M1)
+               wps_er_sta_start(sta, msg);
+       else if (sta->wps) {
+               enum wsc_op_code op_code = WSC_MSG;
+               if (attr.msg_type) {
+                       switch (*attr.msg_type) {
+                       case WPS_WSC_ACK:
+                               op_code = WSC_ACK;
+                               break;
+                       case WPS_WSC_NACK:
+                               op_code = WSC_NACK;
+                               break;
+                       case WPS_WSC_DONE:
+                               op_code = WSC_Done;
+                               break;
+                       }
+               }
+               wps_er_sta_process(sta, msg, op_code);
+       }
+}
+
+
+static void wps_er_process_wlanevent(struct wps_er_ap *ap,
+                                    struct wpabuf *event)
+{
+       u8 *data;
+       u8 wlan_event_type;
+       u8 wlan_event_mac[ETH_ALEN];
+       struct wpabuf msg;
+
+       wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent",
+                   wpabuf_head(event), wpabuf_len(event));
+       if (wpabuf_len(event) < 1 + 17) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent");
+               return;
+       }
+
+       data = wpabuf_mhead(event);
+       wlan_event_type = data[0];
+       if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in "
+                          "WLANEvent");
+               return;
+       }
+
+       wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17));
+
+       switch (wlan_event_type) {
+       case 1:
+               wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg);
+               break;
+       case 2:
+               wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d",
+                          wlan_event_type);
+               break;
+       }
+}
+
+
+static void wps_er_http_event(struct wps_er *er, struct http_request *req,
+                             unsigned int ap_id)
+{
+       struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id);
+       struct wpabuf *event;
+       enum http_reply_code ret;
+
+       if (ap == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id "
+                          "%u", ap_id);
+               wps_er_http_resp_not_found(req);
+               return;
+       }
+       wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s",
+                  ap_id, http_request_get_data(req));
+
+       event = xml_get_base64_item(http_request_get_data(req), "WLANEvent",
+                                   &ret);
+       if (event == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent "
+                          "from the event notification");
+               /*
+                * Reply with OK anyway to avoid getting unregistered from
+                * events.
+                */
+               wps_er_http_resp_ok(req);
+               return;
+       }
+
+       wps_er_process_wlanevent(ap, event);
+
+       wpabuf_free(event);
+       wps_er_http_resp_ok(req);
+}
+
+
+static void wps_er_http_notify(struct wps_er *er, struct http_request *req)
+{
+       char *uri = http_request_get_uri(req);
+
+       if (os_strncmp(uri, "/event/", 7) == 0) {
+               unsigned int event_id;
+               char *pos;
+               event_id = atoi(uri + 7);
+               if (event_id != er->event_id) {
+                       wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an "
+                                  "unknown event id %u", event_id);
+                       return;
+               }
+               pos = os_strchr(uri + 7, '/');
+               if (pos == NULL)
+                       return;
+               pos++;
+               wps_er_http_event(er, req, atoi(pos));
+       } else {
+               wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'",
+                          uri);
+               wps_er_http_resp_not_found(req);
+       }
+}
+
+
+static void wps_er_http_req(void *ctx, struct http_request *req)
+{
+       struct wps_er *er = ctx;
+       struct sockaddr_in *cli = http_request_get_cli_addr(req);
+       enum httpread_hdr_type type = http_request_get_type(req);
+       struct wpabuf *buf;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from "
+                  "%s:%d",
+                  http_request_get_uri(req), type,
+                  inet_ntoa(cli->sin_addr), ntohs(cli->sin_port));
+
+       switch (type) {
+       case HTTPREAD_HDR_TYPE_NOTIFY:
+               wps_er_http_notify(er, req);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type "
+                          "%d", type);
+               buf = wpabuf_alloc(200);
+               if (buf == NULL) {
+                       http_request_deinit(req);
+                       return;
+               }
+               wpabuf_put_str(buf,
+                              "HTTP/1.1 501 Unimplemented\r\n"
+                              "Connection: close\r\n");
+               http_put_date(buf);
+               wpabuf_put_str(buf, "\r\n");
+               http_request_send_and_deinit(req, buf);
+               break;
+       }
+}
+
+
+struct wps_er *
+wps_er_init(struct wps_context *wps, const char *ifname)
+{
+       struct wps_er *er;
+       struct in_addr addr;
+
+       er = os_zalloc(sizeof(*er));
+       if (er == NULL)
+               return NULL;
+       dl_list_init(&er->ap);
+       dl_list_init(&er->ap_unsubscribing);
+
+       er->multicast_sd = -1;
+       er->ssdp_sd = -1;
+
+       os_strlcpy(er->ifname, ifname, sizeof(er->ifname));
+       er->wps = wps;
+       if (os_get_random((unsigned char *) &er->event_id,
+                         sizeof(er->event_id)) < 0) {
+               wps_er_deinit(er, NULL, NULL);
+               return NULL;
+       }
+       /* Limit event_id to < 32 bits to avoid issues with atoi() */
+       er->event_id &= 0x0fffffff;
+
+       if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text,
+                          er->mac_addr)) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+                          "for %s. Does it have IP address?", ifname);
+               wps_er_deinit(er, NULL, NULL);
+               return NULL;
+       }
+
+       if (wps_er_ssdp_init(er) < 0) {
+               wps_er_deinit(er, NULL, NULL);
+               return NULL;
+       }
+
+       addr.s_addr = er->ip_addr;
+       er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er);
+       if (er->http_srv == NULL) {
+               wps_er_deinit(er, NULL, NULL);
+               return NULL;
+       }
+       er->http_port = http_server_get_port(er->http_srv);
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)",
+                  er->ifname, er->ip_addr_text);
+
+       return er;
+}
+
+
+void wps_er_refresh(struct wps_er *er)
+{
+       struct wps_er_ap *ap;
+       struct wps_er_sta *sta;
+
+       dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) {
+               wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD);
+               dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list)
+                       wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD);
+       }
+
+       wps_er_send_ssdp_msearch(er);
+}
+
+
+static void wps_er_deinit_finish(void *eloop_data, void *user_ctx)
+{
+       struct wps_er *er = eloop_data;
+       void (*deinit_done_cb)(void *ctx);
+       void *deinit_done_ctx;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit");
+
+       deinit_done_cb = er->deinit_done_cb;
+       deinit_done_ctx = er->deinit_done_ctx;
+       os_free(er->ip_addr_text);
+       os_free(er);
+
+       if (deinit_done_cb)
+               deinit_done_cb(deinit_done_ctx);
+}
+
+
+void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx)
+{
+       if (er == NULL)
+               return;
+       http_server_deinit(er->http_srv);
+       wps_er_ap_remove_all(er);
+       wps_er_ssdp_deinit(er);
+       eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0,
+                              wps_er_deinit_finish, er, NULL);
+       wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout");
+       er->deinitializing = 1;
+       er->deinit_done_cb = cb;
+       er->deinit_done_ctx = ctx;
+}
+
+
+static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c,
+                                      enum http_client_event event)
+{
+       struct wps_er_ap *ap = ctx;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK");
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed");
+               break;
+       }
+       http_client_free(ap->http);
+       ap->http = NULL;
+}
+
+
+static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+       struct wpabuf *buf;
+       char *len_ptr, *body_ptr;
+       struct sockaddr_in dst;
+       char *url, *path;
+
+       if (ap->control_url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+               return;
+       }
+
+       if (ap->http) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - "
+                          "ignore new request");
+               return;
+       }
+
+       url = http_client_url_parse(ap->control_url, &dst, &path);
+       if (url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+               return;
+       }
+
+       buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path,
+                             &dst, &len_ptr, &body_ptr);
+       os_free(url);
+       if (buf == NULL)
+               return;
+
+       wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr);
+
+       ap->http = http_client_addr(&dst, buf, 1000,
+                                   wps_er_http_set_sel_reg_cb, ap);
+       if (ap->http == NULL)
+               wpabuf_free(buf);
+}
+
+
+static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg)
+{
+       wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, !!sel_reg);
+       return 0;
+}
+
+
+static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id)
+{
+       wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, dev_passwd_id);
+       return 0;
+}
+
+
+static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
+                                              u16 sel_reg_config_methods)
+{
+       wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, sel_reg_config_methods);
+       return 0;
+}
+
+
+void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
+                       u16 sel_reg_config_methods)
+{
+       struct wpabuf *msg;
+       struct wps_er_ap *ap;
+
+       msg = wpabuf_alloc(500);
+       if (msg == NULL)
+               return;
+
+       if (wps_build_version(msg) ||
+           wps_er_build_selected_registrar(msg, sel_reg) ||
+           wps_er_build_dev_password_id(msg, dev_passwd_id) ||
+           wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) {
+               wpabuf_free(msg);
+               return;
+       }
+
+       dl_list_for_each(ap, &er->ap, struct wps_er_ap, list)
+               wps_er_send_set_sel_reg(ap, msg);
+
+       wpabuf_free(msg);
+}
+
+
+int wps_er_pbc(struct wps_er *er, const u8 *uuid)
+{
+       if (er == NULL || er->wps == NULL)
+               return -1;
+
+       /*
+        * TODO: Should enable PBC mode only in a single AP based on which AP
+        * the Enrollee (uuid) is using. Now, we may end up enabling multiple
+        * APs in PBC mode which could result in session overlap at the
+        * Enrollee.
+        */
+       if (wps_registrar_button_pushed(er->wps->registrar))
+               return -1;
+
+       return 0;
+}
+
+
+static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred)
+{
+       struct wps_er_ap *ap = ctx;
+       wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received");
+       os_free(ap->ap_settings);
+       ap->ap_settings = os_malloc(sizeof(*cred));
+       if (ap->ap_settings) {
+               os_memcpy(ap->ap_settings, cred, sizeof(*cred));
+               ap->ap_settings->cred_attr = NULL;
+       }
+
+       /* TODO: send info through ctrl_iface */
+}
+
+
+static void wps_er_http_put_message_cb(void *ctx, struct http_client *c,
+                                      enum http_client_event event)
+{
+       struct wps_er_ap *ap = ctx;
+       struct wpabuf *reply;
+       char *msg = NULL;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK");
+               reply = http_client_get_body(c);
+               if (reply == NULL)
+                       break;
+               msg = os_zalloc(wpabuf_len(reply) + 1);
+               if (msg == NULL)
+                       break;
+               os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply));
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed");
+               if (ap->wps) {
+                       wps_deinit(ap->wps);
+                       ap->wps = NULL;
+               }
+               break;
+       }
+       http_client_free(ap->http);
+       ap->http = NULL;
+
+       if (msg) {
+               struct wpabuf *buf;
+               enum http_reply_code ret;
+               buf = xml_get_base64_item(msg, "NewOutMessage", &ret);
+               os_free(msg);
+               if (buf == NULL) {
+                       wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
+                                  "NewOutMessage from PutMessage response");
+                       return;
+               }
+               wps_er_ap_process(ap, buf);
+               wpabuf_free(buf);
+       }
+}
+
+
+static void wps_er_ap_put_message(struct wps_er_ap *ap,
+                                 const struct wpabuf *msg)
+{
+       struct wpabuf *buf;
+       char *len_ptr, *body_ptr;
+       struct sockaddr_in dst;
+       char *url, *path;
+
+       if (ap->http) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
+                          "with the AP - cannot continue learn");
+               return;
+       }
+
+       if (ap->control_url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+               return;
+       }
+
+       url = http_client_url_parse(ap->control_url, &dst, &path);
+       if (url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+               return;
+       }
+
+       buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst,
+                             &len_ptr, &body_ptr);
+       os_free(url);
+       if (buf == NULL)
+               return;
+
+       wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr);
+
+       ap->http = http_client_addr(&dst, buf, 10000,
+                                   wps_er_http_put_message_cb, ap);
+       if (ap->http == NULL)
+               wpabuf_free(buf);
+}
+
+
+static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg)
+{
+       enum wps_process_res res;
+
+       res = wps_process_msg(ap->wps, WSC_MSG, msg);
+       if (res == WPS_CONTINUE) {
+               enum wsc_op_code op_code;
+               struct wpabuf *next = wps_get_msg(ap->wps, &op_code);
+               if (next) {
+                       wps_er_ap_put_message(ap, next);
+                       wpabuf_free(next);
+               } else {
+                       wpa_printf(MSG_DEBUG, "WPS ER: Failed to build "
+                                  "message");
+                       wps_deinit(ap->wps);
+                       ap->wps = NULL;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from "
+                          "AP (res=%d)", res);
+               wps_deinit(ap->wps);
+               ap->wps = NULL;
+       }
+}
+
+
+static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1)
+{
+       struct wps_config cfg;
+
+       if (ap->wps) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in "
+                          "progress with this AP");
+               return;
+       }
+
+       os_memset(&cfg, 0, sizeof(cfg));
+       cfg.wps = ap->er->wps;
+       cfg.registrar = 1;
+       ap->wps = wps_init(&cfg);
+       if (ap->wps == NULL)
+               return;
+       ap->wps->ap_settings_cb = wps_er_ap_settings_cb;
+       ap->wps->ap_settings_cb_ctx = ap;
+
+       wps_er_ap_process(ap, m1);
+}
+
+
+static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info)
+{
+       struct wpabuf *info;
+       enum http_reply_code ret;
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) "
+                  "from the AP");
+       info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret);
+       if (info == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Could not extract "
+                          "NewDeviceInfo from GetDeviceInfo response");
+               return;
+       }
+
+       ap->m1_handler(ap, info);
+       wpabuf_free(info);
+}
+
+
+static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c,
+                                       enum http_client_event event)
+{
+       struct wps_er_ap *ap = ctx;
+       struct wpabuf *reply;
+       char *dev_info = NULL;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK");
+               reply = http_client_get_body(c);
+               if (reply == NULL)
+                       break;
+               dev_info = os_zalloc(wpabuf_len(reply) + 1);
+               if (dev_info == NULL)
+                       break;
+               os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply));
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed");
+               break;
+       }
+       http_client_free(ap->http);
+       ap->http = NULL;
+
+       if (dev_info) {
+               wps_er_ap_learn(ap, dev_info);
+               os_free(dev_info);
+       }
+}
+
+
+static int wps_er_send_get_device_info(struct wps_er_ap *ap,
+                                      void (*m1_handler)(struct wps_er_ap *ap,
+                                                         struct wpabuf *m1))
+{
+       struct wpabuf *buf;
+       char *len_ptr, *body_ptr;
+       struct sockaddr_in dst;
+       char *url, *path;
+
+       if (ap->http) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing "
+                          "with the AP - cannot get device info");
+               return -1;
+       }
+
+       if (ap->control_url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP");
+               return -1;
+       }
+
+       url = http_client_url_parse(ap->control_url, &dst, &path);
+       if (url == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL");
+               return -1;
+       }
+
+       buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst,
+                             &len_ptr, &body_ptr);
+       os_free(url);
+       if (buf == NULL)
+               return -1;
+
+       wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr);
+
+       ap->http = http_client_addr(&dst, buf, 10000,
+                                   wps_er_http_get_dev_info_cb, ap);
+       if (ap->http == NULL) {
+               wpabuf_free(buf);
+               return -1;
+       }
+
+       ap->m1_handler = m1_handler;
+
+       return 0;
+}
+
+
+int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin,
+                size_t pin_len)
+{
+       struct wps_er_ap *ap;
+
+       if (er == NULL)
+               return -1;
+
+       ap = wps_er_ap_get(er, NULL, uuid);
+       if (ap == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn "
+                          "request");
+               return -1;
+       }
+       if (ap->wps) {
+               wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing "
+                          "with the AP - cannot start learn");
+               return -1;
+       }
+
+       if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0)
+               return -1;
+
+       /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */
+       wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0);
+
+       return 0;
+}
diff --git a/src/wps/wps_er.h b/src/wps/wps_er.h
new file mode 100644 (file)
index 0000000..b13b950
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar
+ * Copyright (c) 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.
+ */
+
+#ifndef WPS_ER_H
+#define WPS_ER_H
+
+#include "utils/list.h"
+
+struct wps_er_sta {
+       struct dl_list list;
+       struct wps_er_ap *ap;
+       u8 addr[ETH_ALEN];
+       u16 config_methods;
+       u8 uuid[WPS_UUID_LEN];
+       u8 pri_dev_type[8];
+       u16 dev_passwd_id;
+       int m1_received;
+       char *manufacturer;
+       char *model_name;
+       char *model_number;
+       char *serial_number;
+       char *dev_name;
+       struct wps_data *wps;
+       struct http_client *http;
+       struct wps_credential *cred;
+};
+
+struct wps_er_ap {
+       struct dl_list list;
+       struct wps_er *er;
+       struct dl_list sta; /* list of STAs/Enrollees using this AP */
+       struct in_addr addr;
+       char *location;
+       struct http_client *http;
+       struct wps_data *wps;
+
+       u8 uuid[WPS_UUID_LEN];
+       u8 pri_dev_type[8];
+       u8 wps_state;
+       u8 mac_addr[ETH_ALEN];
+       char *friendly_name;
+       char *manufacturer;
+       char *manufacturer_url;
+       char *model_description;
+       char *model_name;
+       char *model_number;
+       char *model_url;
+       char *serial_number;
+       char *udn;
+       char *upc;
+
+       char *scpd_url;
+       char *control_url;
+       char *event_sub_url;
+
+       int subscribed;
+       u8 sid[WPS_UUID_LEN];
+       unsigned int id;
+
+       struct wps_credential *ap_settings;
+
+       void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1);
+};
+
+struct wps_er {
+       struct wps_context *wps;
+       char ifname[17];
+       u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+       char *ip_addr_text; /* IP address of network i.f. we use */
+       unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+       int multicast_sd;
+       int ssdp_sd;
+       struct dl_list ap;
+       struct dl_list ap_unsubscribing;
+       struct http_server *http_srv;
+       int http_port;
+       unsigned int next_ap_id;
+       unsigned int event_id;
+       int deinitializing;
+       void (*deinit_done_cb)(void *ctx);
+       void *deinit_done_ctx;
+};
+
+
+/* wps_er.c */
+void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr,
+                  const char *location, int max_age);
+void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr);
+
+/* wps_er_ssdp.c */
+int wps_er_ssdp_init(struct wps_er *er);
+void wps_er_ssdp_deinit(struct wps_er *er);
+void wps_er_send_ssdp_msearch(struct wps_er *er);
+
+#endif /* WPS_ER_H */
diff --git a/src/wps/wps_er_ssdp.c b/src/wps/wps_er_ssdp.c
new file mode 100644 (file)
index 0000000..f108435
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Wi-Fi Protected Setup - External Registrar (SSDP)
+ * Copyright (c) 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 "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "wps_er.h"
+
+
+static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+       struct wps_er *er = eloop_ctx;
+       struct sockaddr_in addr; /* client address */
+       socklen_t addr_len;
+       int nread;
+       char buf[MULTICAST_MAX_READ], *pos, *pos2, *start;
+       int wfa = 0, byebye = 0;
+       int max_age = -1;
+       char *location = NULL;
+       u8 uuid[WPS_UUID_LEN];
+
+       addr_len = sizeof(addr);
+       nread = recvfrom(sd, buf, sizeof(buf) - 1, 0,
+                        (struct sockaddr *) &addr, &addr_len);
+       if (nread <= 0)
+               return;
+       buf[nread] = '\0';
+
+       wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s",
+                  inet_ntoa(addr.sin_addr));
+       wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents",
+                         (u8 *) buf, nread);
+
+       if (sd == er->multicast_sd) {
+               /* Reply to M-SEARCH */
+               if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0)
+                       return; /* unexpected response header */
+       } else {
+               /* Unsolicited message (likely NOTIFY or M-SEARCH) */
+               if (os_strncmp(buf, "NOTIFY ", 7) != 0)
+                       return; /* only process notifications */
+       }
+
+       os_memset(uuid, 0, sizeof(uuid));
+
+       for (start = buf; start && *start; start = pos) {
+               pos = os_strchr(start, '\n');
+               if (pos) {
+                       if (pos[-1] == '\r')
+                               pos[-1] = '\0';
+                       *pos++ = '\0';
+               }
+               if (os_strstr(start, "schemas-wifialliance-org:device:"
+                             "WFADevice:1"))
+                       wfa = 1;
+               if (os_strstr(start, "schemas-wifialliance-org:service:"
+                             "WFAWLANConfig:1"))
+                       wfa = 1;
+               if (os_strncasecmp(start, "LOCATION:", 9) == 0) {
+                       start += 9;
+                       while (*start == ' ')
+                               start++;
+                       location = start;
+               } else if (os_strncasecmp(start, "NTS:", 4) == 0) {
+                       if (os_strstr(start, "ssdp:byebye"))
+                               byebye = 1;
+               } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) {
+                       start += 9;
+                       while (*start == ' ')
+                               start++;
+                       pos2 = os_strstr(start, "max-age=");
+                       if (pos2 == NULL)
+                               continue;
+                       pos2 += 8;
+                       max_age = atoi(pos2);
+               } else if (os_strncasecmp(start, "USN:", 4) == 0) {
+                       start += 4;
+                       pos2 = os_strstr(start, "uuid:");
+                       if (pos2) {
+                               pos2 += 5;
+                               while (*pos2 == ' ')
+                                       pos2++;
+                               if (uuid_str2bin(pos2, uuid) < 0) {
+                                       wpa_printf(MSG_DEBUG, "WPS ER: "
+                                                  "Invalid UUID in USN: %s",
+                                                  pos2);
+                                       return;
+                               }
+                       }
+               }
+       }
+
+       if (!wfa)
+               return; /* Not WPS advertisement/reply */
+
+       if (byebye) {
+               wps_er_ap_remove(er, &addr.sin_addr);
+               return;
+       }
+
+       if (!location)
+               return; /* Unknown location */
+
+       if (max_age < 1)
+               return; /* No max-age reported */
+
+       wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s "
+                  "(packet source: %s  max-age: %d)",
+                  location, inet_ntoa(addr.sin_addr), max_age);
+
+       wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age);
+}
+
+
+void wps_er_send_ssdp_msearch(struct wps_er *er)
+{
+       struct wpabuf *msg;
+       struct sockaddr_in dest;
+
+       msg = wpabuf_alloc(500);
+       if (msg == NULL)
+               return;
+
+       wpabuf_put_str(msg,
+                      "M-SEARCH * HTTP/1.1\r\n"
+                      "HOST: 239.255.255.250:1900\r\n"
+                      "MAN: \"ssdp:discover\"\r\n"
+                      "MX: 3\r\n"
+                      "ST: urn:schemas-wifialliance-org:device:WFADevice:1"
+                      "\r\n"
+                      "\r\n");
+
+       os_memset(&dest, 0, sizeof(dest));
+       dest.sin_family = AF_INET;
+       dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+       dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+       if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+                  (struct sockaddr *) &dest, sizeof(dest)) < 0)
+               wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: "
+                          "%d (%s)", errno, strerror(errno));
+
+       wpabuf_free(msg);
+}
+
+
+int wps_er_ssdp_init(struct wps_er *er)
+{
+       if (add_ssdp_network(er->ifname))
+               return -1;
+
+       er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr);
+       if (er->multicast_sd < 0)
+               return -1;
+
+       er->ssdp_sd = ssdp_listener_open();
+       if (er->ssdp_sd < 0)
+               return -1;
+
+       if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ,
+                               wps_er_ssdp_rx, er, NULL) ||
+           eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ,
+                               wps_er_ssdp_rx, er, NULL))
+               return -1;
+
+       wps_er_send_ssdp_msearch(er);
+
+       return 0;
+}
+
+
+void wps_er_ssdp_deinit(struct wps_er *er)
+{
+       if (er->multicast_sd >= 0) {
+               eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ);
+               close(er->multicast_sd);
+       }
+       if (er->ssdp_sd >= 0) {
+               eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ);
+               close(er->ssdp_sd);
+       }
+}
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
new file mode 100644 (file)
index 0000000..50e66f6
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Wi-Fi Protected Setup - internal definitions
+ * Copyright (c) 2008-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.
+ */
+
+#ifndef WPS_I_H
+#define WPS_I_H
+
+#include "wps.h"
+
+/**
+ * struct wps_data - WPS registration protocol data
+ *
+ * This data is stored at the EAP-WSC server/peer method and it is kept for a
+ * single registration protocol run.
+ */
+struct wps_data {
+       /**
+        * wps - Pointer to long term WPS context
+        */
+       struct wps_context *wps;
+
+       /**
+        * registrar - Whether this end is a Registrar
+        */
+       int registrar;
+
+       /**
+        * er - Whether the local end is an external registrar
+        */
+       int er;
+
+       enum {
+               /* Enrollee states */
+               SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7,
+               RECV_M8, RECEIVED_M2D, WPS_MSG_DONE, RECV_ACK, WPS_FINISHED,
+               SEND_WSC_NACK,
+
+               /* Registrar states */
+               RECV_M1, SEND_M2, RECV_M3, SEND_M4, RECV_M5, SEND_M6,
+               RECV_M7, SEND_M8, RECV_DONE, SEND_M2D, RECV_M2D_ACK
+       } state;
+
+       u8 uuid_e[WPS_UUID_LEN];
+       u8 uuid_r[WPS_UUID_LEN];
+       u8 mac_addr_e[ETH_ALEN];
+       u8 nonce_e[WPS_NONCE_LEN];
+       u8 nonce_r[WPS_NONCE_LEN];
+       u8 psk1[WPS_PSK_LEN];
+       u8 psk2[WPS_PSK_LEN];
+       u8 snonce[2 * WPS_SECRET_NONCE_LEN];
+       u8 peer_hash1[WPS_HASH_LEN];
+       u8 peer_hash2[WPS_HASH_LEN];
+
+       struct wpabuf *dh_privkey;
+       struct wpabuf *dh_pubkey_e;
+       struct wpabuf *dh_pubkey_r;
+       u8 authkey[WPS_AUTHKEY_LEN];
+       u8 keywrapkey[WPS_KEYWRAPKEY_LEN];
+       u8 emsk[WPS_EMSK_LEN];
+
+       struct wpabuf *last_msg;
+
+       u8 *dev_password;
+       size_t dev_password_len;
+       u16 dev_pw_id;
+       int pbc;
+
+       /**
+        * request_type - Request Type attribute from (Re)AssocReq
+        */
+       u8 request_type;
+
+       /**
+        * encr_type - Available encryption types
+        */
+       u16 encr_type;
+
+       /**
+        * auth_type - Available authentication types
+        */
+       u16 auth_type;
+
+       u8 *new_psk;
+       size_t new_psk_len;
+
+       int wps_pin_revealed;
+       struct wps_credential cred;
+
+       struct wps_device_data peer_dev;
+
+       /**
+        * config_error - Configuration Error value to be used in NACK
+        */
+       u16 config_error;
+
+       int ext_reg;
+       int int_reg;
+
+       struct wps_credential *new_ap_settings;
+
+       void *dh_ctx;
+
+       void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred);
+       void *ap_settings_cb_ctx;
+
+       struct wps_credential *use_cred;
+
+       int use_psk_key;
+};
+
+
+struct wps_parse_attr {
+       /* fixed length fields */
+       const u8 *version; /* 1 octet */
+       const u8 *msg_type; /* 1 octet */
+       const u8 *enrollee_nonce; /* WPS_NONCE_LEN (16) octets */
+       const u8 *registrar_nonce; /* WPS_NONCE_LEN (16) octets */
+       const u8 *uuid_r; /* WPS_UUID_LEN (16) octets */
+       const u8 *uuid_e; /* WPS_UUID_LEN (16) octets */
+       const u8 *auth_type_flags; /* 2 octets */
+       const u8 *encr_type_flags; /* 2 octets */
+       const u8 *conn_type_flags; /* 1 octet */
+       const u8 *config_methods; /* 2 octets */
+       const u8 *sel_reg_config_methods; /* 2 octets */
+       const u8 *primary_dev_type; /* 8 octets */
+       const u8 *rf_bands; /* 1 octet */
+       const u8 *assoc_state; /* 2 octets */
+       const u8 *config_error; /* 2 octets */
+       const u8 *dev_password_id; /* 2 octets */
+       const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54)
+                                    * octets */
+       const u8 *os_version; /* 4 octets */
+       const u8 *wps_state; /* 1 octet */
+       const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */
+       const u8 *r_hash1; /* WPS_HASH_LEN (32) octets */
+       const u8 *r_hash2; /* WPS_HASH_LEN (32) octets */
+       const u8 *e_hash1; /* WPS_HASH_LEN (32) octets */
+       const u8 *e_hash2; /* WPS_HASH_LEN (32) octets */
+       const u8 *r_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *r_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *e_snonce1; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *e_snonce2; /* WPS_SECRET_NONCE_LEN (16) octets */
+       const u8 *key_wrap_auth; /* WPS_KWA_LEN (8) octets */
+       const u8 *auth_type; /* 2 octets */
+       const u8 *encr_type; /* 2 octets */
+       const u8 *network_idx; /* 1 octet */
+       const u8 *network_key_idx; /* 1 octet */
+       const u8 *mac_addr; /* ETH_ALEN (6) octets */
+       const u8 *key_prov_auto; /* 1 octet (Bool) */
+       const u8 *dot1x_enabled; /* 1 octet (Bool) */
+       const u8 *selected_registrar; /* 1 octet (Bool) */
+       const u8 *request_type; /* 1 octet */
+       const u8 *response_type; /* 1 octet */
+       const u8 *ap_setup_locked; /* 1 octet */
+
+       /* variable length fields */
+       const u8 *manufacturer;
+       size_t manufacturer_len;
+       const u8 *model_name;
+       size_t model_name_len;
+       const u8 *model_number;
+       size_t model_number_len;
+       const u8 *serial_number;
+       size_t serial_number_len;
+       const u8 *dev_name;
+       size_t dev_name_len;
+       const u8 *public_key;
+       size_t public_key_len;
+       const u8 *encr_settings;
+       size_t encr_settings_len;
+       const u8 *ssid; /* <= 32 octets */
+       size_t ssid_len;
+       const u8 *network_key; /* <= 64 octets */
+       size_t network_key_len;
+       const u8 *eap_type; /* <= 8 octets */
+       size_t eap_type_len;
+       const u8 *eap_identity; /* <= 64 octets */
+       size_t eap_identity_len;
+
+       /* attributes that can occur multiple times */
+#define MAX_CRED_COUNT 10
+       const u8 *cred[MAX_CRED_COUNT];
+       size_t cred_len[MAX_CRED_COUNT];
+       size_t num_cred;
+};
+
+/* wps_common.c */
+void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
+            const char *label, u8 *res, size_t res_len);
+int wps_derive_keys(struct wps_data *wps);
+void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
+                   size_t dev_passwd_len);
+struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
+                                         size_t encr_len);
+void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg);
+void wps_success_event(struct wps_context *wps);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
+void wps_pbc_overlap_event(struct wps_context *wps);
+void wps_pbc_timeout_event(struct wps_context *wps);
+
+extern struct oob_device_data oob_ufd_device_data;
+extern struct oob_device_data oob_nfc_device_data;
+extern struct oob_nfc_device_data oob_nfc_pn531_device_data;
+
+/* wps_attr_parse.c */
+int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
+
+/* wps_attr_build.c */
+int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type);
+int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type);
+int wps_build_config_methods(struct wpabuf *msg, u16 methods);
+int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid);
+int wps_build_dev_password_id(struct wpabuf *msg, u16 id);
+int wps_build_config_error(struct wpabuf *msg, u16 err);
+int wps_build_authenticator(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
+                           struct wpabuf *plain);
+int wps_build_version(struct wpabuf *msg);
+int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
+int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg);
+int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps);
+
+/* wps_attr_process.c */
+int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
+                             const struct wpabuf *msg);
+int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
+                             const u8 *key_wrap_auth);
+int wps_process_cred(struct wps_parse_attr *attr,
+                    struct wps_credential *cred);
+int wps_process_ap_settings(struct wps_parse_attr *attr,
+                           struct wps_credential *cred);
+
+/* wps_enrollee.c */
+struct wpabuf * wps_enrollee_get_msg(struct wps_data *wps,
+                                    enum wsc_op_code *op_code);
+enum wps_process_res wps_enrollee_process_msg(struct wps_data *wps,
+                                             enum wsc_op_code op_code,
+                                             const struct wpabuf *msg);
+
+/* wps_registrar.c */
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+                                     enum wsc_op_code *op_code);
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+                                              enum wsc_op_code op_code,
+                                              const struct wpabuf *msg);
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg);
+int wps_device_store(struct wps_registrar *reg,
+                    struct wps_device_data *dev, const u8 *uuid);
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg);
+
+/* ndef.c */
+struct wpabuf * ndef_parse_wifi(struct wpabuf *buf);
+struct wpabuf * ndef_build_wifi(struct wpabuf *buf);
+
+static inline int wps_version_supported(const u8 *version)
+{
+       /* Require major version match, but allow minor version differences */
+       return version && (*version & 0xf0) == (WPS_VERSION & 0xf0);
+}
+
+#endif /* WPS_I_H */
diff --git a/src/wps/wps_nfc.c b/src/wps/wps_nfc.c
new file mode 100644 (file)
index 0000000..ff12000
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * NFC routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * 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 "wps/wps.h"
+#include "wps_i.h"
+
+
+struct wps_nfc_data {
+       struct oob_nfc_device_data *oob_nfc_dev;
+};
+
+
+static void * init_nfc(struct wps_context *wps,
+                      struct oob_device_data *oob_dev, int registrar)
+{
+       struct oob_nfc_device_data *oob_nfc_dev;
+       struct wps_nfc_data *data;
+
+       oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name);
+       if (oob_nfc_dev == NULL) {
+               wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)",
+                          oob_dev->device_name);
+               return NULL;
+       }
+
+       if (oob_nfc_dev->init_func(oob_dev->device_path) < 0)
+               return NULL;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL) {
+               wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
+                          "nfc data area");
+               return NULL;
+       }
+       data->oob_nfc_dev = oob_nfc_dev;
+       return data;
+}
+
+
+static struct wpabuf * read_nfc(void *priv)
+{
+       struct wps_nfc_data *data = priv;
+       struct wpabuf *wifi, *buf;
+       char *raw_data;
+       size_t len;
+
+       raw_data = data->oob_nfc_dev->read_func(&len);
+       if (raw_data == NULL)
+               return NULL;
+
+       wifi = wpabuf_alloc_copy(raw_data, len);
+       os_free(raw_data);
+       if (wifi == NULL) {
+               wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate "
+                          "nfc read area");
+               return NULL;
+       }
+
+       buf = ndef_parse_wifi(wifi);
+       wpabuf_free(wifi);
+       if (buf == NULL)
+               wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap");
+       return buf;
+}
+
+
+static int write_nfc(void *priv, struct wpabuf *buf)
+{
+       struct wps_nfc_data *data = priv;
+       struct wpabuf *wifi;
+       int ret;
+
+       wifi = ndef_build_wifi(buf);
+       if (wifi == NULL) {
+               wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap");
+               return -1;
+       }
+
+       ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi),
+                                           wpabuf_len(wifi));
+       wpabuf_free(wifi);
+       return ret;
+}
+
+
+static void deinit_nfc(void *priv)
+{
+       struct wps_nfc_data *data = priv;
+
+       data->oob_nfc_dev->deinit_func();
+
+       os_free(data);
+}
+
+
+struct oob_device_data oob_nfc_device_data = {
+       .device_name    = NULL,
+       .device_path    = NULL,
+       .init_func      = init_nfc,
+       .read_func      = read_nfc,
+       .write_func     = write_nfc,
+       .deinit_func    = deinit_nfc,
+};
diff --git a/src/wps/wps_nfc_pn531.c b/src/wps/wps_nfc_pn531.c
new file mode 100644 (file)
index 0000000..7e05e4d
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * NFC PN531 routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * 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 "wps/wps.h"
+#include "wps_i.h"
+
+#include "WpsNfcType.h"
+#include "WpsNfc.h"
+
+
+static int init_nfc_pn531(char *path)
+{
+       u32 ret;
+
+       ret = WpsNfcInit();
+       if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize "
+                          "NFC Library: 0x%08x", ret);
+               return -1;
+       }
+
+       ret = WpsNfcOpenDevice((int8 *) path);
+       if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open "
+                          "NFC Device(%s): 0x%08x", path, ret);
+               goto fail;
+       }
+
+       ret = WpsNfcTokenDiscovery();
+       if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover "
+                          "token: 0x%08x", ret);
+               WpsNfcCloseDevice();
+               goto fail;
+       }
+
+       return 0;
+
+fail:
+       WpsNfcDeinit();
+       return -1;
+}
+
+
+static void * read_nfc_pn531(size_t *size)
+{
+       uint32 len;
+       u32 ret;
+       int8 *data;
+
+       ret = WpsNfcRawReadToken(&data, &len);
+       if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x",
+                          ret);
+               return NULL;
+       }
+
+       *size = len;
+       return data;
+}
+
+
+static int write_nfc_pn531(void *data, size_t len)
+{
+       u32 ret;
+
+       ret = WpsNfcRawWriteToken(data, len);
+       if (ret != WPS_NFCLIB_ERR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x",
+                          ret);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void deinit_nfc_pn531(void)
+{
+       u32 ret;
+
+       ret = WpsNfcCloseDevice();
+       if (ret != WPS_NFCLIB_ERR_SUCCESS)
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close "
+                          "NFC Device: 0x%08x", ret);
+
+       ret = WpsNfcDeinit();
+       if (ret != WPS_NFCLIB_ERR_SUCCESS)
+               wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize "
+                          "NFC Library: 0x%08x", ret);
+}
+
+
+struct oob_nfc_device_data oob_nfc_pn531_device_data = {
+       .init_func      = init_nfc_pn531,
+       .read_func      = read_nfc_pn531,
+       .write_func     = write_nfc_pn531,
+       .deinit_func    = deinit_nfc_pn531,
+};
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
new file mode 100644 (file)
index 0000000..81ddf3a
--- /dev/null
@@ -0,0 +1,2900 @@
+/*
+ * Wi-Fi Protected Setup - Registrar
+ * Copyright (c) 2008-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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "utils/list.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "common/ieee802_11_defs.h"
+#include "wps_i.h"
+#include "wps_dev_attr.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#define WPS_WORKAROUNDS
+
+struct wps_uuid_pin {
+       struct dl_list list;
+       u8 uuid[WPS_UUID_LEN];
+       int wildcard_uuid;
+       u8 *pin;
+       size_t pin_len;
+#define PIN_LOCKED BIT(0)
+#define PIN_EXPIRES BIT(1)
+       int flags;
+       struct os_time expiration;
+};
+
+
+static void wps_free_pin(struct wps_uuid_pin *pin)
+{
+       os_free(pin->pin);
+       os_free(pin);
+}
+
+
+static void wps_remove_pin(struct wps_uuid_pin *pin)
+{
+       dl_list_del(&pin->list);
+       wps_free_pin(pin);
+}
+
+
+static void wps_free_pins(struct dl_list *pins)
+{
+       struct wps_uuid_pin *pin, *prev;
+       dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list)
+               wps_remove_pin(pin);
+}
+
+
+struct wps_pbc_session {
+       struct wps_pbc_session *next;
+       u8 addr[ETH_ALEN];
+       u8 uuid_e[WPS_UUID_LEN];
+       struct os_time timestamp;
+};
+
+
+static void wps_free_pbc_sessions(struct wps_pbc_session *pbc)
+{
+       struct wps_pbc_session *prev;
+
+       while (pbc) {
+               prev = pbc;
+               pbc = pbc->next;
+               os_free(prev);
+       }
+}
+
+
+struct wps_registrar_device {
+       struct wps_registrar_device *next;
+       struct wps_device_data dev;
+       u8 uuid[WPS_UUID_LEN];
+};
+
+
+struct wps_registrar {
+       struct wps_context *wps;
+
+       int pbc;
+       int selected_registrar;
+
+       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
+                         size_t psk_len);
+       int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
+                        struct wpabuf *probe_resp_ie);
+       void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
+                             const struct wps_device_data *dev);
+       void (*reg_success_cb)(void *ctx, const u8 *mac_addr,
+                              const u8 *uuid_e);
+       void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id,
+                              u16 sel_reg_config_methods);
+       void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e,
+                                const u8 *pri_dev_type, u16 config_methods,
+                                u16 dev_password_id, u8 request_type,
+                                const char *dev_name);
+       void *cb_ctx;
+
+       struct dl_list pins;
+       struct wps_pbc_session *pbc_sessions;
+
+       int skip_cred_build;
+       struct wpabuf *extra_cred;
+       int disable_auto_conf;
+       int sel_reg_union;
+       int sel_reg_dev_password_id_override;
+       int sel_reg_config_methods_override;
+       int static_wep_only;
+
+       struct wps_registrar_device *devices;
+
+       int force_pbc_overlap;
+};
+
+
+static int wps_set_ie(struct wps_registrar *reg);
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+                                              void *timeout_ctx);
+
+
+static void wps_free_devices(struct wps_registrar_device *dev)
+{
+       struct wps_registrar_device *prev;
+
+       while (dev) {
+               prev = dev;
+               dev = dev->next;
+               wps_device_data_free(&prev->dev);
+               os_free(prev);
+       }
+}
+
+
+static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg,
+                                                   const u8 *addr)
+{
+       struct wps_registrar_device *dev;
+
+       for (dev = reg->devices; dev; dev = dev->next) {
+               if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0)
+                       return dev;
+       }
+       return NULL;
+}
+
+
+static void wps_device_clone_data(struct wps_device_data *dst,
+                                 struct wps_device_data *src)
+{
+       os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN);
+       os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
+
+#define WPS_STRDUP(n) \
+       os_free(dst->n); \
+       dst->n = src->n ? os_strdup(src->n) : NULL
+
+       WPS_STRDUP(device_name);
+       WPS_STRDUP(manufacturer);
+       WPS_STRDUP(model_name);
+       WPS_STRDUP(model_number);
+       WPS_STRDUP(serial_number);
+#undef WPS_STRDUP
+}
+
+
+int wps_device_store(struct wps_registrar *reg,
+                    struct wps_device_data *dev, const u8 *uuid)
+{
+       struct wps_registrar_device *d;
+
+       d = wps_device_get(reg, dev->mac_addr);
+       if (d == NULL) {
+               d = os_zalloc(sizeof(*d));
+               if (d == NULL)
+                       return -1;
+               d->next = reg->devices;
+               reg->devices = d;
+       }
+
+       wps_device_clone_data(&d->dev, dev);
+       os_memcpy(d->uuid, uuid, WPS_UUID_LEN);
+
+       return 0;
+}
+
+
+static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
+                                         const u8 *addr, const u8 *uuid_e)
+{
+       struct wps_pbc_session *pbc, *prev = NULL;
+       struct os_time now;
+
+       os_get_time(&now);
+
+       pbc = reg->pbc_sessions;
+       while (pbc) {
+               if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+                   os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+                       if (prev)
+                               prev->next = pbc->next;
+                       else
+                               reg->pbc_sessions = pbc->next;
+                       break;
+               }
+               prev = pbc;
+               pbc = pbc->next;
+       }
+
+       if (!pbc) {
+               pbc = os_zalloc(sizeof(*pbc));
+               if (pbc == NULL)
+                       return;
+               os_memcpy(pbc->addr, addr, ETH_ALEN);
+               if (uuid_e)
+                       os_memcpy(pbc->uuid_e, uuid_e, WPS_UUID_LEN);
+       }
+
+       pbc->next = reg->pbc_sessions;
+       reg->pbc_sessions = pbc;
+       pbc->timestamp = now;
+
+       /* remove entries that have timed out */
+       prev = pbc;
+       pbc = pbc->next;
+
+       while (pbc) {
+               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+                       prev->next = NULL;
+                       wps_free_pbc_sessions(pbc);
+                       break;
+               }
+               prev = pbc;
+               pbc = pbc->next;
+       }
+}
+
+
+static void wps_registrar_remove_pbc_session(struct wps_registrar *reg,
+                                            const u8 *addr, const u8 *uuid_e)
+{
+       struct wps_pbc_session *pbc, *prev = NULL;
+
+       pbc = reg->pbc_sessions;
+       while (pbc) {
+               if (os_memcmp(pbc->addr, addr, ETH_ALEN) == 0 &&
+                   os_memcmp(pbc->uuid_e, uuid_e, WPS_UUID_LEN) == 0) {
+                       if (prev)
+                               prev->next = pbc->next;
+                       else
+                               reg->pbc_sessions = pbc->next;
+                       os_free(pbc);
+                       break;
+               }
+               prev = pbc;
+               pbc = pbc->next;
+       }
+}
+
+
+static int wps_registrar_pbc_overlap(struct wps_registrar *reg,
+                                    const u8 *addr, const u8 *uuid_e)
+{
+       int count = 0;
+       struct wps_pbc_session *pbc;
+       struct os_time now;
+
+       os_get_time(&now);
+
+       for (pbc = reg->pbc_sessions; pbc; pbc = pbc->next) {
+               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME)
+                       break;
+               if (addr == NULL || os_memcmp(addr, pbc->addr, ETH_ALEN) ||
+                   uuid_e == NULL ||
+                   os_memcmp(uuid_e, pbc->uuid_e, WPS_UUID_LEN))
+                       count++;
+       }
+
+       if (addr || uuid_e)
+               count++;
+
+       return count > 1 ? 1 : 0;
+}
+
+
+static int wps_build_wps_state(struct wps_context *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Wi-Fi Protected Setup State (%d)",
+                  wps->wps_state);
+       wpabuf_put_be16(msg, ATTR_WPS_STATE);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, wps->wps_state);
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_free_pending_m2(struct wps_context *wps)
+{
+       struct upnp_pending_message *p, *p2, *prev = NULL;
+       p = wps->upnp_msgs;
+       while (p) {
+               if (p->type == WPS_M2 || p->type == WPS_M2D) {
+                       if (prev == NULL)
+                               wps->upnp_msgs = p->next;
+                       else
+                               prev->next = p->next;
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: Drop pending M2/M2D");
+                       p2 = p;
+                       p = p->next;
+                       wpabuf_free(p2->msg);
+                       os_free(p2);
+                       continue;
+               }
+               prev = p;
+               p = p->next;
+       }
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static int wps_build_ap_setup_locked(struct wps_context *wps,
+                                    struct wpabuf *msg)
+{
+       if (wps->ap_setup_locked) {
+               wpa_printf(MSG_DEBUG, "WPS:  * AP Setup Locked");
+               wpabuf_put_be16(msg, ATTR_AP_SETUP_LOCKED);
+               wpabuf_put_be16(msg, 1);
+               wpabuf_put_u8(msg, 1);
+       }
+       return 0;
+}
+
+
+static int wps_build_selected_registrar(struct wps_registrar *reg,
+                                       struct wpabuf *msg)
+{
+       if (!reg->sel_reg_union)
+               return 0;
+       wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar");
+       wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, 1);
+       return 0;
+}
+
+
+static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg,
+                                            struct wpabuf *msg)
+{
+       u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT;
+       if (!reg->sel_reg_union)
+               return 0;
+       if (reg->sel_reg_dev_password_id_override >= 0)
+               id = reg->sel_reg_dev_password_id_override;
+       wpa_printf(MSG_DEBUG, "WPS:  * Device Password ID (%d)", id);
+       wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, id);
+       return 0;
+}
+
+
+static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
+                                           struct wpabuf *msg)
+{
+       u16 methods;
+       if (!reg->sel_reg_union)
+               return 0;
+       methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+       if (reg->pbc)
+               methods |= WPS_CONFIG_PUSHBUTTON;
+       if (reg->sel_reg_config_methods_override >= 0)
+               methods = reg->sel_reg_config_methods_override;
+       wpa_printf(MSG_DEBUG, "WPS:  * Selected Registrar Config Methods (%x)",
+                  methods);
+       wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, methods);
+       return 0;
+}
+
+
+static int wps_build_probe_config_methods(struct wps_registrar *reg,
+                                         struct wpabuf *msg)
+{
+       u16 methods;
+       /*
+        * These are the methods that the AP supports as an Enrollee for adding
+        * external Registrars.
+        */
+       methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+       wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
+       wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, methods);
+       return 0;
+}
+
+
+static int wps_build_config_methods_r(struct wps_registrar *reg,
+                                     struct wpabuf *msg)
+{
+       u16 methods;
+       methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+       if (reg->pbc)
+               methods |= WPS_CONFIG_PUSHBUTTON;
+       return wps_build_config_methods(msg, methods);
+}
+
+
+/**
+ * wps_registrar_init - Initialize WPS Registrar data
+ * @wps: Pointer to longterm WPS context
+ * @cfg: Registrar configuration
+ * Returns: Pointer to allocated Registrar data or %NULL on failure
+ *
+ * This function is used to initialize WPS Registrar functionality. It can be
+ * used for a single Registrar run (e.g., when run in a supplicant) or multiple
+ * runs (e.g., when run as an internal Registrar in an AP). Caller is
+ * responsible for freeing the returned data with wps_registrar_deinit() when
+ * Registrar functionality is not needed anymore.
+ */
+struct wps_registrar *
+wps_registrar_init(struct wps_context *wps,
+                  const struct wps_registrar_config *cfg)
+{
+       struct wps_registrar *reg = os_zalloc(sizeof(*reg));
+       if (reg == NULL)
+               return NULL;
+
+       dl_list_init(&reg->pins);
+       reg->wps = wps;
+       reg->new_psk_cb = cfg->new_psk_cb;
+       reg->set_ie_cb = cfg->set_ie_cb;
+       reg->pin_needed_cb = cfg->pin_needed_cb;
+       reg->reg_success_cb = cfg->reg_success_cb;
+       reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
+       reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
+       reg->cb_ctx = cfg->cb_ctx;
+       reg->skip_cred_build = cfg->skip_cred_build;
+       if (cfg->extra_cred) {
+               reg->extra_cred = wpabuf_alloc_copy(cfg->extra_cred,
+                                                   cfg->extra_cred_len);
+               if (reg->extra_cred == NULL) {
+                       os_free(reg);
+                       return NULL;
+               }
+       }
+       reg->disable_auto_conf = cfg->disable_auto_conf;
+       reg->sel_reg_dev_password_id_override = -1;
+       reg->sel_reg_config_methods_override = -1;
+       reg->static_wep_only = cfg->static_wep_only;
+
+       if (wps_set_ie(reg)) {
+               wps_registrar_deinit(reg);
+               return NULL;
+       }
+
+       return reg;
+}
+
+
+/**
+ * wps_registrar_deinit - Deinitialize WPS Registrar data
+ * @reg: Registrar data from wps_registrar_init()
+ */
+void wps_registrar_deinit(struct wps_registrar *reg)
+{
+       if (reg == NULL)
+               return;
+       eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+       eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       wps_free_pins(&reg->pins);
+       wps_free_pbc_sessions(reg->pbc_sessions);
+       wpabuf_free(reg->extra_cred);
+       wps_free_devices(reg->devices);
+       os_free(reg);
+}
+
+
+/**
+ * wps_registrar_add_pin - Configure a new PIN for Registrar
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E or %NULL for wildcard (any UUID)
+ * @pin: PIN (Device Password)
+ * @pin_len: Length of pin in octets
+ * @timeout: Time (in seconds) when the PIN will be invalidated; 0 = no timeout
+ * Returns: 0 on success, -1 on failure
+ */
+int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid,
+                         const u8 *pin, size_t pin_len, int timeout)
+{
+       struct wps_uuid_pin *p;
+
+       p = os_zalloc(sizeof(*p));
+       if (p == NULL)
+               return -1;
+       if (uuid == NULL)
+               p->wildcard_uuid = 1;
+       else
+               os_memcpy(p->uuid, uuid, WPS_UUID_LEN);
+       p->pin = os_malloc(pin_len);
+       if (p->pin == NULL) {
+               os_free(p);
+               return -1;
+       }
+       os_memcpy(p->pin, pin, pin_len);
+       p->pin_len = pin_len;
+
+       if (timeout) {
+               p->flags |= PIN_EXPIRES;
+               os_get_time(&p->expiration);
+               p->expiration.sec += timeout;
+       }
+
+       dl_list_add(&reg->pins, &p->list);
+
+       wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)",
+                  timeout);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID", uuid, WPS_UUID_LEN);
+       wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len);
+       reg->selected_registrar = 1;
+       reg->pbc = 0;
+       wps_registrar_selected_registrar_changed(reg);
+       eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+                              wps_registrar_set_selected_timeout,
+                              reg, NULL);
+
+       return 0;
+}
+
+
+static void wps_registrar_expire_pins(struct wps_registrar *reg)
+{
+       struct wps_uuid_pin *pin, *prev;
+       struct os_time now;
+
+       os_get_time(&now);
+       dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+       {
+               if ((pin->flags & PIN_EXPIRES) &&
+                   os_time_before(&pin->expiration, &now)) {
+                       wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
+                                   pin->uuid, WPS_UUID_LEN);
+                       wps_remove_pin(pin);
+               }
+       }
+}
+
+
+/**
+ * wps_registrar_invalidate_pin - Invalidate a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure (e.g., PIN not found)
+ */
+int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+       struct wps_uuid_pin *pin, *prev;
+
+       dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
+       {
+               if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+                       wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
+                                   pin->uuid, WPS_UUID_LEN);
+                       wps_remove_pin(pin);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+static const u8 * wps_registrar_get_pin(struct wps_registrar *reg,
+                                       const u8 *uuid, size_t *pin_len)
+{
+       struct wps_uuid_pin *pin, *found = NULL;
+
+       wps_registrar_expire_pins(reg);
+
+       dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+               if (!pin->wildcard_uuid &&
+                   os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+                       found = pin;
+                       break;
+               }
+       }
+
+       if (!found) {
+               /* Check for wildcard UUIDs since none of the UUID-specific
+                * PINs matched */
+               dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+                       if (pin->wildcard_uuid == 1) {
+                               wpa_printf(MSG_DEBUG, "WPS: Found a wildcard "
+                                          "PIN. Assigned it for this UUID-E");
+                               pin->wildcard_uuid = 2;
+                               os_memcpy(pin->uuid, uuid, WPS_UUID_LEN);
+                               found = pin;
+                               break;
+                       }
+               }
+       }
+
+       if (!found)
+               return NULL;
+
+       /*
+        * Lock the PIN to avoid attacks based on concurrent re-use of the PIN
+        * that could otherwise avoid PIN invalidations.
+        */
+       if (found->flags & PIN_LOCKED) {
+               wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not "
+                          "allow concurrent re-use");
+               return NULL;
+       }
+       *pin_len = found->pin_len;
+       found->flags |= PIN_LOCKED;
+       return found->pin;
+}
+
+
+/**
+ * wps_registrar_unlock_pin - Unlock a PIN for a specific UUID-E
+ * @reg: Registrar data from wps_registrar_init()
+ * @uuid: UUID-E
+ * Returns: 0 on success, -1 on failure
+ *
+ * PINs are locked to enforce only one concurrent use. This function unlocks a
+ * PIN to allow it to be used again. If the specified PIN was configured using
+ * a wildcard UUID, it will be removed instead of allowing multiple uses.
+ */
+int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid)
+{
+       struct wps_uuid_pin *pin;
+
+       dl_list_for_each(pin, &reg->pins, struct wps_uuid_pin, list) {
+               if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) {
+                       if (pin->wildcard_uuid == 2) {
+                               wpa_printf(MSG_DEBUG, "WPS: Invalidating used "
+                                          "wildcard PIN");
+                               return wps_registrar_invalidate_pin(reg, uuid);
+                       }
+                       pin->flags &= ~PIN_LOCKED;
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+static void wps_registrar_stop_pbc(struct wps_registrar *reg)
+{
+       reg->selected_registrar = 0;
+       reg->pbc = 0;
+       wps_registrar_selected_registrar_changed(reg);
+}
+
+
+static void wps_registrar_pbc_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wps_registrar *reg = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG, "WPS: PBC timed out - disable PBC mode");
+       wps_pbc_timeout_event(reg->wps);
+       wps_registrar_stop_pbc(reg);
+}
+
+
+/**
+ * wps_registrar_button_pushed - Notify Registrar that AP button was pushed
+ * @reg: Registrar data from wps_registrar_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called on an AP when a push button is pushed to activate
+ * PBC mode. The PBC mode will be stopped after walk time (2 minutes) timeout
+ * or when a PBC registration is completed.
+ */
+int wps_registrar_button_pushed(struct wps_registrar *reg)
+{
+       if (wps_registrar_pbc_overlap(reg, NULL, NULL)) {
+               wpa_printf(MSG_DEBUG, "WPS: PBC overlap - do not start PBC "
+                          "mode");
+               wps_pbc_overlap_event(reg->wps);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "WPS: Button pushed - PBC mode started");
+       reg->force_pbc_overlap = 0;
+       reg->selected_registrar = 1;
+       reg->pbc = 1;
+       wps_registrar_selected_registrar_changed(reg);
+
+       eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+       eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
+                              reg, NULL);
+       return 0;
+}
+
+
+static void wps_registrar_pbc_completed(struct wps_registrar *reg)
+{
+       wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
+       eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
+       wps_registrar_stop_pbc(reg);
+}
+
+
+static void wps_registrar_pin_completed(struct wps_registrar *reg)
+{
+       wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar");
+       eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
+       reg->selected_registrar = 0;
+       wps_registrar_selected_registrar_changed(reg);
+}
+
+
+/**
+ * wps_registrar_probe_req_rx - Notify Registrar of Probe Request
+ * @reg: Registrar data from wps_registrar_init()
+ * @addr: MAC address of the Probe Request sender
+ * @wps_data: WPS IE contents
+ *
+ * This function is called on an AP when a Probe Request with WPS IE is
+ * received. This is used to track PBC mode use and to detect possible overlap
+ * situation with other WPS APs.
+ */
+void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
+                               const struct wpabuf *wps_data)
+{
+       struct wps_parse_attr attr;
+
+       wpa_hexdump_buf(MSG_MSGDUMP,
+                       "WPS: Probe Request with WPS data received",
+                       wps_data);
+
+       if (wps_parse_msg(wps_data, &attr) < 0)
+               return;
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported ProbeReq WPS IE "
+                          "version 0x%x", attr.version ? *attr.version : 0);
+               return;
+       }
+
+       if (attr.config_methods == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Config Methods attribute in "
+                          "Probe Request");
+               return;
+       }
+
+       if (attr.dev_password_id == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute "
+                          "in Probe Request");
+               return;
+       }
+
+       if (reg->enrollee_seen_cb && attr.uuid_e &&
+           attr.primary_dev_type && attr.request_type) {
+               char *dev_name = NULL;
+               if (attr.dev_name) {
+                       dev_name = os_zalloc(attr.dev_name_len + 1);
+                       if (dev_name) {
+                               os_memcpy(dev_name, attr.dev_name,
+                                         attr.dev_name_len);
+                       }
+               }
+               reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e,
+                                     attr.primary_dev_type,
+                                     WPA_GET_BE16(attr.config_methods),
+                                     WPA_GET_BE16(attr.dev_password_id),
+                                     *attr.request_type, dev_name);
+               os_free(dev_name);
+       }
+
+       if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON)
+               return; /* Not PBC */
+
+       wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from "
+                  MACSTR, MAC2STR(addr));
+       if (attr.uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Probe Request WPS IE: No "
+                          "UUID-E included");
+               return;
+       }
+
+       wps_registrar_add_pbc_session(reg, addr, attr.uuid_e);
+       if (wps_registrar_pbc_overlap(reg, addr, attr.uuid_e)) {
+               wpa_printf(MSG_DEBUG, "WPS: PBC session overlap detected");
+               reg->force_pbc_overlap = 1;
+               wps_pbc_overlap_event(reg->wps);
+       }
+}
+
+
+static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+                         const u8 *psk, size_t psk_len)
+{
+       if (reg->new_psk_cb == NULL)
+               return 0;
+
+       return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+}
+
+
+static void wps_cb_pin_needed(struct wps_registrar *reg, const u8 *uuid_e,
+                             const struct wps_device_data *dev)
+{
+       if (reg->pin_needed_cb == NULL)
+               return;
+
+       reg->pin_needed_cb(reg->cb_ctx, uuid_e, dev);
+}
+
+
+static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr,
+                              const u8 *uuid_e)
+{
+       if (reg->reg_success_cb == NULL)
+               return;
+
+       reg->reg_success_cb(reg->cb_ctx, mac_addr, uuid_e);
+}
+
+
+static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie,
+                        struct wpabuf *probe_resp_ie)
+{
+       return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie);
+}
+
+
+static void wps_cb_set_sel_reg(struct wps_registrar *reg)
+{
+       u16 methods = 0;
+       if (reg->set_sel_reg_cb == NULL)
+               return;
+
+       if (reg->selected_registrar) {
+               methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+               if (reg->pbc)
+                       methods |= WPS_CONFIG_PUSHBUTTON;
+       }
+
+       reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar,
+                           reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT,
+                           methods);
+}
+
+
+/* Encapsulate WPS IE data with one (or more, if needed) IE headers */
+static struct wpabuf * wps_ie_encapsulate(struct wpabuf *data)
+{
+       struct wpabuf *ie;
+       const u8 *pos, *end;
+
+       ie = wpabuf_alloc(wpabuf_len(data) + 100);
+       if (ie == NULL) {
+               wpabuf_free(data);
+               return NULL;
+       }
+
+       pos = wpabuf_head(data);
+       end = pos + wpabuf_len(data);
+
+       while (end > pos) {
+               size_t frag_len = end - pos;
+               if (frag_len > 251)
+                       frag_len = 251;
+               wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC);
+               wpabuf_put_u8(ie, 4 + frag_len);
+               wpabuf_put_be32(ie, WPS_DEV_OUI_WFA);
+               wpabuf_put_data(ie, pos, frag_len);
+               pos += frag_len;
+       }
+
+       wpabuf_free(data);
+
+       return ie;
+}
+
+
+static int wps_set_ie(struct wps_registrar *reg)
+{
+       struct wpabuf *beacon;
+       struct wpabuf *probe;
+
+       if (reg->set_ie_cb == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs");
+
+       beacon = wpabuf_alloc(300);
+       if (beacon == NULL)
+               return -1;
+       probe = wpabuf_alloc(400);
+       if (probe == NULL) {
+               wpabuf_free(beacon);
+               return -1;
+       }
+
+       if (wps_build_version(beacon) ||
+           wps_build_wps_state(reg->wps, beacon) ||
+           wps_build_ap_setup_locked(reg->wps, beacon) ||
+           wps_build_selected_registrar(reg, beacon) ||
+           wps_build_sel_reg_dev_password_id(reg, beacon) ||
+           wps_build_sel_reg_config_methods(reg, beacon) ||
+           wps_build_version(probe) ||
+           wps_build_wps_state(reg->wps, probe) ||
+           wps_build_ap_setup_locked(reg->wps, probe) ||
+           wps_build_selected_registrar(reg, probe) ||
+           wps_build_sel_reg_dev_password_id(reg, probe) ||
+           wps_build_sel_reg_config_methods(reg, probe) ||
+           wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP :
+                               WPS_RESP_REGISTRAR) ||
+           wps_build_uuid_e(probe, reg->wps->uuid) ||
+           wps_build_device_attrs(&reg->wps->dev, probe) ||
+           wps_build_probe_config_methods(reg, probe) ||
+           wps_build_rf_bands(&reg->wps->dev, probe)) {
+               wpabuf_free(beacon);
+               wpabuf_free(probe);
+               return -1;
+       }
+
+       beacon = wps_ie_encapsulate(beacon);
+       probe = wps_ie_encapsulate(probe);
+
+       if (!beacon || !probe) {
+               wpabuf_free(beacon);
+               wpabuf_free(probe);
+               return -1;
+       }
+
+       if (reg->static_wep_only) {
+               /*
+                * Windows XP and Vista clients can get confused about
+                * EAP-Identity/Request when they probe the network with
+                * EAPOL-Start. In such a case, they may assume the network is
+                * using IEEE 802.1X and prompt user for a certificate while
+                * the correct (non-WPS) behavior would be to ask for the
+                * static WEP key. As a workaround, use Microsoft Provisioning
+                * IE to advertise that legacy 802.1X is not supported.
+                */
+               const u8 ms_wps[7] = {
+                       WLAN_EID_VENDOR_SPECIFIC, 5,
+                       /* Microsoft Provisioning IE (00:50:f2:5) */
+                       0x00, 0x50, 0xf2, 5,
+                       0x00 /* no legacy 802.1X or MS WPS */
+               };
+               wpa_printf(MSG_DEBUG, "WPS: Add Microsoft Provisioning IE "
+                          "into Beacon/Probe Response frames");
+               wpabuf_put_data(beacon, ms_wps, sizeof(ms_wps));
+               wpabuf_put_data(probe, ms_wps, sizeof(ms_wps));
+       }
+
+       return wps_cb_set_ie(reg, beacon, probe);
+}
+
+
+static int wps_get_dev_password(struct wps_data *wps)
+{
+       const u8 *pin;
+       size_t pin_len = 0;
+
+       os_free(wps->dev_password);
+       wps->dev_password = NULL;
+
+       if (wps->pbc) {
+               wpa_printf(MSG_DEBUG, "WPS: Use default PIN for PBC");
+               pin = (const u8 *) "00000000";
+               pin_len = 8;
+       } else {
+               pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
+                                           &pin_len);
+       }
+       if (pin == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
+                          "the Enrollee");
+               wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
+                                 &wps->peer_dev);
+               return -1;
+       }
+
+       wps->dev_password = os_malloc(pin_len);
+       if (wps->dev_password == NULL)
+               return -1;
+       os_memcpy(wps->dev_password, pin, pin_len);
+       wps->dev_password_len = pin_len;
+
+       return 0;
+}
+
+
+static int wps_build_uuid_r(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * UUID-R");
+       wpabuf_put_be16(msg, ATTR_UUID_R);
+       wpabuf_put_be16(msg, WPS_UUID_LEN);
+       wpabuf_put_data(msg, wps->uuid_r, WPS_UUID_LEN);
+       return 0;
+}
+
+
+static int wps_build_r_hash(struct wps_data *wps, struct wpabuf *msg)
+{
+       u8 *hash;
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (os_get_random(wps->snonce, 2 * WPS_SECRET_NONCE_LEN) < 0)
+               return -1;
+       wpa_hexdump(MSG_DEBUG, "WPS: R-S1", wps->snonce, WPS_SECRET_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-S2",
+                   wps->snonce + WPS_SECRET_NONCE_LEN, WPS_SECRET_NONCE_LEN);
+
+       if (wps->dh_pubkey_e == NULL || wps->dh_pubkey_r == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: DH public keys not available for "
+                          "R-Hash derivation");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS:  * R-Hash1");
+       wpabuf_put_be16(msg, ATTR_R_HASH1);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* R-Hash1 = HMAC_AuthKey(R-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = wps->snonce;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash1", hash, SHA256_MAC_LEN);
+
+       wpa_printf(MSG_DEBUG, "WPS:  * R-Hash2");
+       wpabuf_put_be16(msg, ATTR_R_HASH2);
+       wpabuf_put_be16(msg, SHA256_MAC_LEN);
+       hash = wpabuf_put(msg, SHA256_MAC_LEN);
+       /* R-Hash2 = HMAC_AuthKey(R-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = wps->snonce + WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+       wpa_hexdump(MSG_DEBUG, "WPS: R-Hash2", hash, SHA256_MAC_LEN);
+
+       return 0;
+}
+
+
+static int wps_build_r_snonce1(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce1");
+       wpabuf_put_be16(msg, ATTR_R_SNONCE1);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce, WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * R-SNonce2");
+       wpabuf_put_be16(msg, ATTR_R_SNONCE2);
+       wpabuf_put_be16(msg, WPS_SECRET_NONCE_LEN);
+       wpabuf_put_data(msg, wps->snonce + WPS_SECRET_NONCE_LEN,
+                       WPS_SECRET_NONCE_LEN);
+       return 0;
+}
+
+
+static int wps_build_cred_network_idx(struct wpabuf *msg,
+                                     const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Network Index");
+       wpabuf_put_be16(msg, ATTR_NETWORK_INDEX);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, 1);
+       return 0;
+}
+
+
+static int wps_build_cred_ssid(struct wpabuf *msg,
+                              const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+       wpabuf_put_be16(msg, ATTR_SSID);
+       wpabuf_put_be16(msg, cred->ssid_len);
+       wpabuf_put_data(msg, cred->ssid, cred->ssid_len);
+       return 0;
+}
+
+
+static int wps_build_cred_auth_type(struct wpabuf *msg,
+                                   const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)",
+                  cred->auth_type);
+       wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, cred->auth_type);
+       return 0;
+}
+
+
+static int wps_build_cred_encr_type(struct wpabuf *msg,
+                                   const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)",
+                  cred->encr_type);
+       wpabuf_put_be16(msg, ATTR_ENCR_TYPE);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, cred->encr_type);
+       return 0;
+}
+
+
+static int wps_build_cred_network_key(struct wpabuf *msg,
+                                     const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%d)",
+                  (int) cred->key_len);
+       wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+       wpabuf_put_be16(msg, cred->key_len);
+       wpabuf_put_data(msg, cred->key, cred->key_len);
+       return 0;
+}
+
+
+static int wps_build_cred_mac_addr(struct wpabuf *msg,
+                                  const struct wps_credential *cred)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * MAC Address (" MACSTR ")",
+                  MAC2STR(cred->mac_addr));
+       wpabuf_put_be16(msg, ATTR_MAC_ADDR);
+       wpabuf_put_be16(msg, ETH_ALEN);
+       wpabuf_put_data(msg, cred->mac_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int wps_build_credential(struct wpabuf *msg,
+                               const struct wps_credential *cred)
+{
+       if (wps_build_cred_network_idx(msg, cred) ||
+           wps_build_cred_ssid(msg, cred) ||
+           wps_build_cred_auth_type(msg, cred) ||
+           wps_build_cred_encr_type(msg, cred) ||
+           wps_build_cred_network_key(msg, cred) ||
+           wps_build_cred_mac_addr(msg, cred))
+               return -1;
+       return 0;
+}
+
+
+int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
+{
+       struct wpabuf *cred;
+
+       if (wps->wps->registrar->skip_cred_build)
+               goto skip_cred_build;
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Credential");
+       if (wps->use_cred) {
+               os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred));
+               goto use_provided;
+       }
+       os_memset(&wps->cred, 0, sizeof(wps->cred));
+
+       os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+       wps->cred.ssid_len = wps->wps->ssid_len;
+
+       /* Select the best authentication and encryption type */
+       if (wps->auth_type & WPS_AUTH_WPA2PSK)
+               wps->auth_type = WPS_AUTH_WPA2PSK;
+       else if (wps->auth_type & WPS_AUTH_WPAPSK)
+               wps->auth_type = WPS_AUTH_WPAPSK;
+       else if (wps->auth_type & WPS_AUTH_OPEN)
+               wps->auth_type = WPS_AUTH_OPEN;
+       else if (wps->auth_type & WPS_AUTH_SHARED)
+               wps->auth_type = WPS_AUTH_SHARED;
+       else {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
+                          wps->auth_type);
+               return -1;
+       }
+       wps->cred.auth_type = wps->auth_type;
+
+       if (wps->auth_type == WPS_AUTH_WPA2PSK ||
+           wps->auth_type == WPS_AUTH_WPAPSK) {
+               if (wps->encr_type & WPS_ENCR_AES)
+                       wps->encr_type = WPS_ENCR_AES;
+               else if (wps->encr_type & WPS_ENCR_TKIP)
+                       wps->encr_type = WPS_ENCR_TKIP;
+               else {
+                       wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+                                  "type for WPA/WPA2");
+                       return -1;
+               }
+       } else {
+               if (wps->encr_type & WPS_ENCR_WEP)
+                       wps->encr_type = WPS_ENCR_WEP;
+               else if (wps->encr_type & WPS_ENCR_NONE)
+                       wps->encr_type = WPS_ENCR_NONE;
+               else {
+                       wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
+                                  "type for non-WPA/WPA2 mode");
+                       return -1;
+               }
+       }
+       wps->cred.encr_type = wps->encr_type;
+       /*
+        * Set MAC address in the Credential to be the Enrollee's MAC address
+        */
+       os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+
+       if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->wps->ap &&
+           !wps->wps->registrar->disable_auto_conf) {
+               u8 r[16];
+               /* Generate a random passphrase */
+               if (os_get_random(r, sizeof(r)) < 0)
+                       return -1;
+               os_free(wps->new_psk);
+               wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
+               if (wps->new_psk == NULL)
+                       return -1;
+               wps->new_psk_len--; /* remove newline */
+               while (wps->new_psk_len &&
+                      wps->new_psk[wps->new_psk_len - 1] == '=')
+                       wps->new_psk_len--;
+               wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: Generated passphrase",
+                                     wps->new_psk, wps->new_psk_len);
+               os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
+               wps->cred.key_len = wps->new_psk_len;
+       } else if (wps->use_psk_key && wps->wps->psk_set) {
+               char hex[65];
+               wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
+               wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
+               os_memcpy(wps->cred.key, hex, 32 * 2);
+               wps->cred.key_len = 32 * 2;
+       } else if (wps->wps->network_key) {
+               os_memcpy(wps->cred.key, wps->wps->network_key,
+                         wps->wps->network_key_len);
+               wps->cred.key_len = wps->wps->network_key_len;
+       } else if (wps->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+               char hex[65];
+               /* Generate a random per-device PSK */
+               os_free(wps->new_psk);
+               wps->new_psk_len = 32;
+               wps->new_psk = os_malloc(wps->new_psk_len);
+               if (wps->new_psk == NULL)
+                       return -1;
+               if (os_get_random(wps->new_psk, wps->new_psk_len) < 0) {
+                       os_free(wps->new_psk);
+                       wps->new_psk = NULL;
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+                               wps->new_psk, wps->new_psk_len);
+               wpa_snprintf_hex(hex, sizeof(hex), wps->new_psk,
+                                wps->new_psk_len);
+               os_memcpy(wps->cred.key, hex, wps->new_psk_len * 2);
+               wps->cred.key_len = wps->new_psk_len * 2;
+       }
+
+use_provided:
+       cred = wpabuf_alloc(200);
+       if (cred == NULL)
+               return -1;
+
+       if (wps_build_credential(cred, &wps->cred)) {
+               wpabuf_free(cred);
+               return -1;
+       }
+
+       wpabuf_put_be16(msg, ATTR_CRED);
+       wpabuf_put_be16(msg, wpabuf_len(cred));
+       wpabuf_put_buf(msg, cred);
+       wpabuf_free(cred);
+
+skip_cred_build:
+       if (wps->wps->registrar->extra_cred) {
+               wpa_printf(MSG_DEBUG, "WPS:  * Credential (pre-configured)");
+               wpabuf_put_buf(msg, wps->wps->registrar->extra_cred);
+       }
+
+       return 0;
+}
+
+
+static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *msg)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * AP Settings");
+
+       if (wps_build_credential(msg, &wps->cred))
+               return -1;
+
+       return 0;
+}
+
+
+static struct wpabuf * wps_build_m2(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       if (os_get_random(wps->nonce_r, WPS_NONCE_LEN) < 0)
+               return NULL;
+       wpa_hexdump(MSG_DEBUG, "WPS: Registrar Nonce",
+                   wps->nonce_r, WPS_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-R", wps->uuid_r, WPS_UUID_LEN);
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M2");
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M2) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_uuid_r(wps, msg) ||
+           wps_build_public_key(wps, msg) ||
+           wps_derive_keys(wps) ||
+           wps_build_auth_type_flags(wps, msg) ||
+           wps_build_encr_type_flags(wps, msg) ||
+           wps_build_conn_type_flags(wps, msg) ||
+           wps_build_config_methods_r(wps->wps->registrar, msg) ||
+           wps_build_device_attrs(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_assoc_state(wps, msg) ||
+           wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
+           wps_build_dev_password_id(msg, wps->dev_pw_id) ||
+           wps_build_os_version(&wps->wps->dev, msg) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->int_reg = 1;
+       wps->state = RECV_M3;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m2d(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+       u16 err = wps->config_error;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M2D");
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps->wps->ap && wps->wps->ap_setup_locked &&
+           err == WPS_CFG_NO_ERROR)
+               err = WPS_CFG_SETUP_LOCKED;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M2D) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_uuid_r(wps, msg) ||
+           wps_build_auth_type_flags(wps, msg) ||
+           wps_build_encr_type_flags(wps, msg) ||
+           wps_build_conn_type_flags(wps, msg) ||
+           wps_build_config_methods_r(wps->wps->registrar, msg) ||
+           wps_build_device_attrs(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_assoc_state(wps, msg) ||
+           wps_build_config_error(msg, err) ||
+           wps_build_os_version(&wps->wps->dev, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       wps->state = RECV_M2D_ACK;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m4(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M4");
+
+       wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M4) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_r_hash(wps, msg) ||
+           wps_build_r_snonce1(wps, plain) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->state = RECV_M5;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m6(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M6");
+
+       plain = wpabuf_alloc(200);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M6) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_r_snonce2(wps, plain) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->wps_pin_revealed = 1;
+       wps->state = RECV_M7;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_m8(struct wps_data *wps)
+{
+       struct wpabuf *msg, *plain;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message M8");
+
+       plain = wpabuf_alloc(500);
+       if (plain == NULL)
+               return NULL;
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL) {
+               wpabuf_free(plain);
+               return NULL;
+       }
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_M8) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) ||
+           (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
+           wps_build_key_wrap_auth(wps, plain) ||
+           wps_build_encr_settings(wps, msg, plain) ||
+           wps_build_authenticator(wps, msg)) {
+               wpabuf_free(plain);
+               wpabuf_free(msg);
+               return NULL;
+       }
+       wpabuf_free(plain);
+
+       wps->state = RECV_DONE;
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_ACK");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_ACK) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+static struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building Message WSC_NACK");
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return NULL;
+
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_NACK) ||
+           wps_build_enrollee_nonce(wps, msg) ||
+           wps_build_registrar_nonce(wps, msg) ||
+           wps_build_config_error(msg, wps->config_error)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+struct wpabuf * wps_registrar_get_msg(struct wps_data *wps,
+                                     enum wsc_op_code *op_code)
+{
+       struct wpabuf *msg;
+
+#ifdef CONFIG_WPS_UPNP
+       if (!wps->int_reg && wps->wps->wps_upnp) {
+               struct upnp_pending_message *p, *prev = NULL;
+               if (wps->ext_reg > 1)
+                       wps_registrar_free_pending_m2(wps->wps);
+               p = wps->wps->upnp_msgs;
+               /* TODO: check pending message MAC address */
+               while (p && p->next) {
+                       prev = p;
+                       p = p->next;
+               }
+               if (p) {
+                       wpa_printf(MSG_DEBUG, "WPS: Use pending message from "
+                                  "UPnP");
+                       if (prev)
+                               prev->next = NULL;
+                       else
+                               wps->wps->upnp_msgs = NULL;
+                       msg = p->msg;
+                       switch (p->type) {
+                       case WPS_WSC_ACK:
+                               *op_code = WSC_ACK;
+                               break;
+                       case WPS_WSC_NACK:
+                               *op_code = WSC_NACK;
+                               break;
+                       default:
+                               *op_code = WSC_MSG;
+                               break;
+                       }
+                       os_free(p);
+                       if (wps->ext_reg == 0)
+                               wps->ext_reg = 1;
+                       return msg;
+               }
+       }
+       if (wps->ext_reg) {
+               wpa_printf(MSG_DEBUG, "WPS: Using external Registrar, but no "
+                          "pending message available");
+               return NULL;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       switch (wps->state) {
+       case SEND_M2:
+               if (wps_get_dev_password(wps) < 0)
+                       msg = wps_build_m2d(wps);
+               else
+                       msg = wps_build_m2(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M2D:
+               msg = wps_build_m2d(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M4:
+               msg = wps_build_m4(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M6:
+               msg = wps_build_m6(wps);
+               *op_code = WSC_MSG;
+               break;
+       case SEND_M8:
+               msg = wps_build_m8(wps);
+               *op_code = WSC_MSG;
+               break;
+       case RECV_DONE:
+               msg = wps_build_wsc_ack(wps);
+               *op_code = WSC_ACK;
+               break;
+       case SEND_WSC_NACK:
+               msg = wps_build_wsc_nack(wps);
+               *op_code = WSC_NACK;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported state %d for building "
+                          "a message", wps->state);
+               msg = NULL;
+               break;
+       }
+
+       if (*op_code == WSC_MSG && msg) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return msg;
+}
+
+
+static int wps_process_enrollee_nonce(struct wps_data *wps, const u8 *e_nonce)
+{
+       if (e_nonce == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Enrollee Nonce received");
+               return -1;
+       }
+
+       os_memcpy(wps->nonce_e, e_nonce, WPS_NONCE_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Nonce",
+                   wps->nonce_e, WPS_NONCE_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_registrar_nonce(struct wps_data *wps, const u8 *r_nonce)
+{
+       if (r_nonce == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Registrar Nonce received");
+               return -1;
+       }
+
+       if (os_memcmp(wps->nonce_r, r_nonce, WPS_NONCE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Nonce received");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_uuid_e(struct wps_data *wps, const u8 *uuid_e)
+{
+       if (uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No UUID-E received");
+               return -1;
+       }
+
+       os_memcpy(wps->uuid_e, uuid_e, WPS_UUID_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", wps->uuid_e, WPS_UUID_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_dev_password_id(struct wps_data *wps, const u8 *pw_id)
+{
+       if (pw_id == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Device Password ID received");
+               return -1;
+       }
+
+       wps->dev_pw_id = WPA_GET_BE16(pw_id);
+       wpa_printf(MSG_DEBUG, "WPS: Device Password ID %d", wps->dev_pw_id);
+
+       return 0;
+}
+
+
+static int wps_process_e_hash1(struct wps_data *wps, const u8 *e_hash1)
+{
+       if (e_hash1 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No E-Hash1 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash1, e_hash1, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash1", wps->peer_hash1, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_e_hash2(struct wps_data *wps, const u8 *e_hash2)
+{
+       if (e_hash2 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No E-Hash2 received");
+               return -1;
+       }
+
+       os_memcpy(wps->peer_hash2, e_hash2, WPS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPS: E-Hash2", wps->peer_hash2, WPS_HASH_LEN);
+
+       return 0;
+}
+
+
+static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (e_snonce1 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No E-SNonce1 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce1", e_snonce1,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* E-Hash1 = HMAC_AuthKey(E-S1 || PSK1 || PK_E || PK_R) */
+       addr[0] = e_snonce1;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk1;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+       if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
+                          "not match with the pre-committed value");
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 0, 1);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the first "
+                  "half of the device password");
+
+       return 0;
+}
+
+
+static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
+{
+       u8 hash[SHA256_MAC_LEN];
+       const u8 *addr[4];
+       size_t len[4];
+
+       if (e_snonce2 == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No E-SNonce2 received");
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: E-SNonce2", e_snonce2,
+                       WPS_SECRET_NONCE_LEN);
+
+       /* E-Hash2 = HMAC_AuthKey(E-S2 || PSK2 || PK_E || PK_R) */
+       addr[0] = e_snonce2;
+       len[0] = WPS_SECRET_NONCE_LEN;
+       addr[1] = wps->psk2;
+       len[1] = WPS_PSK_LEN;
+       addr[2] = wpabuf_head(wps->dh_pubkey_e);
+       len[2] = wpabuf_len(wps->dh_pubkey_e);
+       addr[3] = wpabuf_head(wps->dh_pubkey_r);
+       len[3] = wpabuf_len(wps->dh_pubkey_r);
+       hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
+
+       if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
+                          "not match with the pre-committed value");
+               wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
+               wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
+               wps_pwd_auth_fail_event(wps->wps, 0, 2);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee proved knowledge of the second "
+                  "half of the device password");
+       wps->wps_pin_revealed = 0;
+       wps_registrar_unlock_pin(wps->wps->registrar, wps->uuid_e);
+
+       return 0;
+}
+
+
+static int wps_process_mac_addr(struct wps_data *wps, const u8 *mac_addr)
+{
+       if (mac_addr == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No MAC Address received");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee MAC Address " MACSTR,
+                  MAC2STR(mac_addr));
+       os_memcpy(wps->mac_addr_e, mac_addr, ETH_ALEN);
+       os_memcpy(wps->peer_dev.mac_addr, mac_addr, ETH_ALEN);
+
+       return 0;
+}
+
+
+static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
+                             size_t pk_len)
+{
+       if (pk == NULL || pk_len == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: No Public Key received");
+               return -1;
+       }
+
+#ifdef CONFIG_WPS_OOB
+       if (wps->wps->oob_conf.pubkey_hash != NULL) {
+               const u8 *addr[1];
+               u8 hash[WPS_HASH_LEN];
+
+               addr[0] = pk;
+               sha256_vector(1, addr, &pk_len, hash);
+               if (os_memcmp(hash,
+                             wpabuf_head(wps->wps->oob_conf.pubkey_hash),
+                             WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+                       wpa_printf(MSG_ERROR, "WPS: Public Key hash error");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_WPS_OOB */
+
+       wpabuf_free(wps->dh_pubkey_e);
+       wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len);
+       if (wps->dh_pubkey_e == NULL)
+               return -1;
+
+       return 0;
+}
+
+
+static int wps_process_auth_type_flags(struct wps_data *wps, const u8 *auth)
+{
+       u16 auth_types;
+
+       if (auth == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Authentication Type flags "
+                          "received");
+               return -1;
+       }
+
+       auth_types = WPA_GET_BE16(auth);
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Authentication Type flags 0x%x",
+                  auth_types);
+       wps->auth_type = wps->wps->auth_types & auth_types;
+       if (wps->auth_type == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+                          "authentication types (own 0x%x Enrollee 0x%x)",
+                          wps->wps->auth_types, auth_types);
+#ifdef WPS_WORKAROUNDS
+               /*
+                * Some deployed implementations seem to advertise incorrect
+                * information in this attribute. For example, Linksys WRT350N
+                * seems to have a byteorder bug that breaks this negotiation.
+                * In order to interoperate with existing implementations,
+                * assume that the Enrollee supports everything we do.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+                          "does not advertise supported authentication types "
+                          "correctly");
+               wps->auth_type = wps->wps->auth_types;
+#else /* WPS_WORKAROUNDS */
+               return -1;
+#endif /* WPS_WORKAROUNDS */
+       }
+
+       return 0;
+}
+
+
+static int wps_process_encr_type_flags(struct wps_data *wps, const u8 *encr)
+{
+       u16 encr_types;
+
+       if (encr == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Encryption Type flags "
+                          "received");
+               return -1;
+       }
+
+       encr_types = WPA_GET_BE16(encr);
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Encryption Type flags 0x%x",
+                  encr_types);
+       wps->encr_type = wps->wps->encr_types & encr_types;
+       if (wps->encr_type == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: No match in supported "
+                          "encryption types (own 0x%x Enrollee 0x%x)",
+                          wps->wps->encr_types, encr_types);
+#ifdef WPS_WORKAROUNDS
+               /*
+                * Some deployed implementations seem to advertise incorrect
+                * information in this attribute. For example, Linksys WRT350N
+                * seems to have a byteorder bug that breaks this negotiation.
+                * In order to interoperate with existing implementations,
+                * assume that the Enrollee supports everything we do.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Workaround - assume Enrollee "
+                          "does not advertise supported encryption types "
+                          "correctly");
+               wps->encr_type = wps->wps->encr_types;
+#else /* WPS_WORKAROUNDS */
+               return -1;
+#endif /* WPS_WORKAROUNDS */
+       }
+
+       return 0;
+}
+
+
+static int wps_process_conn_type_flags(struct wps_data *wps, const u8 *conn)
+{
+       if (conn == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Connection Type flags "
+                          "received");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Connection Type flags 0x%x",
+                  *conn);
+
+       return 0;
+}
+
+
+static int wps_process_config_methods(struct wps_data *wps, const u8 *methods)
+{
+       u16 m;
+
+       if (methods == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Config Methods received");
+               return -1;
+       }
+
+       m = WPA_GET_BE16(methods);
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x"
+                  "%s%s%s%s%s%s%s%s%s", m,
+                  m & WPS_CONFIG_USBA ? " [USBA]" : "",
+                  m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "",
+                  m & WPS_CONFIG_LABEL ? " [Label]" : "",
+                  m & WPS_CONFIG_DISPLAY ? " [Display]" : "",
+                  m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "",
+                  m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "",
+                  m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "",
+                  m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "",
+                  m & WPS_CONFIG_KEYPAD ? " [Keypad]" : "");
+
+       if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) {
+               /*
+                * The Enrollee does not have a display so it is unlikely to be
+                * able to show the passphrase to a user and as such, could
+                * benefit from receiving PSK to reduce key derivation time.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to "
+                          "Enrollee not supporting display");
+               wps->use_psk_key = 1;
+       }
+
+       return 0;
+}
+
+
+static int wps_process_wps_state(struct wps_data *wps, const u8 *state)
+{
+       if (state == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Wi-Fi Protected Setup State "
+                          "received");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Wi-Fi Protected Setup State %d",
+                  *state);
+
+       return 0;
+}
+
+
+static int wps_process_assoc_state(struct wps_data *wps, const u8 *assoc)
+{
+       u16 a;
+
+       if (assoc == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Association State received");
+               return -1;
+       }
+
+       a = WPA_GET_BE16(assoc);
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Association State %d", a);
+
+       return 0;
+}
+
+
+static int wps_process_config_error(struct wps_data *wps, const u8 *err)
+{
+       u16 e;
+
+       if (err == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Configuration Error received");
+               return -1;
+       }
+
+       e = WPA_GET_BE16(err);
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee Configuration Error %d", e);
+
+       return 0;
+}
+
+
+static enum wps_process_res wps_process_m1(struct wps_data *wps,
+                                          struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Received M1");
+
+       if (wps->state != RECV_M1) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M1", wps->state);
+               return WPS_FAILURE;
+       }
+
+       if (wps_process_uuid_e(wps, attr->uuid_e) ||
+           wps_process_mac_addr(wps, attr->mac_addr) ||
+           wps_process_enrollee_nonce(wps, attr->enrollee_nonce) ||
+           wps_process_pubkey(wps, attr->public_key, attr->public_key_len) ||
+           wps_process_auth_type_flags(wps, attr->auth_type_flags) ||
+           wps_process_encr_type_flags(wps, attr->encr_type_flags) ||
+           wps_process_conn_type_flags(wps, attr->conn_type_flags) ||
+           wps_process_config_methods(wps, attr->config_methods) ||
+           wps_process_wps_state(wps, attr->wps_state) ||
+           wps_process_device_attrs(&wps->peer_dev, attr) ||
+           wps_process_rf_bands(&wps->peer_dev, attr->rf_bands) ||
+           wps_process_assoc_state(wps, attr->assoc_state) ||
+           wps_process_dev_password_id(wps, attr->dev_password_id) ||
+           wps_process_config_error(wps, attr->config_error) ||
+           wps_process_os_version(&wps->peer_dev, attr->os_version))
+               return WPS_FAILURE;
+
+       if (wps->dev_pw_id < 0x10 &&
+           wps->dev_pw_id != DEV_PW_DEFAULT &&
+           wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
+           wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
+           wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+           (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
+            !wps->wps->registrar->pbc)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
+                          wps->dev_pw_id);
+               wps->state = SEND_M2D;
+               return WPS_CONTINUE;
+       }
+
+#ifdef CONFIG_WPS_OOB
+       if (wps->dev_pw_id >= 0x10 &&
+           wps->dev_pw_id != wps->wps->oob_dev_pw_id) {
+               wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID "
+                          "%d mismatch", wps->dev_pw_id);
+               wps->state = SEND_M2D;
+               return WPS_CONTINUE;
+       }
+#endif /* CONFIG_WPS_OOB */
+
+       if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) {
+               if (wps->wps->registrar->force_pbc_overlap ||
+                   wps_registrar_pbc_overlap(wps->wps->registrar,
+                                             wps->mac_addr_e, wps->uuid_e)) {
+                       wpa_printf(MSG_DEBUG, "WPS: PBC overlap - deny PBC "
+                                  "negotiation");
+                       wps->state = SEND_M2D;
+                       wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+                       wps_pbc_overlap_event(wps->wps);
+                       wps->wps->registrar->force_pbc_overlap = 1;
+                       return WPS_CONTINUE;
+               }
+               wps_registrar_add_pbc_session(wps->wps->registrar,
+                                             wps->mac_addr_e, wps->uuid_e);
+               wps->pbc = 1;
+       }
+
+#ifdef WPS_WORKAROUNDS
+       /*
+        * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in
+        * passphrase format. To avoid interop issues, force PSK format to be
+        * used.
+        */
+       if (!wps->use_psk_key &&
+           wps->peer_dev.manufacturer &&
+           os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 &&
+           wps->peer_dev.model_name &&
+           os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in "
+                          "PSK format");
+               wps->use_psk_key = 1;
+       }
+#endif /* WPS_WORKAROUNDS */
+
+       wps->state = SEND_M2;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m3(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Received M3");
+
+       if (wps->state != RECV_M3) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M3", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+               wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+                          "session overlap");
+               wps->state = SEND_WSC_NACK;
+               wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg) ||
+           wps_process_e_hash1(wps, attr->e_hash1) ||
+           wps_process_e_hash2(wps, attr->e_hash2)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wps->state = SEND_M4;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_m5(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received M5");
+
+       if (wps->state != RECV_M5) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M5", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+               wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+                          "session overlap");
+               wps->state = SEND_WSC_NACK;
+               wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_e_snonce1(wps, eattr.e_snonce1)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M6;
+       return WPS_CONTINUE;
+}
+
+
+static void wps_sta_cred_cb(struct wps_data *wps)
+{
+       /*
+        * Update credential to only include a single authentication and
+        * encryption type in case the AP configuration includes more than one
+        * option.
+        */
+       if (wps->cred.auth_type & WPS_AUTH_WPA2PSK)
+               wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+       else if (wps->cred.auth_type & WPS_AUTH_WPAPSK)
+               wps->cred.auth_type = WPS_AUTH_WPAPSK;
+       if (wps->cred.encr_type & WPS_ENCR_AES)
+               wps->cred.encr_type = WPS_ENCR_AES;
+       else if (wps->cred.encr_type & WPS_ENCR_TKIP)
+               wps->cred.encr_type = WPS_ENCR_TKIP;
+       wpa_printf(MSG_DEBUG, "WPS: Update local configuration based on the "
+                  "AP configuration");
+       if (wps->wps->cred_cb)
+               wps->wps->cred_cb(wps->wps->cb_ctx, &wps->cred);
+}
+
+
+static void wps_cred_update(struct wps_credential *dst,
+                           struct wps_credential *src)
+{
+       os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid));
+       dst->ssid_len = src->ssid_len;
+       dst->auth_type = src->auth_type;
+       dst->encr_type = src->encr_type;
+       dst->key_idx = src->key_idx;
+       os_memcpy(dst->key, src->key, sizeof(dst->key));
+       dst->key_len = src->key_len;
+}
+
+
+static int wps_process_ap_settings_r(struct wps_data *wps,
+                                    struct wps_parse_attr *attr)
+{
+       if (wps->wps->ap || wps->er)
+               return 0;
+
+       /* AP Settings Attributes in M7 when Enrollee is an AP */
+       if (wps_process_ap_settings(attr, &wps->cred) < 0)
+               return -1;
+
+       wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP");
+
+       if (wps->new_ap_settings) {
+               wpa_printf(MSG_INFO, "WPS: Update AP configuration based on "
+                          "new settings");
+               wps_cred_update(&wps->cred, wps->new_ap_settings);
+               return 0;
+       } else {
+               /*
+                * Use the AP PIN only to receive the current AP settings, not
+                * to reconfigure the AP.
+                */
+               if (wps->ap_settings_cb) {
+                       wps->ap_settings_cb(wps->ap_settings_cb_ctx,
+                                           &wps->cred);
+                       return 1;
+               }
+               wps_sta_cred_cb(wps);
+               return 1;
+       }
+}
+
+
+static enum wps_process_res wps_process_m7(struct wps_data *wps,
+                                          const struct wpabuf *msg,
+                                          struct wps_parse_attr *attr)
+{
+       struct wpabuf *decrypted;
+       struct wps_parse_attr eattr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received M7");
+
+       if (wps->state != RECV_M7) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving M7", wps->state);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       if (wps->pbc && wps->wps->registrar->force_pbc_overlap) {
+               wpa_printf(MSG_DEBUG, "WPS: Reject negotiation due to PBC "
+                          "session overlap");
+               wps->state = SEND_WSC_NACK;
+               wps->config_error = WPS_CFG_MULTIPLE_PBC_DETECTED;
+               return WPS_CONTINUE;
+       }
+
+       if (wps_process_registrar_nonce(wps, attr->registrar_nonce) ||
+           wps_process_authenticator(wps, attr->authenticator, msg)) {
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                             attr->encr_settings_len);
+       if (decrypted == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt Encrypted "
+                          "Settings attribute");
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted Settings "
+                  "attribute");
+       if (wps_parse_msg(decrypted, &eattr) < 0 ||
+           wps_process_key_wrap_auth(wps, decrypted, eattr.key_wrap_auth) ||
+           wps_process_e_snonce2(wps, eattr.e_snonce2) ||
+           wps_process_ap_settings_r(wps, &eattr)) {
+               wpabuf_free(decrypted);
+               wps->state = SEND_WSC_NACK;
+               return WPS_CONTINUE;
+       }
+
+       wpabuf_free(decrypted);
+
+       wps->state = SEND_M8;
+       return WPS_CONTINUE;
+}
+
+
+static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       enum wps_process_res ret = WPS_CONTINUE;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_MSG");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_M1 &&
+           (attr.registrar_nonce == NULL ||
+            os_memcmp(wps->nonce_r, attr.registrar_nonce,
+                      WPS_NONCE_LEN != 0))) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       switch (*attr.msg_type) {
+       case WPS_M1:
+#ifdef CONFIG_WPS_UPNP
+               if (wps->wps->wps_upnp && attr.mac_addr) {
+                       /* Remove old pending messages when starting new run */
+                       wps_free_pending_msgs(wps->wps->upnp_msgs);
+                       wps->wps->upnp_msgs = NULL;
+
+                       upnp_wps_device_send_wlan_event(
+                               wps->wps->wps_upnp, attr.mac_addr,
+                               UPNP_WPS_WLANEVENT_TYPE_EAP, msg);
+               }
+#endif /* CONFIG_WPS_UPNP */
+               ret = wps_process_m1(wps, &attr);
+               break;
+       case WPS_M3:
+               ret = wps_process_m3(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M3);
+               break;
+       case WPS_M5:
+               ret = wps_process_m5(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M5);
+               break;
+       case WPS_M7:
+               ret = wps_process_m7(wps, msg, &attr);
+               if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
+                       wps_fail_event(wps->wps, WPS_M7);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+       if (ret == WPS_CONTINUE) {
+               /* Save a copy of the last message for Authenticator derivation
+                */
+               wpabuf_free(wps->last_msg);
+               wps->last_msg = wpabuf_dup(msg);
+       }
+
+       return ret;
+}
+
+
+static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
+                                               const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_ACK");
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_ACK) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && wps->ext_reg && wps->state == RECV_M2D_ACK &&
+           upnp_wps_subscribers(wps->wps->wps_upnp)) {
+               if (wps->wps->upnp_msgs)
+                       return WPS_CONTINUE;
+               wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+                          "external Registrar");
+               return WPS_PENDING;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+       {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       if (wps->state == RECV_M2D_ACK) {
+#ifdef CONFIG_WPS_UPNP
+               if (wps->wps->wps_upnp &&
+                   upnp_wps_subscribers(wps->wps->wps_upnp)) {
+                       if (wps->wps->upnp_msgs)
+                               return WPS_CONTINUE;
+                       if (wps->ext_reg == 0)
+                               wps->ext_reg = 1;
+                       wpa_printf(MSG_DEBUG, "WPS: Wait for response from an "
+                                  "external Registrar");
+                       return WPS_PENDING;
+               }
+#endif /* CONFIG_WPS_UPNP */
+
+               wpa_printf(MSG_DEBUG, "WPS: No more registrars available - "
+                          "terminate negotiation");
+       }
+
+       return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
+                                                const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+       int old_state;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_NACK");
+
+       old_state = wps->state;
+       wps->state = SEND_WSC_NACK;
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_NACK) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && wps->ext_reg) {
+               wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+                          "Registrar terminated by the Enrollee");
+               return WPS_FAILURE;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+       {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.config_error == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Configuration Error attribute "
+                          "in WSC_NACK");
+               return WPS_FAILURE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Enrollee terminated negotiation with "
+                  "Configuration Error %d", WPA_GET_BE16(attr.config_error));
+
+       switch (old_state) {
+       case RECV_M3:
+               wps_fail_event(wps->wps, WPS_M2);
+               break;
+       case RECV_M5:
+               wps_fail_event(wps->wps, WPS_M4);
+               break;
+       case RECV_M7:
+               wps_fail_event(wps->wps, WPS_M6);
+               break;
+       case RECV_DONE:
+               wps_fail_event(wps->wps, WPS_M8);
+               break;
+       default:
+               break;
+       }
+
+       return WPS_FAILURE;
+}
+
+
+static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
+                                                const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_printf(MSG_DEBUG, "WPS: Received WSC_Done");
+
+       if (wps->state != RECV_DONE &&
+           (!wps->wps->wps_upnp || !wps->ext_reg)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected state (%d) for "
+                          "receiving WSC_Done", wps->state);
+               return WPS_FAILURE;
+       }
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return WPS_FAILURE;
+
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported message version 0x%x",
+                          attr.version ? *attr.version : 0);
+               return WPS_FAILURE;
+       }
+
+       if (attr.msg_type == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No Message Type attribute");
+               return WPS_FAILURE;
+       }
+
+       if (*attr.msg_type != WPS_WSC_DONE) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid Message Type %d",
+                          *attr.msg_type);
+               return WPS_FAILURE;
+       }
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && wps->ext_reg) {
+               wpa_printf(MSG_DEBUG, "WPS: Negotiation using external "
+                          "Registrar completed successfully");
+               wps_device_store(wps->wps->registrar, &wps->peer_dev,
+                                wps->uuid_e);
+               return WPS_DONE;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       if (attr.registrar_nonce == NULL ||
+           os_memcmp(wps->nonce_r, attr.registrar_nonce, WPS_NONCE_LEN != 0))
+       {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in registrar nonce");
+               return WPS_FAILURE;
+       }
+
+       if (attr.enrollee_nonce == NULL ||
+           os_memcmp(wps->nonce_e, attr.enrollee_nonce, WPS_NONCE_LEN != 0)) {
+               wpa_printf(MSG_DEBUG, "WPS: Mismatch in enrollee nonce");
+               return WPS_FAILURE;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully");
+       wps_device_store(wps->wps->registrar, &wps->peer_dev,
+                        wps->uuid_e);
+
+       if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk &&
+           wps->wps->ap && !wps->wps->registrar->disable_auto_conf) {
+               struct wps_credential cred;
+
+               wpa_printf(MSG_DEBUG, "WPS: Moving to Configured state based "
+                          "on first Enrollee connection");
+
+               os_memset(&cred, 0, sizeof(cred));
+               os_memcpy(cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
+               cred.ssid_len = wps->wps->ssid_len;
+               cred.auth_type = WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK;
+               cred.encr_type = WPS_ENCR_TKIP | WPS_ENCR_AES;
+               os_memcpy(cred.key, wps->new_psk, wps->new_psk_len);
+               cred.key_len = wps->new_psk_len;
+
+               wps->wps->wps_state = WPS_STATE_CONFIGURED;
+               wpa_hexdump_ascii_key(MSG_DEBUG,
+                                     "WPS: Generated random passphrase",
+                                     wps->new_psk, wps->new_psk_len);
+               if (wps->wps->cred_cb)
+                       wps->wps->cred_cb(wps->wps->cb_ctx, &cred);
+
+               os_free(wps->new_psk);
+               wps->new_psk = NULL;
+       }
+
+       if (!wps->wps->ap && !wps->er)
+               wps_sta_cred_cb(wps);
+
+       if (wps->new_psk) {
+               if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
+                                  wps->new_psk, wps->new_psk_len)) {
+                       wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
+                                  "new PSK");
+               }
+               os_free(wps->new_psk);
+               wps->new_psk = NULL;
+       }
+
+       wps_cb_reg_success(wps->wps->registrar, wps->mac_addr_e, wps->uuid_e);
+
+       if (wps->pbc) {
+               wps_registrar_remove_pbc_session(wps->wps->registrar,
+                                                wps->mac_addr_e, wps->uuid_e);
+               wps_registrar_pbc_completed(wps->wps->registrar);
+       } else {
+               wps_registrar_pin_completed(wps->wps->registrar);
+       }
+
+       wps_success_event(wps->wps);
+
+       return WPS_DONE;
+}
+
+
+enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
+                                              enum wsc_op_code op_code,
+                                              const struct wpabuf *msg)
+{
+       enum wps_process_res ret;
+
+       wpa_printf(MSG_DEBUG, "WPS: Processing received message (len=%lu "
+                  "op_code=%d)",
+                  (unsigned long) wpabuf_len(msg), op_code);
+
+#ifdef CONFIG_WPS_UPNP
+       if (wps->wps->wps_upnp && op_code == WSC_MSG && wps->ext_reg == 1) {
+               struct wps_parse_attr attr;
+               if (wps_parse_msg(msg, &attr) == 0 && attr.msg_type &&
+                   *attr.msg_type == WPS_M3)
+                       wps->ext_reg = 2; /* past M2/M2D phase */
+       }
+       if (wps->ext_reg > 1)
+               wps_registrar_free_pending_m2(wps->wps);
+       if (wps->wps->wps_upnp && wps->ext_reg &&
+           wps->wps->upnp_msgs == NULL &&
+           (op_code == WSC_MSG || op_code == WSC_Done || op_code == WSC_NACK))
+       {
+               struct wps_parse_attr attr;
+               int type;
+               if (wps_parse_msg(msg, &attr) < 0 || attr.msg_type == NULL)
+                       type = -1;
+               else
+                       type = *attr.msg_type;
+               wpa_printf(MSG_DEBUG, "WPS: Sending received message (type %d)"
+                          " to external Registrar for processing", type);
+               upnp_wps_device_send_wlan_event(wps->wps->wps_upnp,
+                                               wps->mac_addr_e,
+                                               UPNP_WPS_WLANEVENT_TYPE_EAP,
+                                               msg);
+               if (op_code == WSC_MSG)
+                       return WPS_PENDING;
+       } else if (wps->wps->wps_upnp && wps->ext_reg && op_code == WSC_MSG) {
+               wpa_printf(MSG_DEBUG, "WPS: Skip internal processing - using "
+                          "external Registrar");
+               return WPS_CONTINUE;
+       }
+#endif /* CONFIG_WPS_UPNP */
+
+       switch (op_code) {
+       case WSC_MSG:
+               return wps_process_wsc_msg(wps, msg);
+       case WSC_ACK:
+               return wps_process_wsc_ack(wps, msg);
+       case WSC_NACK:
+               return wps_process_wsc_nack(wps, msg);
+       case WSC_Done:
+               ret = wps_process_wsc_done(wps, msg);
+               if (ret == WPS_FAILURE) {
+                       wps->state = SEND_WSC_NACK;
+                       wps_fail_event(wps->wps, WPS_WSC_DONE);
+               }
+               return ret;
+       default:
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported op_code %d", op_code);
+               return WPS_FAILURE;
+       }
+}
+
+
+int wps_registrar_update_ie(struct wps_registrar *reg)
+{
+       return wps_set_ie(reg);
+}
+
+
+static void wps_registrar_set_selected_timeout(void *eloop_ctx,
+                                              void *timeout_ctx)
+{
+       struct wps_registrar *reg = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - "
+                  "unselect internal Registrar");
+       reg->selected_registrar = 0;
+       reg->pbc = 0;
+       wps_registrar_selected_registrar_changed(reg);
+}
+
+
+#ifdef CONFIG_WPS_UPNP
+static void wps_registrar_sel_reg_add(struct wps_registrar *reg,
+                                     struct subscription *s)
+{
+       wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d "
+                  "config_methods=0x%x)",
+                  s->dev_password_id, s->config_methods);
+       reg->sel_reg_union = 1;
+       if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON)
+               reg->sel_reg_dev_password_id_override = s->dev_password_id;
+       if (reg->sel_reg_config_methods_override == -1)
+               reg->sel_reg_config_methods_override = 0;
+       reg->sel_reg_config_methods_override |= s->config_methods;
+}
+#endif /* CONFIG_WPS_UPNP */
+
+
+static void wps_registrar_sel_reg_union(struct wps_registrar *reg)
+{
+#ifdef CONFIG_WPS_UPNP
+       struct subscription *s;
+
+       if (reg->wps->wps_upnp == NULL)
+               return;
+
+       dl_list_for_each(s, &reg->wps->wps_upnp->subscriptions,
+                        struct subscription, list) {
+               struct subscr_addr *sa;
+               sa = dl_list_first(&s->addr_list, struct subscr_addr, list);
+               if (sa) {
+                       wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d",
+                                  inet_ntoa(sa->saddr.sin_addr),
+                                  ntohs(sa->saddr.sin_port));
+               }
+               if (s->selected_registrar)
+                       wps_registrar_sel_reg_add(reg, s);
+               else
+                       wpa_printf(MSG_DEBUG, "WPS: External Registrar not "
+                                  "selected");
+       }
+#endif /* CONFIG_WPS_UPNP */
+}
+
+
+/**
+ * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change
+ * @reg: Registrar data from wps_registrar_init()
+ *
+ * This function is called when selected registrar state changes, e.g., when an
+ * AP receives a SetSelectedRegistrar UPnP message.
+ */
+void wps_registrar_selected_registrar_changed(struct wps_registrar *reg)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed");
+
+       reg->sel_reg_union = reg->selected_registrar;
+       reg->sel_reg_dev_password_id_override = -1;
+       reg->sel_reg_config_methods_override = -1;
+       if (reg->selected_registrar) {
+               reg->sel_reg_config_methods_override =
+                       reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
+               if (reg->pbc) {
+                       reg->sel_reg_dev_password_id_override =
+                               DEV_PW_PUSHBUTTON;
+                       reg->sel_reg_config_methods_override |=
+                               WPS_CONFIG_PUSHBUTTON;
+               }
+               wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected "
+                          "(pbc=%d)", reg->pbc);
+       } else
+               wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected");
+
+       wps_registrar_sel_reg_union(reg);
+
+       wps_set_ie(reg);
+       wps_cb_set_sel_reg(reg);
+}
+
+
+int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
+                          char *buf, size_t buflen)
+{
+       struct wps_registrar_device *d;
+       int len = 0, ret;
+       char uuid[40];
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+
+       d = wps_device_get(reg, addr);
+       if (d == NULL)
+               return 0;
+       if (uuid_bin2str(d->uuid, uuid, sizeof(uuid)))
+               return 0;
+
+       ret = os_snprintf(buf + len, buflen - len,
+                         "wpsUuid=%s\n"
+                         "wpsPrimaryDeviceType=%s\n"
+                         "wpsDeviceName=%s\n"
+                         "wpsManufacturer=%s\n"
+                         "wpsModelName=%s\n"
+                         "wpsModelNumber=%s\n"
+                         "wpsSerialNumber=%s\n",
+                         uuid,
+                         wps_dev_type_bin2str(d->dev.pri_dev_type, devtype,
+                                              sizeof(devtype)),
+                         d->dev.device_name ? d->dev.device_name : "",
+                         d->dev.manufacturer ? d->dev.manufacturer : "",
+                         d->dev.model_name ? d->dev.model_name : "",
+                         d->dev.model_number ? d->dev.model_number : "",
+                         d->dev.serial_number ? d->dev.serial_number : "");
+       if (ret < 0 || (size_t) ret >= buflen - len)
+               return len;
+       len += ret;
+
+       return len;
+}
diff --git a/src/wps/wps_ufd.c b/src/wps/wps_ufd.c
new file mode 100644 (file)
index 0000000..1a911e1
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * UFD routines for Wi-Fi Protected Setup
+ * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include "wps/wps.h"
+#include "wps/wps_i.h"
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#define UFD_DIR1 "%s\\SMRTNTKY"
+#define UFD_DIR2 UFD_DIR1 "\\WFAWSC"
+#define UFD_FILE UFD_DIR2 "\\%s"
+#else /* CONFIG_NATIVE_WINDOWS */
+#define UFD_DIR1 "%s/SMRTNTKY"
+#define UFD_DIR2 UFD_DIR1 "/WFAWSC"
+#define UFD_FILE UFD_DIR2 "/%s"
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+struct wps_ufd_data {
+       int ufd_fd;
+};
+
+
+static int dev_pwd_e_file_filter(const struct dirent *entry)
+{
+       unsigned int prefix;
+       char ext[5];
+
+       if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2)
+               return 0;
+       if (prefix == 0)
+               return 0;
+       if (os_strcasecmp(ext, "WFA") != 0)
+               return 0;
+
+       return 1;
+}
+
+
+static int wps_get_dev_pwd_e_file_name(char *path, char *file_name)
+{
+       struct dirent **namelist;
+       int i, file_num;
+
+       file_num = scandir(path, &namelist, &dev_pwd_e_file_filter,
+                          alphasort);
+       if (file_num < 0) {
+               wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)",
+                          errno, strerror(errno));
+               return -1;
+       }
+       if (file_num == 0) {
+               wpa_printf(MSG_ERROR, "WPS: OOB file not found");
+               os_free(namelist);
+               return -1;
+       }
+       os_strlcpy(file_name, namelist[0]->d_name, 13);
+       for (i = 0; i < file_num; i++)
+               os_free(namelist[i]);
+       os_free(namelist);
+       return 0;
+}
+
+
+static int get_file_name(struct wps_context *wps, int registrar,
+                        const char *path, char *file_name)
+{
+       switch (wps->oob_conf.oob_method) {
+       case OOB_METHOD_CRED:
+               os_snprintf(file_name, 13, "00000000.WSC");
+               break;
+       case OOB_METHOD_DEV_PWD_E:
+               if (registrar) {
+                       char temp[128];
+                       os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
+                       if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0)
+                               return -1;
+               } else {
+                       u8 *mac_addr = wps->dev.mac_addr;
+
+                       os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA",
+                                   mac_addr[2], mac_addr[3], mac_addr[4],
+                                   mac_addr[5]);
+               }
+               break;
+       case OOB_METHOD_DEV_PWD_R:
+               os_snprintf(file_name, 13, "00000000.WFA");
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int ufd_mkdir(const char *path)
+{
+       if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) {
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory "
+                          "'%s': %d (%s)", path, errno, strerror(errno));
+               return -1;
+       }
+       return 0;
+}
+
+
+static void * init_ufd(struct wps_context *wps,
+                      struct oob_device_data *oob_dev, int registrar)
+{
+       int write_f;
+       char temp[128];
+       char *path = oob_dev->device_path;
+       char filename[13];
+       struct wps_ufd_data *data;
+       int ufd_fd;
+
+       if (path == NULL)
+               return NULL;
+
+       write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ?
+               !registrar : registrar;
+
+       if (get_file_name(wps, registrar, path, filename) < 0) {
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name");
+               return NULL;
+       }
+
+       if (write_f) {
+               os_snprintf(temp, sizeof(temp), UFD_DIR1, path);
+               if (ufd_mkdir(temp))
+                       return NULL;
+               os_snprintf(temp, sizeof(temp), UFD_DIR2, path);
+               if (ufd_mkdir(temp))
+                       return NULL;
+       }
+
+       os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename);
+       if (write_f)
+               ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC,
+                             S_IRUSR | S_IWUSR);
+       else
+               ufd_fd = open(temp, O_RDONLY);
+       if (ufd_fd < 0) {
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s",
+                          temp, strerror(errno));
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->ufd_fd = ufd_fd;
+       return data;
+}
+
+
+static struct wpabuf * read_ufd(void *priv)
+{
+       struct wps_ufd_data *data = priv;
+       struct wpabuf *buf;
+       struct stat s;
+       size_t file_size;
+
+       if (fstat(data->ufd_fd, &s) < 0) {
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size");
+               return NULL;
+       }
+
+       file_size = s.st_size;
+       buf = wpabuf_alloc(file_size);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read "
+                          "buffer");
+               return NULL;
+       }
+
+       if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) !=
+           (int) file_size) {
+               wpabuf_free(buf);
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read");
+               return NULL;
+       }
+       wpabuf_put(buf, file_size);
+       return buf;
+}
+
+
+static int write_ufd(void *priv, struct wpabuf *buf)
+{
+       struct wps_ufd_data *data = priv;
+
+       if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) !=
+           (int) wpabuf_len(buf)) {
+               wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write");
+               return -1;
+       }
+       return 0;
+}
+
+
+static void deinit_ufd(void *priv)
+{
+       struct wps_ufd_data *data = priv;
+       close(data->ufd_fd);
+       os_free(data);
+}
+
+
+struct oob_device_data oob_ufd_device_data = {
+       .device_name    = NULL,
+       .device_path    = NULL,
+       .init_func      = init_ufd,
+       .read_func      = read_ufd,
+       .write_func     = write_ufd,
+       .deinit_func    = deinit_ufd,
+};
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
new file mode 100644 (file)
index 0000000..f99b859
--- /dev/null
@@ -0,0 +1,1093 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See below for more details on licensing and code history.
+ */
+
+/*
+ * This has been greatly stripped down from the original file
+ * (upnp_wps_device.c) by Ted Merrill, Atheros Communications
+ * in order to eliminate use of the bulky libupnp library etc.
+ *
+ * History:
+ * upnp_wps_device.c is/was a shim layer between wps_opt_upnp.c and
+ * the libupnp library.
+ * The layering (by Sony) was well done; only a very minor modification
+ * to API of upnp_wps_device.c was required.
+ * libupnp was found to be undesirable because:
+ * -- It consumed too much code and data space
+ * -- It uses multiple threads, making debugging more difficult
+ *      and possibly reducing reliability.
+ * -- It uses static variables and only supports one instance.
+ * The shim and libupnp are here replaced by special code written
+ * specifically for the needs of hostapd.
+ * Various shortcuts can and are taken to keep the code size small.
+ * Generally, execution time is not as crucial.
+ *
+ * BUGS:
+ * -- UPnP requires that we be able to resolve domain names.
+ * While uncommon, if we have to do it then it will stall the entire
+ * hostapd program, which is bad.
+ * This is because we use the standard linux getaddrinfo() function
+ * which is syncronous.
+ * An asyncronous solution would be to use the free "ares" library.
+ * -- Does not have a robust output buffering scheme.  Uses a single
+ * fixed size output buffer per TCP/HTTP connection, with possible (although
+ * unlikely) possibility of overflow and likely excessive use of RAM.
+ * A better solution would be to write the HTTP output as a buffered stream,
+ * using chunking: (handle header specially, then) generate data with
+ * a printf-like function into a buffer, catching buffer full condition,
+ * then send it out surrounded by http chunking.
+ * -- There is some code that could be separated out into the common
+ * library to be shared with wpa_supplicant.
+ * -- Needs renaming with module prefix to avoid polluting the debugger
+ * namespace and causing possible collisions with other static fncs
+ * and structure declarations when using the debugger.
+ * -- The http error code generation is pretty bogus, hopefully noone cares.
+ *
+ * Author: Ted Merrill, Atheros Communications, based upon earlier work
+ * as explained above and below.
+ *
+ * Copyright:
+ * Copyright 2008 Atheros Communications.
+ *
+ * The original header (of upnp_wps_device.c) reads:
+ *
+ *  Copyright (c) 2006-2007 Sony Corporation. All Rights Reserved.
+ *
+ *  File Name: upnp_wps_device.c
+ *  Description: EAP-WPS UPnP device source
+ *
+ *   Redistribution and use in source and binary forms, with or without
+ *   modification, are permitted provided that the following conditions
+ *   are met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * 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.
+ *     * Neither the name of Sony Corporation 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.
+ *
+ * Portions from Intel libupnp files, e.g. genlib/net/http/httpreadwrite.c
+ * typical header:
+ *
+ * Copyright (c) 2000-2003 Intel Corporation
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * 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.
+ * * Neither name of Intel Corporation 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 INTEL 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.
+*/
+
+/*
+ * Overview of WPS over UPnP:
+ *
+ * UPnP is a protocol that allows devices to discover each other and control
+ * each other. In UPnP terminology, a device is either a "device" (a server
+ * that provides information about itself and allows itself to be controlled)
+ * or a "control point" (a client that controls "devices") or possibly both.
+ * This file implements a UPnP "device".
+ *
+ * For us, we use mostly basic UPnP discovery, but the control part of interest
+ * is WPS carried via UPnP messages. There is quite a bit of basic UPnP
+ * discovery to do before we can get to WPS, however.
+ *
+ * UPnP discovery begins with "devices" send out multicast UDP packets to a
+ * certain fixed multicast IP address and port, and "control points" sending
+ * out other such UDP packets.
+ *
+ * The packets sent by devices are NOTIFY packets (not to be confused with TCP
+ * NOTIFY packets that are used later) and those sent by control points are
+ * M-SEARCH packets. These packets contain a simple HTTP style header. The
+ * packets are sent redundantly to get around packet loss. Devices respond to
+ * M-SEARCH packets with HTTP-like UDP packets containing HTTP/1.1 200 OK
+ * messages, which give similar information as the UDP NOTIFY packets.
+ *
+ * The above UDP packets advertise the (arbitrary) TCP ports that the
+ * respective parties will listen to. The control point can then do a HTTP
+ * SUBSCRIBE (something like an HTTP PUT) after which the device can do a
+ * separate HTTP NOTIFY (also like an HTTP PUT) to do event messaging.
+ *
+ * The control point will also do HTTP GET of the "device file" listed in the
+ * original UDP information from the device (see UPNP_WPS_DEVICE_XML_FILE
+ * data), and based on this will do additional GETs... HTTP POSTs are done to
+ * cause an action.
+ *
+ * Beyond some basic information in HTTP headers, additional information is in
+ * the HTTP bodies, in a format set by the SOAP and XML standards, a markup
+ * language related to HTML used for web pages. This language is intended to
+ * provide the ultimate in self-documentation by providing a universal
+ * namespace based on pseudo-URLs called URIs. Note that although a URI looks
+ * like a URL (a web address), they are never accessed as such but are used
+ * only as identifiers.
+ *
+ * The POST of a GetDeviceInfo gets information similar to what might be
+ * obtained from a probe request or response on Wi-Fi. WPS messages M1-M8
+ * are passed via a POST of a PutMessage; the M1-M8 WPS messages are converted
+ * to a bin64 ascii representation for encapsulation. When proxying messages,
+ * WLANEvent and PutWLANResponse are used.
+ *
+ * This of course glosses over a lot of details.
+ */
+
+#include "includes.h"
+
+#include <assert.h>
+#include <net/if.h>
+#include <netdb.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "base64.h"
+#include "wps.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+/*
+ * UPnP allows a client ("control point") to send a server like us ("device")
+ * a domain name for registration, and we are supposed to resolve it. This is
+ * bad because, using the standard Linux library, we will stall the entire
+ * hostapd waiting for resolution.
+ *
+ * The "correct" solution would be to use an event driven library for domain
+ * name resolution such as "ares". However, this would increase code size
+ * further. Since it is unlikely that we'll actually see such domain names, we
+ * can just refuse to accept them.
+ */
+#define NO_DOMAIN_NAME_RESOLUTION 1  /* 1 to allow only dotted ip addresses */
+
+
+/*
+ * UPnP does not scale well. If we were in a room with thousands of control
+ * points then potentially we could be expected to handle subscriptions for
+ * each of them, which would exhaust our memory. So we must set a limit. In
+ * practice we are unlikely to see more than one or two.
+ */
+#define MAX_SUBSCRIPTIONS 4    /* how many subscribing clients we handle */
+#define MAX_ADDR_PER_SUBSCRIPTION 8
+
+
+/* Write the current date/time per RFC */
+void format_date(struct wpabuf *buf)
+{
+       const char *weekday_str = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat";
+       const char *month_str = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0"
+               "Jul\0Aug\0Sep\0Oct\0Nov\0Dec";
+       struct tm *date;
+       time_t t;
+
+       t = time(NULL);
+       date = gmtime(&t);
+       wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
+                     &weekday_str[date->tm_wday * 4], date->tm_mday,
+                     &month_str[date->tm_mon * 4], date->tm_year + 1900,
+                     date->tm_hour, date->tm_min, date->tm_sec);
+}
+
+
+/***************************************************************************
+ * UUIDs (unique identifiers)
+ *
+ * These are supposed to be unique in all the world.
+ * Sometimes permanent ones are used, sometimes temporary ones
+ * based on random numbers... there are different rules for valid content
+ * of different types.
+ * Each uuid is 16 bytes long.
+ **************************************************************************/
+
+/* uuid_make -- construct a random UUID
+ * The UPnP documents don't seem to offer any guidelines as to which method to
+ * use for constructing UUIDs for subscriptions. Presumably any method from
+ * rfc4122 is good enough; I've chosen random number method.
+ */
+static void uuid_make(u8 uuid[UUID_LEN])
+{
+       os_get_random(uuid, UUID_LEN);
+
+       /* Replace certain bits as specified in rfc4122 or X.667 */
+       uuid[6] &= 0x0f; uuid[6] |= (4 << 4);   /* version 4 == random gen */
+       uuid[8] &= 0x3f; uuid[8] |= 0x80;
+}
+
+
+/*
+ * Subscriber address handling.
+ * Since a subscriber may have an arbitrary number of addresses, we have to
+ * add a bunch of code to handle them.
+ *
+ * Addresses are passed in text, and MAY be domain names instead of the (usual
+ * and expected) dotted IP addresses. Resolving domain names consumes a lot of
+ * resources. Worse, we are currently using the standard Linux getaddrinfo()
+ * which will block the entire program until complete or timeout! The proper
+ * solution would be to use the "ares" library or similar with more state
+ * machine steps etc. or just disable domain name resolution by setting
+ * NO_DOMAIN_NAME_RESOLUTION to 1 at top of this file.
+ */
+
+/* subscr_addr_delete -- delete single unlinked subscriber address
+ * (be sure to unlink first if need be)
+ */
+static void subscr_addr_delete(struct subscr_addr *a)
+{
+       /*
+        * Note: do NOT free domain_and_port or path because they point to
+        * memory within the allocation of "a".
+        */
+       os_free(a);
+}
+
+
+/* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */
+static void subscr_addr_free_all(struct subscription *s)
+{
+       struct subscr_addr *a, *tmp;
+       dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list)
+       {
+               dl_list_del(&a->list);
+               subscr_addr_delete(a);
+       }
+}
+
+
+/* subscr_addr_add_url -- add address(es) for one url to subscription */
+static void subscr_addr_add_url(struct subscription *s, const char *url)
+{
+       int alloc_len;
+       char *scratch_mem = NULL;
+       char *mem;
+       char *domain_and_port;
+       char *delim;
+       char *path;
+       char *domain;
+       int port = 80;  /* port to send to (default is port 80) */
+       struct addrinfo hints;
+       struct addrinfo *result = NULL;
+       struct addrinfo *rp;
+       int rerr;
+       struct subscr_addr *a = NULL;
+
+       /* url MUST begin with http: */
+       if (os_strncasecmp(url, "http://", 7))
+               goto fail;
+       url += 7;
+
+       /* allocate memory for the extra stuff we need */
+       alloc_len = (2 * (os_strlen(url) + 1));
+       scratch_mem = os_zalloc(alloc_len);
+       if (scratch_mem == NULL)
+               goto fail;
+       mem = scratch_mem;
+       strcpy(mem, url);
+       domain_and_port = mem;
+       mem += 1 + os_strlen(mem);
+       delim = os_strchr(domain_and_port, '/');
+       if (delim) {
+               *delim++ = 0;   /* null terminate domain and port */
+               path = delim;
+       } else {
+               path = domain_and_port + os_strlen(domain_and_port);
+       }
+       domain = mem;
+       strcpy(domain, domain_and_port);
+       delim = strchr(domain, ':');
+       if (delim) {
+               *delim++ = 0;   /* null terminate domain */
+               if (isdigit(*delim))
+                       port = atol(delim);
+       }
+
+       /*
+        * getaddrinfo does the right thing with dotted decimal notations, or
+        * will resolve domain names. Resolving domain names will unfortunately
+        * hang the entire program until it is resolved or it times out
+        * internal to getaddrinfo; fortunately we think that the use of actual
+        * domain names (vs. dotted decimal notations) should be uncommon.
+        */
+       os_memset(&hints, 0, sizeof(struct addrinfo));
+       hints.ai_family = AF_INET;      /* IPv4 */
+       hints.ai_socktype = SOCK_STREAM;
+#if NO_DOMAIN_NAME_RESOLUTION
+       /* Suppress domain name resolutions that would halt
+        * the program for periods of time
+        */
+       hints.ai_flags = AI_NUMERICHOST;
+#else
+       /* Allow domain name resolution. */
+       hints.ai_flags = 0;
+#endif
+       hints.ai_protocol = 0;          /* Any protocol? */
+       rerr = getaddrinfo(domain, NULL /* fill in port ourselves */,
+                          &hints, &result);
+       if (rerr) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Resolve error %d (%s) on: %s",
+                          rerr, gai_strerror(rerr), domain);
+               goto fail;
+       }
+       for (rp = result; rp; rp = rp->ai_next) {
+               /* Limit no. of address to avoid denial of service attack */
+               if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) {
+                       wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: "
+                                  "Ignoring excessive addresses");
+                       break;
+               }
+
+               a = os_zalloc(sizeof(*a) + alloc_len);
+               if (a == NULL)
+                       continue;
+               mem = (void *) (a + 1);
+               a->domain_and_port = mem;
+               strcpy(mem, domain_and_port);
+               mem += 1 + strlen(mem);
+               a->path = mem;
+               if (path[0] != '/')
+                       *mem++ = '/';
+               strcpy(mem, path);
+               mem += 1 + strlen(mem);
+               os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr));
+               a->saddr.sin_port = htons(port);
+
+               dl_list_add(&s->addr_list, &a->list);
+               a = NULL;       /* don't free it below */
+       }
+
+fail:
+       if (result)
+               freeaddrinfo(result);
+       os_free(scratch_mem);
+       os_free(a);
+}
+
+
+/* subscr_addr_list_create -- create list from urls in string.
+ *      Each url is enclosed by angle brackets.
+ */
+static void subscr_addr_list_create(struct subscription *s,
+                                   const char *url_list)
+{
+       char *end;
+       for (;;) {
+               while (*url_list == ' ' || *url_list == '\t')
+                       url_list++;
+               if (*url_list != '<')
+                       break;
+               url_list++;
+               end = os_strchr(url_list, '>');
+               if (end == NULL)
+                       break;
+               *end++ = 0;
+               subscr_addr_add_url(s, url_list);
+               url_list = end;
+       }
+}
+
+
+int send_wpabuf(int fd, struct wpabuf *buf)
+{
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
+                  (unsigned long) wpabuf_len(buf));
+       errno = 0;
+       if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
+           (int) wpabuf_len(buf)) {
+               wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
+                          "errno=%d (%s)",
+                          errno, strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void wpabuf_put_property(struct wpabuf *buf, const char *name,
+                               const char *value)
+{
+       wpabuf_put_str(buf, "<e:property>");
+       wpabuf_printf(buf, "<%s>", name);
+       if (value)
+               wpabuf_put_str(buf, value);
+       wpabuf_printf(buf, "</%s>", name);
+       wpabuf_put_str(buf, "</e:property>\n");
+}
+
+
+/**
+ * upnp_wps_device_send_event - Queue event messages for subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function queues the last WLANEvent to be sent for all currently
+ * subscribed UPnP control points. sm->wlanevent must have been set with the
+ * encoded data before calling this function.
+ */
+static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
+{
+       /* Enqueue event message for all subscribers */
+       struct wpabuf *buf; /* holds event message */
+       int buf_size = 0;
+       struct subscription *s, *tmp;
+       /* Actually, utf-8 is the default, but it doesn't hurt to specify it */
+       const char *format_head =
+               "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+               "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+       const char *format_tail = "</e:propertyset>\n";
+
+       if (dl_list_empty(&sm->subscriptions)) {
+               /* optimize */
+               return;
+       }
+
+       /* Determine buffer size needed first */
+       buf_size += os_strlen(format_head);
+       buf_size += 50 + 2 * os_strlen("WLANEvent");
+       if (sm->wlanevent)
+               buf_size += os_strlen(sm->wlanevent);
+       buf_size += os_strlen(format_tail);
+
+       buf = wpabuf_alloc(buf_size);
+       if (buf == NULL)
+               return;
+       wpabuf_put_str(buf, format_head);
+       wpabuf_put_property(buf, "WLANEvent", sm->wlanevent);
+       wpabuf_put_str(buf, format_tail);
+
+       wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s",
+                  (char *) wpabuf_head(buf));
+
+       dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+                             list) {
+               if (event_add(s, buf)) {
+                       wpa_printf(MSG_INFO, "WPS UPnP: Dropping "
+                                  "subscriber due to event backlog");
+                       dl_list_del(&s->list);
+                       subscription_destroy(s);
+               }
+       }
+
+       wpabuf_free(buf);
+}
+
+
+/*
+ * Event subscription (subscriber machines register with us to receive event
+ * messages).
+ * This is the result of an incoming HTTP over TCP SUBSCRIBE request.
+ */
+
+/* subscription_destroy -- destroy an unlinked subscription
+ * Be sure to unlink first if necessary.
+ */
+void subscription_destroy(struct subscription *s)
+{
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s);
+       subscr_addr_free_all(s);
+       event_delete_all(s);
+       upnp_er_remove_notification(s);
+       os_free(s);
+}
+
+
+/* subscription_list_age -- remove expired subscriptions */
+static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now)
+{
+       struct subscription *s, *tmp;
+       dl_list_for_each_safe(s, tmp, &sm->subscriptions,
+                             struct subscription, list) {
+               if (s->timeout_time > now)
+                       break;
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription");
+               dl_list_del(&s->list);
+               subscription_destroy(s);
+       }
+}
+
+
+/* subscription_find -- return existing subscription matching uuid, if any
+ * returns NULL if not found
+ */
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+                                       const u8 uuid[UUID_LEN])
+{
+       struct subscription *s;
+       dl_list_for_each(s, &sm->subscriptions, struct subscription, list) {
+               if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0)
+                       return s; /* Found match */
+       }
+       return NULL;
+}
+
+
+static struct wpabuf * build_fake_wsc_ack(void)
+{
+       struct wpabuf *msg = wpabuf_alloc(100);
+       if (msg == NULL)
+               return NULL;
+       wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP);
+       wpabuf_put_str(msg, "00:00:00:00:00:00");
+       if (wps_build_version(msg) ||
+           wps_build_msg_type(msg, WPS_WSC_ACK)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+       /* Enrollee Nonce */
+       wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE);
+       wpabuf_put_be16(msg, WPS_NONCE_LEN);
+       wpabuf_put(msg, WPS_NONCE_LEN);
+       /* Registrar Nonce */
+       wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
+       wpabuf_put_be16(msg, WPS_NONCE_LEN);
+       wpabuf_put(msg, WPS_NONCE_LEN);
+       return msg;
+}
+
+
+/* subscription_first_event -- send format/queue event that is automatically
+ * sent on a new subscription.
+ */
+static int subscription_first_event(struct subscription *s)
+{
+       /*
+        * Actually, utf-8 is the default, but it doesn't hurt to specify it.
+        *
+        * APStatus is apparently a bit set,
+        * 0x1 = configuration change (but is always set?)
+        * 0x10 = ap is locked
+        *
+        * Per UPnP spec, we send out the last value of each variable, even
+        * for WLANEvent, whatever it was.
+        */
+       char *wlan_event;
+       struct wpabuf *buf;
+       int ap_status = 1;      /* TODO: add 0x10 if access point is locked */
+       const char *head =
+               "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
+               "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
+       const char *tail = "</e:propertyset>\n";
+       char txt[10];
+
+       if (s->sm->wlanevent == NULL) {
+               /*
+                * There has been no events before the subscription. However,
+                * UPnP device architecture specification requires all the
+                * evented variables to be included, so generate a dummy event
+                * for this particular case using a WSC_ACK and all-zeros
+                * nonces. The ER (UPnP control point) will ignore this, but at
+                * least it will learn that WLANEvent variable will be used in
+                * event notifications in the future.
+                */
+               struct wpabuf *msg;
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Use a fake WSC_ACK as the "
+                          "initial WLANEvent");
+               msg = build_fake_wsc_ack();
+               if (msg) {
+                       s->sm->wlanevent = (char *)
+                               base64_encode(wpabuf_head(msg),
+                                             wpabuf_len(msg), NULL);
+                       wpabuf_free(msg);
+               }
+       }
+
+       wlan_event = s->sm->wlanevent;
+       if (wlan_event == NULL || *wlan_event == '\0') {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: WLANEvent not known for "
+                          "initial event message");
+               wlan_event = "";
+       }
+       buf = wpabuf_alloc(500 + os_strlen(wlan_event));
+       if (buf == NULL)
+               return 1;
+
+       wpabuf_put_str(buf, head);
+       wpabuf_put_property(buf, "STAStatus", "1");
+       os_snprintf(txt, sizeof(txt), "%d", ap_status);
+       wpabuf_put_property(buf, "APStatus", txt);
+       if (*wlan_event)
+               wpabuf_put_property(buf, "WLANEvent", wlan_event);
+       wpabuf_put_str(buf, tail);
+
+       if (event_add(s, buf)) {
+               wpabuf_free(buf);
+               return 1;
+       }
+       wpabuf_free(buf);
+
+       return 0;
+}
+
+
+/**
+ * subscription_start - Remember a UPnP control point to send events to.
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @callback_urls: Callback URLs
+ * Returns: %NULL on error, or pointer to new subscription structure.
+ */
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+                                        const char *callback_urls)
+{
+       struct subscription *s;
+       time_t now = time(NULL);
+       time_t expire = now + UPNP_SUBSCRIBE_SEC;
+
+       /* Get rid of expired subscriptions so we have room */
+       subscription_list_age(sm, now);
+
+       /* If too many subscriptions, remove oldest */
+       if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
+               s = dl_list_first(&sm->subscriptions, struct subscription,
+                                 list);
+               wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
+                          "trashing oldest");
+               dl_list_del(&s->list);
+               subscription_destroy(s);
+       }
+
+       s = os_zalloc(sizeof(*s));
+       if (s == NULL)
+               return NULL;
+       dl_list_init(&s->addr_list);
+       dl_list_init(&s->event_queue);
+
+       s->sm = sm;
+       s->timeout_time = expire;
+       uuid_make(s->uuid);
+       subscr_addr_list_create(s, callback_urls);
+       /* Add to end of list, since it has the highest expiration time */
+       dl_list_add_tail(&sm->subscriptions, &s->list);
+       /* Queue up immediate event message (our last event)
+        * as required by UPnP spec.
+        */
+       if (subscription_first_event(s)) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to "
+                          "event backlog");
+               dl_list_del(&s->list);
+               subscription_destroy(s);
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription %p started with %s",
+                  s, callback_urls);
+       /* Schedule sending this */
+       event_send_all_later(sm);
+       return s;
+}
+
+
+/* subscription_renew -- find subscription and reset timeout */
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+                                        const u8 uuid[UUID_LEN])
+{
+       time_t now = time(NULL);
+       time_t expire = now + UPNP_SUBSCRIBE_SEC;
+       struct subscription *s = subscription_find(sm, uuid);
+       if (s == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed");
+       dl_list_del(&s->list);
+       s->timeout_time = expire;
+       /* add back to end of list, since it now has highest expiry */
+       dl_list_add_tail(&sm->subscriptions, &s->list);
+       return s;
+}
+
+
+/**
+ * upnp_wps_device_send_wlan_event - Event notification
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @from_mac_addr: Source (Enrollee) MAC address for the event
+ * @ev_type: Event type
+ * @msg: Event data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Tell external Registrars (UPnP control points) that something happened. In
+ * particular, events include WPS messages from clients that are proxied to
+ * external Registrars.
+ */
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+                                   const u8 from_mac_addr[ETH_ALEN],
+                                   enum upnp_wps_wlanevent_type ev_type,
+                                   const struct wpabuf *msg)
+{
+       int ret = -1;
+       char type[2];
+       const u8 *mac = from_mac_addr;
+       char mac_text[18];
+       u8 *raw = NULL;
+       size_t raw_len;
+       char *val;
+       size_t val_len;
+       int pos = 0;
+
+       if (!sm)
+               goto fail;
+
+       os_snprintf(type, sizeof(type), "%1u", ev_type);
+
+       raw_len = 1 + 17 + (msg ? wpabuf_len(msg) : 0);
+       raw = os_zalloc(raw_len);
+       if (!raw)
+               goto fail;
+
+       *(raw + pos) = (u8) ev_type;
+       pos += 1;
+       os_snprintf(mac_text, sizeof(mac_text), MACSTR, MAC2STR(mac));
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Proxying WLANEvent from %s",
+                  mac_text);
+       os_memcpy(raw + pos, mac_text, 17);
+       pos += 17;
+       if (msg) {
+               os_memcpy(raw + pos, wpabuf_head(msg), wpabuf_len(msg));
+               pos += wpabuf_len(msg);
+       }
+       raw_len = pos;
+
+       val = (char *) base64_encode(raw, raw_len, &val_len);
+       if (val == NULL)
+               goto fail;
+
+       os_free(sm->wlanevent);
+       sm->wlanevent = val;
+       upnp_wps_device_send_event(sm);
+
+       ret = 0;
+
+fail:
+       os_free(raw);
+
+       return ret;
+}
+
+
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+#include <sys/sysctl.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+static int eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+       struct if_msghdr *ifm;
+       struct sockaddr_dl *sdl;
+       u_char *p, *buf;
+       size_t len;
+       int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+       if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+               return -1;
+       if ((buf = os_malloc(len)) == NULL)
+               return -1;
+       if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+               os_free(buf);
+               return -1;
+       }
+       for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+               ifm = (struct if_msghdr *)p;
+               sdl = (struct sockaddr_dl *)(ifm + 1);
+               if (ifm->ifm_type != RTM_IFINFO ||
+                   (ifm->ifm_addrs & RTA_IFP) == 0)
+                       continue;
+               if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+                   os_memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+                       continue;
+               os_memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+               break;
+       }
+       os_free(buf);
+
+       if (p >= buf + len) {
+               errno = ESRCH;
+               return -1;
+       }
+       return 0;
+}
+#endif /* __FreeBSD__ */
+
+
+/**
+ * get_netif_info - Get hw and IP addresses for network device
+ * @net_if: Selected network interface name
+ * @ip_addr: Buffer for returning IP address in network byte order
+ * @ip_addr_text: Buffer for returning a pointer to allocated IP address text
+ * @mac: Buffer for returning MAC address
+ * Returns: 0 on success, -1 on failure
+ */
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+                  u8 mac[ETH_ALEN])
+{
+       struct ifreq req;
+       int sock = -1;
+       struct sockaddr_in *addr;
+       struct in_addr in_addr;
+
+       *ip_addr_text = os_zalloc(16);
+       if (*ip_addr_text == NULL)
+               goto fail;
+
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock < 0)
+               goto fail;
+
+       os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+       if (ioctl(sock, SIOCGIFADDR, &req) < 0) {
+               wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFADDR failed: %d (%s)",
+                          errno, strerror(errno));
+               goto fail;
+       }
+       addr = (void *) &req.ifr_addr;
+       *ip_addr = addr->sin_addr.s_addr;
+       in_addr.s_addr = *ip_addr;
+       os_snprintf(*ip_addr_text, 16, "%s", inet_ntoa(in_addr));
+
+#ifdef __linux__
+       os_strlcpy(req.ifr_name, net_if, sizeof(req.ifr_name));
+       if (ioctl(sock, SIOCGIFHWADDR, &req) < 0) {
+               wpa_printf(MSG_ERROR, "WPS UPnP: SIOCGIFHWADDR failed: "
+                          "%d (%s)", errno, strerror(errno));
+               goto fail;
+       }
+       os_memcpy(mac, req.ifr_addr.sa_data, 6);
+#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+       if (eth_get(net_if, mac) < 0) {
+               wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address");
+               goto fail;
+       }
+#else
+#error MAC address fetch not implemented
+#endif
+
+       close(sock);
+       return 0;
+
+fail:
+       if (sock >= 0)
+               close(sock);
+       os_free(*ip_addr_text);
+       *ip_addr_text = NULL;
+       return -1;
+}
+
+
+static void upnp_wps_free_msearchreply(struct dl_list *head)
+{
+       struct advertisement_state_machine *a, *tmp;
+       dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine,
+                             list)
+               msearchreply_state_machine_stop(a);
+}
+
+
+static void upnp_wps_free_subscriptions(struct dl_list *head)
+{
+       struct subscription *s, *tmp;
+       dl_list_for_each_safe(s, tmp, head, struct subscription, list) {
+               dl_list_del(&s->list);
+               subscription_destroy(s);
+       }
+}
+
+
+/**
+ * upnp_wps_device_stop - Stop WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
+{
+       if (!sm || !sm->started)
+               return;
+
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
+       web_listener_stop(sm);
+       upnp_wps_free_msearchreply(&sm->msearch_replies);
+       upnp_wps_free_subscriptions(&sm->subscriptions);
+
+       advertisement_state_machine_stop(sm, 1);
+
+       event_send_stop_all(sm);
+       os_free(sm->wlanevent);
+       sm->wlanevent = NULL;
+       os_free(sm->ip_addr_text);
+       sm->ip_addr_text = NULL;
+       if (sm->multicast_sd >= 0)
+               close(sm->multicast_sd);
+       sm->multicast_sd = -1;
+       ssdp_listener_stop(sm);
+
+       sm->started = 0;
+}
+
+
+/**
+ * upnp_wps_device_start - Start WPS UPnP operations on an interface
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ */
+int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if)
+{
+       if (!sm || !net_if)
+               return -1;
+
+       if (sm->started)
+               upnp_wps_device_stop(sm);
+
+       sm->multicast_sd = -1;
+       sm->ssdp_sd = -1;
+       sm->started = 1;
+       sm->advertise_count = 0;
+
+       /* Fix up linux multicast handling */
+       if (add_ssdp_network(net_if))
+               goto fail;
+
+       /* Determine which IP and mac address we're using */
+       if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text,
+                          sm->mac_addr)) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address "
+                          "for %s. Does it have IP address?", net_if);
+               goto fail;
+       }
+
+       /* Listen for incoming TCP connections so that others
+        * can fetch our "xml files" from us.
+        */
+       if (web_listener_start(sm))
+               goto fail;
+
+       /* Set up for receiving discovery (UDP) packets */
+       if (ssdp_listener_start(sm))
+               goto fail;
+
+       /* Set up for sending multicast */
+       if (ssdp_open_multicast(sm) < 0)
+               goto fail;
+
+       /*
+        * Broadcast NOTIFY messages to let the world know we exist.
+        * This is done via a state machine since the messages should not be
+        * all sent out at once.
+        */
+       if (advertisement_state_machine_start(sm))
+               goto fail;
+
+       return 0;
+
+fail:
+       upnp_wps_device_stop(sm);
+       return -1;
+}
+
+
+/**
+ * upnp_wps_device_deinit - Deinitialize WPS UPnP
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ */
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm)
+{
+       if (!sm)
+               return;
+
+       upnp_wps_device_stop(sm);
+
+       if (sm->peer.wps)
+               wps_deinit(sm->peer.wps);
+       os_free(sm->root_dir);
+       os_free(sm->desc_url);
+       os_free(sm->ctx->ap_pin);
+       os_free(sm->ctx);
+       os_free(sm);
+}
+
+
+/**
+ * upnp_wps_device_init - Initialize WPS UPnP
+ * @ctx: callback table; we must eventually free it
+ * @wps: Pointer to longterm WPS context
+ * @priv: External context data that will be used in callbacks
+ * Returns: WPS UPnP state or %NULL on failure
+ */
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+                    void *priv)
+{
+       struct upnp_wps_device_sm *sm;
+
+       sm = os_zalloc(sizeof(*sm));
+       if (!sm) {
+               wpa_printf(MSG_ERROR, "WPS UPnP: upnp_wps_device_init failed");
+               return NULL;
+       }
+
+       sm->ctx = ctx;
+       sm->wps = wps;
+       sm->priv = priv;
+       dl_list_init(&sm->msearch_replies);
+       dl_list_init(&sm->subscriptions);
+
+       return sm;
+}
+
+
+/**
+ * upnp_wps_subscribers - Check whether there are any event subscribers
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 if no subscribers, 1 if subscribers
+ */
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm)
+{
+       return !dl_list_empty(&sm->subscriptions);
+}
+
+
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin)
+{
+       if (sm == NULL)
+               return 0;
+
+       os_free(sm->ctx->ap_pin);
+       if (ap_pin) {
+               sm->ctx->ap_pin = os_strdup(ap_pin);
+               if (sm->ctx->ap_pin == NULL)
+                       return -1;
+       } else
+               sm->ctx->ap_pin = NULL;
+
+       return 0;
+}
diff --git a/src/wps/wps_upnp.h b/src/wps/wps_upnp.h
new file mode 100644 (file)
index 0000000..06bc31f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * UPnP WPS Device
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_H
+#define WPS_UPNP_H
+
+struct upnp_wps_device_sm;
+struct wps_context;
+struct wps_data;
+
+struct upnp_wps_peer {
+       struct wps_data *wps;
+};
+
+enum upnp_wps_wlanevent_type {
+       UPNP_WPS_WLANEVENT_TYPE_PROBE = 1,
+       UPNP_WPS_WLANEVENT_TYPE_EAP = 2
+};
+
+struct upnp_wps_device_ctx {
+       int (*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);
+
+       char *ap_pin;
+};
+
+struct upnp_wps_device_sm *
+upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps,
+                    void *priv);
+void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm);
+
+int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if);
+void upnp_wps_device_stop(struct upnp_wps_device_sm *sm);
+
+int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm,
+                                   const u8 from_mac_addr[ETH_ALEN],
+                                   enum upnp_wps_wlanevent_type ev_type,
+                                   const struct wpabuf *msg);
+int upnp_wps_subscribers(struct upnp_wps_device_sm *sm);
+int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin);
+
+#endif /* WPS_UPNP_H */
diff --git a/src/wps/wps_upnp_ap.c b/src/wps/wps_upnp_ap.c
new file mode 100644 (file)
index 0000000..93746da
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Wi-Fi Protected Setup - UPnP AP functionality
+ * Copyright (c) 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 "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+
+static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct subscription *s = eloop_ctx;
+       wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out");
+       s->selected_registrar = 0;
+       wps_registrar_selected_registrar_changed(s->reg);
+}
+
+
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+                                  struct subscription *s,
+                                  const struct wpabuf *msg)
+{
+       struct wps_parse_attr attr;
+
+       wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes",
+                       msg);
+
+       if (wps_parse_msg(msg, &attr) < 0)
+               return -1;
+       if (!wps_version_supported(attr.version)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar "
+                          "version 0x%x", attr.version ? *attr.version : 0);
+               return -1;
+       }
+
+       s->reg = reg;
+       eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
+
+       if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) {
+               wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable "
+                          "Selected Registrar");
+               s->selected_registrar = 0;
+       } else {
+               s->selected_registrar = 1;
+               s->dev_password_id = attr.dev_password_id ?
+                       WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT;
+               s->config_methods = attr.sel_reg_config_methods ?
+                       WPA_GET_BE16(attr.sel_reg_config_methods) : -1;
+               eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
+                                      upnp_er_set_selected_timeout, s, NULL);
+       }
+
+       wps_registrar_selected_registrar_changed(reg);
+
+       return 0;
+}
+
+
+void upnp_er_remove_notification(struct subscription *s)
+{
+       s->selected_registrar = 0;
+       eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL);
+       if (s->reg)
+               wps_registrar_selected_registrar_changed(s->reg);
+}
diff --git a/src/wps/wps_upnp_event.c b/src/wps/wps_upnp_event.c
new file mode 100644 (file)
index 0000000..ae5efdb
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * UPnP WPS Device - Event processing
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "uuid.h"
+#include "http_client.h"
+#include "wps_defs.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+/*
+ * Event message generation (to subscribers)
+ *
+ * We make a separate copy for each message for each subscriber. This memory
+ * wasted could be limited (adding code complexity) by sharing copies, keeping
+ * a usage count and freeing when zero.
+ *
+ * Sending a message requires using a HTTP over TCP NOTIFY
+ * (like a PUT) which requires a number of states..
+ */
+
+#define MAX_EVENTS_QUEUED 20   /* How far behind queued events */
+#define EVENT_TIMEOUT_SEC 30   /* Drop sending event after timeout */
+
+/* How long to wait before sending event */
+#define EVENT_DELAY_SECONDS 0
+#define EVENT_DELAY_MSEC 0
+
+/*
+ * Event information that we send to each subscriber is remembered in this
+ * struct. The event cannot be sent by simple UDP; it has to be sent by a HTTP
+ * over TCP transaction which requires various states.. It may also need to be
+ * retried at a different address (if more than one is available).
+ *
+ * TODO: As an optimization we could share data between subscribers.
+ */
+struct wps_event_ {
+       struct dl_list list;
+       struct subscription *s;         /* parent */
+       unsigned subscriber_sequence;   /* which event for this subscription*/
+       unsigned int retry;             /* which retry */
+       struct subscr_addr *addr;       /* address to connect to */
+       struct wpabuf *data;            /* event data to send */
+       struct http_client *http_event;
+};
+
+
+/* event_clean -- clean sockets etc. of event
+ * Leaves data, retry count etc. alone.
+ */
+static void event_clean(struct wps_event_ *e)
+{
+       if (e->s->current_event == e)
+               e->s->current_event = NULL;
+       http_client_free(e->http_event);
+       e->http_event = NULL;
+}
+
+
+/* event_delete -- delete single unqueued event
+ * (be sure to dequeue first if need be)
+ */
+static void event_delete(struct wps_event_ *e)
+{
+       event_clean(e);
+       wpabuf_free(e->data);
+       os_free(e);
+}
+
+
+/* event_dequeue -- get next event from the queue
+ * Returns NULL if empty.
+ */
+static struct wps_event_ *event_dequeue(struct subscription *s)
+{
+       struct wps_event_ *e;
+       e = dl_list_first(&s->event_queue, struct wps_event_, list);
+       if (e)
+               dl_list_del(&e->list);
+       return e;
+}
+
+
+/* event_delete_all -- delete entire event queue and current event */
+void event_delete_all(struct subscription *s)
+{
+       struct wps_event_ *e;
+       while ((e = event_dequeue(s)) != NULL)
+               event_delete(e);
+       if (s->current_event) {
+               event_delete(s->current_event);
+               /* will set: s->current_event = NULL;  */
+       }
+}
+
+
+/**
+ * event_retry - Called when we had a failure delivering event msg
+ * @e: Event
+ * @do_next_address: skip address e.g. on connect fail
+ */
+static void event_retry(struct wps_event_ *e, int do_next_address)
+{
+       struct subscription *s = e->s;
+       struct upnp_wps_device_sm *sm = s->sm;
+
+       event_clean(e);
+       /* will set: s->current_event = NULL; */
+
+       if (do_next_address)
+               e->retry++;
+       if (e->retry >= dl_list_len(&s->addr_list)) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event "
+                          "for %s", e->addr->domain_and_port);
+               return;
+       }
+       dl_list_add(&s->event_queue, &e->list);
+       event_send_all_later(sm);
+}
+
+
+static struct wpabuf * event_build_message(struct wps_event_ *e)
+{
+       struct wpabuf *buf;
+       char *b;
+
+       buf = wpabuf_alloc(1000 + wpabuf_len(e->data));
+       if (buf == NULL)
+               return NULL;
+       wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path);
+       wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+       wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port);
+       wpabuf_put_str(buf, "CONTENT-TYPE: text/xml; charset=\"utf-8\"\r\n"
+                      "NT: upnp:event\r\n"
+                      "NTS: upnp:propchange\r\n");
+       wpabuf_put_str(buf, "SID: uuid:");
+       b = wpabuf_put(buf, 0);
+       uuid_bin2str(e->s->uuid, b, 80);
+       wpabuf_put(buf, os_strlen(b));
+       wpabuf_put_str(buf, "\r\n");
+       wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence);
+       wpabuf_printf(buf, "CONTENT-LENGTH: %d\r\n",
+                     (int) wpabuf_len(e->data));
+       wpabuf_put_str(buf, "\r\n"); /* terminating empty line */
+       wpabuf_put_buf(buf, e->data);
+       return buf;
+}
+
+
+static void event_http_cb(void *ctx, struct http_client *c,
+                         enum http_client_event event)
+{
+       struct wps_event_ *e = ctx;
+       struct subscription *s = e->s;
+
+       switch (event) {
+       case HTTP_CLIENT_OK:
+               wpa_printf(MSG_DEBUG,
+                          "WPS UPnP: Got event reply OK from "
+                          "%s", e->addr->domain_and_port);
+               event_delete(e);
+
+               /* Schedule sending more if there is more to send */
+               if (!dl_list_empty(&s->event_queue))
+                       event_send_all_later(s->sm);
+               break;
+       case HTTP_CLIENT_FAILED:
+       case HTTP_CLIENT_INVALID_REPLY:
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s",
+                          e->addr->domain_and_port);
+
+               /*
+                * If other side doesn't like what we say, forget about them.
+                * (There is no way to tell other side that we are dropping
+                * them...).
+                * Alternately, we could just do event_delete(e)
+                */
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to "
+                          "errors");
+               dl_list_del(&s->list);
+               subscription_destroy(s);
+               break;
+       case HTTP_CLIENT_TIMEOUT:
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout");
+               event_retry(e, 1);
+       }
+}
+
+
+/* event_send_start -- prepare to send a event message to subscriber
+ *
+ * This gets complicated because:
+ * -- The message is sent via TCP and we have to keep the stream open
+ *      for 30 seconds to get a response... then close it.
+ * -- But we might have other event happen in the meantime...
+ *      we have to queue them, if we lose them then the subscriber will
+ *      be forced to unsubscribe and subscribe again.
+ * -- If multiple URLs are provided then we are supposed to try successive
+ *      ones after 30 second timeout.
+ * -- The URLs might use domain names instead of dotted decimal addresses,
+ *      and resolution of those may cause unwanted sleeping.
+ * -- Doing the initial TCP connect can take a while, so we have to come
+ *      back after connection and then send the data.
+ *
+ * Returns nonzero on error;
+ *
+ * Prerequisite: No current event send (s->current_event == NULL)
+ *      and non-empty queue.
+ */
+static int event_send_start(struct subscription *s)
+{
+       struct wps_event_ *e;
+       unsigned int itry;
+       struct wpabuf *buf;
+
+       /*
+        * Assume we are called ONLY with no current event and ONLY with
+        * nonempty event queue and ONLY with at least one address to send to.
+        */
+       assert(!dl_list_empty(&s->addr_list));
+       assert(s->current_event == NULL);
+       assert(!dl_list_empty(&s->event_queue));
+
+       s->current_event = e = event_dequeue(s);
+
+       /* Use address according to number of retries */
+       itry = 0;
+       dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list)
+               if (itry++ == e->retry)
+                       break;
+       if (itry < e->retry)
+               return -1;
+
+       buf = event_build_message(e);
+       if (buf == NULL) {
+               event_retry(e, 0);
+               return -1;
+       }
+
+       e->http_event = http_client_addr(&e->addr->saddr, buf, 0,
+                                        event_http_cb, e);
+       if (e->http_event == NULL) {
+               wpabuf_free(buf);
+               event_retry(e, 0);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/* event_send_all_later_handler -- actually send events as needed */
+static void event_send_all_later_handler(void *eloop_data, void *user_ctx)
+{
+       struct upnp_wps_device_sm *sm = user_ctx;
+       struct subscription *s, *tmp;
+       int nerrors = 0;
+
+       sm->event_send_all_queued = 0;
+       dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription,
+                             list) {
+               if (dl_list_empty(&s->addr_list)) {
+                       /* if we've given up on all addresses */
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: Removing "
+                                  "subscription with no addresses");
+                       dl_list_del(&s->list);
+                       subscription_destroy(s);
+               } else {
+                       if (s->current_event == NULL /* not busy */ &&
+                           !dl_list_empty(&s->event_queue) /* more to do */) {
+                               if (event_send_start(s))
+                                       nerrors++;
+                       }
+               }
+       }
+
+       if (nerrors) {
+               /* Try again later */
+               event_send_all_later(sm);
+       }
+}
+
+
+/* event_send_all_later -- schedule sending events to all subscribers
+ * that need it.
+ * This avoids two problems:
+ * -- After getting a subscription, we should not send the first event
+ *      until after our reply is fully queued to be sent back,
+ * -- Possible stack depth or infinite recursion issues.
+ */
+void event_send_all_later(struct upnp_wps_device_sm *sm)
+{
+       /*
+        * The exact time in the future isn't too important. Waiting a bit
+        * might let us do several together.
+        */
+       if (sm->event_send_all_queued)
+               return;
+       sm->event_send_all_queued = 1;
+       eloop_register_timeout(EVENT_DELAY_SECONDS, EVENT_DELAY_MSEC,
+                              event_send_all_later_handler, NULL, sm);
+}
+
+
+/* event_send_stop_all -- cleanup */
+void event_send_stop_all(struct upnp_wps_device_sm *sm)
+{
+       if (sm->event_send_all_queued)
+               eloop_cancel_timeout(event_send_all_later_handler, NULL, sm);
+       sm->event_send_all_queued = 0;
+}
+
+
+/**
+ * event_add - Add a new event to a queue
+ * @s: Subscription
+ * @data: Event data (is copied; caller retains ownership)
+ * Returns: 0 on success, 1 on error
+ */
+int event_add(struct subscription *s, const struct wpabuf *data)
+{
+       struct wps_event_ *e;
+
+       if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for "
+                          "subscriber");
+               return 1;
+       }
+
+       e = os_zalloc(sizeof(*e));
+       if (e == NULL)
+               return 1;
+       dl_list_init(&e->list);
+       e->s = s;
+       e->data = wpabuf_dup(data);
+       if (e->data == NULL) {
+               os_free(e);
+               return 1;
+       }
+       e->subscriber_sequence = s->next_subscriber_sequence++;
+       if (s->next_subscriber_sequence == 0)
+               s->next_subscriber_sequence++;
+       dl_list_add_tail(&s->event_queue, &e->list);
+       event_send_all_later(s->sm);
+       return 0;
+}
diff --git a/src/wps/wps_upnp_i.h b/src/wps/wps_upnp_i.h
new file mode 100644 (file)
index 0000000..b31875a
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * UPnP for WPS / internal definitions
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#ifndef WPS_UPNP_I_H
+#define WPS_UPNP_I_H
+
+#include "utils/list.h"
+#include "http.h"
+
+#define UPNP_MULTICAST_ADDRESS  "239.255.255.250" /* for UPnP multicasting */
+#define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */
+
+/* min subscribe time per UPnP standard */
+#define UPNP_SUBSCRIBE_SEC_MIN 1800
+/* subscribe time we use */
+#define UPNP_SUBSCRIBE_SEC (UPNP_SUBSCRIBE_SEC_MIN + 1)
+
+/* "filenames" used in URLs that we service via our "web server": */
+#define UPNP_WPS_DEVICE_XML_FILE "wps_device.xml"
+#define UPNP_WPS_SCPD_XML_FILE   "wps_scpd.xml"
+#define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control"
+#define UPNP_WPS_DEVICE_EVENT_FILE "wps_event"
+
+#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */
+
+
+struct upnp_wps_device_sm;
+struct wps_registrar;
+
+
+enum advertisement_type_enum {
+       ADVERTISE_UP = 0,
+       ADVERTISE_DOWN = 1,
+       MSEARCH_REPLY = 2
+};
+
+/*
+ * Advertisements are broadcast via UDP NOTIFYs, and are also the essence of
+ * the reply to UDP M-SEARCH requests. This struct handles both cases.
+ *
+ * A state machine is needed because a number of variant forms must be sent in
+ * separate packets and spread out in time to avoid congestion.
+ */
+struct advertisement_state_machine {
+       struct dl_list list;
+       enum advertisement_type_enum type;
+       int state;
+       int nerrors;
+       struct sockaddr_in client; /* for M-SEARCH replies */
+};
+
+
+/*
+ * An address of a subscriber (who may have multiple addresses). We are
+ * supposed to send (via TCP) updates to each subscriber, trying each address
+ * for a subscriber until we find one that seems to work.
+ */
+struct subscr_addr {
+       struct dl_list list;
+       char *domain_and_port; /* domain and port part of url */
+       char *path; /* "filepath" part of url (from "mem") */
+       struct sockaddr_in saddr; /* address for doing connect */
+};
+
+
+/*
+ * Subscribers to our events are recorded in this struct. This includes a max
+ * of one outgoing connection (sending an "event message") per subscriber. We
+ * also have to age out subscribers unless they renew.
+ */
+struct subscription {
+       struct dl_list list;
+       struct upnp_wps_device_sm *sm; /* parent */
+       time_t timeout_time; /* when to age out the subscription */
+       unsigned next_subscriber_sequence; /* number our messages */
+       /*
+        * This uuid identifies the subscription and is randomly generated by
+        * us and given to the subscriber when the subscription is accepted;
+        * and is then included with each event sent to the subscriber.
+        */
+       u8 uuid[UUID_LEN];
+       /* Linked list of address alternatives (rotate through on failure) */
+       struct dl_list addr_list;
+       struct dl_list event_queue; /* Queued event messages. */
+       struct wps_event_ *current_event; /* non-NULL if being sent (not in q)
+                                          */
+
+       /* Information from SetSelectedRegistrar action */
+       u8 selected_registrar;
+       u16 dev_password_id;
+       u16 config_methods;
+       struct wps_registrar *reg;
+};
+
+
+/*
+ * Our instance data corresponding to one WiFi network interface
+ * (multiple might share the same wired network interface!).
+ *
+ * This is known as an opaque struct declaration to users of the WPS UPnP code.
+ */
+struct upnp_wps_device_sm {
+       struct upnp_wps_device_ctx *ctx; /* callback table */
+       struct wps_context *wps;
+       void *priv;
+       char *root_dir;
+       char *desc_url;
+       int started; /* nonzero if we are active */
+       u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */
+       char *ip_addr_text; /* IP address of network i.f. we use */
+       unsigned ip_addr; /* IP address of network i.f. we use (host order) */
+       int multicast_sd; /* send multicast messages over this socket */
+       int ssdp_sd; /* receive discovery UPD packets on socket */
+       int ssdp_sd_registered; /* nonzero if we must unregister */
+       unsigned advertise_count; /* how many advertisements done */
+       struct advertisement_state_machine advertisement;
+       struct dl_list msearch_replies;
+       int web_port; /* our port that others get xml files from */
+       struct http_server *web_srv;
+       /* Note: subscriptions are kept in expiry order */
+       struct dl_list subscriptions;
+       int event_send_all_queued; /* if we are scheduled to send events soon
+                                   */
+
+       char *wlanevent; /* the last WLANEvent data */
+
+       /* FIX: maintain separate structures for each UPnP peer */
+       struct upnp_wps_peer peer;
+};
+
+/* wps_upnp.c */
+void format_date(struct wpabuf *buf);
+struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
+                                        const char *callback_urls);
+struct subscription * subscription_renew(struct upnp_wps_device_sm *sm,
+                                        const u8 uuid[UUID_LEN]);
+void subscription_destroy(struct subscription *s);
+struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
+                                       const u8 uuid[UUID_LEN]);
+int send_wpabuf(int fd, struct wpabuf *buf);
+int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
+                  u8 mac[ETH_ALEN]);
+
+/* wps_upnp_ssdp.c */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a);
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm);
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+                                     int send_byebye);
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm);
+int ssdp_listener_start(struct upnp_wps_device_sm *sm);
+int ssdp_listener_open(void);
+int add_ssdp_network(const char *net_if);
+int ssdp_open_multicast_sock(u32 ip_addr);
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_web.c */
+int web_listener_start(struct upnp_wps_device_sm *sm);
+void web_listener_stop(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_event.c */
+int event_add(struct subscription *s, const struct wpabuf *data);
+void event_delete_all(struct subscription *s);
+void event_send_all_later(struct upnp_wps_device_sm *sm);
+void event_send_stop_all(struct upnp_wps_device_sm *sm);
+
+/* wps_upnp_ap.c */
+int upnp_er_set_selected_registrar(struct wps_registrar *reg,
+                                  struct subscription *s,
+                                  const struct wpabuf *msg);
+void upnp_er_remove_notification(struct subscription *s);
+
+#endif /* WPS_UPNP_I_H */
diff --git a/src/wps/wps_upnp_ssdp.c b/src/wps/wps_upnp_ssdp.c
new file mode 100644 (file)
index 0000000..8505d05
--- /dev/null
@@ -0,0 +1,924 @@
+/*
+ * UPnP SSDP for WPS
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/route.h>
+
+#include "common.h"
+#include "uuid.h"
+#include "eloop.h"
+#include "wps.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+
+#define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */
+#define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */
+#define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */
+#define MAX_MSEARCH 20          /* max simultaneous M-SEARCH replies ongoing */
+#define SSDP_TARGET  "239.0.0.0"
+#define SSDP_NETMASK "255.0.0.0"
+
+
+/* Check tokens for equality, where tokens consist of letters, digits,
+ * underscore and hyphen, and are matched case insensitive.
+ */
+static int token_eq(const char *s1, const char *s2)
+{
+       int c1;
+       int c2;
+       int end1 = 0;
+       int end2 = 0;
+       for (;;) {
+               c1 = *s1++;
+               c2 = *s2++;
+               if (isalpha(c1) && isupper(c1))
+                       c1 = tolower(c1);
+               if (isalpha(c2) && isupper(c2))
+                       c2 = tolower(c2);
+               end1 = !(isalnum(c1) || c1 == '_' || c1 == '-');
+               end2 = !(isalnum(c2) || c2 == '_' || c2 == '-');
+               if (end1 || end2 || c1 != c2)
+                       break;
+       }
+       return end1 && end2; /* reached end of both words? */
+}
+
+
+/* Return length of token (see above for definition of token) */
+static int token_length(const char *s)
+{
+       const char *begin = s;
+       for (;; s++) {
+               int c = *s;
+               int end = !(isalnum(c) || c == '_' || c == '-');
+               if (end)
+                       break;
+       }
+       return s - begin;
+}
+
+
+/* return length of interword separation.
+ * This accepts only spaces/tabs and thus will not traverse a line
+ * or buffer ending.
+ */
+static int word_separation_length(const char *s)
+{
+       const char *begin = s;
+       for (;; s++) {
+               int c = *s;
+               if (c == ' ' || c == '\t')
+                       continue;
+               break;
+       }
+       return s - begin;
+}
+
+
+/* No. of chars through (including) end of line */
+static int line_length(const char *l)
+{
+       const char *lp = l;
+       while (*lp && *lp != '\n')
+               lp++;
+       if (*lp == '\n')
+               lp++;
+       return lp - l;
+}
+
+
+/* No. of chars excluding trailing whitespace */
+static int line_length_stripped(const char *l)
+{
+       const char *lp = l + line_length(l);
+       while (lp > l && !isgraph(lp[-1]))
+               lp--;
+       return lp - l;
+}
+
+
+static int str_starts(const char *str, const char *start)
+{
+       return os_strncmp(str, start, os_strlen(start)) == 0;
+}
+
+
+/***************************************************************************
+ * Advertisements.
+ * These are multicast to the world to tell them we are here.
+ * The individual packets are spread out in time to limit loss,
+ * and then after a much longer period of time the whole sequence
+ * is repeated again (for NOTIFYs only).
+ **************************************************************************/
+
+/**
+ * next_advertisement - Build next message and advance the state machine
+ * @a: Advertisement state
+ * @islast: Buffer for indicating whether this is the last message (= 1)
+ * Returns: The new message (caller is responsible for freeing this)
+ *
+ * Note: next_advertisement is shared code with msearchreply_* functions
+ */
+static struct wpabuf *
+next_advertisement(struct upnp_wps_device_sm *sm,
+                  struct advertisement_state_machine *a, int *islast)
+{
+       struct wpabuf *msg;
+       char *NTString = "";
+       char uuid_string[80];
+
+       *islast = 0;
+       uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+       msg = wpabuf_alloc(800); /* more than big enough */
+       if (msg == NULL)
+               goto fail;
+       switch (a->type) {
+       case ADVERTISE_UP:
+       case ADVERTISE_DOWN:
+               NTString = "NT";
+               wpabuf_put_str(msg, "NOTIFY * HTTP/1.1\r\n");
+               wpabuf_printf(msg, "HOST: %s:%d\r\n",
+                             UPNP_MULTICAST_ADDRESS, UPNP_MULTICAST_PORT);
+               wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+                             UPNP_CACHE_SEC);
+               wpabuf_printf(msg, "NTS: %s\r\n",
+                             (a->type == ADVERTISE_UP ?
+                              "ssdp:alive" : "ssdp:byebye"));
+               break;
+       case MSEARCH_REPLY:
+               NTString = "ST";
+               wpabuf_put_str(msg, "HTTP/1.1 200 OK\r\n");
+               wpabuf_printf(msg, "CACHE-CONTROL: max-age=%d\r\n",
+                             UPNP_CACHE_SEC);
+
+               wpabuf_put_str(msg, "DATE: ");
+               format_date(msg);
+               wpabuf_put_str(msg, "\r\n");
+
+               wpabuf_put_str(msg, "EXT:\r\n");
+               break;
+       }
+
+       if (a->type != ADVERTISE_DOWN) {
+               /* Where others may get our XML files from */
+               wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n",
+                             sm->ip_addr_text, sm->web_port,
+                             UPNP_WPS_DEVICE_XML_FILE);
+       }
+
+       /* The SERVER line has three comma-separated fields:
+        *      operating system / version
+        *      upnp version
+        *      software package / version
+        * However, only the UPnP version is really required, the
+        * others can be place holders... for security reasons
+        * it is better to NOT provide extra information.
+        */
+       wpabuf_put_str(msg, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n");
+
+       switch (a->state / UPNP_ADVERTISE_REPEAT) {
+       case 0:
+               wpabuf_printf(msg, "%s: upnp:rootdevice\r\n", NTString);
+               wpabuf_printf(msg, "USN: uuid:%s::upnp:rootdevice\r\n",
+                             uuid_string);
+               break;
+       case 1:
+               wpabuf_printf(msg, "%s: uuid:%s\r\n", NTString, uuid_string);
+               wpabuf_printf(msg, "USN: uuid:%s\r\n", uuid_string);
+               break;
+       case 2:
+               wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:device:"
+                             "WFADevice:1\r\n", NTString);
+               wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+                             "org:device:WFADevice:1\r\n", uuid_string);
+               break;
+       case 3:
+               wpabuf_printf(msg, "%s: urn:schemas-wifialliance-org:service:"
+                             "WFAWLANConfig:1\r\n", NTString);
+               wpabuf_printf(msg, "USN: uuid:%s::urn:schemas-wifialliance-"
+                             "org:service:WFAWLANConfig:1\r\n", uuid_string);
+               break;
+       }
+       wpabuf_put_str(msg, "\r\n");
+
+       if (a->state + 1 >= 4 * UPNP_ADVERTISE_REPEAT)
+               *islast = 1;
+
+       return msg;
+
+fail:
+       wpabuf_free(msg);
+       return NULL;
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+                                               void *user_ctx);
+
+
+/**
+ * advertisement_state_machine_stop - Stop SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @send_byebye: Send byebye advertisement messages immediately
+ */
+void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm,
+                                     int send_byebye)
+{
+       struct advertisement_state_machine *a = &sm->advertisement;
+       int islast = 0;
+       struct wpabuf *msg;
+       struct sockaddr_in dest;
+
+       eloop_cancel_timeout(advertisement_state_machine_handler, NULL, sm);
+       if (!send_byebye || sm->multicast_sd < 0)
+               return;
+
+       a->type = ADVERTISE_DOWN;
+       a->state = 0;
+
+       os_memset(&dest, 0, sizeof(dest));
+       dest.sin_family = AF_INET;
+       dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+       dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+       while (!islast) {
+               msg = next_advertisement(sm, a, &islast);
+               if (msg == NULL)
+                       break;
+               if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg),
+                          0, (struct sockaddr *) &dest, sizeof(dest)) < 0) {
+                       wpa_printf(MSG_INFO, "WPS UPnP: Advertisement sendto "
+                                  "failed: %d (%s)", errno, strerror(errno));
+               }
+               wpabuf_free(msg);
+               a->state++;
+       }
+}
+
+
+static void advertisement_state_machine_handler(void *eloop_data,
+                                               void *user_ctx)
+{
+       struct upnp_wps_device_sm *sm = user_ctx;
+       struct advertisement_state_machine *a = &sm->advertisement;
+       struct wpabuf *msg;
+       int next_timeout_msec = 100;
+       int next_timeout_sec = 0;
+       struct sockaddr_in dest;
+       int islast = 0;
+
+       /*
+        * Each is sent twice (in case lost) w/ 100 msec delay between;
+        * spec says no more than 3 times.
+        * One pair for rootdevice, one pair for uuid, and a pair each for
+        * each of the two urns.
+        * The entire sequence must be repeated before cache control timeout
+        * (which  is min  1800 seconds),
+        * recommend random portion of half of the advertised cache control age
+        * to ensure against loss... perhaps 1800/4 + rand*1800/4 ?
+        * Delay random interval < 100 msec prior to initial sending.
+        * TTL of 4
+        */
+
+       wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state);
+       msg = next_advertisement(sm, a, &islast);
+       if (msg == NULL)
+               return;
+
+       os_memset(&dest, 0, sizeof(dest));
+       dest.sin_family = AF_INET;
+       dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+       dest.sin_port = htons(UPNP_MULTICAST_PORT);
+
+       if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+                  (struct sockaddr *) &dest, sizeof(dest)) == -1) {
+               wpa_printf(MSG_ERROR, "WPS UPnP: Advertisement sendto failed:"
+                          "%d (%s)", errno, strerror(errno));
+               next_timeout_msec = 0;
+               next_timeout_sec = 10; /* ... later */
+       } else if (islast) {
+               a->state = 0; /* wrap around */
+               if (a->type == ADVERTISE_DOWN) {
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_DOWN->UP");
+                       a->type = ADVERTISE_UP;
+                       /* do it all over again right away */
+               } else {
+                       u16 r;
+                       /*
+                        * Start over again after a long timeout
+                        * (see notes above)
+                        */
+                       next_timeout_msec = 0;
+                       os_get_random((void *) &r, sizeof(r));
+                       next_timeout_sec = UPNP_CACHE_SEC / 4 +
+                               (((UPNP_CACHE_SEC / 4) * r) >> 16);
+                       sm->advertise_count++;
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: ADVERTISE_UP (#%u); "
+                                  "next in %d sec",
+                                  sm->advertise_count, next_timeout_sec);
+               }
+       } else {
+               a->state++;
+       }
+
+       wpabuf_free(msg);
+
+       eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+                              advertisement_state_machine_handler, NULL, sm);
+}
+
+
+/**
+ * advertisement_state_machine_start - Start SSDP advertisements
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int advertisement_state_machine_start(struct upnp_wps_device_sm *sm)
+{
+       struct advertisement_state_machine *a = &sm->advertisement;
+       int next_timeout_msec;
+
+       advertisement_state_machine_stop(sm, 0);
+
+       /*
+        * Start out advertising down, this automatically switches
+        * to advertising up which signals our restart.
+        */
+       a->type = ADVERTISE_DOWN;
+       a->state = 0;
+       /* (other fields not used here) */
+
+       /* First timeout should be random interval < 100 msec */
+       next_timeout_msec = (100 * (os_random() & 0xFF)) >> 8;
+       return eloop_register_timeout(0, next_timeout_msec,
+                                     advertisement_state_machine_handler,
+                                     NULL, sm);
+}
+
+
+/***************************************************************************
+ * M-SEARCH replies
+ * These are very similar to the multicast advertisements, with some
+ * small changes in data content; and they are sent (UDP) to a specific
+ * unicast address instead of multicast.
+ * They are sent in response to a UDP M-SEARCH packet.
+ **************************************************************************/
+
+/**
+ * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine
+ * @a: Selected advertisement/reply state
+ */
+void msearchreply_state_machine_stop(struct advertisement_state_machine *a)
+{
+       wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop");
+       dl_list_del(&a->list);
+       os_free(a);
+}
+
+
+static void msearchreply_state_machine_handler(void *eloop_data,
+                                              void *user_ctx)
+{
+       struct advertisement_state_machine *a = user_ctx;
+       struct upnp_wps_device_sm *sm = eloop_data;
+       struct wpabuf *msg;
+       int next_timeout_msec = 100;
+       int next_timeout_sec = 0;
+       int islast = 0;
+
+       /*
+        * Each response is sent twice (in case lost) w/ 100 msec delay
+        * between; spec says no more than 3 times.
+        * One pair for rootdevice, one pair for uuid, and a pair each for
+        * each of the two urns.
+        */
+
+       /* TODO: should only send the requested response types */
+
+       wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)",
+                  a->state, inet_ntoa(a->client.sin_addr),
+                  ntohs(a->client.sin_port));
+       msg = next_advertisement(sm, a, &islast);
+       if (msg == NULL)
+               return;
+
+       /*
+        * Send it on the multicast socket to avoid having to set up another
+        * socket.
+        */
+       if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0,
+                  (struct sockaddr *) &a->client, sizeof(a->client)) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply sendto "
+                          "errno %d (%s) for %s:%d",
+                          errno, strerror(errno),
+                          inet_ntoa(a->client.sin_addr),
+                          ntohs(a->client.sin_port));
+               /* Ignore error and hope for the best */
+       }
+       wpabuf_free(msg);
+       if (islast) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply done");
+               msearchreply_state_machine_stop(a);
+               return;
+       }
+       a->state++;
+
+       wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply in %d.%03d sec",
+                  next_timeout_sec, next_timeout_msec);
+       eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+                              msearchreply_state_machine_handler, sm, a);
+}
+
+
+/**
+ * msearchreply_state_machine_start - Reply to M-SEARCH discovery request
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @mx: Maximum delay in seconds
+ *
+ * Use TTL of 4 (this was done when socket set up).
+ * A response should be given in randomized portion of min(MX,120) seconds
+ *
+ * UPnP-arch-DeviceArchitecture, 1.2.3:
+ * To be found, a device must send a UDP response to the source IP address and
+ * port that sent the request to the multicast channel. Devices respond if the
+ * ST header of the M-SEARCH request is "ssdp:all", "upnp:rootdevice", "uuid:"
+ * followed by a UUID that exactly matches one advertised by the device.
+ */
+static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm,
+                                            struct sockaddr_in *client,
+                                            int mx)
+{
+       struct advertisement_state_machine *a;
+       int next_timeout_sec;
+       int next_timeout_msec;
+       int replies;
+
+       replies = dl_list_len(&sm->msearch_replies);
+       wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d "
+                  "outstanding)", replies);
+       if (replies >= MAX_MSEARCH) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding "
+                          "M-SEARCH replies");
+               return;
+       }
+
+       a = os_zalloc(sizeof(*a));
+       if (a == NULL)
+               return;
+       a->type = MSEARCH_REPLY;
+       a->state = 0;
+       os_memcpy(&a->client, client, sizeof(*client));
+       /* Wait time depending on MX value */
+       next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8;
+       next_timeout_sec = next_timeout_msec / 1000;
+       next_timeout_msec = next_timeout_msec % 1000;
+       if (eloop_register_timeout(next_timeout_sec, next_timeout_msec,
+                                  msearchreply_state_machine_handler, sm,
+                                  a)) {
+               /* No way to recover (from malloc failure) */
+               goto fail;
+       }
+       /* Remember for future cleanup */
+       dl_list_add(&sm->msearch_replies, &a->list);
+       return;
+
+fail:
+       wpa_printf(MSG_INFO, "WPS UPnP: M-SEARCH reply failure!");
+       eloop_cancel_timeout(msearchreply_state_machine_handler, sm, a);
+       os_free(a);
+}
+
+
+/**
+ * ssdp_parse_msearch - Process a received M-SEARCH
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * @client: Client address
+ * @data: NULL terminated M-SEARCH message
+ *
+ * Given that we have received a header w/ M-SEARCH, act upon it
+ *
+ * Format of M-SEARCH (case insensitive!):
+ *
+ * First line must be:
+ *      M-SEARCH * HTTP/1.1
+ * Other lines in arbitrary order:
+ *      HOST:239.255.255.250:1900
+ *      ST:<varies -- must match>
+ *      MAN:"ssdp:discover"
+ *      MX:<varies>
+ *
+ * It should be noted that when Microsoft Vista is still learning its IP
+ * address, it sends out host lines like: HOST:[FF02::C]:1900
+ */
+static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
+                              struct sockaddr_in *client, const char *data)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       const char *start = data;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+       const char *end;
+       int got_host = 0;
+       int got_st = 0, st_match = 0;
+       int got_man = 0;
+       int got_mx = 0;
+       int mx = 0;
+
+       /*
+        * Skip first line M-SEARCH * HTTP/1.1
+        * (perhaps we should check remainder of the line for syntax)
+        */
+       data += line_length(data);
+
+       /* Parse remaining lines */
+       for (; *data != '\0'; data += line_length(data)) {
+               end = data + line_length_stripped(data);
+               if (token_eq(data, "host")) {
+                       /* The host line indicates who the packet
+                        * is addressed to... but do we really care?
+                        * Note that Microsoft sometimes does funny
+                        * stuff with the HOST: line.
+                        */
+#if 0   /* could be */
+                       data += token_length(data);
+                       data += word_separation_length(data);
+                       if (*data != ':')
+                               goto bad;
+                       data++;
+                       data += word_separation_length(data);
+                       /* UPNP_MULTICAST_ADDRESS */
+                       if (!str_starts(data, "239.255.255.250"))
+                               goto bad;
+                       data += os_strlen("239.255.255.250");
+                       if (*data == ':') {
+                               if (!str_starts(data, ":1900"))
+                                       goto bad;
+                       }
+#endif  /* could be */
+                       got_host = 1;
+                       continue;
+               } else if (token_eq(data, "st")) {
+                       /* There are a number of forms; we look
+                        * for one that matches our case.
+                        */
+                       got_st = 1;
+                       data += token_length(data);
+                       data += word_separation_length(data);
+                       if (*data != ':')
+                               continue;
+                       data++;
+                       data += word_separation_length(data);
+                       if (str_starts(data, "ssdp:all")) {
+                               st_match = 1;
+                               continue;
+                       }
+                       if (str_starts(data, "upnp:rootdevice")) {
+                               st_match = 1;
+                               continue;
+                       }
+                       if (str_starts(data, "uuid:")) {
+                               char uuid_string[80];
+                               data += os_strlen("uuid:");
+                               uuid_bin2str(sm->wps->uuid, uuid_string,
+                                            sizeof(uuid_string));
+                               if (str_starts(data, uuid_string))
+                                       st_match = 1;
+                               continue;
+                       }
+#if 0
+                       /* FIX: should we really reply to IGD string? */
+                       if (str_starts(data, "urn:schemas-upnp-org:device:"
+                                      "InternetGatewayDevice:1")) {
+                               st_match = 1;
+                               continue;
+                       }
+#endif
+                       if (str_starts(data, "urn:schemas-wifialliance-org:"
+                                      "service:WFAWLANConfig:1")) {
+                               st_match = 1;
+                               continue;
+                       }
+                       if (str_starts(data, "urn:schemas-wifialliance-org:"
+                                      "device:WFADevice:1")) {
+                               st_match = 1;
+                               continue;
+                       }
+                       continue;
+               } else if (token_eq(data, "man")) {
+                       data += token_length(data);
+                       data += word_separation_length(data);
+                       if (*data != ':')
+                               continue;
+                       data++;
+                       data += word_separation_length(data);
+                       if (!str_starts(data, "\"ssdp:discover\"")) {
+                               wpa_printf(MSG_DEBUG, "WPS UPnP: Unexpected "
+                                          "M-SEARCH man-field");
+                               goto bad;
+                       }
+                       got_man = 1;
+                       continue;
+               } else if (token_eq(data, "mx")) {
+                       data += token_length(data);
+                       data += word_separation_length(data);
+                       if (*data != ':')
+                               continue;
+                       data++;
+                       data += word_separation_length(data);
+                       mx = atol(data);
+                       got_mx = 1;
+                       continue;
+               }
+               /* ignore anything else */
+       }
+       if (!got_host || !got_st || !got_man || !got_mx || mx < 0) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid M-SEARCH: %d %d %d "
+                          "%d mx=%d", got_host, got_st, got_man, got_mx, mx);
+               goto bad;
+       }
+       if (!st_match) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored M-SEARCH (no ST "
+                          "match)");
+               return;
+       }
+       if (mx > 120)
+               mx = 120; /* UPnP-arch-DeviceArchitecture, 1.2.3 */
+       msearchreply_state_machine_start(sm, client, mx);
+       return;
+
+bad:
+       wpa_printf(MSG_INFO, "WPS UPnP: Failed to parse M-SEARCH");
+       wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH data:\n%s", start);
+}
+
+
+/* Listening for (UDP) discovery (M-SEARCH) packets */
+
+/**
+ * ssdp_listener_stop - Stop SSDP listered
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ *
+ * This function stops the SSDP listener that was started by calling
+ * ssdp_listener_start().
+ */
+void ssdp_listener_stop(struct upnp_wps_device_sm *sm)
+{
+       if (sm->ssdp_sd_registered) {
+               eloop_unregister_sock(sm->ssdp_sd, EVENT_TYPE_READ);
+               sm->ssdp_sd_registered = 0;
+       }
+
+       if (sm->ssdp_sd != -1) {
+               close(sm->ssdp_sd);
+               sm->ssdp_sd = -1;
+       }
+
+       eloop_cancel_timeout(msearchreply_state_machine_handler, sm,
+                            ELOOP_ALL_CTX);
+}
+
+
+static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx)
+{
+       struct upnp_wps_device_sm *sm = sock_ctx;
+       struct sockaddr_in addr; /* client address */
+       socklen_t addr_len;
+       int nread;
+       char buf[MULTICAST_MAX_READ], *pos;
+
+       addr_len = sizeof(addr);
+       nread = recvfrom(sm->ssdp_sd, buf, sizeof(buf) - 1, 0,
+                        (struct sockaddr *) &addr, &addr_len);
+       if (nread <= 0)
+               return;
+       buf[nread] = '\0'; /* need null termination for algorithm */
+
+       if (str_starts(buf, "NOTIFY ")) {
+               /*
+                * Silently ignore NOTIFYs to avoid filling debug log with
+                * unwanted messages.
+                */
+               return;
+       }
+
+       pos = os_strchr(buf, '\n');
+       if (pos)
+               *pos = '\0';
+       wpa_printf(MSG_MSGDUMP, "WPS UPnP: Received SSDP packet from %s:%d: "
+                  "%s", inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buf);
+       if (pos)
+               *pos = '\n';
+
+       /* Parse first line */
+       if (os_strncasecmp(buf, "M-SEARCH", os_strlen("M-SEARCH")) == 0 &&
+           !isgraph(buf[strlen("M-SEARCH")])) {
+               ssdp_parse_msearch(sm, &addr, buf);
+               return;
+       }
+
+       /* Ignore anything else */
+}
+
+
+int ssdp_listener_open(void)
+{
+       struct sockaddr_in addr;
+       struct ip_mreq mcast_addr;
+       int on = 1;
+       /* per UPnP spec, keep IP packet time to live (TTL) small */
+       unsigned char ttl = 4;
+       int sd;
+
+       sd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sd < 0)
+               goto fail;
+       if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+               goto fail;
+       if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
+               goto fail;
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl(INADDR_ANY);
+       addr.sin_port = htons(UPNP_MULTICAST_PORT);
+       if (bind(sd, (struct sockaddr *) &addr, sizeof(addr)))
+               goto fail;
+       os_memset(&mcast_addr, 0, sizeof(mcast_addr));
+       mcast_addr.imr_interface.s_addr = htonl(INADDR_ANY);
+       mcast_addr.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+       if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+                      (char *) &mcast_addr, sizeof(mcast_addr)))
+               goto fail;
+       if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+                      &ttl, sizeof(ttl)))
+               goto fail;
+
+       return sd;
+
+fail:
+       if (sd >= 0)
+               close(sd);
+       return -1;
+}
+
+
+/**
+ * ssdp_listener_start - Set up for receiving discovery (UDP) packets
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * The SSDP listener is stopped by calling ssdp_listener_stop().
+ */
+int ssdp_listener_start(struct upnp_wps_device_sm *sm)
+{
+       sm->ssdp_sd = ssdp_listener_open();
+
+       if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ,
+                               ssdp_listener_handler, NULL, sm))
+               goto fail;
+       sm->ssdp_sd_registered = 1;
+       return 0;
+
+fail:
+       /* Error */
+       wpa_printf(MSG_ERROR, "WPS UPnP: ssdp_listener_start failed");
+       ssdp_listener_stop(sm);
+       return -1;
+}
+
+
+/**
+ * add_ssdp_network - Add routing entry for SSDP
+ * @net_if: Selected network interface name
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function assures that the multicast address will be properly
+ * handled by Linux networking code (by a modification to routing tables).
+ * This must be done per network interface. It really only needs to be done
+ * once after booting up, but it does not hurt to call this more frequently
+ * "to be safe".
+ */
+int add_ssdp_network(const char *net_if)
+{
+#ifdef __linux__
+       int ret = -1;
+       int sock = -1;
+       struct rtentry rt;
+       struct sockaddr_in *sin;
+
+       if (!net_if)
+               goto fail;
+
+       os_memset(&rt, 0, sizeof(rt));
+       sock = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sock < 0)
+               goto fail;
+
+       rt.rt_dev = (char *) net_if;
+       sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in);
+       sin->sin_family = AF_INET;
+       sin->sin_port = 0;
+       sin->sin_addr.s_addr = inet_addr(SSDP_TARGET);
+       sin = aliasing_hide_typecast(&rt.rt_genmask, struct sockaddr_in);
+       sin->sin_family = AF_INET;
+       sin->sin_port = 0;
+       sin->sin_addr.s_addr = inet_addr(SSDP_NETMASK);
+       rt.rt_flags = RTF_UP;
+       if (ioctl(sock, SIOCADDRT, &rt) < 0) {
+               if (errno == EPERM) {
+                       wpa_printf(MSG_DEBUG, "add_ssdp_network: No "
+                                  "permissions to add routing table entry");
+                       /* Continue to allow testing as non-root */
+               } else if (errno != EEXIST) {
+                       wpa_printf(MSG_INFO, "add_ssdp_network() ioctl errno "
+                                  "%d (%s)", errno, strerror(errno));
+                       goto fail;
+               }
+       }
+
+       ret = 0;
+
+fail:
+       if (sock >= 0)
+               close(sock);
+
+       return ret;
+#else /* __linux__ */
+       return 0;
+#endif /* __linux__ */
+}
+
+
+int ssdp_open_multicast_sock(u32 ip_addr)
+{
+       int sd;
+        /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet
+         * time to live (TTL) small */
+       unsigned char ttl = 4;
+
+       sd = socket(AF_INET, SOCK_DGRAM, 0);
+       if (sd < 0)
+               return -1;
+
+#if 0   /* maybe ok if we sometimes block on writes */
+       if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+               return -1;
+#endif
+
+       if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF,
+                      &ip_addr, sizeof(ip_addr)))
+               return -1;
+       if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL,
+                      &ttl, sizeof(ttl)))
+               return -1;
+
+#if 0   /* not needed, because we don't receive using multicast_sd */
+       {
+               struct ip_mreq mreq;
+               mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS);
+               mreq.imr_interface.s_addr = ip_addr;
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr "
+                          "0x%x",
+                          mreq.imr_multiaddr.s_addr,
+                          mreq.imr_interface.s_addr);
+               if (setsockopt(sd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,
+                               sizeof(mreq))) {
+                       wpa_printf(MSG_ERROR,
+                                  "WPS UPnP: setsockopt "
+                                  "IP_ADD_MEMBERSHIP errno %d (%s)",
+                                  errno, strerror(errno));
+                       return -1;
+               }
+       }
+#endif  /* not needed */
+
+       /*
+        * TODO: What about IP_MULTICAST_LOOP? It seems to be on by default?
+        * which aids debugging I suppose but isn't really necessary?
+        */
+
+       return sd;
+}
+
+
+/**
+ * ssdp_open_multicast - Open socket for sending multicast SSDP messages
+ * @sm: WPS UPnP state machine from upnp_wps_device_init()
+ * Returns: 0 on success, -1 on failure
+ */
+int ssdp_open_multicast(struct upnp_wps_device_sm *sm)
+{
+       sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr);
+       if (sm->multicast_sd < 0)
+               return -1;
+       return 0;
+}
diff --git a/src/wps/wps_upnp_web.c b/src/wps/wps_upnp_web.c
new file mode 100644 (file)
index 0000000..9a6b36e
--- /dev/null
@@ -0,0 +1,1270 @@
+/*
+ * UPnP WPS Device - Web connections
+ * Copyright (c) 2000-2003 Intel Corporation
+ * Copyright (c) 2006-2007 Sony Corporation
+ * Copyright (c) 2008-2009 Atheros Communications
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * See wps_upnp.c for more details on licensing and code history.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "base64.h"
+#include "uuid.h"
+#include "httpread.h"
+#include "http_server.h"
+#include "wps_i.h"
+#include "wps_upnp.h"
+#include "wps_upnp_i.h"
+#include "upnp_xml.h"
+
+/***************************************************************************
+ * Web connections (we serve pages of info about ourselves, handle
+ * requests, etc. etc.).
+ **************************************************************************/
+
+#define WEB_CONNECTION_TIMEOUT_SEC 30   /* Drop web connection after t.o. */
+#define WEB_CONNECTION_MAX_READ 8000    /* Max we'll read for TCP request */
+#define MAX_WEB_CONNECTIONS 10          /* max simultaneous web connects */
+
+
+static const char *urn_wfawlanconfig =
+       "urn:schemas-wifialliance-org:service:WFAWLANConfig:1";
+static const char *http_server_hdr =
+       "Server: unspecified, UPnP/1.0, unspecified\r\n";
+static const char *http_connection_close =
+       "Connection: close\r\n";
+
+/*
+ * "Files" that we serve via HTTP. The format of these files is given by
+ * WFA WPS specifications. Extra white space has been removed to save space.
+ */
+
+static const char wps_scpd_xml[] =
+"<?xml version=\"1.0\"?>\n"
+"<scpd xmlns=\"urn:schemas-upnp-org:service-1-0\">\n"
+"<specVersion><major>1</major><minor>0</minor></specVersion>\n"
+"<actionList>\n"
+"<action>\n"
+"<name>GetDeviceInfo</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewDeviceInfo</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>DeviceInfo</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutMessage</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewInMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>InMessage</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewOutMessage</name>\n"
+"<direction>out</direction>\n"
+"<relatedStateVariable>OutMessage</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>PutWLANResponse</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventType</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventType</relatedStateVariable>\n"
+"</argument>\n"
+"<argument>\n"
+"<name>NewWLANEventMAC</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>WLANEventMAC</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"<action>\n"
+"<name>SetSelectedRegistrar</name>\n"
+"<argumentList>\n"
+"<argument>\n"
+"<name>NewMessage</name>\n"
+"<direction>in</direction>\n"
+"<relatedStateVariable>Message</relatedStateVariable>\n"
+"</argument>\n"
+"</argumentList>\n"
+"</action>\n"
+"</actionList>\n"
+"<serviceStateTable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>Message</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>InMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>OutMessage</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>DeviceInfo</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>APStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>STAStatus</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"yes\">\n"
+"<name>WLANEvent</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventType</name>\n"
+"<dataType>ui1</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANEventMAC</name>\n"
+"<dataType>string</dataType>\n"
+"</stateVariable>\n"
+"<stateVariable sendEvents=\"no\">\n"
+"<name>WLANResponse</name>\n"
+"<dataType>bin.base64</dataType>\n"
+"</stateVariable>\n"
+"</serviceStateTable>\n"
+"</scpd>\n"
+;
+
+
+static const char *wps_device_xml_prefix =
+       "<?xml version=\"1.0\"?>\n"
+       "<root xmlns=\"urn:schemas-upnp-org:device-1-0\">\n"
+       "<specVersion>\n"
+       "<major>1</major>\n"
+       "<minor>0</minor>\n"
+       "</specVersion>\n"
+       "<device>\n"
+       "<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1"
+       "</deviceType>\n";
+
+static const char *wps_device_xml_postfix =
+       "<serviceList>\n"
+       "<service>\n"
+       "<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1"
+       "</serviceType>\n"
+       "<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>"
+       "\n"
+       "<SCPDURL>" UPNP_WPS_SCPD_XML_FILE "</SCPDURL>\n"
+       "<controlURL>" UPNP_WPS_DEVICE_CONTROL_FILE "</controlURL>\n"
+       "<eventSubURL>" UPNP_WPS_DEVICE_EVENT_FILE "</eventSubURL>\n"
+       "</service>\n"
+       "</serviceList>\n"
+       "</device>\n"
+       "</root>\n";
+
+
+/* format_wps_device_xml -- produce content of "file" wps_device.xml
+ * (UPNP_WPS_DEVICE_XML_FILE)
+ */
+static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+                                 struct wpabuf *buf)
+{
+       const char *s;
+       char uuid_string[80];
+
+       wpabuf_put_str(buf, wps_device_xml_prefix);
+
+       /*
+        * Add required fields with default values if not configured. Add
+        * optional and recommended fields only if configured.
+        */
+       s = sm->wps->friendly_name;
+       s = ((s && *s) ? s : "WPS Access Point");
+       xml_add_tagged_data(buf, "friendlyName", s);
+
+       s = sm->wps->dev.manufacturer;
+       s = ((s && *s) ? s : "");
+       xml_add_tagged_data(buf, "manufacturer", s);
+
+       if (sm->wps->manufacturer_url)
+               xml_add_tagged_data(buf, "manufacturerURL",
+                                   sm->wps->manufacturer_url);
+
+       if (sm->wps->model_description)
+               xml_add_tagged_data(buf, "modelDescription",
+                                   sm->wps->model_description);
+
+       s = sm->wps->dev.model_name;
+       s = ((s && *s) ? s : "");
+       xml_add_tagged_data(buf, "modelName", s);
+
+       if (sm->wps->dev.model_number)
+               xml_add_tagged_data(buf, "modelNumber",
+                                   sm->wps->dev.model_number);
+
+       if (sm->wps->model_url)
+               xml_add_tagged_data(buf, "modelURL", sm->wps->model_url);
+
+       if (sm->wps->dev.serial_number)
+               xml_add_tagged_data(buf, "serialNumber",
+                                   sm->wps->dev.serial_number);
+
+       uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string));
+       s = uuid_string;
+       /* Need "uuid:" prefix, thus we can't use xml_add_tagged_data()
+        * easily...
+        */
+       wpabuf_put_str(buf, "<UDN>uuid:");
+       xml_data_encode(buf, s, os_strlen(s));
+       wpabuf_put_str(buf, "</UDN>\n");
+
+       if (sm->wps->upc)
+               xml_add_tagged_data(buf, "UPC", sm->wps->upc);
+
+       wpabuf_put_str(buf, wps_device_xml_postfix);
+}
+
+
+static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code)
+{
+       wpabuf_put_str(buf, "HTTP/1.1 ");
+       switch (code) {
+       case HTTP_OK:
+               wpabuf_put_str(buf, "200 OK\r\n");
+               break;
+       case HTTP_BAD_REQUEST:
+               wpabuf_put_str(buf, "400 Bad request\r\n");
+               break;
+       case HTTP_PRECONDITION_FAILED:
+               wpabuf_put_str(buf, "412 Precondition failed\r\n");
+               break;
+       case HTTP_UNIMPLEMENTED:
+               wpabuf_put_str(buf, "501 Unimplemented\r\n");
+               break;
+       case HTTP_INTERNAL_SERVER_ERROR:
+       default:
+               wpabuf_put_str(buf, "500 Internal server error\r\n");
+               break;
+       }
+}
+
+
+static void http_put_date(struct wpabuf *buf)
+{
+       wpabuf_put_str(buf, "Date: ");
+       format_date(buf);
+       wpabuf_put_str(buf, "\r\n");
+}
+
+
+static void http_put_empty(struct wpabuf *buf, enum http_reply_code code)
+{
+       http_put_reply_code(buf, code);
+       wpabuf_put_str(buf, http_server_hdr);
+       wpabuf_put_str(buf, http_connection_close);
+       wpabuf_put_str(buf, "Content-Length: 0\r\n"
+                      "\r\n");
+}
+
+
+/* Given that we have received a header w/ GET, act upon it
+ *
+ * Format of GET (case-insensitive):
+ *
+ * First line must be:
+ *      GET /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
+                                    struct http_request *hreq, char *filename)
+{
+       struct wpabuf *buf; /* output buffer, allocated */
+       char *put_length_here;
+       char *body_start;
+       enum {
+               GET_DEVICE_XML_FILE,
+               GET_SCPD_XML_FILE
+       } req;
+       size_t extra_len = 0;
+       int body_length;
+       char len_buf[10];
+
+       /*
+        * It is not required that filenames be case insensitive but it is
+        * allowed and cannot hurt here.
+        */
+       if (filename == NULL)
+               filename = "(null)"; /* just in case */
+       if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
+               req = GET_DEVICE_XML_FILE;
+               extra_len = 3000;
+               if (sm->wps->friendly_name)
+                       extra_len += os_strlen(sm->wps->friendly_name);
+               if (sm->wps->manufacturer_url)
+                       extra_len += os_strlen(sm->wps->manufacturer_url);
+               if (sm->wps->model_description)
+                       extra_len += os_strlen(sm->wps->model_description);
+               if (sm->wps->model_url)
+                       extra_len += os_strlen(sm->wps->model_url);
+               if (sm->wps->upc)
+                       extra_len += os_strlen(sm->wps->upc);
+       } else if (!os_strcasecmp(filename, UPNP_WPS_SCPD_XML_FILE)) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for SCPD XML");
+               req = GET_SCPD_XML_FILE;
+               extra_len = os_strlen(wps_scpd_xml);
+       } else {
+               /* File not found */
+               wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s",
+                          filename);
+               buf = wpabuf_alloc(200);
+               if (buf == NULL) {
+                       http_request_deinit(hreq);
+                       return;
+               }
+               wpabuf_put_str(buf,
+                              "HTTP/1.1 404 Not Found\r\n"
+                              "Connection: close\r\n");
+
+               http_put_date(buf);
+
+               /* terminating empty line */
+               wpabuf_put_str(buf, "\r\n");
+
+               goto send_buf;
+       }
+
+       buf = wpabuf_alloc(1000 + extra_len);
+       if (buf == NULL) {
+               http_request_deinit(hreq);
+               return;
+       }
+
+       wpabuf_put_str(buf,
+                      "HTTP/1.1 200 OK\r\n"
+                      "Content-Type: text/xml; charset=\"utf-8\"\r\n");
+       wpabuf_put_str(buf, "Server: Unspecified, UPnP/1.0, Unspecified\r\n");
+       wpabuf_put_str(buf, "Connection: close\r\n");
+       wpabuf_put_str(buf, "Content-Length: ");
+       /*
+        * We will paste the length in later, leaving some extra whitespace.
+        * HTTP code is supposed to be tolerant of extra whitespace.
+        */
+       put_length_here = wpabuf_put(buf, 0);
+       wpabuf_put_str(buf, "        \r\n");
+
+       http_put_date(buf);
+
+       /* terminating empty line */
+       wpabuf_put_str(buf, "\r\n");
+
+       body_start = wpabuf_put(buf, 0);
+
+       switch (req) {
+       case GET_DEVICE_XML_FILE:
+               format_wps_device_xml(sm, buf);
+               break;
+       case GET_SCPD_XML_FILE:
+               wpabuf_put_str(buf, wps_scpd_xml);
+               break;
+       }
+
+       /* Now patch in the content length at the end */
+       body_length = (char *) wpabuf_put(buf, 0) - body_start;
+       os_snprintf(len_buf, 10, "%d", body_length);
+       os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+
+send_buf:
+       http_request_send_and_deinit(hreq, buf);
+}
+
+
+static enum http_reply_code
+web_process_get_device_info(struct upnp_wps_device_sm *sm,
+                           struct wpabuf **reply, const char **replyname)
+{
+       static const char *name = "NewDeviceInfo";
+       struct wps_config cfg;
+       struct upnp_wps_peer *peer = &sm->peer;
+
+       wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
+
+       if (sm->ctx->ap_pin == NULL)
+               return HTTP_INTERNAL_SERVER_ERROR;
+
+       /*
+        * 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 = sm->wps;
+       cfg.pin = (u8 *) sm->ctx->ap_pin;
+       cfg.pin_len = os_strlen(sm->ctx->ap_pin);
+       peer->wps = wps_init(&cfg);
+       if (peer->wps) {
+               enum wsc_op_code op_code;
+               *reply = wps_get_msg(peer->wps, &op_code);
+               if (*reply == NULL) {
+                       wps_deinit(peer->wps);
+                       peer->wps = NULL;
+               }
+       } else
+               *reply = NULL;
+       if (*reply == NULL) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo");
+               return HTTP_INTERNAL_SERVER_ERROR;
+       }
+       *replyname = name;
+       return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
+                       struct wpabuf **reply, const char **replyname)
+{
+       struct wpabuf *msg;
+       static const char *name = "NewOutMessage";
+       enum http_reply_code ret;
+       enum wps_process_res res;
+       enum wsc_op_code op_code;
+
+       /*
+        * PutMessage is used by external UPnP-based Registrar to perform WPS
+        * operation with the access point itself; as compared with
+        * PutWLANResponse which is for proxying.
+        */
+       wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage");
+       msg = xml_get_base64_item(data, "NewInMessage", &ret);
+       if (msg == NULL)
+               return ret;
+       res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg);
+       if (res == WPS_FAILURE)
+               *reply = NULL;
+       else
+               *reply = wps_get_msg(sm->peer.wps, &op_code);
+       wpabuf_free(msg);
+       if (*reply == NULL)
+               return HTTP_INTERNAL_SERVER_ERROR;
+       *replyname = name;
+       return HTTP_OK;
+}
+
+
+static enum http_reply_code
+web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data,
+                             struct wpabuf **reply, const char **replyname)
+{
+       struct wpabuf *msg;
+       enum http_reply_code ret;
+       u8 macaddr[ETH_ALEN];
+       int ev_type;
+       int type;
+       char *val;
+
+       /*
+        * External UPnP-based Registrar is passing us a message to be proxied
+        * over to a Wi-Fi -based client of ours.
+        */
+
+       wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse");
+       msg = xml_get_base64_item(data, "NewMessage", &ret);
+       if (msg == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage "
+                          "from PutWLANResponse");
+               return ret;
+       }
+       val = xml_get_first_item(data, "NewWLANEventType");
+       if (val == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in "
+                          "PutWLANResponse");
+               wpabuf_free(msg);
+               return UPNP_ARG_VALUE_INVALID;
+       }
+       ev_type = atol(val);
+       os_free(val);
+       val = xml_get_first_item(data, "NewWLANEventMAC");
+       if (val == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in "
+                          "PutWLANResponse");
+               wpabuf_free(msg);
+               return UPNP_ARG_VALUE_INVALID;
+       }
+       if (hwaddr_aton(val, macaddr)) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in "
+                          "PutWLANResponse: '%s'", val);
+               if (hwaddr_aton2(val, macaddr) > 0) {
+                       /*
+                        * At least some versions of Intel PROset seem to be
+                        * using dot-deliminated MAC address format here.
+                        */
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow "
+                                  "incorrect MAC address format in "
+                                  "NewWLANEventMAC");
+               } else {
+                       wpabuf_free(msg);
+                       os_free(val);
+                       return UPNP_ARG_VALUE_INVALID;
+               }
+       }
+       os_free(val);
+       if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) {
+               struct wps_parse_attr attr;
+               if (wps_parse_msg(msg, &attr) < 0 ||
+                   attr.msg_type == NULL)
+                       type = -1;
+               else
+                       type = *attr.msg_type;
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Message Type %d", type);
+       } else
+               type = -1;
+       if (!sm->ctx->rx_req_put_wlan_response ||
+           sm->ctx->rx_req_put_wlan_response(sm->priv, ev_type, macaddr, msg,
+                                             type)) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Fail: sm->ctx->"
+                          "rx_req_put_wlan_response");
+               wpabuf_free(msg);
+               return HTTP_INTERNAL_SERVER_ERROR;
+       }
+       wpabuf_free(msg);
+       *replyname = NULL;
+       *reply = NULL;
+       return HTTP_OK;
+}
+
+
+static int find_er_addr(struct subscription *s, struct sockaddr_in *cli)
+{
+       struct subscr_addr *a;
+
+       dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) {
+               if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static struct subscription * find_er(struct upnp_wps_device_sm *sm,
+                                    struct sockaddr_in *cli)
+{
+       struct subscription *s;
+       dl_list_for_each(s, &sm->subscriptions, struct subscription, list)
+               if (find_er_addr(s, cli))
+                       return s;
+       return NULL;
+}
+
+
+static enum http_reply_code
+web_process_set_selected_registrar(struct upnp_wps_device_sm *sm,
+                                  struct sockaddr_in *cli, char *data,
+                                  struct wpabuf **reply,
+                                  const char **replyname)
+{
+       struct wpabuf *msg;
+       enum http_reply_code ret;
+       struct subscription *s;
+
+       wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar");
+       s = find_er(sm, cli);
+       if (s == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar "
+                          "from unknown ER");
+               return UPNP_ACTION_FAILED;
+       }
+       msg = xml_get_base64_item(data, "NewMessage", &ret);
+       if (msg == NULL)
+               return ret;
+       if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) {
+               wpabuf_free(msg);
+               return HTTP_INTERNAL_SERVER_ERROR;
+       }
+       wpabuf_free(msg);
+       *replyname = NULL;
+       *reply = NULL;
+       return HTTP_OK;
+}
+
+
+static const char *soap_prefix =
+       "<?xml version=\"1.0\"?>\n"
+       "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
+       "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n"
+       "<s:Body>\n";
+static const char *soap_postfix =
+       "</s:Body>\n</s:Envelope>\n";
+
+static const char *soap_error_prefix =
+       "<s:Fault>\n"
+       "<faultcode>s:Client</faultcode>\n"
+       "<faultstring>UPnPError</faultstring>\n"
+       "<detail>\n"
+       "<UPnPError xmlns=\"urn:schemas-upnp-org:control-1-0\">\n";
+static const char *soap_error_postfix =
+       "<errorDescription>Error</errorDescription>\n"
+       "</UPnPError>\n"
+       "</detail>\n"
+       "</s:Fault>\n";
+
+static void web_connection_send_reply(struct http_request *req,
+                                     enum http_reply_code ret,
+                                     const char *action, int action_len,
+                                     const struct wpabuf *reply,
+                                     const char *replyname)
+{
+       struct wpabuf *buf;
+       char *replydata;
+       char *put_length_here = NULL;
+       char *body_start = NULL;
+
+       if (reply) {
+               size_t len;
+               replydata = (char *) base64_encode(wpabuf_head(reply),
+                                                  wpabuf_len(reply), &len);
+       } else
+               replydata = NULL;
+
+       /* Parameters of the response:
+        *      action(action_len) -- action we are responding to
+        *      replyname -- a name we need for the reply
+        *      replydata -- NULL or null-terminated string
+        */
+       buf = wpabuf_alloc(1000 + (replydata ? os_strlen(replydata) : 0U) +
+                          (action_len > 0 ? action_len * 2 : 0));
+       if (buf == NULL) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to "
+                          "POST");
+               os_free(replydata);
+               http_request_deinit(req);
+               return;
+       }
+
+       /*
+        * Assuming we will be successful, put in the output header first.
+        * Note: we do not keep connections alive (and httpread does
+        * not support it)... therefore we must have Connection: close.
+        */
+       if (ret == HTTP_OK) {
+               wpabuf_put_str(buf,
+                              "HTTP/1.1 200 OK\r\n"
+                              "Content-Type: text/xml; "
+                              "charset=\"utf-8\"\r\n");
+       } else {
+               wpabuf_printf(buf, "HTTP/1.1 %d Error\r\n", ret);
+       }
+       wpabuf_put_str(buf, http_connection_close);
+
+       wpabuf_put_str(buf, "Content-Length: ");
+       /*
+        * We will paste the length in later, leaving some extra whitespace.
+        * HTTP code is supposed to be tolerant of extra whitespace.
+        */
+       put_length_here = wpabuf_put(buf, 0);
+       wpabuf_put_str(buf, "        \r\n");
+
+       http_put_date(buf);
+
+       /* terminating empty line */
+       wpabuf_put_str(buf, "\r\n");
+
+       body_start = wpabuf_put(buf, 0);
+
+       if (ret == HTTP_OK) {
+               wpabuf_put_str(buf, soap_prefix);
+               wpabuf_put_str(buf, "<u:");
+               wpabuf_put_data(buf, action, action_len);
+               wpabuf_put_str(buf, "Response xmlns:u=\"");
+               wpabuf_put_str(buf, urn_wfawlanconfig);
+               wpabuf_put_str(buf, "\">\n");
+               if (replydata && replyname) {
+                       /* TODO: might possibly need to escape part of reply
+                        * data? ...
+                        * probably not, unlikely to have ampersand(&) or left
+                        * angle bracket (<) in it...
+                        */
+                       wpabuf_printf(buf, "<%s>", replyname);
+                       wpabuf_put_str(buf, replydata);
+                       wpabuf_printf(buf, "</%s>\n", replyname);
+               }
+               wpabuf_put_str(buf, "</u:");
+               wpabuf_put_data(buf, action, action_len);
+               wpabuf_put_str(buf, "Response>\n");
+               wpabuf_put_str(buf, soap_postfix);
+       } else {
+               /* Error case */
+               wpabuf_put_str(buf, soap_prefix);
+               wpabuf_put_str(buf, soap_error_prefix);
+               wpabuf_printf(buf, "<errorCode>%d</errorCode>\n", ret);
+               wpabuf_put_str(buf, soap_error_postfix);
+               wpabuf_put_str(buf, soap_postfix);
+       }
+       os_free(replydata);
+
+       /* Now patch in the content length at the end */
+       if (body_start && put_length_here) {
+               int body_length = (char *) wpabuf_put(buf, 0) - body_start;
+               char len_buf[10];
+               os_snprintf(len_buf, sizeof(len_buf), "%d", body_length);
+               os_memcpy(put_length_here, len_buf, os_strlen(len_buf));
+       }
+
+       http_request_send_and_deinit(req, buf);
+}
+
+
+static const char * web_get_action(struct http_request *req,
+                                  size_t *action_len)
+{
+       const char *match;
+       int match_len;
+       char *b;
+       char *action;
+
+       *action_len = 0;
+       /* The SOAPAction line of the header tells us what we want to do */
+       b = http_request_get_hdr_line(req, "SOAPAction:");
+       if (b == NULL)
+               return NULL;
+       if (*b == '"')
+               b++;
+       else
+               return NULL;
+       match = urn_wfawlanconfig;
+       match_len = os_strlen(urn_wfawlanconfig) - 1;
+       if (os_strncasecmp(b, match, match_len))
+               return NULL;
+       b += match_len;
+       /* skip over version */
+       while (isgraph(*b) && *b != '#')
+               b++;
+       if (*b != '#')
+               return NULL;
+       b++;
+       /* Following the sharp(#) should be the action and a double quote */
+       action = b;
+       while (isgraph(*b) && *b != '"')
+               b++;
+       if (*b != '"')
+               return NULL;
+       *action_len = b - action;
+       return action;
+}
+
+
+/* Given that we have received a header w/ POST, act upon it
+ *
+ * Format of POST (case-insensitive):
+ *
+ * First line must be:
+ *      POST /<file> HTTP/1.1
+ * Since we don't do anything fancy we just ignore other lines.
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Connection: close
+ * Content-Type: text/xml
+ * Date: <rfc1123-date>
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_post(struct upnp_wps_device_sm *sm,
+                                     struct sockaddr_in *cli,
+                                     struct http_request *req,
+                                     const char *filename)
+{
+       enum http_reply_code ret;
+       char *data = http_request_get_data(req); /* body of http msg */
+       const char *action = NULL;
+       size_t action_len = 0;
+       const char *replyname = NULL; /* argument name for the reply */
+       struct wpabuf *reply = NULL; /* data for the reply */
+
+       if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s",
+                          filename);
+               ret = HTTP_NOT_FOUND;
+               goto bad;
+       }
+
+       ret = UPNP_INVALID_ACTION;
+       action = web_get_action(req, &action_len);
+       if (action == NULL)
+               goto bad;
+
+       if (!os_strncasecmp("GetDeviceInfo", action, action_len))
+               ret = web_process_get_device_info(sm, &reply, &replyname);
+       else if (!os_strncasecmp("PutMessage", action, action_len))
+               ret = web_process_put_message(sm, data, &reply, &replyname);
+       else if (!os_strncasecmp("PutWLANResponse", action, action_len))
+               ret = web_process_put_wlan_response(sm, data, &reply,
+                                                   &replyname);
+       else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len))
+               ret = web_process_set_selected_registrar(sm, cli, data, &reply,
+                                                        &replyname);
+       else
+               wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type");
+
+bad:
+       if (ret != HTTP_OK)
+               wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret);
+       web_connection_send_reply(req, ret, action, action_len, reply,
+                                 replyname);
+       wpabuf_free(reply);
+}
+
+
+/* Given that we have received a header w/ SUBSCRIBE, act upon it
+ *
+ * Format of SUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ *      SUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Server: xx, UPnP/1.0, xx
+ * SID: uuid:xxxxxxxxx
+ * Timeout: Second-<n>
+ * Content-Length: 0
+ * Date: xxxx
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
+                                          struct http_request *req,
+                                          const char *filename)
+{
+       struct wpabuf *buf;
+       char *b;
+       char *hdr = http_request_get_hdr(req);
+       char *h;
+       char *match;
+       int match_len;
+       char *end;
+       int len;
+       int got_nt = 0;
+       u8 uuid[UUID_LEN];
+       int got_uuid = 0;
+       char *callback_urls = NULL;
+       struct subscription *s = NULL;
+       enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+       buf = wpabuf_alloc(1000);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+
+       /* Parse/validate headers */
+       h = hdr;
+       /* First line: SUBSCRIBE /wps_event HTTP/1.1
+        * has already been parsed.
+        */
+       if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+               ret = HTTP_PRECONDITION_FAILED;
+               goto error;
+       }
+       wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
+       end = os_strchr(h, '\n');
+
+       for (; end != NULL; h = end + 1) {
+               /* Option line by option line */
+               h = end + 1;
+               end = os_strchr(h, '\n');
+               if (end == NULL)
+                       break; /* no unterminated lines allowed */
+
+               /* NT assures that it is our type of subscription;
+                * not used for a renewl.
+                **/
+               match = "NT:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       match = "upnp:event";
+                       match_len = os_strlen(match);
+                       if (os_strncasecmp(h, match, match_len) != 0) {
+                               ret = HTTP_BAD_REQUEST;
+                               goto error;
+                       }
+                       got_nt = 1;
+                       continue;
+               }
+               /* HOST should refer to us */
+#if 0
+               match = "HOST:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       .....
+               }
+#endif
+               /* CALLBACK gives one or more URLs for NOTIFYs
+                * to be sent as a result of the subscription.
+                * Each URL is enclosed in angle brackets.
+                */
+               match = "CALLBACK:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       len = end - h;
+                       os_free(callback_urls);
+                       callback_urls = os_malloc(len + 1);
+                       if (callback_urls == NULL) {
+                               ret = HTTP_INTERNAL_SERVER_ERROR;
+                               goto error;
+                       }
+                       os_memcpy(callback_urls, h, len);
+                       callback_urls[len] = 0;
+                       continue;
+               }
+               /* SID is only for renewal */
+               match = "SID:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       match = "uuid:";
+                       match_len = os_strlen(match);
+                       if (os_strncasecmp(h, match, match_len) != 0) {
+                               ret = HTTP_BAD_REQUEST;
+                               goto error;
+                       }
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       if (uuid_str2bin(h, uuid)) {
+                               ret = HTTP_BAD_REQUEST;
+                               goto error;
+                       }
+                       got_uuid = 1;
+                       continue;
+               }
+               /* TIMEOUT is requested timeout, but apparently we can
+                * just ignore this.
+                */
+       }
+
+       if (got_uuid) {
+               /* renewal */
+               if (callback_urls) {
+                       ret = HTTP_BAD_REQUEST;
+                       goto error;
+               }
+               s = subscription_renew(sm, uuid);
+               if (s == NULL) {
+                       ret = HTTP_PRECONDITION_FAILED;
+                       goto error;
+               }
+       } else if (callback_urls) {
+               if (!got_nt) {
+                       ret = HTTP_PRECONDITION_FAILED;
+                       goto error;
+               }
+               s = subscription_start(sm, callback_urls);
+               if (s == NULL) {
+                       ret = HTTP_INTERNAL_SERVER_ERROR;
+                       goto error;
+               }
+       } else {
+               ret = HTTP_PRECONDITION_FAILED;
+               goto error;
+       }
+
+       /* success */
+       http_put_reply_code(buf, HTTP_OK);
+       wpabuf_put_str(buf, http_server_hdr);
+       wpabuf_put_str(buf, http_connection_close);
+       wpabuf_put_str(buf, "Content-Length: 0\r\n");
+       wpabuf_put_str(buf, "SID: uuid:");
+       /* subscription id */
+       b = wpabuf_put(buf, 0);
+       uuid_bin2str(s->uuid, b, 80);
+       wpabuf_put(buf, os_strlen(b));
+       wpabuf_put_str(buf, "\r\n");
+       wpabuf_printf(buf, "Timeout: Second-%d\r\n", UPNP_SUBSCRIBE_SEC);
+       http_put_date(buf);
+       /* And empty line to terminate header: */
+       wpabuf_put_str(buf, "\r\n");
+
+       os_free(callback_urls);
+       http_request_send_and_deinit(req, buf);
+       return;
+
+error:
+       /* Per UPnP spec:
+       * Errors
+       * Incompatible headers
+       *   400 Bad Request. If SID header and one of NT or CALLBACK headers
+       *     are present, the publisher must respond with HTTP error
+       *     400 Bad Request.
+       * Missing or invalid CALLBACK
+       *   412 Precondition Failed. If CALLBACK header is missing or does not
+       *     contain a valid HTTP URL, the publisher must respond with HTTP
+       *     error 412 Precondition Failed.
+       * Invalid NT
+       *   412 Precondition Failed. If NT header does not equal upnp:event,
+       *     the publisher must respond with HTTP error 412 Precondition
+       *     Failed.
+       * [For resubscription, use 412 if unknown uuid].
+       * Unable to accept subscription
+       *   5xx. If a publisher is not able to accept a subscription (such as
+       *     due to insufficient resources), it must respond with a
+       *     HTTP 500-series error code.
+       *   599 Too many subscriptions (not a standard HTTP error)
+       */
+       http_put_empty(buf, ret);
+       http_request_send_and_deinit(req, buf);
+       os_free(callback_urls);
+}
+
+
+/* Given that we have received a header w/ UNSUBSCRIBE, act upon it
+ *
+ * Format of UNSUBSCRIBE (case-insensitive):
+ *
+ * First line must be:
+ *      UNSUBSCRIBE /wps_event HTTP/1.1
+ *
+ * Our response (if no error) which includes only required lines is:
+ * HTTP/1.1 200 OK
+ * Content-Length: 0
+ *
+ * Header lines must end with \r\n
+ * Per RFC 2616, content-length: is not required but connection:close
+ * would appear to be required (given that we will be closing it!).
+ */
+static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
+                                            struct http_request *req,
+                                            const char *filename)
+{
+       struct wpabuf *buf;
+       char *hdr = http_request_get_hdr(req);
+       char *h;
+       char *match;
+       int match_len;
+       char *end;
+       u8 uuid[UUID_LEN];
+       int got_uuid = 0;
+       struct subscription *s = NULL;
+       enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR;
+
+       /* Parse/validate headers */
+       h = hdr;
+       /* First line: UNSUBSCRIBE /wps_event HTTP/1.1
+        * has already been parsed.
+        */
+       if (os_strcasecmp(filename, UPNP_WPS_DEVICE_EVENT_FILE) != 0) {
+               ret = HTTP_PRECONDITION_FAILED;
+               goto send_msg;
+       }
+       wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
+       end = os_strchr(h, '\n');
+
+       for (; end != NULL; h = end + 1) {
+               /* Option line by option line */
+               h = end + 1;
+               end = os_strchr(h, '\n');
+               if (end == NULL)
+                       break; /* no unterminated lines allowed */
+
+               /* HOST should refer to us */
+#if 0
+               match = "HOST:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       .....
+               }
+#endif
+               /* SID is only for renewal */
+               match = "SID:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       match = "uuid:";
+                       match_len = os_strlen(match);
+                       if (os_strncasecmp(h, match, match_len) != 0) {
+                               ret = HTTP_BAD_REQUEST;
+                               goto send_msg;
+                       }
+                       h += match_len;
+                       while (*h == ' ' || *h == '\t')
+                               h++;
+                       if (uuid_str2bin(h, uuid)) {
+                               ret = HTTP_BAD_REQUEST;
+                               goto send_msg;
+                       }
+                       got_uuid = 1;
+                       continue;
+               }
+       }
+
+       if (got_uuid) {
+               s = subscription_find(sm, uuid);
+               if (s) {
+                       struct subscr_addr *sa;
+                       sa = dl_list_first(&s->addr_list, struct subscr_addr,
+                                          list);
+                       wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s",
+                                  s, (sa && sa->domain_and_port) ?
+                                  sa->domain_and_port : "-null-");
+                       dl_list_del(&s->list);
+                       subscription_destroy(s);
+               }
+       } else {
+               wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
+                          "found)");
+               ret = HTTP_PRECONDITION_FAILED;
+               goto send_msg;
+       }
+
+       ret = HTTP_OK;
+
+send_msg:
+       buf = wpabuf_alloc(200);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+       http_put_empty(buf, ret);
+       http_request_send_and_deinit(req, buf);
+}
+
+
+/* Send error in response to unknown requests */
+static void web_connection_unimplemented(struct http_request *req)
+{
+       struct wpabuf *buf;
+       buf = wpabuf_alloc(200);
+       if (buf == NULL) {
+               http_request_deinit(req);
+               return;
+       }
+       http_put_empty(buf, HTTP_UNIMPLEMENTED);
+       http_request_send_and_deinit(req, buf);
+}
+
+
+
+/* Called when we have gotten an apparently valid http request.
+ */
+static void web_connection_check_data(void *ctx, struct http_request *req)
+{
+       struct upnp_wps_device_sm *sm = ctx;
+       enum httpread_hdr_type htype = http_request_get_type(req);
+       char *filename = http_request_get_uri(req);
+       struct sockaddr_in *cli = http_request_get_cli_addr(req);
+
+       if (!filename) {
+               wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI");
+               http_request_deinit(req);
+               return;
+       }
+       /* Trim leading slashes from filename */
+       while (*filename == '/')
+               filename++;
+
+       wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d",
+                  htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port));
+
+       switch (htype) {
+       case HTTPREAD_HDR_TYPE_GET:
+               web_connection_parse_get(sm, req, filename);
+               break;
+       case HTTPREAD_HDR_TYPE_POST:
+               web_connection_parse_post(sm, cli, req, filename);
+               break;
+       case HTTPREAD_HDR_TYPE_SUBSCRIBE:
+               web_connection_parse_subscribe(sm, req, filename);
+               break;
+       case HTTPREAD_HDR_TYPE_UNSUBSCRIBE:
+               web_connection_parse_unsubscribe(sm, req, filename);
+               break;
+
+               /* We are not required to support M-POST; just plain
+                * POST is supposed to work, so we only support that.
+                * If for some reason we need to support M-POST, it is
+                * mostly the same as POST, with small differences.
+                */
+       default:
+               /* Send 501 for anything else */
+               web_connection_unimplemented(req);
+               break;
+       }
+}
+
+
+/*
+ * Listening for web connections
+ * We have a single TCP listening port, and hand off connections as we get
+ * them.
+ */
+
+void web_listener_stop(struct upnp_wps_device_sm *sm)
+{
+       http_server_deinit(sm->web_srv);
+       sm->web_srv = NULL;
+}
+
+
+int web_listener_start(struct upnp_wps_device_sm *sm)
+{
+       struct in_addr addr;
+       addr.s_addr = sm->ip_addr;
+       sm->web_srv = http_server_init(&addr, -1, web_connection_check_data,
+                                      sm);
+       if (sm->web_srv == NULL) {
+               web_listener_stop(sm);
+               return -1;
+       }
+       sm->web_port = http_server_get_port(sm->web_srv);
+
+       return 0;
+}
diff --git a/wpa_supplicant/.gitignore b/wpa_supplicant/.gitignore
new file mode 100644 (file)
index 0000000..e7e034c
--- /dev/null
@@ -0,0 +1,8 @@
+*.d
+.config
+eapol_test
+preauth_test
+wpa_cli
+wpa_passphrase
+wpa_supplicant
+wpa_priv
diff --git a/wpa_supplicant/ChangeLog b/wpa_supplicant/ChangeLog
new file mode 100644 (file)
index 0000000..56046c3
--- /dev/null
@@ -0,0 +1,1314 @@
+ChangeLog for wpa_supplicant
+
+2010-09-07 - v0.7.3
+       * fixed fallback from failed PMKSA caching into full EAP authentication
+         [Bug 355]
+       * fixed issue with early D-Bus signals during initialization
+       * fixed X.509 name handling in internal TLS
+       * fixed WPS ER to use corrent Enrollee MAC Address in Credential
+       * fixed scanning routines ot improve AP selection for WPS
+       * added WPS workaround for open networks
+       * fixed WPS Diffie-Hellman derivation to use correct public key length
+       * fixed wpa_supplicant AP mode operations to ignore Supplicant and
+         scan result events
+       * improved SME operations with nl80211
+       * fixed WPS ER event_id handling in some cases
+       * fixed some issues with bgscan simple to avoid unnecessary scans
+       * fixed issue with l2_packet_ndis overlapped writes corrupting stack
+         [Bug 328]
+       * updated WinPcap to the latest stable version 4.1.2 in Windows
+         installer
+
+2010-04-18 - v0.7.2
+       * nl80211: fixed number of issues with roaming
+       * avoid unnecessary roaming if multiple APs with similar signal
+         strength are present in scan results
+       * add TLS client events and server probing to ease design of
+         automatic detection of EAP parameters
+       * add option for server certificate matching (SHA256 hash of the
+         certificate) instead of trusted CA certificate configuration
+       * bsd: Cleaned up driver wrapper and added various low-level
+         configuration options
+       * wpa_gui-qt4: do not show too frequent WPS AP available events as
+         tray messages
+       * TNC: fixed issues with fragmentation
+       * EAP-TNC: add Flags field into fragment acknowledgement (needed to
+         interoperate with other implementations; may potentially breaks
+         compatibility with older wpa_supplicant/hostapd versions)
+       * wpa_cli: added option for using a separate process to receive event
+         messages to reduce latency in showing these
+         (CFLAGS += -DCONFIG_WPA_CLI_FORK=y in .config to enable this)
+       * maximum BSS table size can now be configured (bss_max_count)
+       * BSSes to be included in the BSS table can be filtered based on
+         configured SSIDs to save memory (filter_ssids)
+       * fix number of issues with IEEE 802.11r/FT; this version is not
+         backwards compatible with old versions
+       * nl80211: add support for IEEE 802.11r/FT protocol (both over-the-air
+         and over-the-DS)
+       * add freq_list network configuration parameter to allow the AP
+         selection to filter out entries based on the operating channel
+       * add signal strength change events for bgscan; this allows more
+         dynamic changes to background scanning interval based on changes in
+         the signal strength with the current AP; this improves roaming within
+         ESS quite a bit, e.g., with bgscan="simple:30:-45:300" in the network
+         configuration block to request background scans less frequently when
+         signal strength remains good and to automatically trigger background
+         scans whenever signal strength drops noticeably
+         (this is currently only available with nl80211)
+       * add BSSID and reason code (if available) to disconnect event messages
+       * wpa_gui-qt4: more complete support for translating the GUI with
+         linguist and add German translation
+       * fix DH padding with internal crypto code (mainly, for WPS)
+       * do not trigger initial scan automatically anymore if there are no
+         enabled networks
+
+2010-01-16 - v0.7.1
+       * cleaned up driver wrapper API (struct wpa_driver_ops); the new API
+         is not fully backwards compatible, so out-of-tree driver wrappers
+         will need modifications
+       * cleaned up various module interfaces
+       * merge hostapd and wpa_supplicant developers' documentation into a
+         single document
+       * nl80211: use explicit deauthentication to clear cfg80211 state to
+         avoid issues when roaming between APs
+       * dbus: major design changes in the new D-Bus API
+         (fi.w1.wpa_supplicant1)
+       * nl80211: added support for IBSS networks
+       * added internal debugging mechanism with backtrace support and memory
+         allocation/freeing validation, etc. tests (CONFIG_WPA_TRACE=y)
+       * added WPS ER unsubscription command to more cleanly unregister from
+         receiving UPnP events when ER is terminated
+       * cleaned up AP mode operations to avoid need for virtual driver_ops
+         wrapper
+       * added BSS table to maintain more complete scan result information
+         over multiple scans (that may include only partial results)
+       * wpa_gui-qt4: update Peers dialog information more dynamically while
+         the dialog is kept open
+       * fixed PKCS#12 use with OpenSSL 1.0.0
+       * driver_wext: Added cfg80211-specific optimization to avoid some
+         unnecessary scans and to speed up association
+
+2009-11-21 - v0.7.0
+       * increased wpa_cli ping interval to 5 seconds and made this
+         configurable with a new command line options (-G<seconds>)
+       * fixed scan buffer processing with WEXT to handle up to 65535
+         byte result buffer (previously, limited to 32768 bytes)
+       * allow multiple driver wrappers to be specified on command line
+         (e.g., -Dnl80211,wext); the first one that is able to initialize the
+         interface will be used
+       * added support for multiple SSIDs per scan request to optimize
+         scan_ssid=1 operations in ap_scan=1 mode (i.e., search for hidden
+         SSIDs); this requires driver support and can currently be used only
+         with nl80211
+       * added support for WPS USBA out-of-band mechanism with USB Flash
+         Drives (UFD) (CONFIG_WPS_UFD=y)
+       * driver_ndis: add PAE group address to the multicast address list to
+         fix wired IEEE 802.1X authentication
+       * fixed IEEE 802.11r key derivation function to match with the standard
+         (note: this breaks interoperability with previous version) [Bug 303]
+       * added better support for drivers that allow separate authentication
+         and association commands (e.g., mac80211-based Linux drivers with
+         nl80211; SME in wpa_supplicant); this allows over-the-air FT protocol
+         to be used (IEEE 802.11r)
+       * fixed SHA-256 based key derivation function to match with the
+         standard when using CCMP (for IEEE 802.11r and IEEE 802.11w)
+         (note: this breaks interoperability with previous version) [Bug 307]
+       * use shared driver wrapper files with hostapd
+       * added AP mode functionality (CONFIG_AP=y) with mode=2 in the network
+         block; this can be used for open and WPA2-Personal networks
+         (optionally, with WPS); this links in parts of hostapd functionality
+         into wpa_supplicant
+       * wpa_gui-qt4: added new Peers dialog to show information about peers
+         (other devices, including APs and stations, etc. in the neighborhood)
+       * added support for WPS External Registrar functionality (configure APs
+         and enroll new devices); can be used with wpa_gui-qt4 Peers dialog
+         and wpa_cli commands wps_er_start, wps_er_stop, wps_er_pin,
+         wps_er_pbc, wps_er_learn
+         (this can also be used with a new 'none' driver wrapper if no
+         wireless device or IEEE 802.1X on wired is needed)
+       * driver_nl80211: multiple updates to provide support for new Linux
+         nl80211/mac80211 functionality
+       * updated management frame protection to use IEEE Std 802.11w-2009
+       * fixed number of small WPS issues and added workarounds to
+         interoperate with common deployed broken implementations
+       * added support for NFC out-of-band mechanism with WPS
+       * driver_ndis: fixed wired IEEE 802.1X authentication with PAE group
+         address frames
+       * added preliminary support for IEEE 802.11r RIC processing
+       * added support for specifying subset of enabled frequencies to scan
+         (scan_freq option in the network configuration block); this can speed
+         up scanning process considerably if it is known that only a small
+         subset of channels is actually used in the network (this is currently
+         supported only with -Dnl80211)
+       * added a workaround for race condition between receiving the
+         association event and the following EAPOL-Key
+       * added background scan and roaming infrastructure to allow
+         network-specific optimizations to be used to improve roaming within
+         an ESS (same SSID)
+       * added new DBus interface (fi.w1.wpa_supplicant1)
+
+2009-01-06 - v0.6.7
+       * added support for Wi-Fi Protected Setup (WPS)
+         (wpa_supplicant can now be configured to act as a WPS Enrollee to
+         enroll credentials for a network using PIN and PBC methods; in
+         addition, wpa_supplicant can act as a wireless WPS Registrar to
+         configure an AP); WPS support can be enabled by adding CONFIG_WPS=y
+         into .config and setting the runtime configuration variables in
+         wpa_supplicant.conf (see WPS section in the example configuration
+         file); new wpa_cli commands wps_pin, wps_pbc, and wps_reg are used to
+         manage WPS negotiation; see README-WPS for more details
+       * added support for EAP-AKA' (draft-arkko-eap-aka-kdf)
+       * added support for using driver_test over UDP socket
+       * fixed PEAPv0 Cryptobinding interoperability issue with Windows Server
+         2008 NPS; optional cryptobinding is now enabled (again) by default
+       * fixed PSK editing in wpa_gui
+       * changed EAP-GPSK to use the IANA assigned EAP method type 51
+       * added a Windows installer that includes WinPcap and all the needed
+         DLLs; in addition, it set up the registry automatically so that user
+         will only need start wpa_gui to get prompted to start the wpasvc
+         servide and add a new interface if needed through wpa_gui dialog
+       * updated management frame protection to use IEEE 802.11w/D7.0
+
+2008-11-23 - v0.6.6
+       * added Milenage SIM/USIM emulator for EAP-SIM/EAP-AKA
+         (can be used to simulate test SIM/USIM card with a known private key;
+         enable with CONFIG_SIM_SIMULATOR=y/CONFIG_USIM_SIMULATOR=y in .config
+         and password="Ki:OPc"/password="Ki:OPc:SQN" in network configuration)
+       * added a new network configuration option, wpa_ptk_rekey, that can be
+         used to enforce frequent PTK rekeying, e.g., to mitigate some attacks
+         against TKIP deficiencies
+       * added an optional mitigation mechanism for certain attacks against
+         TKIP by delaying Michael MIC error reports by a random amount of time
+         between 0 and 60 seconds; this can be enabled with a build option
+         CONFIG_DELAYED_MIC_ERROR_REPORT=y in .config
+       * fixed EAP-AKA to use RES Length field in AT_RES as length in bits,
+         not bytes
+       * 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)
+       * updated userspace MLME instructions to match with the current Linux
+         mac80211 implementation; please also note that this can only be used
+         with driver_nl80211.c (the old code from driver_wext.c was removed)
+       * added support (Linux only) for RoboSwitch chipsets (often found in
+         consumer grade routers); driver interface 'roboswitch'
+       * fixed canceling of PMKSA caching when using drivers that generate
+         RSN IE and refuse to drop PMKIDs that wpa_supplicant does not know
+         about
+
+2008-11-01 - v0.6.5
+       * added support for SHA-256 as X.509 certificate digest when using the
+         internal X.509/TLSv1 implementation
+       * updated management frame protection to use IEEE 802.11w/D6.0
+       * added support for using SHA256-based stronger key derivation for WPA2
+         (IEEE 802.11w)
+       * fixed FT (IEEE 802.11r) authentication after a failed association to
+         use correct FTIE
+       * added support for configuring Phase 2 (inner/tunneled) authentication
+         method with wpa_gui-qt4
+
+2008-08-10 - v0.6.4
+       * added support for EAP Sequences in EAP-FAST Phase 2
+       * added support for using TNC with EAP-FAST
+       * added driver_ps3 for the PS3 Linux wireless driver
+       * added support for optional cryptobinding with PEAPv0
+       * fixed the OpenSSL patches (0.9.8g and 0.9.9) for EAP-FAST to
+         allow fallback to full handshake if server rejects PAC-Opaque
+       * added fragmentation support for EAP-TNC
+       * added support for parsing PKCS #8 formatted private keys into the
+         internal TLS implementation (both PKCS #1 RSA key and PKCS #8
+         encapsulated RSA key can now be used)
+       * added option of using faster, but larger, routines in the internal
+         LibTomMath (for internal TLS implementation) to speed up DH and RSA
+         calculations (CONFIG_INTERNAL_LIBTOMMATH_FAST=y)
+       * fixed race condition between disassociation event and group key
+         handshake to avoid getting stuck in incorrect state [Bug 261]
+       * fixed opportunistic key caching (proactive_key_caching)
+
+2008-02-22 - v0.6.3
+       * removed 'nai' and 'eappsk' network configuration variables that were
+         previously used for configuring user identity and key for EAP-PSK,
+         EAP-PAX, EAP-SAKE, and EAP-GPSK. 'identity' field is now used as the
+         replacement for 'nai' (if old configuration used a separate
+         'identity' value, that would now be configured as
+         'anonymous_identity'). 'password' field is now used as the
+         replacement for 'eappsk' (it can also be set using hexstring to
+         present random binary data)
+       * removed '-w' command line parameter (wait for interface to be added,
+         if needed); cleaner way of handling this functionality is to use an
+         external mechanism (e.g., hotplug scripts) that start wpa_supplicant
+         when an interface is added
+       * updated FT support to use the latest draft, IEEE 802.11r/D9.0
+       * added ctrl_iface monitor event (CTRL-EVENT-SCAN-RESULTS) for
+         indicating when new scan results become available
+       * added new ctrl_iface command, BSS, to allow scan results to be
+         fetched without hitting the message size limits (this command
+         can be used to iterate through the scan results one BSS at the time)
+       * fixed EAP-SIM not to include AT_NONCE_MT and AT_SELECTED_VERSION
+         attributes in EAP-SIM Start/Response when using fast reauthentication
+       * fixed EAPOL not to end up in infinite loop when processing dynamic
+         WEP keys with IEEE 802.1X
+       * fixed problems in getting NDIS events from WMI on Windows 2000
+
+2008-01-01 - v0.6.2
+       * added support for Makefile builds to include debug-log-to-a-file
+         functionality (CONFIG_DEBUG_FILE=y and -f<path> on command line)
+       * 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 wpa_supplicant 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 (phase1="result_ind=1")
+       * added driver_wext workaround for race condition between scanning and
+         association with drivers that take very long time to scan all
+         channels (e.g., madwifi with dual-band cards); wpa_supplicant is now
+         using a longer hardcoded timeout for the scan if the driver supports
+         notifications for scan completion (SIOCGIWSCAN event); this helps,
+         e.g., in cases where wpa_supplicant and madwifi driver ended up in
+         loop where the driver did not even try to associate
+       * stop EAPOL timer tick when no timers are in use in order to reduce
+         power consumption (no need to wake up the process once per second)
+         [Bug 237]
+       * added support for privilege separation (run only minimal part of
+         wpa_supplicant functionality as root and rest as unprivileged,
+         non-root process); see 'Privilege separation' in README for details;
+         this is disabled by default and can be enabled with CONFIG_PRIVSEP=y
+         in .config
+       * changed scan results data structure to include all information
+         elements to make it easier to support new IEs; old get_scan_result()
+         driver_ops is still supported for backwards compatibility (results
+         are converted internally to the new format), but all drivers should
+         start using the new get_scan_results2() to make them more likely to
+         work with new features
+       * Qt4 version of wpa_gui (wpa_gui-qt4 subdirectory) is now native Qt4
+         application, i.e., it does not require Qt3Support anymore; Windows
+         binary of wpa_gui.exe is now from this directory and only requires
+         QtCore4.dll and QtGui4.dll libraries
+       * updated Windows binary build to use Qt 4.3.3 and made Qt DLLs
+         available as a separate package to make wpa_gui installation easier:
+         http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip
+       * 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 support for configuring password as NtPasswordHash
+         (16-byte MD4 hash of password) in hash:<32 hex digits> format
+       * added support for fallback from abbreviated TLS handshake to
+         full handshake when using EAP-FAST (e.g., due to an expired
+         PAC-Opaque)
+       * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+         draft (draft-ietf-emu-eap-gpsk-07.txt)
+       * added support for drivers that take care of RSN 4-way handshake
+         internally (WPA_DRIVER_FLAGS_4WAY_HANDSHAKE in get_capa flags and
+         WPA_ALG_PMK in set_key)
+       * added an experimental port for Mac OS X (CONFIG_DRIVER_OSX=y in
+         .config); this version supports only ap_scan=2 mode and allow the
+         driver to take care of the 4-way handshake
+       * fixed a buffer overflow in parsing TSF from scan results when using
+         driver_wext.c with a driver that includes the TSF (e.g., iwl4965)
+         [Bug 232]
+       * updated FT support to use the latest draft, IEEE 802.11r/D8.0
+       * fixed an integer overflow issue in the ASN.1 parser used by the
+         (experimental) internal TLS implementation to avoid a potential
+         buffer read overflow
+       * fixed a race condition with -W option (wait for a control interface
+         monitor before starting) that could have caused the first messages to
+         be lost
+       * added support for processing TNCC-TNCS-Messages to report
+         recommendation (allow/none/isolate) when using TNC [Bug 243]
+
+2007-05-28 - v0.6.0
+       * added network configuration parameter 'frequency' for setting
+         initial channel for IBSS (adhoc) networks
+       * 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-PAX key derivation
+       * fixed EAP-PSK bit ordering of the Flags field
+       * fixed EAP-PEAP/TTLS/FAST to use the correct EAP identifier in
+         tunnelled identity request (previously, the identifier from the outer
+         method was used, not the tunnelled identifier which could be
+         different)
+       * added support for fragmentation of outer TLS packets during Phase 2
+         of EAP-PEAP/TTLS/FAST
+       * fixed EAP-TTLS AVP parser processing for too short AVP lengths
+       * added support for EAP-FAST authentication with inner methods that
+         generate MSK (e.g., EAP-MSCHAPv2 that was previously only supported
+         for PAC provisioning)
+       * added support for authenticated EAP-FAST provisioning
+       * added support for configuring maximum number of EAP-FAST PACs to
+         store in a PAC list (fast_max_pac_list_len=<max> in phase1 string)
+       * added support for storing EAP-FAST PACs in binary format
+         (fast_pac_format=binary in phase1 string)
+       * fixed dbus ctrl_iface to validate message interface before
+         dispatching to avoid a possible segfault [Bug 190]
+       * fixed PeerKey key derivation to use the correct PRF label
+       * updated Windows binary build to link against OpenSSL 0.9.8d and
+         added support for EAP-FAST
+       * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest
+         draft (draft-ietf-emu-eap-gpsk-04.txt)
+       * fixed EAP-AKA Notification processing to allow Notification to be
+         processed after AKA Challenge response has been sent
+       * updated to use IEEE 802.11w/D2.0 for management frame protection
+         (still experimental)
+       * fixed EAP-TTLS implementation not to crash on use of freed memory
+         if TLS library initialization fails
+       * added support for EAP-TNC (Trusted Network Connect)
+         (this version implements the EAP-TNC method and EAP-TTLS changes
+         needed to run two methods in sequence (IF-T) and the IF-IMC and
+         IF-TNCCS interfaces from TNCC)
+
+2006-11-24 - v0.5.6
+       * added experimental, integrated TLSv1 client 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
+         (total size of this internal TLS/crypto code is bit under 50 kB on
+         x86 and the crypto code is shared by rest of the supplicant so some
+         of it was already required; TLSv1/X.509/ASN.1/RSA added about 25 kB)
+       * 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.)
+       * fixed Windows named pipes ctrl_iface to not stop listening for
+         commands if client program opens a named pipe and closes it
+         immediately without sending a command
+       * fixed USIM PIN status determination for the case that PIN is not
+         needed (this allows EAP-AKA to be used with USIM cards that do not
+         use PIN)
+       * added support for reading 3G USIM AID from EF_DIR to allow EAP-AKA to
+         be used with cards that do not support file selection based on
+         partial AID
+       * added support for matching the subjectAltName of the authentication
+         server certificate against multiple name components (e.g.,
+         altsubject_match="DNS:server.example.com;DNS:server2.example.com")
+       * fixed EAP-SIM/AKA key derivation for re-authentication case (only
+         affects IEEE 802.1X with dynamic WEP keys)
+       * changed ctrl_iface network configuration 'get' operations to not
+         return password/key material; if these fields are requested, "*"
+         will be returned if the password/key is set, but the value of the
+         parameter is not exposed
+
+2006-08-27 - v0.5.5
+       * added support for building Windows version with UNICODE defined
+         (wide-char functions)
+       * driver_ndis: fixed static WEP configuration to avoid race condition
+         issues with some NDIS drivers between association and setting WEP
+         keys
+       * driver_ndis: added validation for IELength value in scan results to
+         avoid crashes when using buggy NDIS drivers [Bug 165]
+       * fixed Release|Win32 target in the Visual Studio project files
+         (previously, only Debug|Win32 target was set properly)
+       * changed control interface API call wpa_ctrl_pending() to allow it to
+         return -1 on error (e.g., connection lost); control interface clients
+         will need to make sure that they verify that the value is indeed >0
+         when determining whether there are pending messages
+       * added an alternative control interface backend for Windows targets:
+         Named Pipe (CONFIG_CTRL_IFACE=named_pipe); this is now the default
+         control interface mechanism for Windows builds (previously, UDP to
+         localhost was used)
+       * changed ctrl_interface configuration for UNIX domain sockets:
+         - deprecated ctrl_interface_group variable (it may be removed in
+           future versions)
+         - allow both directory and group be configured with ctrl_interface
+           in following format: DIR=/var/run/wpa_supplicant GROUP=wheel
+         - ctrl_interface=/var/run/wpa_supplicant is still supported for the
+           case when group is not changed
+       * added support for controlling more than one interface per process in
+         Windows version
+       * added a workaround for a case where the AP is using unknown address
+         (e.g., MAC address of the wired interface) as the source address for
+         EAPOL-Key frames; previously, that source address was used as the
+         destination for EAPOL-Key frames and in key derivation; now, BSSID is
+         used even if the source address does not match with it
+         (this resolves an interoperability issue with Thomson SpeedTouch 580)
+       * added a workaround for UDP-based control interface (which was used in
+         Windows builds before this release) to prevent packets with forged
+         addresses from being accepted as local control requests
+       * removed ndis_events.cpp and possibility of using external
+         ndis_events.exe; C version (ndis_events.c) is fully functional and
+         there is no desire to maintain two separate versions of this
+         implementation
+       * ndis_events: Changed NDIS event notification design to use WMI to
+         learn the adapter description through Win32_PnPEntity class; this
+         should fix some cases where the adapter name was not recognized
+         correctly (e.g., with some USB WLAN adapters, e.g., Ralink RT2500
+         USB) [Bug 113]
+       * fixed selection of the first network in ap_scan=2 mode; previously,
+         wpa_supplicant could get stuck in SCANNING state when only the first
+         network for enabled (e.g., after 'wpa_cli select_network 0')
+       * winsvc: added support for configuring ctrl_interface parameters in
+         registry (ctrl_interface string value in
+         HKLM\SOFTWARE\wpa_supplicant\interfaces\0000 key); this new value is
+         required to enable control interface (previously, this was hardcoded
+         to be enabled)
+       * allow wpa_gui subdirectory to be built with both Qt3 and Qt4
+       * converted wpa_gui-qt4 subdirectory to use Qt4 specific project format
+
+2006-06-20 - v0.5.4
+       * fixed build with CONFIG_STAKEY=y [Bug 143]
+       * added support for doing MLME (IEEE 802.11 management frame
+         processing) in wpa_supplicant when using Devicescape IEEE 802.11
+         stack (wireless-dev.git tree)
+       * added a new network block configuration option, fragment_size, to
+         configure the maximum EAP fragment size
+       * driver_ndis: Disable WZC automatically for the selected interface to
+         avoid conflicts with two programs trying to control the radio; WZC
+         will be re-enabled (if it was enabled originally) when wpa_supplicant
+         is terminated
+       * added an experimental TLSv1 client implementation
+         (CONFIG_TLS=internal) that can be used instead of an external TLS
+         library, e.g., to reduce total size requirement on systems that do
+         not include any TLS library by default (this is not yet complete;
+         basic functionality is there, but certificate validation is not yet
+         included)
+       * added PeerKey handshake implementation for IEEE 802.11e
+         direct link setup (DLS) to replace STAKey handshake
+       * fixed WPA PSK update through ctrl_iface for the case where the old
+         PSK was derived from an ASCII passphrase and the new PSK is set as
+         a raw PSK (hex string)
+       * added new configuration option for identifying which network block
+         was used (id_str in wpa_supplicant.conf; included on
+         WPA_EVENT_CONNECT monitor event and as WPA_ID_STR environmental
+         variable in wpa_cli action scripts; in addition WPA_ID variable is
+         set to the current unique identifier that wpa_supplicant assigned
+         automatically for the network and that can be used with
+         GET_NETWORK/SET_NETWORK ctrl_iface commands)
+       * wpa_cli action script is now called only when the connect/disconnect
+         status changes or when associating with a different network
+       * fixed configuration parser not to remove CCMP from group cipher list
+         if WPA-None (adhoc) is used (pairwise=NONE in that case)
+       * fixed integrated NDIS events processing not to hang the process due
+         to a missed change in eloop_win.c API in v0.5.3 [Bug 155]
+       * added support for EAP Generalized Pre-Shared Key (EAP-GPSK,
+         draft-clancy-emu-eap-shared-secret-00.txt)
+       * added Microsoft Visual Studio 2005 solution and project files for
+         build wpa_supplicant for Windows (see vs2005 subdirectory)
+       * eloop_win: fixed unregistration of Windows events
+       * l2_packet_winpcap: fixed a deadlock in deinitializing l2_packet
+         at the end of RSN pre-authentication and added unregistration of
+         a Windows event to avoid getting eloop_win stuck with an invalid
+         handle
+       * driver_ndis: added support for selecting AP based on BSSID
+       * added new environmental variable for wpa_cli action scripts:
+         WPA_CTRL_DIR is the current control interface directory
+       * driver_ndis: added support for using NDISUIO instead of WinPcap for
+         OID set/query operations (CONFIG_USE_NDISUIO=y in .config); with new
+         l2_packet_ndis (CONFIG_L2_PACKET=ndis), this can be used to build
+         wpa_supplicant without requiring WinPcap; note that using NDISUIO
+         requires that WZC is disabled (net stop wzcsvc) since NDISUIO allows
+         only one application to open the device
+       * changed NDIS driver naming to only include device GUID, e.g.,
+         {7EE3EFE5-C165-472F-986D-F6FBEDFE8C8D}, instead of including WinPcap
+         specific \Device\NPF_ prefix before the GUID; the prefix is still
+         allowed for backwards compatibility, but it is not required anymore
+         when specifying the interface
+       * driver_ndis: re-initialize driver interface is the adapter is removed
+         and re-inserted [Bug 159]
+       * driver_madwifi: fixed TKIP and CCMP sequence number configuration on
+         big endian hosts [Bug 146]
+
+2006-04-27 - v0.5.3
+       * fixed EAP-GTC response to include correct user identity when run as
+         phase 2 method of EAP-FAST (i.e., EAP-FAST did not work in v0.5.2)
+       * driver_ndis: Fixed encryption mode configuration for unencrypted
+         networks (some NDIS drivers ignored this, but others, e.g., Broadcom,
+         refused to associate with open networks) [Bug 106]
+       * driver_ndis: use BSSID OID polling to detect when IBSS network is
+         formed even when ndis_events code is included since some NDIS drivers
+         do not generate media connect events in IBSS mode
+       * config_winreg: allow global ctrl_interface parameter to be configured
+         in Windows registry
+       * config_winreg: added support for saving configuration data into
+         Windows registry
+       * added support for controlling network device operational state
+         (dormant/up) for Linux 2.6.17 to improve DHCP processing (see
+         http://www.flamewarmaster.de/software/dhcpclient/ for a DHCP client
+         that can use this information)
+       * driver_wext: added support for WE-21 change to SSID configuration
+       * driver_wext: fixed privacy configuration for static WEP keys mode
+         [Bug 140]
+       * added an optional driver_ops callback for MLME-SETPROTECTION.request
+         primitive
+       * added support for EAP-SAKE (no EAP method number allocated yet, so
+         this is using the same experimental type 255 as EAP-PSK)
+       * added support for dynamically loading EAP methods (.so files) instead
+         of requiring them to be statically linked in; this is disabled by
+         default (see CONFIG_DYNAMIC_EAP_METHODS in defconfig for information
+         on how to use this)
+
+2006-03-19 - v0.5.2
+       * do not try to use USIM APDUs when initializing PC/SC for SIM card
+         access for a network that has not enabled EAP-AKA
+       * fixed EAP phase 2 Nak for EAP-{PEAP,TTLS,FAST} (this was broken in
+         v0.5.1 due to the new support for expanded EAP types)
+       * added support for generating EAP Expanded Nak
+       * try to fetch scan results once before requesting new scan when
+         starting up in ap_scan=1 mode (this can speed up initial association
+         a lot with, e.g., madwifi-ng driver)
+       * added support for receiving EAPOL frames from a Linux bridge
+         interface (-bbr0 on command line)
+       * fixed EAPOL re-authentication for sessions that used PMKSA caching
+       * changed EAP method registration to use a dynamic list of methods
+         instead of a static list generated at build time
+       * fixed PMKSA cache deinitialization not to use freed memory when
+         removing PMKSA entries
+       * fixed a memory leak in EAP-TTLS re-authentication
+       * reject WPA/WPA2 message 3/4 if it does not include any valid
+         WPA/RSN IE
+       * driver_wext: added fallback to use SIOCSIWENCODE for setting auth_alg
+         if the driver does not support SIOCSIWAUTH
+
+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 (driver_param=test_dir=/tmp/test)
+       * added support for EAP expanded type (vendor specific EAP methods)
+       * added AP_SCAN command into ctrl_iface so that ap_scan configuration
+         option can be changed if needed
+       * wpa_cli/wpa_gui: skip non-socket files in control directory when
+         using UNIX domain sockets; this avoids selecting an incorrect
+         interface (e.g., a PID file could be in this directory, even though
+         use of this directory for something else than socket files is not
+         recommended)
+       * fixed TLS library deinitialization after RSN pre-authentication not
+         to disable TLS library for normal authentication
+       * driver_wext: Remove null-termination from SSID length if the driver
+         used it; some Linux drivers do this and they were causing problems in
+         wpa_supplicant not finding matching configuration block. This change
+         would break a case where the SSID actually ends in '\0', but that is
+         not likely to happen in real use.
+       * fixed PMKSA cache processing not to trigger deauthentication if the
+         current PMKSA cache entry is replaced with a valid new entry
+       * fixed PC/SC initialization for ap_scan != 1 modes (this fixes
+         EAP-SIM and EAP-AKA with real SIM/USIM card when using ap_scan=0 or
+         ap_scan=2)
+
+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)
+       * fixed EAP-SIM and EAP-AKA pseudonym and fast re-authentication to
+         decrypt AT_ENCR_DATA attributes correctly
+       * fixed EAP-AKA to allow resynchronization within the same session
+       * made code closer to ANSI C89 standard to make it easier to port to
+         other C libraries and compilers
+       * started moving operating system or C library specific functions into
+         wrapper functions defined in os.h and implemented in os_*.c to make
+         code more portable
+       * wpa_supplicant can now be built with Microsoft Visual C++
+         (e.g., with the freely available Toolkit 2003 version or Visual
+         C++ 2005 Express Edition and Platform SDK); see nmake.mak for an
+         example makefile for nmake
+       * added support for using Windows registry for command line parameters
+         (CONFIG_MAIN=main_winsvc) and configuration data
+         (CONFIG_BACKEND=winreg); see win_example.reg for an example registry
+         contents; this version can be run both as a Windows service and as a
+         normal application; 'wpasvc.exe app' to start as applicant,
+         'wpasvc.exe reg <full path to wpasvc.exe>' to register a service,
+         'net start wpasvc' to start the service, 'wpasvc.exe unreg' to
+         unregister a service
+       * made it possible to link ndis_events.exe functionality into
+         wpa_supplicant.exe by defining CONFIG_NDIS_EVENTS_INTEGRATED
+       * added better support for multiple control interface backends
+         (CONFIG_CTRL_IFACE option); currently, 'unix' and 'udp' are supported
+       * fixed PC/SC code to use correct length for GSM AUTH command buffer
+         and to not use pioRecvPci with SCardTransmit() calls; these were not
+         causing visible problems with pcsc-lite, but Windows Winscard.dll
+         refused the previously used parameters; this fixes EAP-SIM and
+         EAP-AKA authentication using SIM/USIM card under Windows
+       * added new event loop implementation for Windows using
+         WaitForMultipleObject() instead of select() in order to allow waiting
+         for non-socket objects; this can be selected with
+         CONFIG_ELOOP=eloop_win in .config
+       * added support for selecting l2_packet implementation in .config
+         (CONFIG_L2_PACKET; following options are available now: linux, pcap,
+         winpcap, freebsd, none)
+       * added new l2_packet implementation for WinPcap
+         (CONFIG_L2_PACKET=winpcap) that uses a separate receive thread to
+         reduce latency in EAPOL receive processing from about 100 ms to about
+         3 ms
+       * added support for EAP-FAST key derivation using other ciphers than
+         RC4-128-SHA for authentication and AES128-SHA for provisioning
+       * added support for configuring CA certificate as DER file and as a
+         configuration blob
+       * fixed private key configuration as configuration blob and added
+         support for using PKCS#12 as a blob
+       * tls_gnutls: added support for using PKCS#12 files; added support for
+         session resumption
+       * added support for loading trusted CA certificates from Windows
+         certificate store: ca_cert="cert_store://<name>", where <name> is
+         likely CA (Intermediate CA certificates) or ROOT (root certificates)
+       * added C version of ndis_events.cpp and made it possible to build this
+         with MinGW so that CONFIG_NDIS_EVENTS_INTEGRATED can be used more
+         easily on cross-compilation builds
+       * added wpasvc.exe into Windows binary release; this is an alternative
+         version of wpa_supplicant.exe with configuration backend using
+         Windows registry and with the entry point designed to run as a
+         Windows service
+       * integrated ndis_events.exe functionality into wpa_supplicant.exe and
+         wpasvc.exe and removed this additional tool from the Windows binary
+         release since it is not needed anymore
+       * load winscard.dll functions dynamically when building with MinGW
+         since MinGW does not yet include winscard library
+
+2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases)
+       * l2_packet_pcap: fixed wired IEEE 802.1X authentication with libpcap
+         and WinPcap to receive frames sent to PAE group address
+       * disable EAP state machine when IEEE 802.1X authentication is not used
+         in order to get rid of bogus "EAP failed" messages
+       * fixed OpenSSL error reporting to go through all pending errors to
+         avoid confusing reports of old errors being reported at later point
+         during handshake
+       * fixed configuration file updating to not write empty variables
+         (e.g., proto or key_mgmt) that the file parser would not accept
+       * fixed ADD_NETWORK ctrl_iface command to use the same default values
+         for variables as empty network definitions read from config file
+         would get
+       * fixed EAP state machine to not discard EAP-Failure messages in many
+         cases (e.g., during TLS handshake)
+       * fixed a infinite loop in private key reading if the configured file
+         cannot be parsed successfully
+       * driver_madwifi: added support for madwifi-ng
+       * wpa_gui: do not display password/PSK field contents
+       * wpa_gui: added CA certificate configuration
+       * driver_ndis: fixed scan request in ap_scan=2 mode not to change SSID
+       * driver_ndis: include Beacon IEs in AssocInfo in order to notice if
+         the new AP is using different WPA/RSN IE
+       * use longer timeout for IEEE 802.11 association to avoid problems with
+         drivers that may take more than five second to associate
+
+2005-10-27 - v0.4.6
+       * allow fallback to WPA, if mixed WPA+WPA2 networks have mismatch in
+         RSN IE, but WPA IE would match with wpa_supplicant configuration
+       * added support for named configuration blobs in order to avoid having
+         to use file system for external files (e.g., certificates);
+         variables can be set to "blob://<blob name>" instead of file path to
+         use a named blob; supported fields: pac_file, client_cert,
+         private_key
+       * fixed RSN pre-authentication (it was broken in the clean up of WPA
+         state machine interface in v0.4.5)
+       * driver_madwifi: set IEEE80211_KEY_GROUP flag for group keys to make
+         sure the driver configures broadcast decryption correctly
+       * added ca_path (and ca_path2) configuration variables that can be used
+         to configure OpenSSL CA path, e.g., /etc/ssl/certs, for using the
+         system-wide trusted CA list
+       * added support for starting wpa_supplicant without a configuration
+         file (-C argument must be used to set ctrl_interface parameter for
+         this case; in addition, -p argument can be used to provide
+         driver_param; these new arguments can also be used with a
+         configuration to override the values from the configuration)
+       * added global control interface that can be optionally used for adding
+         and removing network interfaces dynamically (-g command line argument
+         for both wpa_supplicant and wpa_cli) without having to restart
+         wpa_supplicant process
+       * wpa_gui:
+         - try to save configuration whenever something is modified
+         - added WEP key configuration
+         - added possibility to edit the current network configuration
+       * driver_ndis: fixed driver polling not to increase frequency on each
+         received EAPOL frame due to incorrectly cancelled timeout
+       * added simple configuration file examples (in examples subdirectory)
+       * fixed driver_wext.c to filter wireless events based on ifindex to
+         avoid interfaces receiving events from other interfaces
+       * delay sending initial EAPOL-Start couple of seconds to speed up
+         authentication for the most common case of Authenticator starting
+         EAP authentication immediately after association
+
+2005-09-25 - v0.4.5
+       * added a workaround for clearing keys with ndiswrapper to allow
+         roaming from WPA enabled AP to plaintext one
+       * added docbook documentation (doc/docbook) that can be used to
+         generate, e.g., man pages
+       * l2_packet_linux: use socket type SOCK_DGRAM instead of SOCK_RAW for
+         PF_PACKET in order to prepare for network devices that do not use
+         Ethernet headers (e.g., network stack with native IEEE 802.11 frames)
+       * use receipt of EAPOL-Key frame as a lower layer success indication
+         for EAP state machine to allow recovery from dropped EAP-Success
+         frame
+       * cleaned up internal EAPOL frame processing by not including link
+         layer (Ethernet) header during WPA and EAPOL/EAP processing; this
+         header is added only when transmitted the frame; this makes it easier
+         to use wpa_supplicant on link layers that use different header than
+         Ethernet
+       * updated EAP-PSK to use draft 9 by default since this can now be
+         tested with hostapd; removed support for draft 3, including
+         server_nai configuration option from network blocks
+       * driver_wired: add PAE address to the multicast address list in order
+         to be able to receive EAPOL frames with drivers that do not include
+         these multicast addresses by default
+       * driver_wext: add support for WE-19
+       * added support for multiple configuration backends (CONFIG_BACKEND
+         option); currently, only 'file' is supported (i.e., the format used
+         in wpa_supplicant.conf)
+       * added support for updating configuration ('wpa_cli save_config');
+         this is disabled by default and can be enabled with global
+         update_config=1 variable in wpa_supplicant.conf; this allows wpa_cli
+         and wpa_gui to store the configuration changes in a permanent store
+       * added GET_NETWORK ctrl_iface command
+         (e.g., 'wpa_cli get_network 0 ssid')
+
+2005-08-21 - v0.4.4
+       * replaced OpenSSL patch for EAP-FAST support
+         (openssl-tls-extensions.patch) with a more generic and correct
+         patch (the new patch is not compatible with the previous one, so the
+         OpenSSL library will need to be patched with the new patch in order
+         to be able to build wpa_supplicant with EAP-FAST support)
+       * added support for using Windows certificate store (through CryptoAPI)
+         for client certificate and private key operations (EAP-TLS)
+         (see wpa_supplicant.conf for more information on how to configure
+         this with private_key)
+       * ported wpa_gui to Windows
+       * added Qt4 version of wpa_gui (wpa_gui-qt4 directory); this can be
+         built with the open source version of the Qt4 for Windows
+       * allow non-WPA modes (e.g., IEEE 802.1X with dynamic WEP) to be used
+         with drivers that do not support WPA
+       * ndis_events: fixed Windows 2000 support
+       * added support for enabling/disabling networks from the list of all
+         configured networks ('wpa_cli enable_network <network id>' and
+         'wpa_cli disable_network <network id>')
+       * added support for adding and removing network from the current
+         configuration ('wpa_cli add_network' and 'wpa_cli remove_network
+         <network id>'); added networks are disabled by default and they can
+         be enabled with enable_network command once the configuration is done
+         for the new network; note: configuration file is not yet updated, so
+         these new networks are lost when wpa_supplicant is restarted
+       * added support for setting network configuration parameters through
+         the control interface, for example:
+         wpa_cli set_network 0 ssid "\"my network\""
+       * fixed parsing of strings that include both " and # within double
+         quoted area (e.g., "start"#end")
+       * added EAP workaround for PEAP session resumption: allow outer,
+         i.e., not tunneled, EAP-Success to terminate session since; this can
+         be disabled with eap_workaround=0
+         (this was allowed for PEAPv1 before, but now it is also allowed for
+         PEAPv0 since at least one RADIUS authentication server seems to be
+         doing this for PEAPv0, too)
+       * wpa_gui: added preliminary support for adding new networks to the
+         wpa_supplicant configuration (double click on the scan results to
+         open network configuration)
+
+2005-06-26 - v0.4.3
+       * removed interface for external EAPOL/EAP supplicant (e.g.,
+         Xsupplicant), (CONFIG_XSUPPLICANT_IFACE) since it is not required
+         anymore and is unlikely to be used by anyone
+       * driver_ndis: fixed WinPcap 3.0 support
+       * fixed build with CONFIG_DNET_PCAP=y on Linux
+       * l2_packet: moved different implementations into separate files
+         (l2_packet_*.c)
+
+2005-06-12 - v0.4.2
+       * driver_ipw: updated driver structures to match with ipw2200-1.0.4
+         (note: ipw2100-1.1.0 is likely to require an update to work with
+         this)
+       * added support for using ap_scan=2 mode with multiple network blocks;
+         wpa_supplicant will go through the networks one by one until the
+         driver reports a successful association; this uses the same order for
+         networks as scan_ssid=1 scans, i.e., the priority field is ignored
+         and the network block order in the file is used instead
+       * fixed a potential issue in RSN pre-authentication ending up using
+         freed memory if pre-authentication times out
+       * added support for matching alternative subject name extensions of the
+         authentication server certificate; new configuration variables
+         altsubject_match and altsubject_match2
+       * driver_ndis: added support for IEEE 802.1X authentication with wired
+         NDIS drivers
+       * added support for querying private key password (EAP-TLS) through the
+         control interface (wpa_cli/wpa_gui) if one is not included in the
+         configuration file
+       * driver_broadcom: fixed couple of memory leaks in scan result
+         processing
+       * EAP-PAX is now registered as EAP type 46
+       * fixed EAP-PAX MAC calculation
+       * fixed EAP-PAX CK and ICK key derivation
+       * added support for using password with EAP-PAX (as an alternative to
+         entering key with eappsk); SHA-1 hash of the password will be used as
+         the key in this case
+       * added support for arbitrary driver interface parameters through the
+         configuration file with a new driver_param field; this adds a new
+         driver_ops function set_param()
+       * added possibility to override l2_packet module with driver interface
+         API (new send_eapol handler); this can be used to implement driver
+         specific TX/RX functions for EAPOL frames
+       * fixed ctrl_interface_group processing for the case where gid is
+         entered as a number, not group name
+       * 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
+       * driver_madwifi: fixed WPA/WPA2 mode configuration to allow EAPOL
+         packets to be encrypted; this was apparently broken by the changed
+         ioctl order in v0.4.0
+       * driver_madwifi: added preliminary support for compiling against 'BSD'
+         branch of madwifi CVS tree
+       * added support for EAP-MSCHAPv2 password retries within the same EAP
+         authentication session
+       * added support for password changes with EAP-MSCHAPv2 (used when the
+         password has expired)
+       * added support for reading additional certificates from PKCS#12 files
+         and adding them to the certificate chain
+       * fixed association with IEEE 802.1X (no WPA) when dynamic WEP keys
+         were used
+       * fixed a possible double free in EAP-TTLS fast-reauthentication when
+         identity or password is entered through control interface
+       * display EAP Notification messages to user through control interface
+         with "CTRL-EVENT-EAP-NOTIFICATION" prefix
+       * added GUI version of wpa_cli, wpa_gui; this is not build
+         automatically with 'make'; use 'make wpa_gui' to build (this requires
+         Qt development tools)
+       * added 'disconnect' command to control interface for setting
+         wpa_supplicant in state where it will not associate before
+         'reassociate' command has been used
+       * added support for selecting a network from the list of all configured
+         networks ('wpa_cli select_network <network id>'; this disabled all
+         other networks; to re-enable, 'wpa_cli select_network any')
+       * added support for getting scan results through control interface
+       * added EAP workaround for PEAPv1 session resumption: allow outer,
+         i.e., not tunneled, EAP-Success to terminate session since; this can
+         be disabled with eap_workaround=0
+
+2005-04-25 - v0.4.0 (beginning of 0.4.x development releases)
+       * added a new build time option, CONFIG_NO_STDOUT_DEBUG, that can be
+         used to reduce the size of the wpa_supplicant considerably if
+         debugging code is not needed
+       * fixed EAPOL-Key validation to drop packets with invalid Key Data
+         Length; such frames could have crashed wpa_supplicant due to buffer
+         overflow
+       * added support for wired authentication (IEEE 802.1X on wired
+         Ethernet); driver interface 'wired'
+       * obsoleted set_wpa() handler in the driver interface API (it can be
+         replaced by moving enable/disable functionality into init()/deinit())
+         (calls to set_wpa() are still present for backwards compatibility,
+         but they may be removed in the future)
+       * driver_madwifi: fixed association in plaintext mode
+       * modified the EAP workaround that accepts EAP-Success with incorrect
+         Identifier to be even less strict about verification in order to
+         interoperate with some authentication servers
+       * added support for sending TLS alerts
+       * added support for 'any' SSID wildcard; if ssid is not configured or
+         is set to an empty string, any SSID will be accepted for non-WPA AP
+       * added support for asking PIN (for SIM) from frontends (e.g.,
+         wpa_cli); if a PIN is needed, but not included in the configuration
+         file, a control interface request is sent and EAP processing is
+         delayed until the PIN is available
+       * added support for using external devices (e.g., a smartcard) for
+         private key operations in EAP-TLS (CONFIG_SMARTCARD=y in .config);
+         new wpa_supplicant.conf variables:
+         - global: opensc_engine_path, pkcs11_engine_path, pkcs11_module_path
+         - network: engine, engine_id, key_id
+       * added experimental support for EAP-PAX
+       * added monitor mode for wpa_cli (-a<path to a program to run>) that
+         allows external commands (e.g., shell scripts) to be run based on
+         wpa_supplicant events, e.g., when authentication has been completed
+         and data connection is ready; other related wpa_cli arguments:
+         -B (run in background), -P (write PID file); wpa_supplicant has a new
+         command line argument (-W) that can be used to make it wait until a
+         control interface command is received in order to avoid missing
+         events
+       * added support for opportunistic WPA2 PMKSA key caching (disabled by
+         default, can be enabled with proactive_key_caching=1)
+       * fixed RSN IE in 4-Way Handshake message 2/4 for the case where
+         Authenticator rejects PMKSA caching attempt and the driver is not
+         using assoc_info events
+       * added -P<pid file> argument for wpa_supplicant to write the current
+         process id into a file
+
+2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases)
+       * added new phase1 option parameter, include_tls_length=1, to force
+         wpa_supplicant to add TLS Message Length field to all TLS messages
+         even if the packet is not fragmented; this may be needed with some
+         authentication servers
+       * fixed WPA/RSN IE verification in message 3 of 4-Way Handshake when
+         using drivers that take care of AP selection (e.g., when using
+         ap_scan=2)
+       * fixed reprocessing of pending request after ctrl_iface requests for
+         identity/password/otp
+       * fixed ctrl_iface requests for identity/password/otp in Phase 2 of
+         EAP-PEAP and EAP-TTLS
+       * all drivers using driver_wext: set interface up and select Managed
+         mode when starting wpa_supplicant; set interface down when exiting
+       * renamed driver_ipw2100.c to driver_ipw.c since it now supports both
+         ipw2100 and ipw2200; please note that this also changed the
+         configuration variable in .config to CONFIG_DRIVER_IPW
+
+2005-01-24 - v0.3.6
+       * fixed a busy loop introduced in v0.3.5 for scan result processing
+         when no matching AP is found
+
+2005-01-23 - v0.3.5
+       * added a workaround for an interoperability issue with a Cisco AP
+         when using WPA2-PSK
+       * fixed non-WPA IEEE 802.1X to use the same authentication timeout as
+         WPA with IEEE 802.1X (i.e., timeout 10 -> 70 sec to allow
+         retransmission of dropped frames)
+       * fixed issues with 64-bit CPUs and SHA1 cleanup in previous version
+         (e.g., segfault when processing EAPOL-Key frames)
+       * fixed EAP workaround and fast reauthentication configuration for
+         RSN pre-authentication; previously these were disabled and
+         pre-authentication would fail if the used authentication server
+         requires EAP workarounds
+       * added support for blacklisting APs that fail or timeout
+         authentication in ap_scan=1 mode so that all APs are tried in cases
+         where the ones with strongest signal level are failing authentication
+       * fixed CA certificate loading after a failed EAP-TLS/PEAP/TTLS
+         authentication attempt
+       * allow EAP-PEAP/TTLS fast reauthentication only if Phase 2 succeeded
+         in the previous authentication (previously, only Phase 1 success was
+         verified)
+
+2005-01-09 - v0.3.4
+       * added preliminary support for IBSS (ad-hoc) mode configuration
+         (mode=1 in network block); this included a new key_mgmt mode
+         WPA-NONE, i.e., TKIP or CCMP with a fixed key (based on psk) and no
+         key management; see wpa_supplicant.conf for more details and an
+         example on how to configure this (note: this is currently implemented
+         only for driver_hostapd.c, but the changes should be trivial to add
+         in associate() handler for other drivers, too (assuming the driver
+         supports WPA-None)
+       * added preliminary port for native Windows (i.e., no cygwin) using
+         mingw
+
+2005-01-02 - v0.3.3
+       * added optional support for GNU Readline and History Libraries for
+         wpa_cli (CONFIG_READLINE)
+       * cleaned up EAP state machine <-> method interface and number of
+         small problems with error case processing not terminating on
+         EAP-Failure but waiting for timeout
+       * added couple of workarounds for interoperability issues with a
+         Cisco AP when using WPA2
+       * added support for EAP-FAST (draft-cam-winget-eap-fast-00.txt);
+         Note: This requires a patch for openssl to add support for TLS
+         extensions and number of workarounds for operations without
+         certificates. Proof of concept type of experimental patch is
+         included in openssl-tls-extensions.patch.
+
+2004-12-19 - v0.3.2
+       * fixed private key loading for cases where passphrase is not set
+       * fixed Windows/cygwin L2 packet handler freeing; previous version
+         could cause a segfault when RSN pre-authentication was completed
+       * added support for PMKSA caching with drivers that generate RSN IEs
+         (e.g., NDIS); currently, this is only implemented in driver_ndis.c,
+         but similar code can be easily added to driver_ndiswrapper.c once
+         ndiswrapper gets full support for RSN PMKSA caching
+       * improved recovery from PMKID mismatches by requesting full EAP
+         authentication in case of failed PMKSA caching attempt
+       * driver_ndis: added support for NDIS NdisMIncidateStatus() events
+         (this requires that ndis_events is ran while wpa_supplicant is
+         running)
+       * driver_ndis: use ADD_WEP/REMOVE_WEP when configuring WEP keys
+       * added support for driver interfaces to replace the interface name
+         based on driver/OS specific mapping, e.g., in case of driver_ndis,
+         this allows the beginning of the adapter description to be used as
+         the interface name
+       * added support for CR+LF (Windows-style) line ends in configuration
+         file
+       * driver_ndis: enable radio before starting scanning, disable radio
+         when exiting
+       * modified association event handler to set portEnabled = FALSE before
+         clearing port Valid in order to reset EAP state machine and avoid
+         problems with new authentication getting ignored because of state
+         machines ending up in AUTHENTICATED/SUCCESS state based on old
+         information
+       * added support for driver events to add PMKID candidates in order to
+         allow drivers to give priority to most likely roaming candidates
+       * driver_hostap: moved PrivacyInvoked configuration to associate()
+         function so that this will not be set for plaintext connections
+       * added KEY_MGMT_802_1X_NO_WPA as a new key_mgmt type so that driver
+         interface can distinguish plaintext and IEEE 802.1X (no WPA)
+         authentication
+       * fixed static WEP key configuration to use broadcast/default type for
+         all keys (previously, the default TX key was configured as pairwise/
+         unicast key)
+       * driver_ndis: added legacy WPA capability detection for non-WPA2
+         drivers
+       * added support for setting static WEP keys for IEEE 802.1X without
+         dynamic WEP keying (eapol_flags=0)
+
+2004-12-12 - v0.3.1
+       * added support for reading PKCS#12 (PFX) files (as a replacement for
+         PEM/DER) to get certificate and private key (CONFIG_PKCS12)
+       * fixed compilation with CONFIG_PCSC=y
+       * added new ap_scan mode, ap_scan=2, for drivers that take care of
+         association, but need to be configured with security policy and SSID,
+         e.g., ndiswrapper and NDIS driver; this mode should allow such
+         drivers to work with hidden SSIDs and optimized roaming; when
+         ap_scan=2 is used, only the first network block in the configuration
+         file is used and this configuration should have explicit security
+         policy (i.e., only one option in the lists) for key_mgmt, pairwise,
+         group, proto variables
+       * added experimental port of wpa_supplicant for Windows
+         - driver_ndis.c driver interface (NDIS OIDs)
+         - currently, this requires cygwin and WinPcap
+         - small utility, win_if_list, can be used to get interface name
+       * control interface can now be removed at build time; add
+         CONFIG_CTRL_IFACE=y to .config to maintain old functionality
+       * optional Xsupplicant interface can now be removed at build time;
+         (CONFIG_XSUPPLICANT_IFACE=y in .config to bring it back)
+       * added auth_alg to driver interface associate() parameters to make it
+         easier for drivers to configure authentication algorithm as part of
+         the association
+
+2004-12-05 - v0.3.0 (beginning of 0.3.x development releases)
+       * driver_broadcom: added new driver interface for Broadcom wl.o driver
+         (a generic driver for Broadcom IEEE 802.11a/g cards)
+       * wpa_cli: fixed parsing of -p <path> command line argument
+       * PEAPv1: fixed tunneled EAP-Success reply handling to reply with TLS
+         ACK, not tunneled EAP-Success (of which only the first byte was
+         actually send due to a bug in previous code); this seems to
+         interoperate with most RADIUS servers that implements PEAPv1
+       * PEAPv1: added support for terminating PEAP authentication on tunneled
+         EAP-Success message; this can be configured by adding
+         peap_outer_success=0 on phase1 parameters in wpa_supplicant.conf
+         (some RADIUS servers require this whereas others require a tunneled
+         reply
+       * PEAPv1: changed phase1 option peaplabel to use default to 0, i.e., to
+         the old label for key derivation; previously, the default was 1,
+         but it looks like most existing PEAPv1 implementations use the old
+         label which is thus more suitable default option
+       * added support for EAP-PSK (draft-bersani-eap-psk-03.txt)
+       * fixed parsing of wep_tx_keyidx
+       * added support for configuring list of allowed Phase 2 EAP types
+         (for both EAP-PEAP and EAP-TTLS) instead of only one type
+       * added support for configuring IEEE 802.11 authentication algorithm
+         (auth_alg; mainly for using Shared Key authentication with static
+         WEP keys)
+       * added support for EAP-AKA (with UMTS SIM)
+       * fixed couple of errors in PCSC handling that could have caused
+         random-looking errors for EAP-SIM
+       * added support for EAP-SIM pseudonyms and fast re-authentication
+       * added support for EAP-TLS/PEAP/TTLS fast re-authentication (TLS
+         session resumption)
+       * added support for EAP-SIM with two challanges
+         (phase1="sim_min_num_chal=3" can be used to require three challenges)
+       * added support for configuring DH/DSA parameters for an ephemeral DH
+         key exchange (EAP-TLS/PEAP/TTLS) using new configuration parameters
+         dh_file and dh_file2 (phase 2); this adds support for using DSA keys
+         and optional DH key exchange to achieve forward secracy with RSA keys
+       * added support for matching subject of the authentication server
+         certificate with a substring when using EAP-TLS/PEAP/TTLS; new
+         configuration variables subject_match and subject_match2
+       * changed SSID configuration in driver_wext.c (used by many driver
+         interfaces) to use ssid_len+1 as the length for SSID since some Linux
+         drivers expect this
+       * fixed couple of unaligned reads in scan result parsing to fix WPA
+         connection on some platforms (e.g., ARM)
+       * added driver interface for Intel ipw2100 driver
+       * added support for LEAP with WPA
+       * added support for larger scan results report (old limit was 4 kB of
+         data, i.e., about 35 or so APs) when using Linux wireless extensions
+         v17 or newer
+       * fixed a bug in PMKSA cache processing: skip sending of EAPOL-Start
+         only if there is a PMKSA cache entry for the current AP
+       * fixed error handling for case where reading of scan results fails:
+         must schedule a new scan or wpa_supplicant will remain waiting
+         forever
+       * changed debug output to remove shared password/key material by
+         default; all key information can be included with -K command line
+         argument to match the previous behavior
+       * added support for timestamping debug log messages (disabled by
+         default, can be enabled with -t command line argument)
+       * set pairwise/group cipher suite for non-WPA IEEE 802.1X to WEP-104
+         if keys are not configured to be used; this fixes IEEE 802.1X mode
+         with drivers that use this information to configure whether Privacy
+         bit can be in Beacon frames (e.g., ndiswrapper)
+       * avoid clearing driver keys if no keys have been configured since last
+         key clear request; this seems to improve reliability of group key
+         handshake for ndiswrapper & NDIS driver which seems to be suffering
+         of some kind of timing issue when the keys are cleared again after
+         association
+       * changed driver interface API:
+         - WPA_SUPPLICANT_DRIVER_VERSION define can be used to determine which
+           version is being used (now, this is set to 2; previously, it was
+           not defined)
+         - pass pointer to private data structure to all calls
+         - the new API is not backwards compatible; all in-tree driver
+           interfaces has been converted to the new API
+       * added support for controlling multiple interfaces (radios) per
+         wpa_supplicant process; each interface needs to be listed on the
+         command line (-c, -i, -D arguments) with -N as a separator
+         (-cwpa1.conf -iwlan0 -Dhostap -N -cwpa2.conf -iath0 -Dmadwifi)
+       * added a workaround for EAP servers that incorrectly use same Id for
+         sequential EAP packets
+       * changed libpcap/libdnet configuration to use .config variable,
+         CONFIG_DNET_PCAP, instead of requiring Makefile modification
+       * improved downgrade attack detection in IE verification of msg 3/4:
+         verify both WPA and RSN IEs, if present, not only the selected one;
+         reject the AP if an RSN IE is found in msg 3/4, but not in Beacon or
+         Probe Response frame, and RSN is enabled in wpa_supplicant
+         configuration
+       * fixed WPA msg 3/4 processing to allow Key Data field contain other
+         IEs than just one WPA IE
+       * added support for FreeBSD and driver interface for the BSD net80211
+         layer (CONFIG_DRIVER_BSD=y in .config); please note that some of the
+         required kernel mods have not yet been committed
+       * made EAP workarounds configurable; enabled by default, can be
+         disabled with network block option eap_workaround=0
+
+2004-07-17 - v0.2.4 (beginning of 0.2.x stable releases)
+       * resolved couple of interoperability issues with EAP-PEAPv1 and
+         Phase 2 (inner EAP) fragment reassembly
+       * driver_madwifi: fixed WEP key configuration for IEEE 802.1X when the
+         AP is using non-zero key index for the unicast key and key index zero
+         for the broadcast key
+       * driver_hostap: fixed IEEE 802.1X WEP key updates and
+         re-authentication by allowing unencrypted EAPOL frames when not using
+         WPA
+       * added a new driver interface, 'wext', which uses only standard,
+         driver independent functionality in Linux wireless extensions;
+         currently, this can be used only for non-WPA IEEE 802.1X mode, but
+         eventually, this is to be extended to support full WPA/WPA2 once
+         Linux wireless extensions get support for this
+       * added support for mode in which the driver is responsible for AP
+         scanning and selection; this is disabled by default and can be
+         enabled with global ap_scan=0 variable in wpa_supplicant.conf;
+         this mode can be used, e.g., with generic 'wext' driver interface to
+         use wpa_supplicant as IEEE 802.1X Supplicant with any Linux driver
+         supporting wireless extensions.
+       * driver_madwifi: fixed WPA2 configuration and scan_ssid=1 (e.g.,
+         operation with an AP that does not include SSID in the Beacon frames)
+       * added support for new EAP authentication methods:
+         EAP-TTLS/EAP-OTP, EAP-PEAPv0/OTP, EAP-PEAPv1/OTP, EAP-OTP
+       * added support for asking one-time-passwords from frontends (e.g.,
+         wpa_cli); this 'otp' command works otherwise like 'password' command,
+         but the password is used only once and the frontend will be asked for
+         a new password whenever a request from authenticator requires a
+         password; this can be used with both EAP-OTP and EAP-GTC
+       * changed wpa_cli to automatically re-establish connection so that it
+         does not need to be re-started when wpa_supplicant is terminated and
+         started again
+       * improved user data (identity/password/otp) requests through
+         frontends: process pending EAPOL packets after getting new
+         information so that full authentication does not need to be
+         restarted; in addition, send pending requests again whenever a new
+         frontend is attached
+       * changed control frontends to use a new directory for socket files to
+         make it easier for wpa_cli to automatically select between interfaces
+         and to provide access control for the control interface;
+         wpa_supplicant.conf: ctrl_interface is now a path
+         (/var/run/wpa_supplicant is the recommended path) and
+         ctrl_interface_group can be used to select which group gets access to
+         the control interface;
+         wpa_cli: by default, try to connect to the first interface available
+         in /var/run/wpa_supplicant; this path can be overriden with -p option
+         and an interface can be selected with -i option (i.e., in most common
+         cases, wpa_cli does not need to get any arguments)
+       * added support for LEAP
+       * added driver interface for Linux ndiswrapper
+       * added priority option for network blocks in the configuration file;
+         this allows networks to be grouped based on priority (the scan
+         results are searched for matches with network blocks in this order)
+
+2004-06-20 - v0.2.3
+       * sort scan results to improve AP selection
+       * fixed control interface socket removal for some error cases
+       * improved scan requesting and authentication timeout
+       * small improvements/bug fixes for EAP-MSCHAPv2, EAP-PEAP, and
+         TLS processing
+       * PEAP version can now be forced with phase1="peapver=<ver>"
+         (mostly for testing; by default, the highest version supported by
+         both the Supplicant and Authentication Server is selected
+         automatically)
+       * added support for madwifi driver (Atheros ar521x)
+       * added a workaround for cases where AP sets Install Tx/Rx bit for
+         WPA Group Key messages when pairwise keys are used (without this,
+         the Group Key would be used for Tx and the AP would drop frames
+         from the station)
+       * added GSM SIM/USIM interface for GSM authentication algorithm for
+         EAP-SIM; this requires pcsc-lite
+       * added support for ATMEL AT76C5XXx driver
+       * fixed IEEE 802.1X WEP key derivation in the case where Authenticator
+         does not include key data in the EAPOL-Key frame (i.e., part of
+         EAP keying material is used as data encryption key)
+       * added support for using plaintext and static WEP networks
+         (key_mgmt=NONE)
+
+2004-05-31 - v0.2.2
+       * added support for new EAP authentication methods:
+         EAP-TTLS/EAP-MD5-Challenge
+         EAP-TTLS/EAP-GTC
+         EAP-TTLS/EAP-MSCHAPv2
+         EAP-TTLS/EAP-TLS
+         EAP-TTLS/MSCHAPv2
+         EAP-TTLS/MSCHAP
+         EAP-TTLS/PAP
+         EAP-TTLS/CHAP
+         EAP-PEAP/TLS
+         EAP-PEAP/GTC
+         EAP-PEAP/MD5-Challenge
+         EAP-GTC
+         EAP-SIM (not yet complete; needs GSM/SIM authentication interface)
+       * added support for anonymous identity (to be used when identity is
+         sent in plaintext; real identity will be used within TLS protected
+         tunnel (e.g., with EAP-TTLS)
+       * added event messages from wpa_supplicant to frontends, e.g., wpa_cli
+       * added support for requesting identity and password information using
+         control interface; in other words, the password for EAP-PEAP or
+         EAP-TTLS does not need to be included in the configuration file since
+         a frontand (e.g., wpa_cli) can ask it from the user
+       * improved RSN pre-authentication to use a candidate list and process
+         all candidates from each scan; not only one per scan
+       * fixed RSN IE and WPA IE capabilities field parsing
+       * ignore Tx bit in GTK IE when Pairwise keys are used
+       * avoid making new scan requests during IEEE 802.1X negotiation
+       * use openssl/libcrypto for MD5 and SHA-1 when compiling wpa_supplicant
+         with TLS support (this replaces the included implementation with
+         library code to save about 8 kB since the library code is needed
+         anyway for TLS)
+       * fixed WPA-PSK only mode when compiled without IEEE 802.1X support
+         (i.e., without CONFIG_IEEE8021X_EAPOL=y in .config)
+
+2004-05-06 - v0.2.1
+       * added support for internal IEEE 802.1X (actually, IEEE 802.1aa/D6.1)
+         Supplicant
+         - EAPOL state machines for Supplicant [IEEE 802.1aa/D6.1]
+         - EAP peer state machine [draft-ietf-eap-statemachine-02.pdf]
+         - EAP-MD5 (cannot be used with WPA-RADIUS)
+           [draft-ietf-eap-rfc2284bis-09.txt]
+         - EAP-TLS [RFC 2716]
+         - EAP-MSCHAPv2 (currently used only with EAP-PEAP)
+         - EAP-PEAP/MSCHAPv2 [draft-josefsson-pppext-eap-tls-eap-07.txt]
+           [draft-kamath-pppext-eap-mschapv2-00.txt]
+           (PEAP version 0, 1, and parts of 2; only 0 and 1 are enabled by
+           default; tested with FreeRADIUS, Microsoft IAS, and Funk Odyssey)
+         - new configuration file options: eap, identity, password, ca_cert,
+           client_cert, privatekey, private_key_passwd
+         - Xsupplicant is not required anymore, but it can be used by
+           disabling the internal IEEE 802.1X Supplicant with -e command line
+           option
+         - this code is not included in the default build; Makefile need to
+           be edited for this (uncomment lines for selected functionality)
+         - EAP-TLS and EAP-PEAP require openssl libraries
+       * use module prefix in debug messages (WPA, EAP, EAP-TLS, ..)
+       * added support for non-WPA IEEE 802.1X mode with dynamic WEP keys
+         (i.e., complete IEEE 802.1X/EAP authentication and use IEEE 802.1X
+          EAPOL-Key frames instead of WPA key handshakes)
+       * added support for IEEE 802.11i/RSN (WPA2)
+         - improved PTK Key Handshake
+         - PMKSA caching, pre-authentication
+       * fixed wpa_supplicant to ignore possible extra data after WPA
+         EAPOL-Key packets (this fixes 'Invalid EAPOL-Key MIC when using
+         TPTK' error from message 3 of 4-Way Handshake in case the AP
+         includes extra data after the EAPOL-Key)
+       * added interface for external programs (frontends) to control
+         wpa_supplicant
+         - CLI example (wpa_cli) with interactive mode and command line
+           mode
+         - replaced SIGUSR1 status/statistics with the new control interface
+       * made some feature compile time configurable
+         - .config file for make
+         - driver interfaces (hostap, hermes, ..)
+         - EAPOL/EAP functions
+
+2004-02-15 - v0.2.0
+       * Initial version of wpa_supplicant
diff --git a/wpa_supplicant/Makefile b/wpa_supplicant/Makefile
new file mode 100644 (file)
index 0000000..1d25623
--- /dev/null
@@ -0,0 +1,1380 @@
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+export LIBDIR ?= /usr/local/lib/
+export BINDIR ?= /usr/local/sbin/
+
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+ALL=wpa_supplicant wpa_passphrase wpa_cli
+
+all: verify_config $(ALL) dynamic_eap_methods
+
+verify_config:
+       @if [ ! -r .config ]; then \
+               echo 'Building wpa_supplicant requires a configuration file'; \
+               echo '(.config). See README for more instructions. You can'; \
+               echo 'run "cp defconfig .config" to create an example'; \
+               echo 'configuration.'; \
+               exit 1; \
+       fi
+
+mkconfig:
+       @if [ -f .config ]; then \
+               echo '.config exists - did not replace it'; \
+               exit 1; \
+       fi
+       echo CONFIG_DRIVER_HOSTAP=y >> .config
+       echo CONFIG_DRIVER_WEXT=y >> .config
+
+install: all
+       mkdir -p $(DESTDIR)$(BINDIR)
+       for i in $(ALL); do cp $$i $(DESTDIR)$(BINDIR)/$$i; done
+       $(MAKE) -C ../src install
+
+OBJS = config.o
+OBJS += notify.o
+OBJS += bss.o
+OBJS += eap_register.o
+OBJS += ../src/utils/common.o
+OBJS += ../src/utils/wpa_debug.o
+OBJS += ../src/utils/wpabuf.o
+OBJS_p = wpa_passphrase.o
+OBJS_p += ../src/utils/common.o
+OBJS_p += ../src/utils/wpa_debug.o
+OBJS_p += ../src/utils/wpabuf.o
+OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
+
+-include .config
+
+ifndef CONFIG_OS
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_OS=win32
+else
+CONFIG_OS=unix
+endif
+endif
+
+ifeq ($(CONFIG_OS), internal)
+CFLAGS += -DOS_NO_C_LIB_DEFINES
+endif
+
+OBJS += ../src/utils/os_$(CONFIG_OS).o
+OBJS_p += ../src/utils/os_$(CONFIG_OS).o
+OBJS_c += ../src/utils/os_$(CONFIG_OS).o
+
+ifdef CONFIG_WPA_TRACE
+CFLAGS += -DWPA_TRACE
+OBJS += ../src/utils/trace.o
+OBJS_p += ../src/utils/trace.o
+OBJS_c += ../src/utils/trace.o
+OBJS_c += ../src/utils/wpa_debug.o
+LDFLAGS += -rdynamic
+CFLAGS += -funwind-tables
+ifdef CONFIG_WPA_TRACE_BFD
+CFLAGS += -DWPA_TRACE_BFD
+LIBS += -lbfd
+LIBS_p += -lbfd
+LIBS_c += -lbfd
+endif
+endif
+
+ifndef CONFIG_ELOOP
+CONFIG_ELOOP=eloop
+endif
+OBJS += ../src/utils/$(CONFIG_ELOOP).o
+
+
+ifdef CONFIG_EAPOL_TEST
+CFLAGS += -Werror -DEAPOL_TEST
+endif
+
+ifndef CONFIG_BACKEND
+CONFIG_BACKEND=file
+endif
+
+ifeq ($(CONFIG_BACKEND), file)
+OBJS += config_file.o
+ifndef CONFIG_NO_CONFIG_BLOBS
+NEED_BASE64=y
+endif
+CFLAGS += -DCONFIG_BACKEND_FILE
+endif
+
+ifeq ($(CONFIG_BACKEND), winreg)
+OBJS += config_winreg.o
+endif
+
+ifeq ($(CONFIG_BACKEND), none)
+OBJS += config_none.o
+endif
+
+ifdef CONFIG_NO_CONFIG_WRITE
+CFLAGS += -DCONFIG_NO_CONFIG_WRITE
+endif
+
+ifdef CONFIG_NO_CONFIG_BLOBS
+CFLAGS += -DCONFIG_NO_CONFIG_BLOBS
+endif
+
+ifdef CONFIG_NO_SCAN_PROCESSING
+CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
+endif
+
+ifdef CONFIG_IEEE80211W
+CFLAGS += -DCONFIG_IEEE80211W
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_IEEE80211R
+CFLAGS += -DCONFIG_IEEE80211R
+OBJS += ../src/rsn_supp/wpa_ft.o
+NEED_80211_COMMON=y
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_PEERKEY
+CFLAGS += -DCONFIG_PEERKEY
+endif
+
+ifndef CONFIG_NO_WPA
+OBJS += ../src/rsn_supp/wpa.o
+OBJS += ../src/rsn_supp/preauth.o
+OBJS += ../src/rsn_supp/pmksa_cache.o
+OBJS += ../src/rsn_supp/peerkey.o
+OBJS += ../src/rsn_supp/wpa_ie.o
+OBJS += ../src/common/wpa_common.o
+NEED_AES=y
+NEED_SHA1=y
+NEED_MD5=y
+NEED_RC4=y
+else
+CFLAGS += -DCONFIG_NO_WPA -DCONFIG_NO_WPA2
+endif
+
+ifdef CONFIG_IBSS_RSN
+NEED_RSN_AUTHENTICATOR=y
+CFLAGS += -DCONFIG_IBSS_RSN
+OBJS += ibss_rsn.o
+endif
+
+ifdef CONFIG_NO_WPA2
+CFLAGS += -DCONFIG_NO_WPA2
+endif
+
+include ../src/drivers/drivers.mak
+ifdef CONFIG_AP
+OBJS_d += $(DRV_BOTH_OBJS)
+CFLAGS += $(DRV_BOTH_CFLAGS)
+LDFLAGS += $(DRV_BOTH_LDFLAGS)
+LIBS += $(DRV_BOTH_LIBS)
+else
+NEED_AP_MLME=
+OBJS_d += $(DRV_WPA_OBJS)
+CFLAGS += $(DRV_WPA_CFLAGS)
+LDFLAGS += $(DRV_WPA_LDFLAGS)
+LIBS += $(DRV_WPA_LIBS)
+endif
+
+ifndef CONFIG_L2_PACKET
+CONFIG_L2_PACKET=linux
+endif
+
+OBJS_l2 += ../src/l2_packet/l2_packet_$(CONFIG_L2_PACKET).o
+
+ifeq ($(CONFIG_L2_PACKET), pcap)
+ifdef CONFIG_WINPCAP
+CFLAGS += -DCONFIG_WINPCAP
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+else
+LIBS += -ldnet -lpcap
+endif
+endif
+
+ifeq ($(CONFIG_L2_PACKET), winpcap)
+LIBS += -lwpcap -lpacket
+LIBS_w += -lwpcap
+endif
+
+ifeq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -lpcap
+endif
+
+ifdef CONFIG_EAP_TLS
+# EAP-TLS
+ifeq ($(CONFIG_EAP_TLS), dyn)
+CFLAGS += -DEAP_TLS_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_tls.so
+else
+CFLAGS += -DEAP_TLS
+OBJS += ../src/eap_peer/eap_tls.o
+OBJS_h += ../src/eap_server/eap_server_tls.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PEAP
+# EAP-PEAP
+ifeq ($(CONFIG_EAP_PEAP), dyn)
+CFLAGS += -DEAP_PEAP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_peap.so
+else
+CFLAGS += -DEAP_PEAP
+OBJS += ../src/eap_peer/eap_peap.o
+OBJS += ../src/eap_common/eap_peap_common.o
+OBJS_h += ../src/eap_server/eap_server_peap.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TTLS
+# EAP-TTLS
+ifeq ($(CONFIG_EAP_TTLS), dyn)
+CFLAGS += -DEAP_TTLS_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_ttls.so
+else
+CFLAGS += -DEAP_TTLS
+OBJS += ../src/eap_peer/eap_ttls.o
+OBJS_h += ../src/eap_server/eap_server_ttls.o
+endif
+MS_FUNCS=y
+TLS_FUNCS=y
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_MD5
+# EAP-MD5
+ifeq ($(CONFIG_EAP_MD5), dyn)
+CFLAGS += -DEAP_MD5_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_md5.so
+else
+CFLAGS += -DEAP_MD5
+OBJS += ../src/eap_peer/eap_md5.o
+OBJS_h += ../src/eap_server/eap_server_md5.o
+endif
+CHAP=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+# backwards compatibility for old spelling
+ifdef CONFIG_MSCHAPV2
+ifndef CONFIG_EAP_MSCHAPV2
+CONFIG_EAP_MSCHAPV2=y
+endif
+endif
+
+ifdef CONFIG_EAP_MSCHAPV2
+# EAP-MSCHAPv2
+ifeq ($(CONFIG_EAP_MSCHAPV2), dyn)
+CFLAGS += -DEAP_MSCHAPv2_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_mschapv2.so
+EAPDYN += ../src/eap_peer/mschapv2.so
+else
+CFLAGS += -DEAP_MSCHAPv2
+OBJS += ../src/eap_peer/eap_mschapv2.o
+OBJS += ../src/eap_peer/mschapv2.o
+OBJS_h += ../src/eap_server/eap_server_mschapv2.o
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GTC
+# EAP-GTC
+ifeq ($(CONFIG_EAP_GTC), dyn)
+CFLAGS += -DEAP_GTC_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_gtc.so
+else
+CFLAGS += -DEAP_GTC
+OBJS += ../src/eap_peer/eap_gtc.o
+OBJS_h += ../src/eap_server/eap_server_gtc.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_OTP
+# EAP-OTP
+ifeq ($(CONFIG_EAP_OTP), dyn)
+CFLAGS += -DEAP_OTP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_otp.so
+else
+CFLAGS += -DEAP_OTP
+OBJS += ../src/eap_peer/eap_otp.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SIM
+# EAP-SIM
+ifeq ($(CONFIG_EAP_SIM), dyn)
+CFLAGS += -DEAP_SIM_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_sim.so
+else
+CFLAGS += -DEAP_SIM
+OBJS += ../src/eap_peer/eap_sim.o
+OBJS_h += ../src/eap_server/eap_server_sim.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_LEAP
+# EAP-LEAP
+ifeq ($(CONFIG_EAP_LEAP), dyn)
+CFLAGS += -DEAP_LEAP_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_leap.so
+else
+CFLAGS += -DEAP_LEAP
+OBJS += ../src/eap_peer/eap_leap.o
+endif
+MS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_PSK
+# EAP-PSK
+ifeq ($(CONFIG_EAP_PSK), dyn)
+CFLAGS += -DEAP_PSK_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_psk.so
+else
+CFLAGS += -DEAP_PSK
+OBJS += ../src/eap_peer/eap_psk.o ../src/eap_common/eap_psk_common.o
+OBJS_h += ../src/eap_server/eap_server_psk.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_AES=y
+NEED_AES_OMAC1=y
+NEED_AES_ENCBLOCK=y
+NEED_AES_EAX=y
+endif
+
+ifdef CONFIG_EAP_AKA
+# EAP-AKA
+ifeq ($(CONFIG_EAP_AKA), dyn)
+CFLAGS += -DEAP_AKA_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_aka.so
+else
+CFLAGS += -DEAP_AKA
+OBJS += ../src/eap_peer/eap_aka.o
+OBJS_h += ../src/eap_server/eap_server_aka.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_SIM_COMMON=y
+NEED_AES_CBC=y
+endif
+
+ifdef CONFIG_EAP_AKA_PRIME
+# EAP-AKA'
+ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
+CFLAGS += -DEAP_AKA_PRIME_DYNAMIC
+else
+CFLAGS += -DEAP_AKA_PRIME
+endif
+NEED_SHA256=y
+endif
+
+ifdef CONFIG_EAP_SIM_COMMON
+OBJS += ../src/eap_common/eap_sim_common.o
+OBJS_h += ../src/eap_server/eap_sim_db.o
+NEED_AES=y
+NEED_FIPS186_2_PRF=y
+endif
+
+ifdef CONFIG_EAP_FAST
+# EAP-FAST
+ifeq ($(CONFIG_EAP_FAST), dyn)
+CFLAGS += -DEAP_FAST_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_fast.so
+EAPDYN += ../src/eap_common/eap_fast_common.o
+else
+CFLAGS += -DEAP_FAST
+OBJS += ../src/eap_peer/eap_fast.o ../src/eap_peer/eap_fast_pac.o
+OBJS += ../src/eap_common/eap_fast_common.o
+OBJS_h += ../src/eap_server/eap_server_fast.o
+endif
+TLS_FUNCS=y
+CONFIG_IEEE8021X_EAPOL=y
+NEED_T_PRF=y
+endif
+
+ifdef CONFIG_EAP_PAX
+# EAP-PAX
+ifeq ($(CONFIG_EAP_PAX), dyn)
+CFLAGS += -DEAP_PAX_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_pax.so
+else
+CFLAGS += -DEAP_PAX
+OBJS += ../src/eap_peer/eap_pax.o ../src/eap_common/eap_pax_common.o
+OBJS_h += ../src/eap_server/eap_server_pax.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_SAKE
+# EAP-SAKE
+ifeq ($(CONFIG_EAP_SAKE), dyn)
+CFLAGS += -DEAP_SAKE_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_sake.so
+else
+CFLAGS += -DEAP_SAKE
+OBJS += ../src/eap_peer/eap_sake.o ../src/eap_common/eap_sake_common.o
+OBJS_h += ../src/eap_server/eap_server_sake.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_GPSK
+# EAP-GPSK
+ifeq ($(CONFIG_EAP_GPSK), dyn)
+CFLAGS += -DEAP_GPSK_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_gpsk.so
+else
+CFLAGS += -DEAP_GPSK
+OBJS += ../src/eap_peer/eap_gpsk.o ../src/eap_common/eap_gpsk_common.o
+OBJS_h += ../src/eap_server/eap_server_gpsk.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+ifdef CONFIG_EAP_GPSK_SHA256
+CFLAGS += -DEAP_GPSK_SHA256
+endif
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_WPS
+# EAP-WSC
+CFLAGS += -DCONFIG_WPS -DEAP_WSC
+OBJS += wps_supplicant.o
+OBJS += ../src/utils/uuid.o
+OBJS += ../src/eap_peer/eap_wsc.o ../src/eap_common/eap_wsc_common.o
+OBJS += ../src/wps/wps.o
+OBJS += ../src/wps/wps_common.o
+OBJS += ../src/wps/wps_attr_parse.o
+OBJS += ../src/wps/wps_attr_build.o
+OBJS += ../src/wps/wps_attr_process.o
+OBJS += ../src/wps/wps_dev_attr.o
+OBJS += ../src/wps/wps_enrollee.o
+OBJS += ../src/wps/wps_registrar.o
+OBJS_h += ../src/eap_server/eap_server_wsc.o
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_80211_COMMON=y
+NEED_AES_CBC=y
+NEED_MODEXP=y
+
+ifdef CONFIG_WPS_UFD
+CFLAGS += -DCONFIG_WPS_UFD
+OBJS += ../src/wps/wps_ufd.o
+NEED_WPS_OOB=y
+endif
+
+ifdef CONFIG_WPS_NFC
+CFLAGS += -DCONFIG_WPS_NFC
+OBJS += ../src/wps/ndef.o
+OBJS += ../src/wps/wps_nfc.o
+NEED_WPS_OOB=y
+ifdef CONFIG_WPS_NFC_PN531
+PN531_PATH ?= /usr/local/src/nfc
+CFLAGS += -DCONFIG_WPS_NFC_PN531
+CFLAGS += -I${PN531_PATH}/inc
+OBJS += ../src/wps/wps_nfc_pn531.o
+LIBS += ${PN531_PATH}/lib/wpsnfc.dll
+LIBS += ${PN531_PATH}/lib/libnfc_mapping_pn53x.dll
+endif
+endif
+
+ifdef NEED_WPS_OOB
+CFLAGS += -DCONFIG_WPS_OOB
+endif
+
+ifdef CONFIG_WPS_ER
+CONFIG_WPS_UPNP=y
+CFLAGS += -DCONFIG_WPS_ER
+OBJS += ../src/wps/wps_er.o
+OBJS += ../src/wps/wps_er_ssdp.o
+endif
+
+ifdef CONFIG_WPS_UPNP
+CFLAGS += -DCONFIG_WPS_UPNP
+OBJS += ../src/wps/wps_upnp.o
+OBJS += ../src/wps/wps_upnp_ssdp.o
+OBJS += ../src/wps/wps_upnp_web.o
+OBJS += ../src/wps/wps_upnp_event.o
+OBJS += ../src/wps/wps_upnp_ap.o
+OBJS += ../src/wps/upnp_xml.o
+OBJS += ../src/wps/httpread.o
+OBJS += ../src/wps/http_client.o
+OBJS += ../src/wps/http_server.o
+endif
+
+endif
+
+ifdef CONFIG_EAP_IKEV2
+# EAP-IKEv2
+ifeq ($(CONFIG_EAP_IKEV2), dyn)
+CFLAGS += -DEAP_IKEV2_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_ikev2.so ../src/eap_peer/ikev2.o
+EAPDYN += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
+else
+CFLAGS += -DEAP_IKEV2
+OBJS += ../src/eap_peer/eap_ikev2.o ../src/eap_peer/ikev2.o
+OBJS += ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.o
+OBJS_h += ../src/eap_server/eap_server_ikev2.o
+OBJS_h += ../src/eap_server/ikev2.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+endif
+
+ifdef CONFIG_EAP_VENDOR_TEST
+ifeq ($(CONFIG_EAP_VENDOR_TEST), dyn)
+CFLAGS += -DEAP_VENDOR_TEST_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_vendor_test.so
+else
+CFLAGS += -DEAP_VENDOR_TEST
+OBJS += ../src/eap_peer/eap_vendor_test.o
+OBJS_h += ../src/eap_server/eap_server_vendor_test.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
+ifdef CONFIG_EAP_TNC
+# EAP-TNC
+CFLAGS += -DEAP_TNC
+OBJS += ../src/eap_peer/eap_tnc.o
+OBJS += ../src/eap_peer/tncc.o
+OBJS_h += ../src/eap_server/eap_server_tnc.o
+OBJS_h += ../src/eap_server/tncs.o
+NEED_BASE64=y
+ifndef CONFIG_NATIVE_WINDOWS
+ifndef CONFIG_DRIVER_BSD
+LIBS += -ldl
+endif
+endif
+endif
+
+ifdef CONFIG_IEEE8021X_EAPOL
+# IEEE 802.1X/EAPOL state machines (e.g., for RADIUS authentication)
+CFLAGS += -DIEEE8021X_EAPOL
+OBJS += ../src/eapol_supp/eapol_supp_sm.o
+OBJS += ../src/eap_peer/eap.o ../src/eap_peer/eap_methods.o
+NEED_EAP_COMMON=y
+ifdef CONFIG_DYNAMIC_EAP_METHODS
+CFLAGS += -DCONFIG_DYNAMIC_EAP_METHODS
+LIBS += -ldl -rdynamic
+endif
+endif
+
+ifdef CONFIG_AP
+NEED_80211_COMMON=y
+NEED_EAP_COMMON=y
+NEED_RSN_AUTHENTICATOR=y
+CFLAGS += -DCONFIG_AP
+OBJS += ap.o
+CFLAGS += -DCONFIG_NO_RADIUS
+CFLAGS += -DCONFIG_NO_ACCOUNTING
+CFLAGS += -DCONFIG_NO_VLAN
+OBJS += ../src/ap/hostapd.o
+OBJS += ../src/ap/wpa_auth_glue.o
+OBJS += ../src/ap/utils.o
+OBJS += ../src/ap/authsrv.o
+OBJS += ../src/ap/ap_config.o
+OBJS += ../src/utils/ip_addr.o
+OBJS += ../src/ap/sta_info.o
+OBJS += ../src/ap/tkip_countermeasures.o
+OBJS += ../src/ap/ap_mlme.o
+OBJS += ../src/ap/ieee802_1x.o
+OBJS += ../src/eapol_auth/eapol_auth_sm.o
+OBJS += ../src/ap/ieee802_11_auth.o
+OBJS += ../src/ap/drv_callbacks.o
+OBJS += ../src/ap/ap_drv_ops.o
+ifdef CONFIG_CTRL_IFACE
+OBJS += ../src/ap/ctrl_iface_ap.o
+endif
+
+CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
+OBJS += ../src/eap_server/eap_server.o
+OBJS += ../src/eap_server/eap_server_identity.o
+OBJS += ../src/eap_server/eap_server_methods.o
+
+ifdef CONFIG_IEEE80211N
+CFLAGS += -DCONFIG_IEEE80211N
+endif
+
+ifdef NEED_AP_MLME
+OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/wmm.o
+OBJS += ../src/ap/ap_list.o
+OBJS += ../src/ap/ieee802_11.o
+OBJS += ../src/ap/hw_features.o
+ifdef CONFIG_IEEE80211N
+OBJS += ../src/ap/ieee802_11_ht.o
+endif
+CFLAGS += -DNEED_AP_MLME
+endif
+ifdef CONFIG_WPS
+CFLAGS += -DEAP_SERVER_WSC
+OBJS += ../src/ap/wps_hostapd.o
+OBJS += ../src/eap_server/eap_server_wsc.o
+endif
+endif
+
+ifdef NEED_RSN_AUTHENTICATOR
+CFLAGS += -DCONFIG_NO_RADIUS
+NEED_AES_WRAP=y
+OBJS += ../src/ap/wpa_auth.o
+OBJS += ../src/ap/wpa_auth_ie.o
+OBJS += ../src/ap/pmksa_cache_auth.o
+ifdef CONFIG_IEEE80211R
+OBJS += ../src/ap/wpa_auth_ft.o
+endif
+ifdef CONFIG_PEERKEY
+OBJS += ../src/ap/peerkey_auth.o
+endif
+endif
+
+ifdef CONFIG_EAP_SERVER
+CFLAGS += -DEAP_SERVER
+OBJS_h += ../src/eap_server/eap_server.o
+OBJS_h += ../src/eap_server/eap_server_identity.o
+OBJS_h += ../src/eap_server/eap_server_methods.o
+endif
+
+ifdef CONFIG_RADIUS_CLIENT
+OBJS_h += ../src/utils/ip_addr.o
+OBJS_h += ../src/radius/radius.o
+OBJS_h += ../src/radius/radius_client.o
+endif
+
+ifdef CONFIG_AUTHENTICATOR
+OBJS_h += ../src/eapol_auth/eapol_auth_sm.o
+OBJS_h += ../src/ap/ieee802_1x.o
+endif
+
+ifdef CONFIG_WPA_AUTHENTICATOR
+OBJS_h += ../src/ap/wpa_auth.o
+OBJS_h += ../src/ap/wpa_auth_ie.o
+OBJS_h += ../src/ap/pmksa_cache_auth.o
+ifdef CONFIG_IEEE80211R
+OBJS_h += ../src/ap/wpa_auth_ft.o
+endif
+ifdef CONFIG_PEERKEY
+OBJS_h += ../src/ap/peerkey_auth.o
+endif
+endif
+
+ifdef CONFIG_PCSC
+# PC/SC interface for smartcards (USIM, GSM SIM)
+CFLAGS += -DPCSC_FUNCS -I/usr/include/PCSC
+OBJS += ../src/utils/pcsc_funcs.o
+# -lpthread may not be needed depending on how pcsc-lite was configured
+ifdef CONFIG_NATIVE_WINDOWS
+#Once MinGW gets support for WinScard, -lwinscard could be used instead of the
+#dynamic symbol loading that is now used in pcsc_funcs.c
+#LIBS += -lwinscard
+else
+LIBS += -lpcsclite -lpthread
+endif
+endif
+
+ifdef CONFIG_SIM_SIMULATOR
+CFLAGS += -DCONFIG_SIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef CONFIG_USIM_SIMULATOR
+CFLAGS += -DCONFIG_USIM_SIMULATOR
+NEED_MILENAGE=y
+endif
+
+ifdef NEED_MILENAGE
+OBJS += ../src/crypto/milenage.o
+endif
+
+ifdef CONFIG_PKCS12
+CFLAGS += -DPKCS12_FUNCS
+endif
+
+ifdef CONFIG_SMARTCARD
+CFLAGS += -DCONFIG_SMARTCARD
+endif
+
+ifdef MS_FUNCS
+OBJS += ../src/crypto/ms_funcs.o
+NEED_DES=y
+NEED_MD4=y
+endif
+
+ifdef CHAP
+OBJS += ../src/eap_common/chap.o
+endif
+
+ifdef TLS_FUNCS
+NEED_DES=y
+# Shared TLS functions (needed for EAP_TLS, EAP_PEAP, EAP_TTLS, and EAP_FAST)
+OBJS += ../src/eap_peer/eap_tls_common.o
+OBJS_h += ../src/eap_server/eap_server_tls_common.o
+NEED_TLS_PRF=y
+endif
+
+ifndef CONFIG_TLS
+CONFIG_TLS=openssl
+endif
+
+ifeq ($(CONFIG_TLS), openssl)
+ifdef TLS_FUNCS
+CFLAGS += -DEAP_TLS_OPENSSL
+OBJS += ../src/crypto/tls_openssl.o
+LIBS += -lssl
+endif
+OBJS += ../src/crypto/crypto_openssl.o
+OBJS_p += ../src/crypto/crypto_openssl.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_openssl.o
+endif
+LIBS += -lcrypto
+LIBS_p += -lcrypto
+endif
+
+ifeq ($(CONFIG_TLS), gnutls)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_gnutls.o
+LIBS += -lgnutls -lgpg-error
+ifdef CONFIG_GNUTLS_EXTRA
+CFLAGS += -DCONFIG_GNUTLS_EXTRA
+LIBS += -lgnutls-extra
+endif
+endif
+OBJS += ../src/crypto/crypto_gnutls.o
+OBJS_p += ../src/crypto/crypto_gnutls.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_gnutls.o
+endif
+LIBS += -lgcrypt
+LIBS_p += -lgcrypt
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), schannel)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_schannel.o
+endif
+OBJS += ../src/crypto/crypto_cryptoapi.o
+OBJS_p += ../src/crypto/crypto_cryptoapi.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_cryptoapi.o
+endif
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), nss)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_nss.o
+LIBS += -lssl3
+endif
+OBJS += ../src/crypto/crypto_nss.o
+OBJS_p += ../src/crypto/crypto_nss.o
+ifdef NEED_FIPS186_2_PRF
+OBJS += ../src/crypto/fips_prf_nss.o
+endif
+LIBS += -lnss3
+LIBS_p += -lnss3
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+
+ifeq ($(CONFIG_TLS), internal)
+ifndef CONFIG_CRYPTO
+CONFIG_CRYPTO=internal
+endif
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/crypto_internal-rsa.o
+OBJS += ../src/crypto/tls_internal.o
+OBJS += ../src/tls/tlsv1_common.o
+OBJS += ../src/tls/tlsv1_record.o
+OBJS += ../src/tls/tlsv1_cred.o
+OBJS += ../src/tls/tlsv1_client.o
+OBJS += ../src/tls/tlsv1_client_write.o
+OBJS += ../src/tls/tlsv1_client_read.o
+OBJS += ../src/tls/asn1.o
+OBJS += ../src/tls/rsa.o
+OBJS += ../src/tls/x509v3.o
+OBJS += ../src/tls/pkcs1.o
+OBJS += ../src/tls/pkcs5.o
+OBJS += ../src/tls/pkcs8.o
+NEED_SHA256=y
+NEED_BASE64=y
+NEED_TLS_PRF=y
+NEED_MODEXP=y
+NEED_CIPHER=y
+CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
+endif
+ifdef NEED_CIPHER
+NEED_DES=y
+OBJS += ../src/crypto/crypto_internal-cipher.o
+endif
+ifdef NEED_MODEXP
+OBJS += ../src/crypto/crypto_internal-modexp.o
+OBJS += ../src/tls/bignum.o
+endif
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS += ../src/crypto/crypto_libtomcrypt.o
+OBJS_p += ../src/crypto/crypto_libtomcrypt.o
+LIBS += -ltomcrypt -ltfm
+LIBS_p += -ltomcrypt -ltfm
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), internal)
+OBJS += ../src/crypto/crypto_internal.o
+OBJS_p += ../src/crypto/crypto_internal.o
+NEED_AES_ENC=y
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
+ifdef CONFIG_INTERNAL_LIBTOMMATH
+CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH
+ifdef CONFIG_INTERNAL_LIBTOMMATH_FAST
+CFLAGS += -DLTM_FAST
+endif
+else
+LIBS += -ltommath
+LIBS_p += -ltommath
+endif
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_DES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD4=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+CONFIG_INTERNAL_DH_GROUP5=y
+endif
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+OBJS += ../src/crypto/crypto_cryptoapi.o
+OBJS_p += ../src/crypto/crypto_cryptoapi.o
+CFLAGS += -DCONFIG_CRYPTO_CRYPTOAPI
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+ifeq ($(CONFIG_TLS), none)
+ifdef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+CFLAGS += -DEAP_TLS_NONE
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+endif
+OBJS += ../src/crypto/crypto_none.o
+OBJS_p += ../src/crypto/crypto_none.o
+CONFIG_INTERNAL_SHA256=y
+CONFIG_INTERNAL_RC4=y
+endif
+
+ifdef TLS_FUNCS
+ifdef CONFIG_SMARTCARD
+ifndef CONFIG_NATIVE_WINDOWS
+ifneq ($(CONFIG_L2_PACKET), freebsd)
+LIBS += -ldl
+endif
+endif
+endif
+endif
+
+ifndef TLS_FUNCS
+OBJS += ../src/crypto/tls_none.o
+ifeq ($(CONFIG_TLS), internal)
+CONFIG_INTERNAL_AES=y
+CONFIG_INTERNAL_SHA1=y
+CONFIG_INTERNAL_MD5=y
+CONFIG_INTERNAL_RC4=y
+endif
+endif
+
+AESOBJS = # none so far (see below)
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
+endif
+
+AESOBJS += ../src/crypto/aes-unwrap.o
+ifdef NEED_AES_EAX
+AESOBJS += ../src/crypto/aes-eax.o
+NEED_AES_CTR=y
+endif
+ifdef NEED_AES_CTR
+AESOBJS += ../src/crypto/aes-ctr.o
+endif
+ifdef NEED_AES_ENCBLOCK
+AESOBJS += ../src/crypto/aes-encblock.o
+endif
+ifdef NEED_AES_OMAC1
+NEED_AES_ENC=y
+AESOBJS += ../src/crypto/aes-omac1.o
+endif
+ifdef NEED_AES_WRAP
+NEED_AES_ENC=y
+AESOBJS += ../src/crypto/aes-wrap.o
+endif
+ifdef NEED_AES_CBC
+NEED_AES_ENC=y
+AESOBJS += ../src/crypto/aes-cbc.o
+endif
+ifdef NEED_AES_ENC
+ifdef CONFIG_INTERNAL_AES
+AESOBJS += ../src/crypto/aes-internal-enc.o
+endif
+endif
+ifdef NEED_AES
+OBJS += $(AESOBJS)
+endif
+
+ifdef NEED_SHA1
+SHA1OBJS += ../src/crypto/sha1.o
+ifdef CONFIG_INTERNAL_SHA1
+SHA1OBJS += ../src/crypto/sha1-internal.o
+ifdef NEED_FIPS186_2_PRF
+SHA1OBJS += ../src/crypto/fips_prf_internal.o
+endif
+endif
+ifndef CONFIG_NO_WPA_PASSPHRASE
+SHA1OBJS += ../src/crypto/sha1-pbkdf2.o
+endif
+ifdef NEED_T_PRF
+SHA1OBJS += ../src/crypto/sha1-tprf.o
+endif
+ifdef NEED_TLS_PRF
+SHA1OBJS += ../src/crypto/sha1-tlsprf.o
+endif
+endif
+
+MD5OBJS = ../src/crypto/md5.o
+ifdef NEED_MD5
+ifdef CONFIG_INTERNAL_MD5
+MD5OBJS += ../src/crypto/md5-internal.o
+endif
+ifdef CONFIG_FIPS
+MD5OBJS += ../src/crypto/md5-non-fips.o
+endif
+OBJS += $(MD5OBJS)
+OBJS_p += $(MD5OBJS)
+endif
+
+ifdef NEED_MD4
+ifdef CONFIG_INTERNAL_MD4
+OBJS += ../src/crypto/md4-internal.o
+endif
+endif
+
+DESOBJS = # none needed when not internal
+ifdef NEED_DES
+ifdef CONFIG_INTERNAL_DES
+DESOBJS += ../src/crypto/des-internal.o
+endif
+endif
+
+ifdef NEED_RC4
+ifdef CONFIG_INTERNAL_RC4
+OBJS += ../src/crypto/rc4.o
+endif
+endif
+
+SHA256OBJS = # none by default
+ifdef NEED_SHA256
+CFLAGS += -DCONFIG_SHA256
+SHA256OBJS += ../src/crypto/sha256.o
+ifdef CONFIG_INTERNAL_SHA256
+SHA256OBJS += ../src/crypto/sha256-internal.o
+endif
+OBJS += $(SHA256OBJS)
+endif
+
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_groups.o
+endif
+ifdef NEED_DH_GROUPS_ALL
+CFLAGS += -DALL_DH_GROUPS
+endif
+ifdef CONFIG_INTERNAL_DH_GROUP5
+ifdef NEED_DH_GROUPS
+OBJS += ../src/crypto/dh_group5.o
+endif
+endif
+
+ifdef CONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), y)
+ifdef CONFIG_NATIVE_WINDOWS
+CONFIG_CTRL_IFACE=named_pipe
+else
+CONFIG_CTRL_IFACE=unix
+endif
+endif
+CFLAGS += -DCONFIG_CTRL_IFACE
+ifeq ($(CONFIG_CTRL_IFACE), unix)
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+endif
+ifeq ($(CONFIG_CTRL_IFACE), udp)
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+endif
+ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
+CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
+endif
+OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS -DDBUS_API_SUBJECT_TO_CHANGE
+DBUS_OBJS += dbus/dbus_old.o dbus/dbus_old_handlers.o
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_old_handlers_wps.o
+endif
+DBUS_OBJS += dbus/dbus_dict_helpers.o
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+endif
+dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
+DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
+DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
+ifeq ($(DBUS_VERSION_MAJOR),)
+DBUS_VERSION_MAJOR=0
+endif
+ifeq ($(DBUS_VERSION_MINOR),)
+DBUS_VERSION_MINOR=0
+endif
+DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
+DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+DBUS=y
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+DBUS_OBJS ?= dbus/dbus_dict_helpers.o
+DBUS_OBJS += dbus/dbus_new_helpers.o
+DBUS_OBJS += dbus/dbus_new.o dbus/dbus_new_handlers.o
+ifdef CONFIG_WPS
+DBUS_OBJS += dbus/dbus_new_handlers_wps.o
+endif
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_OBJS += dbus/dbus_new_introspect.o
+DBUS_CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+endif
+DBUS_CFLAGS += $(DBUS_INCLUDE)
+endif
+
+ifdef DBUS
+DBUS_CFLAGS += -DCONFIG_DBUS
+DBUS_OBJS += dbus/dbus_common.o
+endif
+
+OBJS += $(DBUS_OBJS)
+CFLAGS += $(DBUS_CFLAGS)
+LIBS += $(DBUS_LIBS)
+
+ifdef CONFIG_READLINE
+CFLAGS += -DCONFIG_READLINE
+LIBS_c += -lncurses -lreadline
+endif
+
+ifdef CONFIG_NATIVE_WINDOWS
+CFLAGS += -DCONFIG_NATIVE_WINDOWS
+LIBS += -lws2_32 -lgdi32 -lcrypt32
+LIBS_c += -lws2_32
+LIBS_p += -lws2_32 -lgdi32
+ifeq ($(CONFIG_CRYPTO), cryptoapi)
+LIBS_p += -lcrypt32
+endif
+endif
+
+ifdef CONFIG_NO_STDOUT_DEBUG
+CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
+ifndef CONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_NO_WPA_MSG
+endif
+endif
+
+ifdef CONFIG_IPV6
+# for eapol_test only
+CFLAGS += -DCONFIG_IPV6
+endif
+
+ifdef NEED_BASE64
+OBJS += ../src/utils/base64.o
+endif
+
+ifdef NEED_SME
+NEED_80211_COMMON=y
+OBJS += sme.o
+CFLAGS += -DCONFIG_SME
+endif
+
+ifdef CONFIG_CLIENT_MLME
+OBJS += mlme.o
+CFLAGS += -DCONFIG_CLIENT_MLME
+NEED_80211_COMMON=y
+endif
+
+ifdef NEED_80211_COMMON
+OBJS += ../src/common/ieee802_11_common.o
+endif
+
+ifdef NEED_EAP_COMMON
+OBJS += ../src/eap_common/eap_common.o
+endif
+
+ifndef CONFIG_MAIN
+CONFIG_MAIN=main
+endif
+
+ifdef CONFIG_DEBUG_SYSLOG
+CFLAGS += -DCONFIG_DEBUG_SYSLOG
+endif
+
+ifdef CONFIG_DEBUG_FILE
+CFLAGS += -DCONFIG_DEBUG_FILE
+endif
+
+ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+CFLAGS += -DCONFIG_DELAYED_MIC_ERROR_REPORT
+endif
+
+ifdef CONFIG_FIPS
+CFLAGS += -DCONFIG_FIPS
+endif
+
+OBJS += $(SHA1OBJS) $(DESOBJS)
+
+OBJS_p += $(SHA1OBJS)
+
+ifdef CONFIG_BGSCAN_SIMPLE
+CFLAGS += -DCONFIG_BGSCAN_SIMPLE
+OBJS += bgscan_simple.o
+NEED_BGSCAN=y
+endif
+
+ifdef NEED_BGSCAN
+CFLAGS += -DCONFIG_BGSCAN
+OBJS += bgscan.o
+endif
+
+OBJS_wpa_rm := ctrl_iface.o mlme.o ctrl_iface_unix.o
+OBJS_wpa := $(filter-out $(OBJS_wpa_rm),$(OBJS)) $(OBJS_h) tests/test_wpa.o
+ifdef CONFIG_AUTHENTICATOR
+OBJS_wpa += tests/link_test.o
+endif
+OBJS_wpa += $(OBJS_l2)
+OBJS += wpa_supplicant.o events.o blacklist.o wpas_glue.o scan.o
+OBJS_t := $(OBJS) $(OBJS_l2) eapol_test.o
+OBJS_t += ../src/radius/radius_client.o
+OBJS_t += ../src/radius/radius.o
+ifndef CONFIG_AP
+OBJS_t += ../src/utils/ip_addr.o
+endif
+OBJS_t2 := $(OBJS) $(OBJS_l2) preauth_test.o
+OBJS += $(CONFIG_MAIN).o
+
+ifdef CONFIG_PRIVSEP
+OBJS_priv += $(OBJS_d) ../src/drivers/drivers.o
+OBJS_priv += $(OBJS_l2)
+OBJS_priv += ../src/utils/os_$(CONFIG_OS).o
+OBJS_priv += ../src/utils/$(CONFIG_ELOOP).o
+OBJS_priv += ../src/utils/common.o
+OBJS_priv += ../src/utils/wpa_debug.o
+OBJS_priv += ../src/utils/wpabuf.o
+OBJS_priv += wpa_priv.o
+ifdef CONFIG_DRIVER_TEST
+OBJS_priv += $(SHA1OBJS)
+OBJS_priv += $(MD5OBJS)
+ifeq ($(CONFIG_TLS), openssl)
+OBJS_priv += ../src/crypto/crypto_openssl.o
+endif
+ifeq ($(CONFIG_TLS), gnutls)
+OBJS_priv += ../src/crypto/crypto_gnutls.o
+endif
+ifeq ($(CONFIG_TLS), nss)
+OBJS_priv += ../src/crypto/crypto_nss.o
+endif
+ifeq ($(CONFIG_TLS), internal)
+ifeq ($(CONFIG_CRYPTO), libtomcrypt)
+OBJS_priv += ../src/crypto/crypto_libtomcrypt.o
+else
+OBJS_priv += ../src/crypto/crypto_internal.o
+endif
+endif
+endif # CONFIG_DRIVER_TEST
+OBJS += ../src/l2_packet/l2_packet_privsep.o
+OBJS += ../src/drivers/driver_privsep.o
+EXTRA_progs += wpa_priv
+else
+OBJS += $(OBJS_d) ../src/drivers/drivers.o
+OBJS += $(OBJS_l2)
+endif
+
+ifdef CONFIG_NDIS_EVENTS_INTEGRATED
+CFLAGS += -DCONFIG_NDIS_EVENTS_INTEGRATED
+OBJS += ../src/drivers/ndis_events.o
+EXTRALIBS += -loleaut32 -lole32 -luuid
+ifdef PLATFORMSDKLIB
+EXTRALIBS += $(PLATFORMSDKLIB)/WbemUuid.Lib
+else
+EXTRALIBS += WbemUuid.Lib
+endif
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+dynamic_eap_methods: $(EAPDYN)
+
+../src/drivers/build.wpa_supplicant:
+       @if [ -f ../src/drivers/build.hostapd ]; then \
+               $(MAKE) -C ../src/drivers clean; \
+       fi
+       @touch ../src/drivers/build.wpa_supplicant
+
+BCHECK=../src/drivers/build.wpa_supplicant
+
+wpa_priv: $(BCHECK) $(OBJS_priv)
+       $(LDO) $(LDFLAGS) -o wpa_priv $(OBJS_priv) $(LIBS)
+
+wpa_supplicant: .config $(BCHECK) $(OBJS) $(EXTRA_progs)
+       $(LDO) $(LDFLAGS) -o wpa_supplicant $(OBJS) $(LIBS) $(EXTRALIBS)
+
+eapol_test: .config $(OBJS_t)
+       $(LDO) $(LDFLAGS) -o eapol_test $(OBJS_t) $(LIBS)
+
+preauth_test: .config $(OBJS_t2) 
+       $(LDO) $(LDFLAGS) -o preauth_test $(OBJS_t2) $(LIBS)
+
+wpa_passphrase: $(OBJS_p)
+       $(LDO) $(LDFLAGS) -o wpa_passphrase $(OBJS_p) $(LIBS_p)
+
+wpa_cli: $(OBJS_c)
+       $(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
+
+link_test: $(OBJS) $(OBJS_h) tests/link_test.o
+       $(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
+
+test_wpa: $(OBJS_wpa) $(OBJS_h)
+       $(LDO) $(LDFLAGS) -o test_wpa $(OBJS_wpa) $(LIBS)
+
+win_if_list: win_if_list.c
+       $(LDO) $(LDFLAGS) -o $@ win_if_list.c $(CFLAGS) $(LIBS_w)
+
+eap_psk.so: ../src/eap_peer/eap_psk.c ../src/eap_common/eap_psk_common.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+               -Deap_peer_psk_register=eap_peer_method_dynamic_init
+
+eap_pax.so: ../src/eap_peer/eap_pax.c ../src/eap_common/eap_pax_common.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+               -Deap_peer_pax_register=eap_peer_method_dynamic_init
+
+eap_sake.so: ../src/eap_peer/eap_sake.c ../src/eap_common/eap_sake_common.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+               -Deap_peer_sake_register=eap_peer_method_dynamic_init
+
+eap_wsc.so: ../src/eap_peer/eap_wsc.c ../src/eap_common/eap_wsc_common.c ../src/wps/wps.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+               -Deap_peer_wsc_register=eap_peer_method_dynamic_init
+
+eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_common/eap_ikev2_common.o ../src/eap_common/ikev2_common.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+               -Deap_peer_ikev2_register=eap_peer_method_dynamic_init
+
+%.so: %.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
+               -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+%.o: %.c
+       $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+       @$(E) "  CC " $<
+
+wpa_supplicant.exe: wpa_supplicant
+       mv -f $< $@
+wpa_cli.exe: wpa_cli
+       mv -f $< $@
+wpa_passphrase.exe: wpa_passphrase
+       mv -f $< $@
+win_if_list.exe: win_if_list
+       mv -f $< $@
+eapol_test.exe: eapol_test
+       mv -f $< $@
+
+WINALL=wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe win_if_list.exe
+
+windows-bin: $(WINALL)
+       $(STRIP) $(WINALL)
+
+wpa_gui/Makefile:
+       qmake -o wpa_gui/Makefile wpa_gui/wpa_gui.pro 
+
+wpa_gui: wpa_gui/Makefile
+       $(MAKE) -C wpa_gui
+
+wpa_gui-qt4/Makefile:
+       qmake -o wpa_gui-qt4/Makefile wpa_gui-qt4/wpa_gui.pro 
+
+wpa_gui-qt4/lang/wpa_gui_de.qm: wpa_gui-qt4/lang/wpa_gui_de.ts
+       lrelease wpa_gui-qt4/wpa_gui.pro
+
+wpa_gui-qt4: wpa_gui-qt4/Makefile wpa_gui-qt4/lang/wpa_gui_de.qm
+       $(MAKE) -C wpa_gui-qt4
+
+TEST_EAP_SIM_COMMON_OBJS = $(SHA1OBJS) $(MD5OBJS) \
+       ../src/utils/common.o ../src/utils/os_unix.o \
+       ../src/utils/wpa_debug.o $(AESOBJS) \
+       tests/test_eap_sim_common.o
+test-eap_sim_common: $(TEST_EAP_SIM_COMMON_OBJS)
+       $(LDO) $(LDFLAGS) -o $@ $(TEST_EAP_SIM_COMMON_OBJS) $(LIBS)
+       ./test-eap_sim_common
+       rm test-eap_sim_common
+
+tests: test-eap_sim_common
+
+clean:
+       $(MAKE) -C ../src clean
+       $(MAKE) -C dbus clean
+       rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
+       rm -f wpa_priv
+
+-include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/README b/wpa_supplicant/README
new file mode 100644 (file)
index 0000000..45c8bae
--- /dev/null
@@ -0,0 +1,1032 @@
+WPA Supplicant
+==============
+
+Copyright (c) 2003-2010, 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.
+
+
+
+Features
+--------
+
+Supported WPA/IEEE 802.11i features:
+- WPA-PSK ("WPA-Personal")
+- WPA with EAP (e.g., with RADIUS authentication server) ("WPA-Enterprise")
+  Following authentication methods are supported with an integrate IEEE 802.1X
+  Supplicant:
+  * EAP-TLS
+  * EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/TLS (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/GTC (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/OTP (both PEAPv0 and PEAPv1)
+  * EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)
+  * EAP-TTLS/EAP-MD5-Challenge
+  * EAP-TTLS/EAP-GTC
+  * EAP-TTLS/EAP-OTP
+  * EAP-TTLS/EAP-MSCHAPv2
+  * EAP-TTLS/EAP-TLS
+  * EAP-TTLS/MSCHAPv2
+  * EAP-TTLS/MSCHAP
+  * EAP-TTLS/PAP
+  * EAP-TTLS/CHAP
+  * EAP-SIM
+  * EAP-AKA
+  * EAP-PSK
+  * EAP-PAX
+  * EAP-SAKE
+  * EAP-IKEv2
+  * EAP-GPSK
+  * LEAP (note: requires special support from the driver for IEEE 802.11
+         authentication)
+  (following methods are supported, but since they do not generate keying
+   material, they cannot be used with WPA or IEEE 802.1X WEP keying)
+  * EAP-MD5-Challenge 
+  * EAP-MSCHAPv2
+  * EAP-GTC
+  * EAP-OTP
+- key management for CCMP, TKIP, WEP104, WEP40
+- RSN/WPA2 (IEEE 802.11i)
+  * pre-authentication
+  * PMKSA caching
+
+Supported TLS/crypto libraries:
+- OpenSSL (default)
+- GnuTLS
+
+Internal TLS/crypto implementation (optional):
+- can be used in place of an external TLS/crypto library
+- TLSv1
+- X.509 certificate processing
+- PKCS #1
+- ASN.1
+- RSA
+- bignum
+- minimal size (ca. 50 kB binary, parts of which are already needed for WPA;
+  TLSv1/X.509/ASN.1/RSA/bignum parts are about 25 kB on x86)
+
+
+Requirements
+------------
+
+Current hardware/software requirements:
+- Linux kernel 2.4.x or 2.6.x with Linux Wireless Extensions v15 or newer
+- FreeBSD 6-CURRENT
+- NetBSD-current
+- Microsoft Windows with WinPcap (at least WinXP, may work with other versions)
+- drivers:
+       Linux drivers that support WPA/WPA2 configuration with the generic
+       Linux wireless extensions (WE-18 or newer). Even though there are
+       number of driver specific interface included in wpa_supplicant, please
+       note that Linux drivers are moving to use generic wireless extensions
+       and driver_wext (-Dwext on wpa_supplicant command line) should be the
+       default option to start with before falling back to driver specific
+       interface.
+
+       Host AP driver for Prism2/2.5/3 (development snapshot/v0.2.x)
+       (http://hostap.epitest.fi/)
+       Driver need to be set in Managed mode ('iwconfig wlan0 mode managed').
+       Please note that station firmware version needs to be 1.7.0 or newer
+       to work in WPA mode.
+
+       Linuxant DriverLoader (http://www.linuxant.com/driverloader/)
+       with Windows NDIS driver for your wlan card supporting WPA.
+
+       Agere Systems Inc. Linux Driver
+       (http://www.agere.com/support/drivers/)
+       Please note that the driver interface file (driver_hermes.c) and
+       hardware specific include files are not included in the
+       wpa_supplicant distribution. You will need to copy these from the
+       source package of the Agere driver.
+
+       madwifi driver for cards based on Atheros chip set (ar521x)
+       (http://sourceforge.net/projects/madwifi/)
+       Please note that you will need to modify the wpa_supplicant .config
+       file to use the correct path for the madwifi driver root directory
+       (CFLAGS += -I../madwifi/wpa line in example defconfig).
+
+       ATMEL AT76C5XXx driver for USB and PCMCIA cards
+       (http://atmelwlandriver.sourceforge.net/).
+
+       Linux ndiswrapper (http://ndiswrapper.sourceforge.net/) with
+       Windows NDIS driver.
+
+       Broadcom wl.o driver (old version only)
+       This is a generic Linux driver for Broadcom IEEE 802.11a/g cards.
+       However, it is proprietary driver that is not publicly available
+       except for couple of exceptions, mainly Broadcom-based APs/wireless
+       routers that use Linux. The driver binary can be downloaded, e.g.,
+       from Linksys support site (http://www.linksys.com/support/gpl.asp)
+       for Linksys WRT54G. The GPL tarball includes cross-compiler and
+       the needed header file, wlioctl.h, for compiling wpa_supplicant.
+       This driver support in wpa_supplicant is expected to work also with
+       other devices based on Broadcom driver (assuming the driver includes
+       client mode support). Please note that the newer Broadcom driver
+       ("hybrid Linux driver") supports Linux wireless extensions and does
+       not need (or even work) with the specific driver wrapper. Use -Dwext
+       with that driver.
+
+       Intel ipw2100 driver
+       (http://sourceforge.net/projects/ipw2100/)
+
+       Intel ipw2200 driver
+       (http://sourceforge.net/projects/ipw2200/)
+
+       In theory, any driver that supports Linux wireless extensions can be
+       used with IEEE 802.1X (i.e., not WPA) when using ap_scan=0 option in
+       configuration file.
+
+       Wired Ethernet drivers (with ap_scan=0)
+
+       BSD net80211 layer (e.g., Atheros driver)
+       At the moment, this is for FreeBSD 6-CURRENT branch and NetBSD-current.
+
+       Windows NDIS
+       The current Windows port requires WinPcap (http://winpcap.polito.it/).
+       See README-Windows.txt for more information.
+
+wpa_supplicant was designed to be portable for different drivers and
+operating systems. Hopefully, support for more wlan cards and OSes will be
+added in the future. See developer's documentation
+(http://hostap.epitest.fi/wpa_supplicant/devel/) for more information about the
+design of wpa_supplicant and porting to other drivers. One main goal
+is to add full WPA/WPA2 support to Linux wireless extensions to allow
+new drivers to be supported without having to implement new
+driver-specific interface code in wpa_supplicant.
+
+Optional libraries for layer2 packet processing:
+- libpcap (tested with 0.7.2, most relatively recent versions assumed to work,
+       this is likely to be available with most distributions,
+       http://tcpdump.org/)
+- libdnet (tested with v1.4, most versions assumed to work,
+       http://libdnet.sourceforge.net/)
+
+These libraries are _not_ used in the default Linux build. Instead,
+internal Linux specific implementation is used. libpcap/libdnet are
+more portable and they can be used by adding CONFIG_L2_PACKET=pcap into
+.config. They may also be selected automatically for other operating
+systems. In case of Windows builds, WinPcap is used by default
+(CONFIG_L2_PACKET=winpcap).
+
+
+Optional libraries for EAP-TLS, EAP-PEAP, and EAP-TTLS:
+- OpenSSL (tested with 0.9.7c and 0.9.7d, and 0.9.8 versions; assumed to
+  work with most relatively recent versions; this is likely to be
+  available with most distributions, http://www.openssl.org/)
+- GnuTLS
+- internal TLSv1 implementation
+
+TLS options for EAP-FAST:
+- OpenSSL 0.9.8d _with_ openssl-0.9.8d-tls-extensions.patch applied
+  (i.e., the default OpenSSL package does not include support for
+  extensions needed for EAP-FAST)
+- internal TLSv1 implementation
+
+One of these libraries is needed when EAP-TLS, EAP-PEAP, EAP-TTLS, or
+EAP-FAST support is enabled. WPA-PSK mode does not require this or EAPOL/EAP
+implementation. A configuration file, .config, for compilation is
+needed to enable IEEE 802.1X/EAPOL and EAP methods. Note that EAP-MD5,
+EAP-GTC, EAP-OTP, and EAP-MSCHAPV2 cannot be used alone with WPA, so
+they should only be enabled if testing the EAPOL/EAP state
+machines. However, there can be used as inner authentication
+algorithms with EAP-PEAP and EAP-TTLS.
+
+See Building and installing section below for more detailed
+information about the wpa_supplicant build time configuration.
+
+
+
+WPA
+---
+
+The original security mechanism of IEEE 802.11 standard was not
+designed to be strong and has proven to be insufficient for most
+networks that require some kind of security. Task group I (Security)
+of IEEE 802.11 working group (http://www.ieee802.org/11/) has worked
+to address the flaws of the base standard and has in practice
+completed its work in May 2004. The IEEE 802.11i amendment to the IEEE
+802.11 standard was approved in June 2004 and 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).
+
+
+
+wpa_supplicant
+--------------
+
+wpa_supplicant is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations. It implements WPA key
+negotiation with a WPA Authenticator and EAP authentication with
+Authentication Server. In addition, it controls the roaming and IEEE
+802.11 authentication/association of the wlan driver.
+
+wpa_supplicant is designed to be a "daemon" program that runs in the
+background and acts as the backend component controlling the wireless
+connection. wpa_supplicant supports separate frontend programs and an
+example text-based frontend, wpa_cli, is included with wpa_supplicant.
+
+Following steps are used when associating with an AP using WPA:
+
+- wpa_supplicant requests the kernel driver to scan neighboring BSSes
+- wpa_supplicant selects a BSS based on its configuration
+- wpa_supplicant requests the kernel driver to associate with the chosen
+  BSS
+- If WPA-EAP: integrated IEEE 802.1X Supplicant completes EAP
+  authentication with the authentication server (proxied by the
+  Authenticator in the AP)
+- If WPA-EAP: master key is received from the IEEE 802.1X Supplicant
+- If WPA-PSK: wpa_supplicant uses PSK as the master session key
+- wpa_supplicant completes WPA 4-Way Handshake and Group Key Handshake
+  with the Authenticator (AP)
+- wpa_supplicant configures encryption keys for unicast and broadcast
+- normal data packets can be transmitted and received
+
+
+
+Building and installing
+-----------------------
+
+In order to be able to build wpa_supplicant, you will first need to
+select which parts of it will be included. This is done by creating a
+build time configuration file, .config, in the wpa_supplicant root
+directory. Configuration options are text lines using following
+format: CONFIG_<option>=y. Lines starting with # are considered
+comments and are ignored. See defconfig file for an example configuration
+and a list of available options and additional notes.
+
+The build time configuration can be used to select only the needed
+features and limit the binary size and requirements for external
+libraries. The main configuration parts are the selection of which
+driver interfaces (e.g., hostap, madwifi, ..) and which authentication
+methods (e.g., EAP-TLS, EAP-PEAP, ..) are included.
+
+Following build time configuration options are used to control IEEE
+802.1X/EAPOL and EAP state machines and all EAP methods. Including
+TLS, PEAP, or TTLS will require linking wpa_supplicant with OpenSSL
+library for TLS implementation. Alternatively, GnuTLS or the internal
+TLSv1 implementation can be used for TLS functionaly.
+
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+CONFIG_EAP_IKEV2=y
+
+Following option can be used to include GSM SIM/USIM interface for GSM/UMTS
+authentication algorithm (for EAP-SIM/EAP-AKA). This requires pcsc-lite
+(http://www.linuxnet.com/) for smart card access.
+
+CONFIG_PCSC=y
+
+Following options can be added to .config to select which driver
+interfaces are included. Hermes driver interface needs to be downloaded
+from Agere (see above).
+
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_RALINK=y
+CONFIG_DRIVER_NDISWRAPPER=y
+CONFIG_DRIVER_BROADCOM=y
+CONFIG_DRIVER_IPW=y
+CONFIG_DRIVER_BSD=y
+CONFIG_DRIVER_NDIS=y
+
+Following example includes all features and driver interfaces that are
+included in the wpa_supplicant package:
+
+CONFIG_DRIVER_HOSTAP=y
+CONFIG_DRIVER_HERMES=y
+CONFIG_DRIVER_MADWIFI=y
+CONFIG_DRIVER_ATMEL=y
+CONFIG_DRIVER_WEXT=y
+CONFIG_DRIVER_NDISWRAPPER=y
+CONFIG_DRIVER_BROADCOM=y
+CONFIG_DRIVER_IPW=y
+CONFIG_DRIVER_BSD=y
+CONFIG_DRIVER_NDIS=y
+CONFIG_IEEE8021X_EAPOL=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+CONFIG_EAP_IKEV2=y
+CONFIG_PCSC=y
+
+EAP-PEAP and EAP-TTLS will automatically include configured EAP
+methods (MD5, OTP, GTC, MSCHAPV2) for inner authentication selection.
+
+
+After you have created a configuration file, you can build
+wpa_supplicant and wpa_cli with 'make' command. You may then install
+the binaries to a suitable system directory, e.g., /usr/local/bin.
+
+Example commands:
+
+# build wpa_supplicant and wpa_cli
+make
+# install binaries (this may need root privileges)
+cp wpa_cli wpa_supplicant /usr/local/bin
+
+
+You will need to make a configuration file, e.g.,
+/etc/wpa_supplicant.conf, with network configuration for the networks
+you are going to use. Configuration file section below includes
+explanation fo the configuration file format and includes various
+examples. Once the configuration is ready, you can test whether the
+configuration work by first running wpa_supplicant with following
+command to start it on foreground with debugging enabled:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+
+Assuming everything goes fine, you can start using following command
+to start wpa_supplicant on background without debugging:
+
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+
+Please note that if you included more than one driver interface in the
+build time configuration (.config), you may need to specify which
+interface to use by including -D<driver name> option on the command
+line. See following section for more details on command line options
+for wpa_supplicant.
+
+
+
+Command line options
+--------------------
+
+usage:
+  wpa_supplicant [-BddfhKLqqtuvwW] [-P<pid file>] [-g<global ctrl>] \
+        -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \
+        [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
+        [-p<driver_param>] [-b<br_ifname>] ...]
+
+options:
+  -b = optional bridge interface name
+  -B = run daemon in the background
+  -c = Configuration file
+  -C = ctrl_interface parameter (only used if -c is not)
+  -i = interface name
+  -d = increase debugging verbosity (-dd even more)
+  -D = driver name (can be multiple drivers: nl80211,wext)
+  -f = Log output to default log location (normally /tmp)
+  -g = global ctrl_interface
+  -K = include keys (passwords, etc.) in debug output
+  -t = include timestamp in debug messages
+  -h = show this help text
+  -L = show license (GPL and BSD)
+  -p = driver parameters
+  -P = PID file
+  -q = decrease debugging verbosity (-qq even less)
+  -u = enable DBus control interface
+  -v = show version
+  -w = wait for interface to be added, if needed
+  -W = wait for a control interface monitor before starting
+  -N = start describing new interface
+
+drivers:
+  hostap = Host AP driver (Intersil Prism2/2.5/3) [default]
+       (this can also be used with Linuxant DriverLoader)
+  hermes = Agere Systems Inc. driver (Hermes-I/Hermes-II)
+  madwifi = MADWIFI 802.11 support (Atheros, etc.) (deprecated; use wext)
+  atmel = ATMEL AT76C5XXx (USB, PCMCIA)
+  wext = Linux wireless extensions (generic)
+  ralink = Ralink Client driver
+  ndiswrapper = Linux ndiswrapper (deprecated; use wext)
+  broadcom = Broadcom wl.o driver
+  ipw = Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 or newer)
+  wired = wpa_supplicant wired Ethernet driver
+  roboswitch = wpa_supplicant Broadcom switch driver
+  bsd = BSD 802.11 support (Atheros, etc.)
+  ndis = Windows NDIS driver
+
+In most common cases, wpa_supplicant is started with
+
+wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
+
+This makes the process fork into background.
+
+The easiest way to debug problems, and to get debug log for bug
+reports, is to start wpa_supplicant on foreground with debugging
+enabled:
+
+wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
+
+If the specific driver wrapper is not known beforehand, it is possible
+to specify multiple comma separated driver wrappers on the command
+line. wpa_supplicant will use the first driver wrapper that is able to
+initialize the interface.
+
+wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
+
+
+wpa_supplicant can control multiple interfaces (radios) either by
+running one process for each interface separately or by running just
+one process and list of options at command line. Each interface is
+separated with -N argument. As an example, following command would
+start wpa_supplicant for two interfaces:
+
+wpa_supplicant \
+       -c wpa1.conf -i wlan0 -D hostap -N \
+       -c wpa2.conf -i ath0 -D madwifi
+
+
+If the interface is added in a Linux bridge (e.g., br0), the bridge
+interface needs to be configured to wpa_supplicant in addition to the
+main interface:
+
+wpa_supplicant -cw.conf -Dmadwifi -iath0 -bbr0
+
+
+Configuration file
+------------------
+
+wpa_supplicant is configured using a text file that lists all accepted
+networks and security policies, including pre-shared keys. See
+example configuration file, wpa_supplicant.conf, for detailed
+information about the configuration format and supported fields.
+
+Changes to configuration file can be reloaded be sending SIGHUP signal
+to wpa_supplicant ('killall -HUP wpa_supplicant'). Similarly,
+reloading can be triggered with 'wpa_cli reconfigure' command.
+
+Configuration file can include one or more network blocks, e.g., one
+for each used SSID. wpa_supplicant will automatically select the best
+betwork based on the order of network blocks in the configuration
+file, network security level (WPA/WPA2 is preferred), and signal
+strength.
+
+Example configuration files for some common configurations:
+
+1) WPA-Personal (PSK) as home network and WPA-Enterprise with EAP-TLS as work
+   network
+
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+#
+# home network; allow all valid ciphers
+network={
+       ssid="home"
+       scan_ssid=1
+       key_mgmt=WPA-PSK
+       psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+       ssid="work"
+       scan_ssid=1
+       key_mgmt=WPA-EAP
+       pairwise=CCMP TKIP
+       group=CCMP TKIP
+       eap=TLS
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+}
+
+
+2) WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
+   (e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series)
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP
+       eap=PEAP
+       identity="user@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       phase1="peaplabel=0"
+       phase2="auth=MSCHAPV2"
+}
+
+
+3) EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+   unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="user@example.com"
+       anonymous_identity="anonymous@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       phase2="auth=MD5"
+}
+
+
+4) IEEE 802.1X (i.e., no WPA) with dynamic WEP keys (require both unicast and
+   broadcast); use EAP-TLS for authentication
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+       ssid="1x-test"
+       scan_ssid=1
+       key_mgmt=IEEE8021X
+       eap=TLS
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       eapol_flags=3
+}
+
+
+5) Catch all example that allows more or less all configuration modes. The
+   configuration options are used based on what security policy is used in the
+   selected SSID. This is mostly for testing and is not recommended for normal
+   use.
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+       pairwise=CCMP TKIP
+       group=CCMP TKIP WEP104 WEP40
+       psk="very secret passphrase"
+       eap=TTLS PEAP TLS
+       identity="user@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       phase1="peaplabel=0"
+       ca_cert2="/etc/cert/ca2.pem"
+       client_cert2="/etc/cer/user.pem"
+       private_key2="/etc/cer/user.prv"
+       private_key2_passwd="password"
+}
+
+
+6) Authentication for wired Ethernet. This can be used with 'wired' or
+   'roboswitch' interface (-Dwired or -Droboswitch on command line).
+
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+ap_scan=0
+network={
+       key_mgmt=IEEE8021X
+       eap=MD5
+       identity="user"
+       password="password"
+       eapol_flags=0
+}
+
+
+
+Certificates
+------------
+
+Some EAP authentication methods require use of certificates. EAP-TLS
+uses both server side and client certificates whereas EAP-PEAP and
+EAP-TTLS only require the server side certificate. When client
+certificate is used, a matching private key file has to also be
+included in configuration. If the private key uses a passphrase, this
+has to be configured in wpa_supplicant.conf ("private_key_passwd").
+
+wpa_supplicant supports X.509 certificates in PEM and DER
+formats. User certificate and private key can be included in the same
+file.
+
+If the user certificate and private key is received in PKCS#12/PFX
+format, they need to be converted to suitable PEM/DER format for
+wpa_supplicant. This can be done, e.g., with following commands:
+
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+
+
+
+wpa_cli
+-------
+
+wpa_cli is a text-based frontend program for interacting with
+wpa_supplicant. It is used to query current status, change
+configuration, trigger events, and request interactive user input.
+
+wpa_cli can show the current authentication status, selected security
+mode, dot11 and dot1x MIBs, etc. In addition, it can configure some
+variables like EAPOL state machine parameters and trigger events like
+reassociation and IEEE 802.1X logoff/logon. wpa_cli provides a user
+interface to request authentication information, like username and
+password, if these are not included in the configuration. This can be
+used to implement, e.g., one-time-passwords or generic token card
+authentication where the authentication is based on a
+challenge-response that uses an external device for generating the
+response.
+
+The control interface of wpa_supplicant can be configured to allow
+non-root user access (ctrl_interface_group in the configuration
+file). This makes it possible to run wpa_cli with a normal user
+account.
+
+wpa_cli supports two modes: interactive and command line. Both modes
+share the same command set and the main difference is in interactive
+mode providing access to unsolicited messages (event messages,
+username/password requests).
+
+Interactive mode is started when wpa_cli is executed without including
+the command as a command line parameter. Commands are then entered on
+the wpa_cli prompt. In command line mode, the same commands are
+entered as command line arguments for wpa_cli.
+
+
+Interactive authentication parameters request
+
+When wpa_supplicant need authentication parameters, like username and
+password, which are not present in the configuration file, it sends a
+request message to all attached frontend programs, e.g., wpa_cli in
+interactive mode. wpa_cli shows these requests with
+"CTRL-REQ-<type>-<id>:<text>" prefix. <type> is IDENTITY, PASSWORD, or
+OTP (one-time-password). <id> is a unique identifier for the current
+network. <text> is description of the request. In case of OTP request,
+it includes the challenge from the authentication server.
+
+The reply to these requests can be given with 'identity', 'password',
+and 'otp' commands. <id> needs to be copied from the the matching
+request. 'password' and 'otp' commands can be used regardless of
+whether the request was for PASSWORD or OTP. The main difference
+between these two commands is that values given with 'password' are
+remembered as long as wpa_supplicant is running whereas values given
+with 'otp' are used only once and then forgotten, i.e., wpa_supplicant
+will ask frontend for a new value for every use. This can be used to
+implement one-time-password lists and generic token card -based
+authentication.
+
+Example request for password and a matching reply:
+
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+
+Example request for generic token card challenge-response:
+
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+
+
+wpa_cli commands
+
+  status = get current WPA/EAPOL/EAP status
+  mib = get MIB variables (dot1x, dot11)
+  help = show this usage help
+  interface [ifname] = show interfaces/select interface
+  level <debug level> = change debug level
+  license = show full wpa_cli license
+  logoff = IEEE 802.1X EAPOL state machine logoff
+  logon = IEEE 802.1X EAPOL state machine logon
+  set = set variables (shows list of variables when run without arguments)
+  pmksa = show PMKSA cache
+  reassociate = force reassociation
+  reconfigure = force wpa_supplicant to re-read its configuration file
+  preauthenticate <BSSID> = force preauthentication
+  identity <network id> <identity> = configure identity for an SSID
+  password <network id> <password> = configure password for an SSID
+  pin <network id> <pin> = configure pin for an SSID
+  otp <network id> <password> = configure one-time-password for an SSID
+  passphrase <network id> <passphrase> = configure private key passphrase
+    for an SSID
+  bssid <network id> <BSSID> = set preferred BSSID for an SSID
+  list_networks = list configured networks
+  select_network <network id> = select a network (disable others)
+  enable_network <network id> = enable a network
+  disable_network <network id> = disable a network
+  add_network = add a network
+  remove_network <network id> = remove a network
+  set_network <network id> <variable> <value> = set network variables (shows
+    list of variables when run without arguments)
+  get_network <network id> <variable> = get network variables
+  save_config = save the current configuration
+  disconnect = disconnect and wait for reassociate command before connecting
+  scan = request new BSS scan
+  scan_results = get latest scan results
+  get_capability <eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies
+  terminate = terminate wpa_supplicant
+  quit = exit wpa_cli
+
+
+wpa_cli command line options
+
+wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] [-a<action file>] \
+        [-P<pid file>] [-g<global ctrl>]  [command..]
+  -h = help (show this usage text)
+  -v = shown version information
+  -a = run in daemon mode executing the action file based on events from
+       wpa_supplicant
+  -B = run a daemon in the background
+  default path: /var/run/wpa_supplicant
+  default interface: first interface found in socket path
+
+
+Using wpa_cli to run external program on connect/disconnect
+-----------------------------------------------------------
+
+wpa_cli can used to run external programs whenever wpa_supplicant
+connects or disconnects from a network. This can be used, e.g., to
+update network configuration and/or trigget DHCP client to update IP
+addresses, etc.
+
+One wpa_cli process in "action" mode needs to be started for each
+interface. For example, the following command starts wpa_cli for the
+default ingterface (-i can be used to select the interface in case of
+more than one interface being used at the same time):
+
+wpa_cli -a/sbin/wpa_action.sh -B
+
+The action file (-a option, /sbin/wpa_action.sh in this example) will
+be executed whenever wpa_supplicant completes authentication (connect
+event) or detects disconnection). The action script will be called
+with two command line arguments: interface name and event (CONNECTED
+or DISCONNECTED). If the action script needs to get more information
+about the current network, it can use 'wpa_cli status' to query
+wpa_supplicant for more information.
+
+Following example can be used as a simple template for an action
+script:
+
+#!/bin/sh
+
+IFNAME=$1
+CMD=$2
+
+if [ "$CMD" == "CONNECTED" ]; then
+    SSID=`wpa_cli -i$IFNAME status | grep ^ssid= | cut -f2- -d=`
+    # configure network, signal DHCP client, etc.
+fi
+
+if [ "$CMD" == "DISCONNECTED" ]; then
+    # remove network configuration, if needed
+fi
+
+
+
+Integrating with pcmcia-cs/cardmgr scripts
+------------------------------------------
+
+wpa_supplicant needs to be running when using a wireless network with
+WPA. It can be started either from system startup scripts or from
+pcmcia-cs/cardmgr scripts (when using PC Cards). WPA handshake must be
+completed before data frames can be exchanged, so wpa_supplicant
+should be started before DHCP client.
+
+For example, following small changes to pcmcia-cs scripts can be used
+to enable WPA support:
+
+Add MODE="Managed" and WPA="y" to the network scheme in
+/etc/pcmcia/wireless.opts.
+
+Add the following block to the end of 'start' action handler in
+/etc/pcmcia/wireless:
+
+    if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+       /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf \
+               -i$DEVICE
+    fi
+
+Add the following block to the end of 'stop' action handler (may need
+to be separated from other actions) in /etc/pcmcia/wireless:
+
+    if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+       killall wpa_supplicant
+    fi
+
+This will make cardmgr start wpa_supplicant when the card is plugged
+in.
+
+
+
+Dynamic interface add and operation without configuration files
+---------------------------------------------------------------
+
+wpa_supplicant can be started without any configuration files or
+network interfaces. When used in this way, a global (i.e., per
+wpa_supplicant process) control interface is used to add and remove
+network interfaces. Each network interface can then be configured
+through a per-network interface control interface. For example,
+following commands show how to start wpa_supplicant without any
+network interfaces and then add a network interface and configure a
+network (SSID):
+
+# Start wpa_supplicant in the background
+wpa_supplicant -g/var/run/wpa_supplicant-global -B
+
+# Add a new interface (wlan0, no configuration file, driver=wext, and
+# enable control interface)
+wpa_cli -g/var/run/wpa_supplicant-global interface_add wlan0 \
+       "" wext /var/run/wpa_supplicant
+
+# Configure a network using the newly added network interface:
+wpa_cli -iwlan0 add_network
+wpa_cli -iwlan0 set_network 0 ssid '"test"'
+wpa_cli -iwlan0 set_network 0 key_mgmt WPA-PSK
+wpa_cli -iwlan0 set_network 0 psk '"12345678"'
+wpa_cli -iwlan0 set_network 0 pairwise TKIP
+wpa_cli -iwlan0 set_network 0 group TKIP
+wpa_cli -iwlan0 set_network 0 proto WPA
+wpa_cli -iwlan0 enable_network 0
+
+# At this point, the new network interface should start trying to associate
+# with the WPA-PSK network using SSID test.
+
+# Remove network interface
+wpa_cli -g/var/run/wpa_supplicant-global interface_remove wlan0
+
+
+Privilege separation
+--------------------
+
+To minimize the size of code that needs to be run with root privileges
+(e.g., to control wireless interface operation), wpa_supplicant
+supports optional privilege separation. If enabled, this separates the
+privileged operations into a separate process (wpa_priv) while leaving
+rest of the code (e.g., EAP authentication and WPA handshakes) into an
+unprivileged process (wpa_supplicant) that can be run as non-root
+user. Privilege separation restricts the effects of potential software
+errors by containing the majority of the code in an unprivileged
+process to avoid full system compromise.
+
+Privilege separation is not enabled by default and it can be enabled
+by adding CONFIG_PRIVSEP=y to the build configuration (.config). When
+enabled, the privileged operations (driver wrapper and l2_packet) are
+linked into a separate daemon program, wpa_priv. The unprivileged
+program, wpa_supplicant, will be built with a special driver/l2_packet
+wrappers that communicate with the privileged wpa_priv process to
+perform the needed operations. wpa_priv can control what privileged
+are allowed.
+
+wpa_priv needs to be run with network admin privileges (usually, root
+user). It opens a UNIX domain socket for each interface that is
+included on the command line; any other interface will be off limits
+for wpa_supplicant in this kind of configuration. After this,
+wpa_supplicant can be run as a non-root user (e.g., all standard users
+on a laptop or as a special non-privileged user account created just
+for this purpose to limit access to user files even further).
+
+
+Example configuration:
+- create user group for users that are allowed to use wpa_supplicant
+  ('wpapriv' in this example) and assign users that should be able to
+  use wpa_supplicant into that group
+- create /var/run/wpa_priv directory for UNIX domain sockets and control
+  user access by setting it accessible only for the wpapriv group:
+  mkdir /var/run/wpa_priv
+  chown root:wpapriv /var/run/wpa_priv
+  chmod 0750 /var/run/wpa_priv
+- start wpa_priv as root (e.g., from system startup scripts) with the
+  enabled interfaces configured on the command line:
+  wpa_priv -B -P /var/run/wpa_priv.pid wext:ath0
+- run wpa_supplicant as non-root with a user that is in wpapriv group:
+  wpa_supplicant -i ath0 -c wpa_supplicant.conf
+
+wpa_priv does not use the network interface before wpa_supplicant is
+started, so it is fine to include network interfaces that are not
+available at the time wpa_priv is started. As an alternative, wpa_priv
+can be started when an interface is added (hotplug/udev/etc. scripts).
+wpa_priv can control multiple interface with one process, but it is
+also possible to run multiple wpa_priv processes at the same time, if
+desired.
diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS
new file mode 100644 (file)
index 0000000..8f0d0d6
--- /dev/null
@@ -0,0 +1,200 @@
+wpa_supplicant and Wi-Fi Protected Setup (WPS)
+==============================================
+
+This document describes how the WPS implementation in wpa_supplicant
+can be configured and how an external component on the client (e.g.,
+management GUI) is used to enable WPS enrollment and registrar
+registration.
+
+
+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
+
+
+wpa_supplicant implementation
+-----------------------------
+
+wpa_supplicant includes an optional WPS component that can be used as
+an Enrollee to enroll new network credential or as a Registrar to
+configure an AP. The current version of wpa_supplicant does not
+support operation as an external WLAN Management Registrar for adding
+new client devices or configuring the AP over UPnP.
+
+
+wpa_supplicant configuration
+----------------------------
+
+WPS is an optional component that needs to be enabled in
+wpa_supplicant build configuration (.config). Here is an example
+configuration that includes WPS support and Linux wireless extensions
+-based driver interface:
+
+CONFIG_DRIVER_WEXT=y
+CONFIG_WPS=y
+
+
+WPS needs the Universally Unique IDentifier (UUID; see RFC 4122) for
+the device. This is configured in the runtime configuration for
+wpa_supplicant (if not set, UUID will be generated based on local MAC
+address):
+
+# example UUID for WPS
+uuid=12345678-9abc-def0-1234-56789abcdef0
+
+The network configuration blocks needed for WPS are added
+automatically based on control interface commands, so they do not need
+to be added explicitly in the configuration file.
+
+WPS registration will generate new network blocks for the acquired
+credentials. If these are to be stored for future use (after
+restarting wpa_supplicant), wpa_supplicant will need to be configured
+to allow configuration file updates:
+
+update_config=1
+
+
+
+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. wpa_supplicant uses the control interface as an input channel
+for these events.
+
+If the client device has a display, a random PIN has to be generated
+for each WPS registration session. wpa_supplicant can do this with a
+control interface request, e.g., by calling wpa_cli:
+
+wpa_cli wps_pin any
+
+This will return the generated 8-digit PIN which will then need to be
+entered at the Registrar to complete WPS registration. At that point,
+the client will be enrolled with credentials needed to connect to the
+AP to access the network.
+
+
+If the client device does not have a display that could show the
+random PIN, a hardcoded PIN that is printed on a label can be
+used. wpa_supplicant is notified this with a control interface
+request, e.g., by calling wpa_cli:
+
+wpa_cli wps_pin any 12345670
+
+This starts the WPS negotiation in the same way as above with the
+generated PIN.
+
+
+If the client design wants to support optional WPS PBC mode, this can
+be enabled by either a physical button in the client device or a
+virtual button in the user interface. The PBC operation requires that
+a button is also pressed at the AP/Registrar at about the same time (2
+minute window). wpa_supplicant is notified of the local button event
+over the control interface, e.g., by calling wpa_cli:
+
+wpa_cli wps_pbc
+
+At this point, the AP/Registrar has two minutes to complete WPS
+negotiation which will generate a new WPA PSK in the same way as the
+PIN method described above.
+
+
+If the client wants to operate in the Registrar role to learn the
+current AP configuration and optionally, to configure an AP,
+wpa_supplicant is notified over the control interface, e.g., with
+wpa_cli:
+
+wpa_cli wps_reg <AP BSSID> <AP PIN>
+(example: wpa_cli wps_reg 02:34:56:78:9a:bc 12345670)
+
+This is used to fetch the current AP settings instead of actually
+changing them. The main difference with the wps_pin command is that
+wps_reg uses the AP PIN (e.g., from a label on the AP) instead of a
+PIN generated at the client.
+
+In order to change the AP configuration, the new configuration
+parameters are given to the wps_reg command:
+
+wpa_cli wps_reg <AP BSSID> <AP PIN> <new SSID> <auth> <encr> <new key>
+examples:
+  wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 testing WPA2PSK CCMP 12345678
+  wpa_cli wps_reg 02:34:56:78:9a:bc 12345670 clear OPEN NONE ""
+
+<auth> must be one of the following: OPEN WPAPSK WPA2PSK
+<encr> must be one of the following: NONE WEP TKIP CCMP
+
+
+Scanning
+--------
+
+Scan results ('wpa_cli scan_results' or 'wpa_cli bss <idx>') include a
+flags field that is used to indicate whether the BSS support WPS. If
+the AP support WPS, but has not recently activated a Registrar, [WPS]
+flag will be included. If PIN method has been recently selected,
+[WPS-PIN] is shown instead. Similarly, [WPS-PBC] is shown if PBC mode
+is in progress. GUI programs can use these as triggers for suggesting
+a guided WPS configuration to the user. In addition, control interface
+monitor events WPS-AP-AVAILABLE{,-PBC,-PIN} can be used to find out if
+there are WPS enabled APs in scan results without having to go through
+all the details in the GUI. These notification could be used, e.g., to
+suggest possible WPS connection to the user.
+
+
+wpa_gui
+-------
+
+wpa_gui-qt4 directory contains a sample GUI that shows an example of
+how WPS support can be integrated into the GUI. Its main window has a
+WPS tab that guides user through WPS registration with automatic AP
+selection. In addition, it shows how WPS can be started manually by
+selecting an AP from scan results.
+
+
+Credential processing
+---------------------
+
+By default, wpa_supplicant processes received credentials and updates
+its configuration internally. However, it is possible to
+control these operations from external programs, if desired.
+
+This internal processing can be disabled with wps_cred_processing=1
+option. When this is used, an external program is responsible for
+processing the credential attributes and updating wpa_supplicant
+configuration based on them.
+
+Following control interface messages are sent out for external programs:
+
+WPS-CRED-RECEIVED  <hexdump of Credential attribute(s)>
+For example:
+<2>WPS-CRED-RECEIVED 100e006f10260001011045000c6a6b6d2d7770732d74657374100300020020100f000200081027004030653462303435366332363666653064333961643135353461316634626637313234333761636664623766333939653534663166316230323061643434386235102000060266a0ee1727
diff --git a/wpa_supplicant/README-Windows.txt b/wpa_supplicant/README-Windows.txt
new file mode 100644 (file)
index 0000000..292223d
--- /dev/null
@@ -0,0 +1,450 @@
+wpa_supplicant for Windows
+==========================
+
+Copyright (c) 2003-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.
+
+This product includes software developed by the OpenSSL Project
+for use in the OpenSSL Toolkit (http://www.openssl.org/). This
+product includes cryptographic software written by Eric Young
+(eay@cryptsoft.com).
+
+
+wpa_supplicant has support for being used as a WPA/WPA2/IEEE 802.1X
+Supplicant on Windows. The current port requires that WinPcap
+(http://winpcap.polito.it/) is installed for accessing packets and the
+driver interface. Both release versions 3.0 and 3.1 are supported.
+
+The current port is still somewhat experimental. It has been tested
+mainly on Windows XP (SP2) with limited set of NDIS drivers. In
+addition, the current version has been reported to work with Windows
+2000.
+
+All security modes have been verified to work (at least complete
+authentication and successfully ping a wired host):
+- plaintext
+- static WEP / open system authentication
+- static WEP / shared key authentication
+- IEEE 802.1X with dynamic WEP keys
+- WPA-PSK, TKIP, CCMP, TKIP+CCMP
+- WPA-EAP, TKIP, CCMP, TKIP+CCMP
+- WPA2-PSK, TKIP, CCMP, TKIP+CCMP
+- WPA2-EAP, TKIP, CCMP, TKIP+CCMP
+
+
+Binary version
+--------------
+
+Compiled binary version of the wpa_supplicant and additional tools is
+available from http://w1.fi/wpa_supplicant/. These binaries can be
+used after installing WinPcap.
+
+wpa_gui uses Qt 4 framework and may need additional dynamic libraries
+(DLLs). These libraries are available from
+http://w1.fi/wpa_supplicant/qt4/wpa_gui-qt433-windows-dll.zip
+You can copy the DLL files from this ZIP package into the same directory
+with wpa_gui.exe to allow wpa_gui to be started.
+
+
+Building wpa_supplicant with mingw
+----------------------------------
+
+The default build setup for wpa_supplicant is to use MinGW and
+cross-compiling from Linux to MinGW/Windows. It should also be
+possible to build this under Windows using the MinGW tools, but that
+is not tested nor supported and is likely to require some changes to
+the Makefile unless cygwin is used.
+
+
+Building wpa_supplicant with MSVC
+---------------------------------
+
+wpa_supplicant can be built with Microsoft Visual C++ compiler. This
+has been tested with Microsoft Visual C++ Toolkit 2003 and Visual
+Studio 2005 using the included nmake.mak as a Makefile for nmake. IDE
+can also be used by creating a project that includes the files and
+defines mentioned in nmake.mak. Example VS2005 solution and project
+files are included in vs2005 subdirectory. This can be used as a
+starting point for building the programs with VS2005 IDE. Visual Studio
+2008 Express Edition is also able to use these project files.
+
+WinPcap development package is needed for the build and this can be
+downloaded from http://www.winpcap.org/install/bin/WpdPack_4_0_2.zip. The
+default nmake.mak expects this to be unpacked into C:\dev\WpdPack so
+that Include and Lib directories are in this directory. The files can be
+stored elsewhere as long as the WINPCAPDIR in nmake.mak is updated to
+match with the selected directory. In case a project file in the IDE is
+used, these Include and Lib directories need to be added to project
+properties as additional include/library directories.
+
+OpenSSL source package can be downloaded from
+http://www.openssl.org/source/openssl-0.9.8i.tar.gz and built and
+installed following instructions in INSTALL.W32. Note that if EAP-FAST
+support will be included in the wpa_supplicant, OpenSSL needs to be
+patched to# support it openssl-0.9.8i-tls-extensions.patch. The example
+nmake.mak file expects OpenSSL to be installed into C:\dev\openssl, but
+this directory can be modified by changing OPENSSLDIR variable in
+nmake.mak.
+
+If you do not need EAP-FAST support, you may also be able to use Win32
+binary installation package of OpenSSL from
+http://www.slproweb.com/products/Win32OpenSSL.html instead of building
+the library yourself. In this case, you will need to copy Include and
+Lib directories in suitable directory, e.g., C:\dev\openssl for the
+default nmake.mak. Copy {Win32OpenSSLRoot}\include into
+C:\dev\openssl\include and make C:\dev\openssl\lib subdirectory with
+files from {Win32OpenSSLRoot}\VC (i.e., libeay*.lib and ssleay*.lib).
+This will end up using dynamically linked OpenSSL (i.e., .dll files are
+needed) for it. Alternative, you can copy files from
+{Win32OpenSSLRoot}\VC\static to create a static build (no OpenSSL .dll
+files needed).
+
+
+Building wpa_supplicant for cygwin
+----------------------------------
+
+wpa_supplicant can be built for cygwin by installing the needed
+development packages for cygwin. This includes things like compiler,
+make, openssl development package, etc. In addition, developer's pack
+for WinPcap (WPdpack.zip) from
+http://winpcap.polito.it/install/default.htm is needed.
+
+.config file should enable only one driver interface,
+CONFIG_DRIVER_NDIS. In addition, include directories may need to be
+added to match the system. An example configuration is available in
+defconfig. The library and include files for WinPcap will either need
+to be installed in compiler/linker default directories or their
+location will need to be adding to .config when building
+wpa_supplicant.
+
+Othen than this, the build should be more or less identical to Linux
+version, i.e., just run make after having created .config file. An
+additional tool, win_if_list.exe, can be built by running "make
+win_if_list".
+
+
+Building wpa_gui
+----------------
+
+wpa_gui uses Qt application framework from Trolltech. It can be built
+with the open source version of Qt4 and MinGW. Following commands can
+be used to build the binary in the Qt 4 Command Prompt:
+
+# go to the root directory of wpa_supplicant source code
+cd wpa_gui-qt4
+qmake -o Makefile wpa_gui.pro
+make
+# the wpa_gui.exe binary is created into 'release' subdirectory
+
+
+Using wpa_supplicant for Windows
+--------------------------------
+
+wpa_supplicant, wpa_cli, and wpa_gui behave more or less identically to
+Linux version, so instructions in README and example wpa_supplicant.conf
+should be applicable for most parts. In addition, there is another
+version of wpa_supplicant, wpasvc.exe, which can be used as a Windows
+service and which reads its configuration from registry instead of
+text file.
+
+When using access points in "hidden SSID" mode, ap_scan=2 mode need to
+be used (see wpa_supplicant.conf for more information).
+
+Windows NDIS/WinPcap uses quite long interface names, so some care
+will be needed when starting wpa_supplicant. Alternatively, the
+adapter description can be used as the interface name which may be
+easier since it is usually in more human-readable
+format. win_if_list.exe can be used to find out the proper interface
+name.
+
+Example steps in starting up wpa_supplicant:
+
+# win_if_list.exe
+ifname: \Device\NPF_GenericNdisWanAdapter
+description: Generic NdisWan adapter
+
+ifname: \Device\NPF_{769E012B-FD17-4935-A5E3-8090C38E25D2}
+description: Atheros Wireless Network Adapter (Microsoft's Packet Scheduler)
+
+ifname: \Device\NPF_{732546E7-E26C-48E3-9871-7537B020A211}
+description: Intel 8255x-based Integrated Fast Ethernet (Microsoft's Packet Scheduler)
+
+
+Since the example configuration used Atheros WLAN card, the middle one
+is the correct interface in this case. The interface name for -i
+command line option is the full string following "ifname:" (the
+"\Device\NPF_" prefix can be removed). In other words, wpa_supplicant
+would be started with the following command:
+
+# wpa_supplicant.exe -i'{769E012B-FD17-4935-A5E3-8090C38E25D2}' -c wpa_supplicant.conf -d
+
+-d optional enables some more debugging (use -dd for even more, if
+needed). It can be left out if debugging information is not needed.
+
+With the alternative mechanism for selecting the interface, this
+command has identical results in this case:
+
+# wpa_supplicant.exe -iAtheros -c wpa_supplicant.conf -d
+
+
+Simple configuration example for WPA-PSK:
+
+#ap_scan=2
+ctrl_interface=
+network={
+       ssid="test"
+       key_mgmt=WPA-PSK
+       proto=WPA
+       pairwise=TKIP
+       psk="secret passphrase"
+}
+
+(remove '#' from the comment out ap_scan line to enable mode in which
+wpa_supplicant tries to associate with the SSID without doing
+scanning; this allows APs with hidden SSIDs to be used)
+
+
+wpa_cli.exe and wpa_gui.exe can be used to interact with the
+wpa_supplicant.exe program in the same way as with Linux. Note that
+ctrl_interface is using UNIX domain sockets when built for cygwin, but
+the native build for Windows uses named pipes and the contents of the
+ctrl_interface configuration item is used to control access to the
+interface. Anyway, this variable has to be included in the configuration
+to enable the control interface.
+
+
+Example SDDL string formats:
+
+(local admins group has permission, but nobody else):
+
+ctrl_interface=SDDL=D:(A;;GA;;;BA)
+
+("A" == "access allowed", "GA" == GENERIC_ALL == all permissions, and
+"BA" == "builtin administrators" == the local admins.  The empty fields
+are for flags and object GUIDs, none of which should be required in this
+case.)
+
+(local admins and the local "power users" group have permissions,
+but nobody else):
+
+ctrl_interface=SDDL=D:(A;;GA;;;BA)(A;;GA;;;PU)
+
+(One ACCESS_ALLOWED ACE for GENERIC_ALL for builtin administrators, and
+one ACCESS_ALLOWED ACE for GENERIC_ALL for power users.)
+
+(close to wide open, but you have to be a valid user on
+the machine):
+
+ctrl_interface=SDDL=D:(A;;GA;;;AU)
+
+(One ACCESS_ALLOWED ACE for GENERIC_ALL for the "authenticated users"
+group.)
+
+This one would allow absolutely everyone (including anonymous
+users) -- this is *not* recommended, since named pipes can be attached
+to from anywhere on the network (i.e. there's no "this machine only"
+like there is with 127.0.0.1 sockets):
+
+ctrl_interface=SDDL=D:(A;;GA;;;BU)(A;;GA;;;AN)
+
+(BU == "builtin users", "AN" == "anonymous")
+
+See also [1] for the format of ACEs, and [2] for the possible strings
+that can be used for principal names.
+
+[1]
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/ace_strings.asp
+[2]
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/sid_strings.asp
+
+
+Starting wpa_supplicant as a Windows service (wpasvc.exe)
+---------------------------------------------------------
+
+wpa_supplicant can be started as a Windows service by using wpasvc.exe
+program that is alternative build of wpa_supplicant.exe. Most of the
+core functionality of wpasvc.exe is identical to wpa_supplicant.exe,
+but it is using Windows registry for configuration information instead
+of a text file and command line parameters. In addition, it can be
+registered as a service that can be started automatically or manually
+like any other Windows service.
+
+The root of wpa_supplicant configuration in registry is
+HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant. This level includes global
+parameters and a 'interfaces' subkey with all the interface configuration
+(adapter to confname mapping). Each such mapping is a subkey that has
+'adapter', 'config', and 'ctrl_interface' values.
+
+This program can be run either as a normal command line application,
+e.g., for debugging, with 'wpasvc.exe app' or as a Windows service.
+Service need to be registered with 'wpasvc.exe reg <full path to
+wpasvc.exe>'. Alternatively, 'wpasvc.exe reg' can be used to register
+the service with the current location of wpasvc.exe. After this, wpasvc
+can be started like any other Windows service (e.g., 'net start wpasvc')
+or it can be configured to start automatically through the Services tool
+in administrative tasks. The service can be unregistered with
+'wpasvc.exe unreg'.
+
+If the service is set to start during system bootup to make the
+network connection available before any user has logged in, there may
+be a long (half a minute or so) delay in starting up wpa_supplicant
+due to WinPcap needing a driver called "Network Monitor Driver" which
+is started by default on demand.
+
+To speed up wpa_supplicant start during system bootup, "Network
+Monitor Driver" can be configured to be started sooner by setting its
+startup type to System instead of the default Demand. To do this, open
+up Device Manager, select Show Hidden Devices, expand the "Non
+Plug-and-Play devices" branch, double click "Network Monitor Driver",
+go to the Driver tab, and change the Demand setting to System instead.
+
+Configuration data is in HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs
+key. Each configuration profile has its own key under this. In terms of text
+files, each profile would map to a separate text file with possibly multiple
+networks. Under each profile, there is a networks key that lists all
+networks as a subkey. Each network has set of values in the same way as
+network block in the configuration file. In addition, blobs subkey has
+possible blobs as values.
+
+HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000
+   ssid="example"
+   key_mgmt=WPA-PSK
+
+See win_example.reg for an example on how to setup wpasvc.exe
+parameters in registry. It can also be imported to registry as a
+starting point for the configuration.
+
+
+
+License information for third party software used in this product:
+
+  OpenSSL License
+  ---------------
+
+/* ====================================================================
+ * Copyright (c) 1998-2004 The OpenSSL Project.  All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED 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 OpenSSL PROJECT OR
+ * ITS 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.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+
+ Original SSLeay License
+ -----------------------
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * 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 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. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``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 AUTHOR 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.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+
+
+   Qt Open Source Edition
+   ----------------------
+
+The Qt GUI Toolkit is Copyright (C) 1994-2007 Trolltech ASA.
+Qt Open Source Edition is licensed under GPL version 2.
+
+Source code for the library is available at
+http://w1.fi/wpa_supplicant/qt4/qt-win-opensource-src-4.3.3.zip
diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c
new file mode 100644 (file)
index 0000000..2b93984
--- /dev/null
@@ -0,0 +1,481 @@
+/*
+ * WPA Supplicant - Basic AP mode support routines
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/ap_config.h"
+#ifdef NEED_AP_MLME
+#include "ap/ieee802_11.h"
+#endif /* NEED_AP_MLME */
+#include "ap/ieee802_1x.h"
+#include "ap/wps_hostapd.h"
+#include "ap/ctrl_iface_ap.h"
+#include "eap_common/eap_defs.h"
+#include "eap_server/eap_methods.h"
+#include "eap_common/eap_wsc_common.h"
+#include "wps/wps.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ap.h"
+
+
+static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid,
+                                 struct hostapd_config *conf)
+{
+       struct hostapd_bss_config *bss = &conf->bss[0];
+       int pairwise;
+
+       conf->driver = wpa_s->driver;
+
+       os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
+
+       if (ssid->frequency == 0) {
+               /* default channel 11 */
+               conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+               conf->channel = 11;
+       } else if (ssid->frequency >= 2412 && ssid->frequency <= 2472) {
+               conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+               conf->channel = (ssid->frequency - 2407) / 5;
+       } else if ((ssid->frequency >= 5180 && ssid->frequency <= 5240) ||
+                  (ssid->frequency >= 5745 && ssid->frequency <= 5825)) {
+               conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+               conf->channel = (ssid->frequency - 5000) / 5;
+       } else {
+               wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
+                          ssid->frequency);
+               return -1;
+       }
+
+       /* TODO: enable HT if driver supports it;
+        * drop to 11b if driver does not support 11g */
+
+       if (ssid->ssid_len == 0) {
+               wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
+               return -1;
+       }
+       os_memcpy(bss->ssid.ssid, ssid->ssid, ssid->ssid_len);
+       bss->ssid.ssid[ssid->ssid_len] = '\0';
+       bss->ssid.ssid_len = ssid->ssid_len;
+       bss->ssid.ssid_set = 1;
+
+       if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt))
+               bss->wpa = ssid->proto;
+       bss->wpa_key_mgmt = ssid->key_mgmt;
+       bss->wpa_pairwise = ssid->pairwise_cipher;
+       if (ssid->passphrase) {
+               bss->ssid.wpa_passphrase = os_strdup(ssid->passphrase);
+       } else if (ssid->psk_set) {
+               os_free(bss->ssid.wpa_psk);
+               bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+               if (bss->ssid.wpa_psk == NULL)
+                       return -1;
+               os_memcpy(bss->ssid.wpa_psk->psk, ssid->psk, PMK_LEN);
+               bss->ssid.wpa_psk->group = 1;
+       }
+
+       /* 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;
+
+       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;
+
+#ifdef CONFIG_WPS
+       /*
+        * Enable WPS by default, but require user interaction to actually use
+        * it. Only the internal Registrar is supported.
+        */
+       bss->eap_server = 1;
+       bss->wps_state = 2;
+       bss->ap_setup_locked = 1;
+       if (wpa_s->conf->config_methods)
+               bss->config_methods = os_strdup(wpa_s->conf->config_methods);
+       if (wpa_s->conf->device_type)
+               bss->device_type = os_strdup(wpa_s->conf->device_type);
+#endif /* CONFIG_WPS */
+
+       return 0;
+}
+
+
+static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
+{
+}
+
+
+static int ap_probe_req_rx(void *ctx, const u8 *addr, const u8 *ie,
+                          size_t ie_len)
+{
+       return 0;
+}
+
+
+static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
+                                 const u8 *uuid_e)
+{
+}
+
+
+int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid *ssid)
+{
+       struct wpa_driver_associate_params params;
+       struct hostapd_iface *hapd_iface;
+       struct hostapd_config *conf;
+       size_t i;
+
+       if (ssid->ssid == NULL || ssid->ssid_len == 0) {
+               wpa_printf(MSG_ERROR, "No SSID configured for AP mode");
+               return -1;
+       }
+
+       wpa_supplicant_ap_deinit(wpa_s);
+
+       wpa_printf(MSG_DEBUG, "Setting up AP (SSID='%s')",
+                  wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+
+       os_memset(&params, 0, sizeof(params));
+       params.ssid = ssid->ssid;
+       params.ssid_len = ssid->ssid_len;
+       switch (ssid->mode) {
+       case WPAS_MODE_INFRA:
+               params.mode = IEEE80211_MODE_INFRA;
+               break;
+       case WPAS_MODE_IBSS:
+               params.mode = IEEE80211_MODE_IBSS;
+               break;
+       case WPAS_MODE_AP:
+               params.mode = IEEE80211_MODE_AP;
+               break;
+       }
+       params.freq = ssid->frequency;
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
+               wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+       else
+               wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
+       params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+
+       if (ssid->pairwise_cipher & WPA_CIPHER_CCMP)
+               wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+       else if (ssid->pairwise_cipher & WPA_CIPHER_TKIP)
+               wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
+       else if (ssid->pairwise_cipher & WPA_CIPHER_NONE)
+               wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+       else {
+               wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
+                          "cipher.");
+               return -1;
+       }
+       params.pairwise_suite = cipher_suite2driver(wpa_s->pairwise_cipher);
+       params.group_suite = params.pairwise_suite;
+
+       if (wpa_drv_associate(wpa_s, &params) < 0) {
+               wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
+               return -1;
+       }
+
+       wpa_s->ap_iface = hapd_iface = os_zalloc(sizeof(*wpa_s->ap_iface));
+       if (hapd_iface == NULL)
+               return -1;
+       hapd_iface->owner = wpa_s;
+
+       wpa_s->ap_iface->conf = conf = hostapd_config_defaults();
+       if (conf == NULL) {
+               wpa_supplicant_ap_deinit(wpa_s);
+               return -1;
+       }
+
+       if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
+               wpa_printf(MSG_ERROR, "Failed to create AP configuration");
+               wpa_supplicant_ap_deinit(wpa_s);
+               return -1;
+       }
+
+       hapd_iface->num_bss = conf->num_bss;
+       hapd_iface->bss = os_zalloc(conf->num_bss *
+                                   sizeof(struct hostapd_data *));
+       if (hapd_iface->bss == NULL) {
+               wpa_supplicant_ap_deinit(wpa_s);
+               return -1;
+       }
+
+       for (i = 0; i < conf->num_bss; i++) {
+               hapd_iface->bss[i] =
+                       hostapd_alloc_bss_data(hapd_iface, conf,
+                                              &conf->bss[i]);
+               if (hapd_iface->bss[i] == NULL) {
+                       wpa_supplicant_ap_deinit(wpa_s);
+                       return -1;
+               }
+
+               hapd_iface->bss[i]->msg_ctx = wpa_s;
+               hapd_iface->bss[i]->public_action_cb = ap_public_action_rx;
+               hapd_iface->bss[i]->public_action_cb_ctx = wpa_s;
+               hostapd_register_probereq_cb(hapd_iface->bss[i],
+                                            ap_probe_req_rx, wpa_s);
+               hapd_iface->bss[i]->wps_reg_success_cb = ap_wps_reg_success_cb;
+               hapd_iface->bss[i]->wps_reg_success_cb_ctx = wpa_s;
+       }
+
+       os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
+       hapd_iface->bss[0]->driver = wpa_s->driver;
+       hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv;
+
+       if (hostapd_setup_interface(wpa_s->ap_iface)) {
+               wpa_printf(MSG_ERROR, "Failed to initialize AP interface");
+               wpa_supplicant_ap_deinit(wpa_s);
+               return -1;
+       }
+
+       wpa_s->current_ssid = ssid;
+       os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
+       wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+       if (wpa_s->ap_configured_cb)
+               wpa_s->ap_configured_cb(wpa_s->ap_configured_cb_ctx,
+                                       wpa_s->ap_configured_cb_data);
+
+       return 0;
+}
+
+
+void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->ap_iface == NULL)
+               return;
+
+       wpa_s->current_ssid = NULL;
+       hostapd_interface_deinit(wpa_s->ap_iface);
+       hostapd_interface_free(wpa_s->ap_iface);
+       wpa_s->ap_iface = NULL;
+       wpa_drv_deinit_ap(wpa_s);
+}
+
+
+void ap_tx_status(void *ctx, const u8 *addr,
+                 const u8 *buf, size_t len, int ack)
+{
+#ifdef NEED_AP_MLME
+       struct wpa_supplicant *wpa_s = ctx;
+       hostapd_tx_status(wpa_s->ap_iface->bss[0], addr, buf, len, ack);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len)
+{
+#ifdef NEED_AP_MLME
+       struct wpa_supplicant *wpa_s = ctx;
+       const struct ieee80211_hdr *hdr =
+               (const struct ieee80211_hdr *) frame;
+       u16 fc = le_to_host16(hdr->frame_control);
+       ieee802_11_rx_from_unknown(wpa_s->ap_iface->bss[0], hdr->addr2,
+                                  (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+                                  (WLAN_FC_TODS | WLAN_FC_FROMDS));
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt)
+{
+#ifdef NEED_AP_MLME
+       struct wpa_supplicant *wpa_s = ctx;
+       struct hostapd_frame_info fi;
+       os_memset(&fi, 0, sizeof(fi));
+       fi.datarate = rx_mgmt->datarate;
+       fi.ssi_signal = rx_mgmt->ssi_signal;
+       ieee802_11_mgmt(wpa_s->ap_iface->bss[0], rx_mgmt->frame,
+                       rx_mgmt->frame_len, &fi);
+#endif /* NEED_AP_MLME */
+}
+
+
+void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok)
+{
+#ifdef NEED_AP_MLME
+       struct wpa_supplicant *wpa_s = ctx;
+       ieee802_11_mgmt_cb(wpa_s->ap_iface->bss[0], buf, len, stype, ok);
+#endif /* NEED_AP_MLME */
+}
+
+
+void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
+                               const u8 *src_addr, const u8 *buf, size_t len)
+{
+       ieee802_1x_receive(wpa_s->ap_iface->bss[0], src_addr, buf, len);
+}
+
+
+#ifdef CONFIG_WPS
+
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       if (!wpa_s->ap_iface)
+               return -1;
+       return hostapd_wps_button_pushed(wpa_s->ap_iface->bss[0]);
+}
+
+
+int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                             const char *pin, char *buf, size_t buflen)
+{
+       int ret, ret_len = 0;
+
+       if (!wpa_s->ap_iface)
+               return -1;
+
+       if (pin == NULL) {
+               unsigned int rpin = wps_generate_pin();
+               ret_len = os_snprintf(buf, buflen, "%d", rpin);
+               pin = buf;
+       }
+
+       ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], "any", pin, 0);
+       if (ret)
+               return -1;
+       return ret_len;
+}
+
+#endif /* CONFIG_WPS */
+
+
+#ifdef CONFIG_CTRL_IFACE
+
+int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+                           char *buf, size_t buflen)
+{
+       if (wpa_s->ap_iface == NULL)
+               return -1;
+       return hostapd_ctrl_iface_sta_first(wpa_s->ap_iface->bss[0],
+                                           buf, buflen);
+}
+
+
+int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+                     char *buf, size_t buflen)
+{
+       if (wpa_s->ap_iface == NULL)
+               return -1;
+       return hostapd_ctrl_iface_sta(wpa_s->ap_iface->bss[0], txtaddr,
+                                     buf, buflen);
+}
+
+
+int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
+                          char *buf, size_t buflen)
+{
+       if (wpa_s->ap_iface == NULL)
+               return -1;
+       return hostapd_ctrl_iface_sta_next(wpa_s->ap_iface->bss[0], txtaddr,
+                                          buf, buflen);
+}
+
+
+int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
+                                size_t buflen, int verbose)
+{
+       char *pos = buf, *end = buf + buflen;
+       int ret;
+       struct hostapd_bss_config *conf;
+
+       if (wpa_s->ap_iface == NULL)
+               return -1;
+
+       conf = wpa_s->ap_iface->bss[0]->conf;
+       if (conf->wpa == 0)
+               return 0;
+
+       ret = os_snprintf(pos, end - pos,
+                         "pairwise_cipher=%s\n"
+                         "group_cipher=%s\n"
+                         "key_mgmt=%s\n",
+                         wpa_cipher_txt(conf->rsn_pairwise),
+                         wpa_cipher_txt(conf->wpa_group),
+                         wpa_key_mgmt_txt(conf->wpa_key_mgmt,
+                                          conf->wpa));
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+       return pos - buf;
+}
+
+#endif /* CONFIG_CTRL_IFACE */
+
+
+int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
+                                     const u8 *addr)
+{
+       struct hostapd_data *hapd;
+       struct hostapd_bss_config *conf;
+
+       if (!wpa_s->ap_iface)
+               return -1;
+
+       if (addr)
+               wpa_printf(MSG_DEBUG, "AP: Set MAC address filter: " MACSTR,
+                          MAC2STR(addr));
+       else
+               wpa_printf(MSG_DEBUG, "AP: Clear MAC address filter");
+
+       hapd = wpa_s->ap_iface->bss[0];
+       conf = hapd->conf;
+
+       os_free(conf->accept_mac);
+       conf->accept_mac = NULL;
+       conf->num_accept_mac = 0;
+       os_free(conf->deny_mac);
+       conf->deny_mac = NULL;
+       conf->num_deny_mac = 0;
+
+       if (addr == NULL) {
+               conf->macaddr_acl = ACCEPT_UNLESS_DENIED;
+               return 0;
+       }
+
+       conf->macaddr_acl = DENY_UNLESS_ACCEPTED;
+       conf->accept_mac = os_zalloc(sizeof(struct mac_acl_entry));
+       if (conf->accept_mac == NULL)
+               return -1;
+       os_memcpy(conf->accept_mac[0].addr, addr, ETH_ALEN);
+       conf->num_accept_mac = 1;
+
+       return 0;
+}
diff --git a/wpa_supplicant/ap.h b/wpa_supplicant/ap.h
new file mode 100644 (file)
index 0000000..381a432
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * WPA Supplicant - Basic AP mode support routines
+ * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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
+
+int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid *ssid);
+void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_ap_rx_eapol(struct wpa_supplicant *wpa_s,
+                               const u8 *src_addr, const u8 *buf, size_t len);
+int wpa_supplicant_ap_wps_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                             const char *pin, char *buf, size_t buflen);
+int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
+                           char *buf, size_t buflen);
+int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
+                     char *buf, size_t buflen);
+int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
+                          char *buf, size_t buflen);
+int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
+                                size_t buflen, int verbose);
+void ap_tx_status(void *ctx, const u8 *addr,
+                 const u8 *buf, size_t len, int ack);
+void ap_rx_from_unknown_sta(void *ctx, const u8 *frame, size_t len);
+void ap_mgmt_rx(void *ctx, struct rx_mgmt *rx_mgmt);
+void ap_mgmt_tx_cb(void *ctx, const u8 *buf, size_t len, u16 stype, int ok);
+int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
+                                     const u8 *addr);
+
+#endif /* AP_H */
diff --git a/wpa_supplicant/bgscan.c b/wpa_supplicant/bgscan.c
new file mode 100644 (file)
index 0000000..31b5d27
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * WPA Supplicant - background scan and roaming interface
+ * Copyright (c) 2009-2010, 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 "wpa_supplicant_i.h"
+#include "config_ssid.h"
+#include "bgscan.h"
+
+#ifdef CONFIG_BGSCAN_SIMPLE
+extern const struct bgscan_ops bgscan_simple_ops;
+#endif /* CONFIG_BGSCAN_SIMPLE */
+
+static const struct bgscan_ops * bgscan_modules[] = {
+#ifdef CONFIG_BGSCAN_SIMPLE
+       &bgscan_simple_ops,
+#endif /* CONFIG_BGSCAN_SIMPLE */
+       NULL
+};
+
+
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+       const char *name = ssid->bgscan;
+       const char *params;
+       size_t nlen;
+       int i;
+       const struct bgscan_ops *ops = NULL;
+
+       bgscan_deinit(wpa_s);
+       if (name == NULL)
+               return 0;
+
+       params = os_strchr(name, ':');
+       if (params == NULL) {
+               params = "";
+               nlen = os_strlen(name);
+       } else {
+               nlen = params - name;
+               params++;
+       }
+
+       for (i = 0; bgscan_modules[i]; i++) {
+               if (os_strncmp(name, bgscan_modules[i]->name, nlen) == 0) {
+                       ops = bgscan_modules[i];
+                       break;
+               }
+       }
+
+       if (ops == NULL) {
+               wpa_printf(MSG_ERROR, "bgscan: Could not find module "
+                          "matching the parameter '%s'", name);
+               return -1;
+       }
+
+       wpa_s->bgscan_priv = ops->init(wpa_s, params, ssid);
+       if (wpa_s->bgscan_priv == NULL)
+               return -1;
+       wpa_s->bgscan = ops;
+       wpa_printf(MSG_DEBUG, "bgscan: Initialized module '%s' with "
+                  "parameters '%s'", ops->name, params);
+
+       return 0;
+}
+
+
+void bgscan_deinit(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->bgscan && wpa_s->bgscan_priv) {
+               wpa_printf(MSG_DEBUG, "bgscan: Deinitializing module '%s'",
+                          wpa_s->bgscan->name);
+               wpa_s->bgscan->deinit(wpa_s->bgscan_priv);
+               wpa_s->bgscan = NULL;
+               wpa_s->bgscan_priv = NULL;
+       }
+}
+
+
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->bgscan && wpa_s->bgscan_priv)
+               return wpa_s->bgscan->notify_scan(wpa_s->bgscan_priv);
+       return 0;
+}
+
+
+void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->bgscan && wpa_s->bgscan_priv)
+               wpa_s->bgscan->notify_beacon_loss(wpa_s->bgscan_priv);
+}
+
+
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above)
+{
+       if (wpa_s->bgscan && wpa_s->bgscan_priv)
+               wpa_s->bgscan->notify_signal_change(wpa_s->bgscan_priv, above);
+}
diff --git a/wpa_supplicant/bgscan.h b/wpa_supplicant/bgscan.h
new file mode 100644 (file)
index 0000000..69e99b6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * WPA Supplicant - background scan and roaming interface
+ * Copyright (c) 2009-2010, 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 BGSCAN_H
+#define BGSCAN_H
+
+struct wpa_supplicant;
+struct wpa_ssid;
+
+struct bgscan_ops {
+       const char *name;
+
+       void * (*init)(struct wpa_supplicant *wpa_s, const char *params,
+                      const struct wpa_ssid *ssid);
+       void (*deinit)(void *priv);
+
+       int (*notify_scan)(void *priv);
+       void (*notify_beacon_loss)(void *priv);
+       void (*notify_signal_change)(void *priv, int above);
+};
+
+#ifdef CONFIG_BGSCAN
+
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void bgscan_deinit(struct wpa_supplicant *wpa_s);
+int bgscan_notify_scan(struct wpa_supplicant *wpa_s);
+void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s);
+void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above);
+
+#else /* CONFIG_BGSCAN */
+
+static inline int bgscan_init(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid)
+{
+       return 0;
+}
+
+static inline void bgscan_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int bgscan_notify_scan(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline void bgscan_notify_beacon_loss(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s,
+                                              int above)
+{
+}
+
+#endif /* CONFIG_BGSCAN */
+
+#endif /* BGSCAN_H */
diff --git a/wpa_supplicant/bgscan_simple.c b/wpa_supplicant/bgscan_simple.c
new file mode 100644 (file)
index 0000000..8e80b12
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * WPA Supplicant - background scan and roaming module: simple
+ * Copyright (c) 2009-2010, 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 "drivers/driver.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "bgscan.h"
+
+struct bgscan_simple_data {
+       struct wpa_supplicant *wpa_s;
+       const struct wpa_ssid *ssid;
+       int scan_interval;
+       int signal_threshold;
+       int short_interval; /* use if signal < threshold */
+       int long_interval; /* use if signal > threshold */
+       struct os_time last_bgscan;
+};
+
+
+static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct bgscan_simple_data *data = eloop_ctx;
+       struct wpa_supplicant *wpa_s = data->wpa_s;
+       struct wpa_driver_scan_params params;
+
+       os_memset(&params, 0, sizeof(params));
+       params.num_ssids = 1;
+       params.ssids[0].ssid = data->ssid->ssid;
+       params.ssids[0].ssid_len = data->ssid->ssid_len;
+       params.freqs = data->ssid->scan_freq;
+
+       /*
+        * A more advanced bgscan module would learn about most like channels
+        * over time and request scans only for some channels (probing others
+        * every now and then) to reduce effect on the data connection.
+        */
+
+       wpa_printf(MSG_DEBUG, "bgscan simple: Request a background scan");
+       if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+               wpa_printf(MSG_DEBUG, "bgscan simple: Failed to trigger scan");
+               eloop_register_timeout(data->scan_interval, 0,
+                                      bgscan_simple_timeout, data, NULL);
+       } else
+               os_get_time(&data->last_bgscan);
+}
+
+
+static int bgscan_simple_get_params(struct bgscan_simple_data *data,
+                                   const char *params)
+{
+       const char *pos;
+
+       if (params == NULL)
+               return 0;
+
+       data->short_interval = atoi(params);
+
+       pos = os_strchr(params, ':');
+       if (pos == NULL)
+               return 0;
+       pos++;
+       data->signal_threshold = atoi(pos);
+       pos = os_strchr(pos, ':');
+       if (pos == NULL) {
+               wpa_printf(MSG_ERROR, "bgscan simple: Missing scan interval "
+                          "for high signal");
+               return -1;
+       }
+       pos++;
+       data->long_interval = atoi(pos);
+
+       return 0;
+}
+
+
+static void * bgscan_simple_init(struct wpa_supplicant *wpa_s,
+                                const char *params,
+                                const struct wpa_ssid *ssid)
+{
+       struct bgscan_simple_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->wpa_s = wpa_s;
+       data->ssid = ssid;
+       if (bgscan_simple_get_params(data, params) < 0) {
+               os_free(data);
+               return NULL;
+       }
+       if (data->short_interval <= 0)
+               data->short_interval = 30;
+       if (data->long_interval <= 0)
+               data->long_interval = 30;
+
+       wpa_printf(MSG_DEBUG, "bgscan simple: Signal strength threshold %d  "
+                  "Short bgscan interval %d  Long bgscan interval %d",
+                  data->signal_threshold, data->short_interval,
+                  data->long_interval);
+
+       if (data->signal_threshold &&
+           wpa_drv_signal_monitor(wpa_s, data->signal_threshold, 4) < 0) {
+               wpa_printf(MSG_ERROR, "bgscan simple: Failed to enable "
+                          "signal strength monitoring");
+       }
+
+       data->scan_interval = data->short_interval;
+       eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
+                              data, NULL);
+
+       /*
+        * This function is called immediately after an association, so it is
+        * reasonable to assume that a scan was completed recently. This makes
+        * us skip an immediate new scan in cases where the current signal
+        * level is below the bgscan threshold.
+        */
+       os_get_time(&data->last_bgscan);
+
+       return data;
+}
+
+
+static void bgscan_simple_deinit(void *priv)
+{
+       struct bgscan_simple_data *data = priv;
+       eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+       if (data->signal_threshold)
+               wpa_drv_signal_monitor(data->wpa_s, 0, 0);
+       os_free(data);
+}
+
+
+static int bgscan_simple_notify_scan(void *priv)
+{
+       struct bgscan_simple_data *data = priv;
+
+       wpa_printf(MSG_DEBUG, "bgscan simple: scan result notification");
+
+       eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+       eloop_register_timeout(data->scan_interval, 0, bgscan_simple_timeout,
+                              data, NULL);
+
+       /*
+        * A more advanced bgscan could process scan results internally, select
+        * the BSS and request roam if needed. This sample uses the existing
+        * BSS/ESS selection routine. Change this to return 1 if selection is
+        * done inside the bgscan module.
+        */
+
+       return 0;
+}
+
+
+static void bgscan_simple_notify_beacon_loss(void *priv)
+{
+       wpa_printf(MSG_DEBUG, "bgscan simple: beacon loss");
+       /* TODO: speed up background scanning */
+}
+
+
+static void bgscan_simple_notify_signal_change(void *priv, int above)
+{
+       struct bgscan_simple_data *data = priv;
+       int scan = 0;
+       struct os_time now;
+
+       if (data->short_interval == data->long_interval ||
+           data->signal_threshold == 0)
+               return;
+
+       wpa_printf(MSG_DEBUG, "bgscan simple: signal level changed "
+                  "(above=%d)", above);
+       if (data->scan_interval == data->long_interval && !above) {
+               wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
+                          "bgscan interval");
+               data->scan_interval = data->short_interval;
+               os_get_time(&now);
+               if (now.sec > data->last_bgscan.sec + 1)
+                       scan = 1;
+       } else if (data->scan_interval == data->short_interval && above) {
+               wpa_printf(MSG_DEBUG, "bgscan simple: Start using long bgscan "
+                          "interval");
+               data->scan_interval = data->long_interval;
+               eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+               eloop_register_timeout(data->scan_interval, 0,
+                                      bgscan_simple_timeout, data, NULL);
+       } else if (!above) {
+               /*
+                * Signal dropped further 4 dB. Request a new scan if we have
+                * not yet scanned in a while.
+                */
+               os_get_time(&now);
+               if (now.sec > data->last_bgscan.sec + 10)
+                       scan = 1;
+       }
+
+       if (scan) {
+               wpa_printf(MSG_DEBUG, "bgscan simple: Trigger immediate scan");
+               eloop_cancel_timeout(bgscan_simple_timeout, data, NULL);
+               eloop_register_timeout(0, 0, bgscan_simple_timeout, data,
+                                      NULL);
+       }
+}
+
+
+const struct bgscan_ops bgscan_simple_ops = {
+       .name = "simple",
+       .init = bgscan_simple_init,
+       .deinit = bgscan_simple_deinit,
+       .notify_scan = bgscan_simple_notify_scan,
+       .notify_beacon_loss = bgscan_simple_notify_beacon_loss,
+       .notify_signal_change = bgscan_simple_notify_signal_change,
+};
diff --git a/wpa_supplicant/blacklist.c b/wpa_supplicant/blacklist.c
new file mode 100644 (file)
index 0000000..4ffb220
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * wpa_supplicant - Temporary BSSID blacklist
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+/**
+ * wpa_blacklist_get - Get the blacklist entry for a BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Matching blacklist entry for the BSSID or %NULL if not found
+ */
+struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
+                                        const u8 *bssid)
+{
+       struct wpa_blacklist *e;
+
+       e = wpa_s->blacklist;
+       while (e) {
+               if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
+                       return e;
+               e = e->next;
+       }
+
+       return NULL;
+}
+
+
+/**
+ * wpa_blacklist_add - Add an BSSID to the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be added to the blacklist
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function adds the specified BSSID to the blacklist or increases the
+ * blacklist count if the BSSID was already listed. It should be called when
+ * an association attempt fails either due to the selected BSS rejecting
+ * association or due to timeout.
+ *
+ * This blacklist is used to force %wpa_supplicant to go through all available
+ * BSSes before retrying to associate with an BSS that rejected or timed out
+ * association. It does not prevent the listed BSS from being used; it only
+ * changes the order in which they are tried.
+ */
+int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_blacklist *e;
+
+       e = wpa_blacklist_get(wpa_s, bssid);
+       if (e) {
+               e->count++;
+               wpa_printf(MSG_DEBUG, "BSSID " MACSTR " blacklist count "
+                          "incremented to %d",
+                          MAC2STR(bssid), e->count);
+               return 0;
+       }
+
+       e = os_zalloc(sizeof(*e));
+       if (e == NULL)
+               return -1;
+       os_memcpy(e->bssid, bssid, ETH_ALEN);
+       e->count = 1;
+       e->next = wpa_s->blacklist;
+       wpa_s->blacklist = e;
+       wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR " into blacklist",
+                  MAC2STR(bssid));
+
+       return 0;
+}
+
+
+/**
+ * wpa_blacklist_del - Remove an BSSID from the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be removed from the blacklist
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_blacklist *e, *prev = NULL;
+
+       e = wpa_s->blacklist;
+       while (e) {
+               if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
+                       if (prev == NULL) {
+                               wpa_s->blacklist = e->next;
+                       } else {
+                               prev->next = e->next;
+                       }
+                       wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+                                  "blacklist", MAC2STR(bssid));
+                       os_free(e);
+                       return 0;
+               }
+               prev = e;
+               e = e->next;
+       }
+       return -1;
+}
+
+
+/**
+ * wpa_blacklist_clear - Clear the blacklist of all entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_blacklist_clear(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_blacklist *e, *prev;
+
+       e = wpa_s->blacklist;
+       wpa_s->blacklist = NULL;
+       while (e) {
+               prev = e;
+               e = e->next;
+               wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR " from "
+                          "blacklist (clear)", MAC2STR(prev->bssid));
+               os_free(prev);
+       }
+}
diff --git a/wpa_supplicant/blacklist.h b/wpa_supplicant/blacklist.h
new file mode 100644 (file)
index 0000000..de280cd
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * wpa_supplicant - Temporary BSSID blacklist
+ * 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.
+ */
+
+#ifndef BLACKLIST_H
+#define BLACKLIST_H
+
+struct wpa_blacklist {
+       struct wpa_blacklist *next;
+       u8 bssid[ETH_ALEN];
+       int count;
+};
+
+struct wpa_blacklist * wpa_blacklist_get(struct wpa_supplicant *wpa_s,
+                                        const u8 *bssid);
+int wpa_blacklist_add(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_blacklist_del(struct wpa_supplicant *wpa_s, const u8 *bssid);
+void wpa_blacklist_clear(struct wpa_supplicant *wpa_s);
+
+#endif /* BLACKLIST_H */
diff --git a/wpa_supplicant/bss.c b/wpa_supplicant/bss.c
new file mode 100644 (file)
index 0000000..e2ac230
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * BSS table
+ * Copyright (c) 2009-2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "notify.h"
+#include "scan.h"
+#include "bss.h"
+
+
+/**
+ * WPA_BSS_EXPIRATION_PERIOD - Period of expiration run in seconds
+ */
+#define WPA_BSS_EXPIRATION_PERIOD 10
+
+/**
+ * WPA_BSS_EXPIRATION_AGE - BSS entry age after which it can be expired
+ *
+ * This value control the time in seconds after which a BSS entry gets removed
+ * if it has not been updated or is not in use.
+ */
+#define WPA_BSS_EXPIRATION_AGE 180
+
+/**
+ * WPA_BSS_EXPIRATION_SCAN_COUNT - Expire BSS after number of scans
+ *
+ * If the BSS entry has not been seen in this many scans, it will be removed.
+ * Value 1 means that the entry is removed after the first scan without the
+ * BSSID being seen. Larger values can be used to avoid BSS entries
+ * disappearing if they are not visible in every scan (e.g., low signal quality
+ * or interference).
+ */
+#define WPA_BSS_EXPIRATION_SCAN_COUNT 2
+
+#define WPA_BSS_FREQ_CHANGED_FLAG      BIT(0)
+#define WPA_BSS_SIGNAL_CHANGED_FLAG    BIT(1)
+#define WPA_BSS_PRIVACY_CHANGED_FLAG   BIT(2)
+#define WPA_BSS_MODE_CHANGED_FLAG      BIT(3)
+#define WPA_BSS_WPAIE_CHANGED_FLAG     BIT(4)
+#define WPA_BSS_RSNIE_CHANGED_FLAG     BIT(5)
+#define WPA_BSS_WPS_CHANGED_FLAG       BIT(6)
+#define WPA_BSS_RATES_CHANGED_FLAG     BIT(7)
+#define WPA_BSS_IES_CHANGED_FLAG       BIT(8)
+
+
+static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+       dl_list_del(&bss->list);
+       dl_list_del(&bss->list_id);
+       wpa_s->num_bss--;
+       wpa_printf(MSG_DEBUG, "BSS: Remove id %u BSSID " MACSTR " SSID '%s'",
+                  bss->id, MAC2STR(bss->bssid),
+                  wpa_ssid_txt(bss->ssid, bss->ssid_len));
+       wpas_notify_bss_removed(wpa_s, bss->bssid, bss->id);
+       os_free(bss);
+}
+
+
+struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                            const u8 *ssid, size_t ssid_len)
+{
+       struct wpa_bss *bss;
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+                   bss->ssid_len == ssid_len &&
+                   os_memcmp(bss->ssid, ssid, ssid_len) == 0)
+                       return bss;
+       }
+       return NULL;
+}
+
+
+static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src)
+{
+       os_time_t usec;
+
+       dst->flags = src->flags;
+       os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
+       dst->freq = src->freq;
+       dst->beacon_int = src->beacon_int;
+       dst->caps = src->caps;
+       dst->qual = src->qual;
+       dst->noise = src->noise;
+       dst->level = src->level;
+       dst->tsf = src->tsf;
+
+       os_get_time(&dst->last_update);
+       dst->last_update.sec -= src->age / 1000;
+       usec = (src->age % 1000) * 1000;
+       if (dst->last_update.usec < usec) {
+               dst->last_update.sec--;
+               dst->last_update.usec += 1000000;
+       }
+       dst->last_update.usec -= usec;
+}
+
+
+static void wpa_bss_add(struct wpa_supplicant *wpa_s,
+                       const u8 *ssid, size_t ssid_len,
+                       struct wpa_scan_res *res)
+{
+       struct wpa_bss *bss;
+
+       bss = os_zalloc(sizeof(*bss) + res->ie_len + res->beacon_ie_len);
+       if (bss == NULL)
+               return;
+       bss->id = wpa_s->bss_next_id++;
+       bss->last_update_idx = wpa_s->bss_update_idx;
+       wpa_bss_copy_res(bss, res);
+       os_memcpy(bss->ssid, ssid, ssid_len);
+       bss->ssid_len = ssid_len;
+       bss->ie_len = res->ie_len;
+       bss->beacon_ie_len = res->beacon_ie_len;
+       os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+
+       dl_list_add_tail(&wpa_s->bss, &bss->list);
+       dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
+       wpa_s->num_bss++;
+       wpa_printf(MSG_DEBUG, "BSS: Add new id %u BSSID " MACSTR " SSID '%s'",
+                  bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
+       wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
+       if (wpa_s->num_bss > wpa_s->conf->bss_max_count) {
+               /* Remove the oldest entry */
+               wpa_bss_remove(wpa_s, dl_list_first(&wpa_s->bss,
+                                                   struct wpa_bss, list));
+       }
+}
+
+
+static int are_ies_equal(const struct wpa_bss *old,
+                        const struct wpa_scan_res *new, u32 ie)
+{
+       const u8 *old_ie, *new_ie;
+       struct wpabuf *old_ie_buff = NULL;
+       struct wpabuf *new_ie_buff = NULL;
+       int new_ie_len, old_ie_len, ret, is_multi;
+
+       switch (ie) {
+       case WPA_IE_VENDOR_TYPE:
+               old_ie = wpa_bss_get_vendor_ie(old, ie);
+               new_ie = wpa_scan_get_vendor_ie(new, ie);
+               is_multi = 0;
+               break;
+       case WPS_IE_VENDOR_TYPE:
+               old_ie_buff = wpa_bss_get_vendor_ie_multi(old, ie);
+               new_ie_buff = wpa_scan_get_vendor_ie_multi(new, ie);
+               is_multi = 1;
+               break;
+       case WLAN_EID_RSN:
+       case WLAN_EID_SUPP_RATES:
+       case WLAN_EID_EXT_SUPP_RATES:
+               old_ie = wpa_bss_get_ie(old, ie);
+               new_ie = wpa_scan_get_ie(new, ie);
+               is_multi = 0;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "bss: %s: cannot compare IEs", __func__);
+               return 0;
+       }
+
+       if (is_multi) {
+               /* in case of multiple IEs stored in buffer */
+               old_ie = old_ie_buff ? wpabuf_head_u8(old_ie_buff) : NULL;
+               new_ie = new_ie_buff ? wpabuf_head_u8(new_ie_buff) : NULL;
+               old_ie_len = old_ie_buff ? wpabuf_len(old_ie_buff) : 0;
+               new_ie_len = new_ie_buff ? wpabuf_len(new_ie_buff) : 0;
+       } else {
+               /* in case of single IE */
+               old_ie_len = old_ie ? old_ie[1] + 2 : 0;
+               new_ie_len = new_ie ? new_ie[1] + 2 : 0;
+       }
+
+       ret = (old_ie_len == new_ie_len &&
+              os_memcmp(old_ie, new_ie, old_ie_len) == 0);
+
+       wpabuf_free(old_ie_buff);
+       wpabuf_free(new_ie_buff);
+
+       return ret;
+}
+
+
+static u32 wpa_bss_compare_res(const struct wpa_bss *old,
+                              const struct wpa_scan_res *new)
+{
+       u32 changes = 0;
+       int caps_diff = old->caps ^ new->caps;
+
+       if (old->freq != new->freq)
+               changes |= WPA_BSS_FREQ_CHANGED_FLAG;
+
+       if (old->level != new->level)
+               changes |= WPA_BSS_SIGNAL_CHANGED_FLAG;
+
+       if (caps_diff & IEEE80211_CAP_PRIVACY)
+               changes |= WPA_BSS_PRIVACY_CHANGED_FLAG;
+
+       if (caps_diff & IEEE80211_CAP_IBSS)
+               changes |= WPA_BSS_MODE_CHANGED_FLAG;
+
+       if (old->ie_len == new->ie_len &&
+           os_memcmp(old + 1, new + 1, old->ie_len) == 0)
+               return changes;
+       changes |= WPA_BSS_IES_CHANGED_FLAG;
+
+       if (!are_ies_equal(old, new, WPA_IE_VENDOR_TYPE))
+               changes |= WPA_BSS_WPAIE_CHANGED_FLAG;
+
+       if (!are_ies_equal(old, new, WLAN_EID_RSN))
+               changes |= WPA_BSS_RSNIE_CHANGED_FLAG;
+
+       if (!are_ies_equal(old, new, WPS_IE_VENDOR_TYPE))
+               changes |= WPA_BSS_WPS_CHANGED_FLAG;
+
+       if (!are_ies_equal(old, new, WLAN_EID_SUPP_RATES) ||
+           !are_ies_equal(old, new, WLAN_EID_EXT_SUPP_RATES))
+               changes |= WPA_BSS_RATES_CHANGED_FLAG;
+
+       return changes;
+}
+
+
+static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
+                              const struct wpa_bss *bss)
+{
+       if (changes & WPA_BSS_FREQ_CHANGED_FLAG)
+               wpas_notify_bss_freq_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_SIGNAL_CHANGED_FLAG)
+               wpas_notify_bss_signal_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_PRIVACY_CHANGED_FLAG)
+               wpas_notify_bss_privacy_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_MODE_CHANGED_FLAG)
+               wpas_notify_bss_mode_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_WPAIE_CHANGED_FLAG)
+               wpas_notify_bss_wpaie_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_RSNIE_CHANGED_FLAG)
+               wpas_notify_bss_rsnie_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_WPS_CHANGED_FLAG)
+               wpas_notify_bss_wps_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_IES_CHANGED_FLAG)
+               wpas_notify_bss_ies_changed(wpa_s, bss->id);
+
+       if (changes & WPA_BSS_RATES_CHANGED_FLAG)
+               wpas_notify_bss_rates_changed(wpa_s, bss->id);
+}
+
+
+static void wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+                          struct wpa_scan_res *res)
+{
+       u32 changes;
+
+       changes = wpa_bss_compare_res(bss, res);
+       bss->scan_miss_count = 0;
+       bss->last_update_idx = wpa_s->bss_update_idx;
+       wpa_bss_copy_res(bss, res);
+       /* Move the entry to the end of the list */
+       dl_list_del(&bss->list);
+       if (bss->ie_len + bss->beacon_ie_len >=
+           res->ie_len + res->beacon_ie_len) {
+               os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
+               bss->ie_len = res->ie_len;
+               bss->beacon_ie_len = res->beacon_ie_len;
+       } else {
+               struct wpa_bss *nbss;
+               struct dl_list *prev = bss->list_id.prev;
+               dl_list_del(&bss->list_id);
+               nbss = os_realloc(bss, sizeof(*bss) + res->ie_len +
+                                 res->beacon_ie_len);
+               if (nbss) {
+                       bss = nbss;
+                       os_memcpy(bss + 1, res + 1,
+                                 res->ie_len + res->beacon_ie_len);
+                       bss->ie_len = res->ie_len;
+                       bss->beacon_ie_len = res->beacon_ie_len;
+               }
+               dl_list_add(prev, &bss->list_id);
+       }
+       dl_list_add_tail(&wpa_s->bss, &bss->list);
+
+       notify_bss_changes(wpa_s, changes, bss);
+}
+
+
+static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+{
+       return bss == wpa_s->current_bss ||
+               os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+               os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+}
+
+
+void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->bss_update_idx++;
+       wpa_printf(MSG_DEBUG, "BSS: Start scan result update %u",
+                  wpa_s->bss_update_idx);
+}
+
+
+void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
+                            struct wpa_scan_res *res)
+{
+       const u8 *ssid;
+       struct wpa_bss *bss;
+
+       ssid = wpa_scan_get_ie(res, WLAN_EID_SSID);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "BSS: No SSID IE included for " MACSTR,
+                          MAC2STR(res->bssid));
+               return;
+       }
+       if (ssid[1] > 32) {
+               wpa_printf(MSG_DEBUG, "BSS: Too long SSID IE included for "
+                          MACSTR, MAC2STR(res->bssid));
+               return;
+       }
+
+       /* TODO: add option for ignoring BSSes we are not interested in
+        * (to save memory) */
+       bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
+       if (bss == NULL)
+               wpa_bss_add(wpa_s, ssid + 2, ssid[1], res);
+       else
+               wpa_bss_update(wpa_s, bss, res);
+}
+
+
+static int wpa_bss_included_in_scan(const struct wpa_bss *bss,
+                                   const struct scan_info *info)
+{
+       int found;
+       size_t i;
+
+       if (info == NULL)
+               return 1;
+
+       if (info->num_freqs) {
+               found = 0;
+               for (i = 0; i < info->num_freqs; i++) {
+                       if (bss->freq == info->freqs[i]) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       return 0;
+       }
+
+       if (info->num_ssids) {
+               found = 0;
+               for (i = 0; i < info->num_ssids; i++) {
+                       const struct wpa_driver_scan_ssid *s = &info->ssids[i];
+                       if ((s->ssid == NULL || s->ssid_len == 0) ||
+                           (s->ssid_len == bss->ssid_len &&
+                            os_memcmp(s->ssid, bss->ssid, bss->ssid_len) ==
+                            0)) {
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
+                       int new_scan)
+{
+       struct wpa_bss *bss, *n;
+
+       if (!new_scan)
+               return; /* do not expire entries without new scan */
+
+       dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+               if (wpa_bss_in_use(wpa_s, bss))
+                       continue;
+               if (!wpa_bss_included_in_scan(bss, info))
+                       continue; /* expire only BSSes that were scanned */
+               if (bss->last_update_idx < wpa_s->bss_update_idx)
+                       bss->scan_miss_count++;
+               if (bss->scan_miss_count >= WPA_BSS_EXPIRATION_SCAN_COUNT) {
+                       wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to no "
+                                  "match in scan", bss->id);
+                       wpa_bss_remove(wpa_s, bss);
+               }
+       }
+}
+
+
+static void wpa_bss_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wpa_bss *bss, *n;
+       struct os_time t;
+
+       if (dl_list_empty(&wpa_s->bss))
+               return;
+
+       os_get_time(&t);
+       t.sec -= WPA_BSS_EXPIRATION_AGE;
+
+       dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
+               if (wpa_bss_in_use(wpa_s, bss))
+                       continue;
+
+               if (os_time_before(&bss->last_update, &t)) {
+                       wpa_printf(MSG_DEBUG, "BSS: Expire BSS %u due to age",
+                                  bss->id);
+                       wpa_bss_remove(wpa_s, bss);
+               } else
+                       break;
+       }
+       eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
+                              wpa_bss_timeout, wpa_s, NULL);
+}
+
+
+int wpa_bss_init(struct wpa_supplicant *wpa_s)
+{
+       dl_list_init(&wpa_s->bss);
+       dl_list_init(&wpa_s->bss_id);
+       eloop_register_timeout(WPA_BSS_EXPIRATION_PERIOD, 0,
+                              wpa_bss_timeout, wpa_s, NULL);
+       return 0;
+}
+
+
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss, *n;
+       eloop_cancel_timeout(wpa_bss_timeout, wpa_s, NULL);
+       if (wpa_s->bss.next == NULL)
+               return; /* BSS table not yet initialized */
+       dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list)
+               wpa_bss_remove(wpa_s, bss);
+}
+
+
+struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
+                                  const u8 *bssid)
+{
+       struct wpa_bss *bss;
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+                       return bss;
+       }
+       return NULL;
+}
+
+
+struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+       struct wpa_bss *bss;
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (bss->id == id)
+                       return bss;
+       }
+       return NULL;
+}
+
+
+const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie)
+{
+       const u8 *end, *pos;
+
+       pos = (const u8 *) (bss + 1);
+       end = pos + bss->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
+{
+       const u8 *end, *pos;
+
+       pos = (const u8 *) (bss + 1);
+       end = pos + bss->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   vendor_type == WPA_GET_BE32(&pos[2]))
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
+                                           u32 vendor_type)
+{
+       struct wpabuf *buf;
+       const u8 *end, *pos;
+
+       buf = wpabuf_alloc(bss->ie_len);
+       if (buf == NULL)
+               return NULL;
+
+       pos = (const u8 *) (bss + 1);
+       end = pos + bss->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   vendor_type == WPA_GET_BE32(&pos[2]))
+                       wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+               pos += 2 + pos[1];
+       }
+
+       if (wpabuf_len(buf) == 0) {
+               wpabuf_free(buf);
+               buf = NULL;
+       }
+
+       return buf;
+}
+
+
+int wpa_bss_get_max_rate(const struct wpa_bss *bss)
+{
+       int rate = 0;
+       const u8 *ie;
+       int i;
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+       for (i = 0; ie && i < ie[1]; i++) {
+               if ((ie[i + 2] & 0x7f) > rate)
+                       rate = ie[i + 2] & 0x7f;
+       }
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+       for (i = 0; ie && i < ie[1]; i++) {
+               if ((ie[i + 2] & 0x7f) > rate)
+                       rate = ie[i + 2] & 0x7f;
+       }
+
+       return rate;
+}
+
+
+int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates)
+{
+       const u8 *ie, *ie2;
+       int i, j;
+       unsigned int len;
+       u8 *r;
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_SUPP_RATES);
+       ie2 = wpa_bss_get_ie(bss, WLAN_EID_EXT_SUPP_RATES);
+
+       len = (ie ? ie[1] : 0) + (ie2 ? ie2[1] : 0);
+
+       r = os_malloc(len);
+       if (!r)
+               return -1;
+
+       for (i = 0; ie && i < ie[1]; i++)
+               r[i] = ie[i + 2] & 0x7f;
+
+       for (j = 0; ie2 && j < ie2[1]; j++)
+               r[i + j] = ie2[j + 2] & 0x7f;
+
+       *rates = r;
+       return len;
+}
diff --git a/wpa_supplicant/bss.h b/wpa_supplicant/bss.h
new file mode 100644 (file)
index 0000000..1de4722
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * BSS table
+ * Copyright (c) 2009-2010, 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 BSS_H
+#define BSS_H
+
+struct wpa_scan_res;
+
+#define WPA_BSS_QUAL_INVALID           BIT(0)
+#define WPA_BSS_NOISE_INVALID          BIT(1)
+#define WPA_BSS_LEVEL_INVALID          BIT(2)
+#define WPA_BSS_LEVEL_DBM              BIT(3)
+#define WPA_BSS_AUTHENTICATED          BIT(4)
+#define WPA_BSS_ASSOCIATED             BIT(5)
+
+/**
+ * struct wpa_bss - BSS table
+ * @list: List entry for struct wpa_supplicant::bss
+ * @list_id: List entry for struct wpa_supplicant::bss_id
+ * @id: Unique identifier for this BSS entry
+ * @scan_miss_count: Number of counts without seeing this BSS
+ * @flags: information flags about the BSS/IBSS (WPA_BSS_*)
+ * @last_update_idx: Index of the last scan update
+ * @bssid: BSSID
+ * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1)
+ * @beacon_int: beacon interval in TUs (host byte order)
+ * @caps: capability information field in host byte order
+ * @qual: signal quality
+ * @noise: noise level
+ * @level: signal level
+ * @tsf: Timestamp of last Beacon/Probe Response frame
+ * @last_update: Time of the last update (i.e., Beacon or Probe Response RX)
+ * @ie_len: length of the following IE field in octets (from Probe Response)
+ * @beacon_ie_len: length of the following Beacon IE field in octets
+ *
+ * This structure is used to store information about neighboring BSSes in
+ * generic format. It is mainly updated based on scan results from the driver.
+ */
+struct wpa_bss {
+       struct dl_list list;
+       struct dl_list list_id;
+       unsigned int id;
+       unsigned int scan_miss_count;
+       unsigned int last_update_idx;
+       unsigned int flags;
+       u8 bssid[ETH_ALEN];
+       u8 ssid[32];
+       size_t ssid_len;
+       int freq;
+       u16 beacon_int;
+       u16 caps;
+       int qual;
+       int noise;
+       int level;
+       u64 tsf;
+       struct os_time last_update;
+       size_t ie_len;
+       size_t beacon_ie_len;
+       /* followed by ie_len octets of IEs */
+       /* followed by beacon_ie_len octets of IEs */
+};
+
+void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
+void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
+                            struct wpa_scan_res *res);
+void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
+                       int new_scan);
+int wpa_bss_init(struct wpa_supplicant *wpa_s);
+void wpa_bss_deinit(struct wpa_supplicant *wpa_s);
+struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                            const u8 *ssid, size_t ssid_len);
+struct wpa_bss * wpa_bss_get_bssid(struct wpa_supplicant *wpa_s,
+                                  const u8 *bssid);
+struct wpa_bss * wpa_bss_get_id(struct wpa_supplicant *wpa_s, unsigned int id);
+const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
+const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
+struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
+                                           u32 vendor_type);
+int wpa_bss_get_max_rate(const struct wpa_bss *bss);
+int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
+
+#endif /* BSS_H */
diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c
new file mode 100644 (file)
index 0000000..7e2a5b4
--- /dev/null
@@ -0,0 +1,2167 @@
+/*
+ * WPA Supplicant / Configuration parser and common functions
+ * Copyright (c) 2003-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 "crypto/sha1.h"
+#include "rsn_supp/wpa.h"
+#include "eap_peer/eap.h"
+#include "config.h"
+
+
+#if !defined(CONFIG_CTRL_IFACE) && defined(CONFIG_NO_CONFIG_WRITE)
+#define NO_CONFIG_WRITE
+#endif
+
+/*
+ * Structure for network configuration parsing. This data is used to implement
+ * a generic parser for each network block variable. The table of configuration
+ * variables is defined below in this file (ssid_fields[]).
+ */
+struct parse_data {
+       /* Configuration variable name */
+       char *name;
+
+       /* Parser function for this variable */
+       int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
+                     int line, const char *value);
+
+#ifndef NO_CONFIG_WRITE
+       /* Writer function (i.e., to get the variable in text format from
+        * internal presentation). */
+       char * (*writer)(const struct parse_data *data, struct wpa_ssid *ssid);
+#endif /* NO_CONFIG_WRITE */
+
+       /* Variable specific parameters for the parser. */
+       void *param1, *param2, *param3, *param4;
+
+       /* 0 = this variable can be included in debug output and ctrl_iface
+        * 1 = this variable contains key/private data and it must not be
+        *     included in debug output unless explicitly requested. In
+        *     addition, this variable will not be readable through the
+        *     ctrl_iface.
+        */
+       int key_data;
+};
+
+
+static char * wpa_config_parse_string(const char *value, size_t *len)
+{
+       if (*value == '"') {
+               const char *pos;
+               char *str;
+               value++;
+               pos = os_strrchr(value, '"');
+               if (pos == NULL || pos[1] != '\0')
+                       return NULL;
+               *len = pos - value;
+               str = os_malloc(*len + 1);
+               if (str == NULL)
+                       return NULL;
+               os_memcpy(str, value, *len);
+               str[*len] = '\0';
+               return str;
+       } else {
+               u8 *str;
+               size_t tlen, hlen = os_strlen(value);
+               if (hlen & 1)
+                       return NULL;
+               tlen = hlen / 2;
+               str = os_malloc(tlen + 1);
+               if (str == NULL)
+                       return NULL;
+               if (hexstr2bin(value, str, tlen)) {
+                       os_free(str);
+                       return NULL;
+               }
+               str[tlen] = '\0';
+               *len = tlen;
+               return (char *) str;
+       }
+}
+
+
+static int wpa_config_parse_str(const struct parse_data *data,
+                               struct wpa_ssid *ssid,
+                               int line, const char *value)
+{
+       size_t res_len, *dst_len;
+       char **dst, *tmp;
+
+       if (os_strcmp(value, "NULL") == 0) {
+               wpa_printf(MSG_DEBUG, "Unset configuration string '%s'",
+                          data->name);
+               tmp = NULL;
+               res_len = 0;
+               goto set;
+       }
+
+       tmp = wpa_config_parse_string(value, &res_len);
+       if (tmp == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: failed to parse %s '%s'.",
+                          line, data->name,
+                          data->key_data ? "[KEY DATA REMOVED]" : value);
+               return -1;
+       }
+
+       if (data->key_data) {
+               wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+                                     (u8 *) tmp, res_len);
+       } else {
+               wpa_hexdump_ascii(MSG_MSGDUMP, data->name,
+                                 (u8 *) tmp, res_len);
+       }
+
+       if (data->param3 && res_len < (size_t) data->param3) {
+               wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+                          "min_len=%ld)", line, data->name,
+                          (unsigned long) res_len, (long) data->param3);
+               os_free(tmp);
+               return -1;
+       }
+
+       if (data->param4 && res_len > (size_t) data->param4) {
+               wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+                          "max_len=%ld)", line, data->name,
+                          (unsigned long) res_len, (long) data->param4);
+               os_free(tmp);
+               return -1;
+       }
+
+set:
+       dst = (char **) (((u8 *) ssid) + (long) data->param1);
+       dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
+       os_free(*dst);
+       *dst = tmp;
+       if (data->param2)
+               *dst_len = res_len;
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static int is_hex(const u8 *data, size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; i++) {
+               if (data[i] < 32 || data[i] >= 127)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static char * wpa_config_write_string_ascii(const u8 *value, size_t len)
+{
+       char *buf;
+
+       buf = os_malloc(len + 3);
+       if (buf == NULL)
+               return NULL;
+       buf[0] = '"';
+       os_memcpy(buf + 1, value, len);
+       buf[len + 1] = '"';
+       buf[len + 2] = '\0';
+
+       return buf;
+}
+
+
+static char * wpa_config_write_string_hex(const u8 *value, size_t len)
+{
+       char *buf;
+
+       buf = os_zalloc(2 * len + 1);
+       if (buf == NULL)
+               return NULL;
+       wpa_snprintf_hex(buf, 2 * len + 1, value, len);
+
+       return buf;
+}
+
+
+static char * wpa_config_write_string(const u8 *value, size_t len)
+{
+       if (value == NULL)
+               return NULL;
+
+       if (is_hex(value, len))
+               return wpa_config_write_string_hex(value, len);
+       else
+               return wpa_config_write_string_ascii(value, len);
+}
+
+
+static char * wpa_config_write_str(const struct parse_data *data,
+                                  struct wpa_ssid *ssid)
+{
+       size_t len;
+       char **src;
+
+       src = (char **) (((u8 *) ssid) + (long) data->param1);
+       if (*src == NULL)
+               return NULL;
+
+       if (data->param2)
+               len = *((size_t *) (((u8 *) ssid) + (long) data->param2));
+       else
+               len = os_strlen(*src);
+
+       return wpa_config_write_string((const u8 *) *src, len);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_int(const struct parse_data *data,
+                               struct wpa_ssid *ssid,
+                               int line, const char *value)
+{
+       int *dst;
+
+       dst = (int *) (((u8 *) ssid) + (long) data->param1);
+       *dst = atoi(value);
+       wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
+
+       if (data->param3 && *dst < (long) data->param3) {
+               wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+                          "min_value=%ld)", line, data->name, *dst,
+                          (long) data->param3);
+               *dst = (long) data->param3;
+               return -1;
+       }
+
+       if (data->param4 && *dst > (long) data->param4) {
+               wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+                          "max_value=%ld)", line, data->name, *dst,
+                          (long) data->param4);
+               *dst = (long) data->param4;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_int(const struct parse_data *data,
+                                  struct wpa_ssid *ssid)
+{
+       int *src, res;
+       char *value;
+
+       src = (int *) (((u8 *) ssid) + (long) data->param1);
+
+       value = os_malloc(20);
+       if (value == NULL)
+               return NULL;
+       res = os_snprintf(value, 20, "%d", *src);
+       if (res < 0 || res >= 20) {
+               os_free(value);
+               return NULL;
+       }
+       value[20 - 1] = '\0';
+       return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid(const struct parse_data *data,
+                                 struct wpa_ssid *ssid, int line,
+                                 const char *value)
+{
+       if (hwaddr_aton(value, ssid->bssid)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID '%s'.",
+                          line, value);
+               return -1;
+       }
+       ssid->bssid_set = 1;
+       wpa_hexdump(MSG_MSGDUMP, "BSSID", ssid->bssid, ETH_ALEN);
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid(const struct parse_data *data,
+                                    struct wpa_ssid *ssid)
+{
+       char *value;
+       int res;
+
+       if (!ssid->bssid_set)
+               return NULL;
+
+       value = os_malloc(20);
+       if (value == NULL)
+               return NULL;
+       res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
+       if (res < 0 || res >= 20) {
+               os_free(value);
+               return NULL;
+       }
+       value[20 - 1] = '\0';
+       return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_psk(const struct parse_data *data,
+                               struct wpa_ssid *ssid, int line,
+                               const char *value)
+{
+       if (*value == '"') {
+#ifndef CONFIG_NO_PBKDF2
+               const char *pos;
+               size_t len;
+
+               value++;
+               pos = os_strrchr(value, '"');
+               if (pos)
+                       len = pos - value;
+               else
+                       len = os_strlen(value);
+               if (len < 8 || len > 63) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid passphrase "
+                                  "length %lu (expected: 8..63) '%s'.",
+                                  line, (unsigned long) len, value);
+                       return -1;
+               }
+               wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
+                                     (u8 *) value, len);
+               if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
+                   os_memcmp(ssid->passphrase, value, len) == 0)
+                       return 0;
+               ssid->psk_set = 0;
+               os_free(ssid->passphrase);
+               ssid->passphrase = os_malloc(len + 1);
+               if (ssid->passphrase == NULL)
+                       return -1;
+               os_memcpy(ssid->passphrase, value, len);
+               ssid->passphrase[len] = '\0';
+               return 0;
+#else /* CONFIG_NO_PBKDF2 */
+               wpa_printf(MSG_ERROR, "Line %d: ASCII passphrase not "
+                          "supported.", line);
+               return -1;
+#endif /* CONFIG_NO_PBKDF2 */
+       }
+
+       if (hexstr2bin(value, ssid->psk, PMK_LEN) ||
+           value[PMK_LEN * 2] != '\0') {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+                          line, value);
+               return -1;
+       }
+
+       os_free(ssid->passphrase);
+       ssid->passphrase = NULL;
+
+       ssid->psk_set = 1;
+       wpa_hexdump_key(MSG_MSGDUMP, "PSK", ssid->psk, PMK_LEN);
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk(const struct parse_data *data,
+                                  struct wpa_ssid *ssid)
+{
+       if (ssid->passphrase)
+               return wpa_config_write_string_ascii(
+                       (const u8 *) ssid->passphrase,
+                       os_strlen(ssid->passphrase));
+
+       if (ssid->psk_set)
+               return wpa_config_write_string_hex(ssid->psk, PMK_LEN);
+
+       return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_proto(const struct parse_data *data,
+                                 struct wpa_ssid *ssid, int line,
+                                 const char *value)
+{
+       int val = 0, last, errors = 0;
+       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") == 0)
+                       val |= WPA_PROTO_WPA;
+               else if (os_strcmp(start, "RSN") == 0 ||
+                        os_strcmp(start, "WPA2") == 0)
+                       val |= WPA_PROTO_RSN;
+               else {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
+                                  line, start);
+                       errors++;
+               }
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+       os_free(buf);
+
+       if (val == 0) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: no proto values configured.", line);
+               errors++;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
+       ssid->proto = val;
+       return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_proto(const struct parse_data *data,
+                                    struct wpa_ssid *ssid)
+{
+       int first = 1, ret;
+       char *buf, *pos, *end;
+
+       pos = buf = os_zalloc(10);
+       if (buf == NULL)
+               return NULL;
+       end = buf + 10;
+
+       if (ssid->proto & WPA_PROTO_WPA) {
+               ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (ssid->proto & WPA_PROTO_RSN) {
+               ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return buf;
+               pos += ret;
+               first = 0;
+       }
+
+       return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_key_mgmt(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       int val = 0, last, errors = 0;
+       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;
+               else if (os_strcmp(start, "IEEE8021X") == 0)
+                       val |= WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+               else if (os_strcmp(start, "NONE") == 0)
+                       val |= WPA_KEY_MGMT_NONE;
+               else if (os_strcmp(start, "WPA-NONE") == 0)
+                       val |= WPA_KEY_MGMT_WPA_NONE;
+#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 */
+#ifdef CONFIG_WPS
+               else if (os_strcmp(start, "WPS") == 0)
+                       val |= WPA_KEY_MGMT_WPS;
+#endif /* CONFIG_WPS */
+               else {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
+                                  line, start);
+                       errors++;
+               }
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+       os_free(buf);
+
+       if (val == 0) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: no key_mgmt values configured.", line);
+               errors++;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
+       ssid->key_mgmt = val;
+       return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_key_mgmt(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       char *buf, *pos, *end;
+       int ret;
+
+       pos = buf = os_zalloc(50);
+       if (buf == NULL)
+               return NULL;
+       end = buf + 50;
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
+               ret = os_snprintf(pos, end - pos, "%sNONE",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK)
+               pos += os_snprintf(pos, end - pos, "%sFT-PSK",
+                                  pos == buf ? "" : " ");
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+               pos += os_snprintf(pos, end - pos, "%sFT-EAP",
+                                  pos == buf ? "" : " ");
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+       if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+               pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
+                                  pos == buf ? "" : " ");
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+               pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
+                                  pos == buf ? "" : " ");
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_WPS
+       if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+               pos += os_snprintf(pos, end - pos, "%sWPS",
+                                  pos == buf ? "" : " ");
+#endif /* CONFIG_WPS */
+
+       return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_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;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_cipher(int cipher)
+{
+       char *buf, *pos, *end;
+       int ret;
+
+       pos = buf = os_zalloc(50);
+       if (buf == NULL)
+               return NULL;
+       end = buf + 50;
+
+       if (cipher & WPA_CIPHER_CCMP) {
+               ret = os_snprintf(pos, end - pos, "%sCCMP",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (cipher & WPA_CIPHER_TKIP) {
+               ret = os_snprintf(pos, end - pos, "%sTKIP",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (cipher & WPA_CIPHER_WEP104) {
+               ret = os_snprintf(pos, end - pos, "%sWEP104",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (cipher & WPA_CIPHER_WEP40) {
+               ret = os_snprintf(pos, end - pos, "%sWEP40",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (cipher & WPA_CIPHER_NONE) {
+               ret = os_snprintf(pos, end - pos, "%sNONE",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_pairwise(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       int val;
+       val = wpa_config_parse_cipher(line, value);
+       if (val == -1)
+               return -1;
+       if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)) {
+               wpa_printf(MSG_ERROR, "Line %d: not allowed pairwise cipher "
+                          "(0x%x).", line, val);
+               return -1;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
+       ssid->pairwise_cipher = val;
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_pairwise(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       return wpa_config_write_cipher(ssid->pairwise_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_group(const struct parse_data *data,
+                                 struct wpa_ssid *ssid, int line,
+                                 const char *value)
+{
+       int val;
+       val = wpa_config_parse_cipher(line, value);
+       if (val == -1)
+               return -1;
+       if (val & ~(WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 |
+                   WPA_CIPHER_WEP40)) {
+               wpa_printf(MSG_ERROR, "Line %d: not allowed group cipher "
+                          "(0x%x).", line, val);
+               return -1;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
+       ssid->group_cipher = val;
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group(const struct parse_data *data,
+                                    struct wpa_ssid *ssid)
+{
+       return wpa_config_write_cipher(ssid->group_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_auth_alg(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       int val = 0, last, errors = 0;
+       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, "OPEN") == 0)
+                       val |= WPA_AUTH_ALG_OPEN;
+               else if (os_strcmp(start, "SHARED") == 0)
+                       val |= WPA_AUTH_ALG_SHARED;
+               else if (os_strcmp(start, "LEAP") == 0)
+                       val |= WPA_AUTH_ALG_LEAP;
+               else {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid auth_alg '%s'",
+                                  line, start);
+                       errors++;
+               }
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+       os_free(buf);
+
+       if (val == 0) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: no auth_alg values configured.", line);
+               errors++;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
+       ssid->auth_alg = val;
+       return errors ? -1 : 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_auth_alg(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       char *buf, *pos, *end;
+       int ret;
+
+       pos = buf = os_zalloc(30);
+       if (buf == NULL)
+               return NULL;
+       end = buf + 30;
+
+       if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
+               ret = os_snprintf(pos, end - pos, "%sOPEN",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
+               ret = os_snprintf(pos, end - pos, "%sSHARED",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
+               ret = os_snprintf(pos, end - pos, "%sLEAP",
+                                 pos == buf ? "" : " ");
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       return buf;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int * wpa_config_parse_freqs(const struct parse_data *data,
+                                   struct wpa_ssid *ssid, int line,
+                                   const char *value)
+{
+       int *freqs;
+       size_t used, len;
+       const char *pos;
+
+       used = 0;
+       len = 10;
+       freqs = os_zalloc((len + 1) * sizeof(int));
+       if (freqs == NULL)
+               return NULL;
+
+       pos = value;
+       while (pos) {
+               while (*pos == ' ')
+                       pos++;
+               if (used == len) {
+                       int *n;
+                       size_t i;
+                       n = os_realloc(freqs, (len * 2 + 1) * sizeof(int));
+                       if (n == NULL) {
+                               os_free(freqs);
+                               return NULL;
+                       }
+                       for (i = len; i <= len * 2; i++)
+                               n[i] = 0;
+                       freqs = n;
+                       len *= 2;
+               }
+
+               freqs[used] = atoi(pos);
+               if (freqs[used] == 0)
+                       break;
+               used++;
+               pos = os_strchr(pos + 1, ' ');
+       }
+
+       return freqs;
+}
+
+
+static int wpa_config_parse_scan_freq(const struct parse_data *data,
+                                     struct wpa_ssid *ssid, int line,
+                                     const char *value)
+{
+       int *freqs;
+
+       freqs = wpa_config_parse_freqs(data, ssid, line, value);
+       if (freqs == NULL)
+               return -1;
+       os_free(ssid->scan_freq);
+       ssid->scan_freq = freqs;
+
+       return 0;
+}
+
+
+static int wpa_config_parse_freq_list(const struct parse_data *data,
+                                     struct wpa_ssid *ssid, int line,
+                                     const char *value)
+{
+       int *freqs;
+
+       freqs = wpa_config_parse_freqs(data, ssid, line, value);
+       if (freqs == NULL)
+               return -1;
+       os_free(ssid->freq_list);
+       ssid->freq_list = freqs;
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_freqs(const struct parse_data *data,
+                                    const int *freqs)
+{
+       char *buf, *pos, *end;
+       int i, ret;
+       size_t count;
+
+       if (freqs == NULL)
+               return NULL;
+
+       count = 0;
+       for (i = 0; freqs[i]; i++)
+               count++;
+
+       pos = buf = os_zalloc(10 * count + 1);
+       if (buf == NULL)
+               return NULL;
+       end = buf + 10 * count + 1;
+
+       for (i = 0; freqs[i]; i++) {
+               ret = os_snprintf(pos, end - pos, "%s%u",
+                                 i == 0 ? "" : " ", freqs[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       return buf;
+}
+
+
+static char * wpa_config_write_scan_freq(const struct parse_data *data,
+                                        struct wpa_ssid *ssid)
+{
+       return wpa_config_write_freqs(data, ssid->scan_freq);
+}
+
+
+static char * wpa_config_write_freq_list(const struct parse_data *data,
+                                        struct wpa_ssid *ssid)
+{
+       return wpa_config_write_freqs(data, ssid->freq_list);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+#ifdef IEEE8021X_EAPOL
+static int wpa_config_parse_eap(const struct parse_data *data,
+                               struct wpa_ssid *ssid, int line,
+                               const char *value)
+{
+       int last, errors = 0;
+       char *start, *end, *buf;
+       struct eap_method_type *methods = NULL, *tmp;
+       size_t num_methods = 0;
+
+       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';
+               tmp = methods;
+               methods = os_realloc(methods,
+                                    (num_methods + 1) * sizeof(*methods));
+               if (methods == NULL) {
+                       os_free(tmp);
+                       os_free(buf);
+                       return -1;
+               }
+               methods[num_methods].method = eap_peer_get_type(
+                       start, &methods[num_methods].vendor);
+               if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
+                   methods[num_methods].method == EAP_TYPE_NONE) {
+                       wpa_printf(MSG_ERROR, "Line %d: unknown EAP method "
+                                  "'%s'", line, start);
+                       wpa_printf(MSG_ERROR, "You may need to add support for"
+                                  " this EAP method during wpa_supplicant\n"
+                                  "build time configuration.\n"
+                                  "See README for more information.");
+                       errors++;
+               } else if (methods[num_methods].vendor == EAP_VENDOR_IETF &&
+                          methods[num_methods].method == EAP_TYPE_LEAP)
+                       ssid->leap++;
+               else
+                       ssid->non_leap++;
+               num_methods++;
+               if (last)
+                       break;
+               start = end + 1;
+       }
+       os_free(buf);
+
+       tmp = methods;
+       methods = os_realloc(methods, (num_methods + 1) * sizeof(*methods));
+       if (methods == NULL) {
+               os_free(tmp);
+               return -1;
+       }
+       methods[num_methods].vendor = EAP_VENDOR_IETF;
+       methods[num_methods].method = EAP_TYPE_NONE;
+       num_methods++;
+
+       wpa_hexdump(MSG_MSGDUMP, "eap methods",
+                   (u8 *) methods, num_methods * sizeof(*methods));
+       ssid->eap.eap_methods = methods;
+       return errors ? -1 : 0;
+}
+
+
+static char * wpa_config_write_eap(const struct parse_data *data,
+                                  struct wpa_ssid *ssid)
+{
+       int i, ret;
+       char *buf, *pos, *end;
+       const struct eap_method_type *eap_methods = ssid->eap.eap_methods;
+       const char *name;
+
+       if (eap_methods == NULL)
+               return NULL;
+
+       pos = buf = os_zalloc(100);
+       if (buf == NULL)
+               return NULL;
+       end = buf + 100;
+
+       for (i = 0; eap_methods[i].vendor != EAP_VENDOR_IETF ||
+                    eap_methods[i].method != EAP_TYPE_NONE; i++) {
+               name = eap_get_name(eap_methods[i].vendor,
+                                   eap_methods[i].method);
+               if (name) {
+                       ret = os_snprintf(pos, end - pos, "%s%s",
+                                         pos == buf ? "" : " ", name);
+                       if (ret < 0 || ret >= end - pos)
+                               break;
+                       pos += ret;
+               }
+       }
+
+       end[-1] = '\0';
+
+       return buf;
+}
+
+
+static int wpa_config_parse_password(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       u8 *hash;
+
+       if (os_strcmp(value, "NULL") == 0) {
+               wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
+               os_free(ssid->eap.password);
+               ssid->eap.password = NULL;
+               ssid->eap.password_len = 0;
+               return 0;
+       }
+
+       if (os_strncmp(value, "hash:", 5) != 0) {
+               char *tmp;
+               size_t res_len;
+
+               tmp = wpa_config_parse_string(value, &res_len);
+               if (tmp == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: failed to parse "
+                                  "password.", line);
+                       return -1;
+               }
+               wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+                                     (u8 *) tmp, res_len);
+
+               os_free(ssid->eap.password);
+               ssid->eap.password = (u8 *) tmp;
+               ssid->eap.password_len = res_len;
+               ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+
+               return 0;
+       }
+
+
+       /* NtPasswordHash: hash:<32 hex digits> */
+       if (os_strlen(value + 5) != 2 * 16) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
+                          "(expected 32 hex digits)", line);
+               return -1;
+       }
+
+       hash = os_malloc(16);
+       if (hash == NULL)
+               return -1;
+
+       if (hexstr2bin(value + 5, hash, 16)) {
+               os_free(hash);
+               wpa_printf(MSG_ERROR, "Line %d: Invalid password hash", line);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
+
+       os_free(ssid->eap.password);
+       ssid->eap.password = hash;
+       ssid->eap.password_len = 16;
+       ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
+
+       return 0;
+}
+
+
+static char * wpa_config_write_password(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       char *buf;
+
+       if (ssid->eap.password == NULL)
+               return NULL;
+
+       if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
+               return wpa_config_write_string(
+                       ssid->eap.password, ssid->eap.password_len);
+       }
+
+       buf = os_malloc(5 + 32 + 1);
+       if (buf == NULL)
+               return NULL;
+
+       os_memcpy(buf, "hash:", 5);
+       wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.password, 16);
+
+       return buf;
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
+                                   const char *value, int idx)
+{
+       char *buf, title[20];
+       int res;
+
+       buf = wpa_config_parse_string(value, len);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid WEP key %d '%s'.",
+                          line, idx, value);
+               return -1;
+       }
+       if (*len > MAX_WEP_KEY_LEN) {
+               wpa_printf(MSG_ERROR, "Line %d: Too long WEP key %d '%s'.",
+                          line, idx, value);
+               os_free(buf);
+               return -1;
+       }
+       os_memcpy(key, buf, *len);
+       os_free(buf);
+       res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
+       if (res >= 0 && (size_t) res < sizeof(title))
+               wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
+       return 0;
+}
+
+
+static int wpa_config_parse_wep_key0(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       return wpa_config_parse_wep_key(ssid->wep_key[0],
+                                       &ssid->wep_key_len[0], line,
+                                       value, 0);
+}
+
+
+static int wpa_config_parse_wep_key1(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       return wpa_config_parse_wep_key(ssid->wep_key[1],
+                                       &ssid->wep_key_len[1], line,
+                                       value, 1);
+}
+
+
+static int wpa_config_parse_wep_key2(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       return wpa_config_parse_wep_key(ssid->wep_key[2],
+                                       &ssid->wep_key_len[2], line,
+                                       value, 2);
+}
+
+
+static int wpa_config_parse_wep_key3(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       return wpa_config_parse_wep_key(ssid->wep_key[3],
+                                       &ssid->wep_key_len[3], line,
+                                       value, 3);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_wep_key(struct wpa_ssid *ssid, int idx)
+{
+       if (ssid->wep_key_len[idx] == 0)
+               return NULL;
+       return wpa_config_write_string(ssid->wep_key[idx],
+                                      ssid->wep_key_len[idx]);
+}
+
+
+static char * wpa_config_write_wep_key0(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       return wpa_config_write_wep_key(ssid, 0);
+}
+
+
+static char * wpa_config_write_wep_key1(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       return wpa_config_write_wep_key(ssid, 1);
+}
+
+
+static char * wpa_config_write_wep_key2(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       return wpa_config_write_wep_key(ssid, 2);
+}
+
+
+static char * wpa_config_write_wep_key3(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       return wpa_config_write_wep_key(ssid, 3);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+/* Helper macros for network block parser */
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_ssid structure */
+#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
+
+/* STR: Define a string variable for an ASCII string; f = field name */
+#ifdef NO_CONFIG_WRITE
+#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
+#else /* NO_CONFIG_WRITE */
+#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
+#endif /* NO_CONFIG_WRITE */
+#define STR(f) _STR(f), NULL, NULL, NULL, 0
+#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
+#define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
+#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
+
+/* STR_LEN: Define a string variable with a separate variable for storing the
+ * data length. Unlike STR(), this can be used to store arbitrary binary data
+ * (i.e., even nul termination character). */
+#define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
+#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
+#define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
+#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
+#define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
+
+/* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
+ * explicitly specified. */
+#define _STR_RANGE(f, min, max) _STR_LEN(f), (void *) (min), (void *) (max)
+#define STR_RANGE(f, min, max) _STR_RANGE(f, min, max), 0
+#define STR_RANGE_KEY(f, min, max) _STR_RANGE(f, min, max), 1
+
+#ifdef NO_CONFIG_WRITE
+#define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
+#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
+#else /* NO_CONFIG_WRITE */
+#define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
+       OFFSET(f), (void *) 0
+#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
+       OFFSET(eap.f), (void *) 0
+#endif /* NO_CONFIG_WRITE */
+
+/* INT: Define an integer variable */
+#define INT(f) _INT(f), NULL, NULL, 0
+#define INTe(f) _INTe(f), NULL, NULL, 0
+
+/* INT_RANGE: Define an integer variable with allowed value range */
+#define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
+
+/* FUNC: Define a configuration variable that uses a custom function for
+ * parsing and writing the value. */
+#ifdef NO_CONFIG_WRITE
+#define _FUNC(f) #f, wpa_config_parse_ ## f, NULL, NULL, NULL, NULL
+#else /* NO_CONFIG_WRITE */
+#define _FUNC(f) #f, wpa_config_parse_ ## f, wpa_config_write_ ## f, \
+       NULL, NULL, NULL, NULL
+#endif /* NO_CONFIG_WRITE */
+#define FUNC(f) _FUNC(f), 0
+#define FUNC_KEY(f) _FUNC(f), 1
+
+/*
+ * Table of network configuration variables. This table is used to parse each
+ * network configuration variable, e.g., each line in wpa_supplicant.conf file
+ * that is inside a network block.
+ *
+ * This table is generated using the helper macros defined above and with
+ * generous help from the C pre-processor. The field name is stored as a string
+ * into .name and for STR and INT types, the offset of the target buffer within
+ * struct wpa_ssid is stored in .param1. .param2 (if not NULL) is similar
+ * offset to the field containing the length of the configuration variable.
+ * .param3 and .param4 can be used to mark the allowed range (length for STR
+ * and value for INT).
+ *
+ * For each configuration line in wpa_supplicant.conf, the parser goes through
+ * this table and select the entry that matches with the field name. The parser
+ * function (.parser) is then called to parse the actual value of the field.
+ *
+ * This kind of mechanism makes it easy to add new configuration parameters,
+ * since only one line needs to be added into this table and into the
+ * struct wpa_ssid definition if the new variable is either a string or
+ * integer. More complex types will need to use their own parser and writer
+ * functions.
+ */
+static const struct parse_data ssid_fields[] = {
+       { STR_RANGE(ssid, 0, MAX_SSID_LEN) },
+       { INT_RANGE(scan_ssid, 0, 1) },
+       { FUNC(bssid) },
+       { FUNC_KEY(psk) },
+       { FUNC(proto) },
+       { FUNC(key_mgmt) },
+       { FUNC(pairwise) },
+       { FUNC(group) },
+       { FUNC(auth_alg) },
+       { FUNC(scan_freq) },
+       { FUNC(freq_list) },
+#ifdef IEEE8021X_EAPOL
+       { FUNC(eap) },
+       { STR_LENe(identity) },
+       { STR_LENe(anonymous_identity) },
+       { FUNC_KEY(password) },
+       { STRe(ca_cert) },
+       { STRe(ca_path) },
+       { STRe(client_cert) },
+       { STRe(private_key) },
+       { STR_KEYe(private_key_passwd) },
+       { STRe(dh_file) },
+       { STRe(subject_match) },
+       { STRe(altsubject_match) },
+       { STRe(ca_cert2) },
+       { STRe(ca_path2) },
+       { STRe(client_cert2) },
+       { STRe(private_key2) },
+       { STR_KEYe(private_key2_passwd) },
+       { STRe(dh_file2) },
+       { STRe(subject_match2) },
+       { STRe(altsubject_match2) },
+       { STRe(phase1) },
+       { STRe(phase2) },
+       { STRe(pcsc) },
+       { STR_KEYe(pin) },
+       { STRe(engine_id) },
+       { STRe(key_id) },
+       { STRe(cert_id) },
+       { STRe(ca_cert_id) },
+       { STR_KEYe(pin2) },
+       { STRe(engine2_id) },
+       { STRe(key2_id) },
+       { STRe(cert2_id) },
+       { STRe(ca_cert2_id) },
+       { INTe(engine) },
+       { INTe(engine2) },
+       { INT(eapol_flags) },
+#endif /* IEEE8021X_EAPOL */
+       { FUNC_KEY(wep_key0) },
+       { FUNC_KEY(wep_key1) },
+       { FUNC_KEY(wep_key2) },
+       { FUNC_KEY(wep_key3) },
+       { INT(wep_tx_keyidx) },
+       { INT(priority) },
+#ifdef IEEE8021X_EAPOL
+       { INT(eap_workaround) },
+       { STRe(pac_file) },
+       { INTe(fragment_size) },
+#endif /* IEEE8021X_EAPOL */
+       { INT_RANGE(mode, 0, 2) },
+       { INT_RANGE(proactive_key_caching, 0, 1) },
+       { INT_RANGE(disabled, 0, 1) },
+       { STR(id_str) },
+#ifdef CONFIG_IEEE80211W
+       { INT_RANGE(ieee80211w, 0, 2) },
+#endif /* CONFIG_IEEE80211W */
+       { INT_RANGE(peerkey, 0, 1) },
+       { INT_RANGE(mixed_cell, 0, 1) },
+       { INT_RANGE(frequency, 0, 10000) },
+       { INT(wpa_ptk_rekey) },
+       { STR(bgscan) },
+};
+
+#undef OFFSET
+#undef _STR
+#undef STR
+#undef STR_KEY
+#undef _STR_LEN
+#undef STR_LEN
+#undef STR_LEN_KEY
+#undef _STR_RANGE
+#undef STR_RANGE
+#undef STR_RANGE_KEY
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _FUNC
+#undef FUNC
+#undef FUNC_KEY
+#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
+
+
+/**
+ * wpa_config_add_prio_network - Add a network to priority lists
+ * @config: Configuration data from wpa_config_read()
+ * @ssid: Pointer to the network configuration to be added to the list
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to add a network block to the priority list of
+ * networks. This must be called for each network when reading in the full
+ * configuration. In addition, this can be used indirectly when updating
+ * priorities by calling wpa_config_update_prio_list().
+ */
+int wpa_config_add_prio_network(struct wpa_config *config,
+                               struct wpa_ssid *ssid)
+{
+       int prio;
+       struct wpa_ssid *prev, **nlist;
+
+       /*
+        * Add to an existing priority list if one is available for the
+        * configured priority level for this network.
+        */
+       for (prio = 0; prio < config->num_prio; prio++) {
+               prev = config->pssid[prio];
+               if (prev->priority == ssid->priority) {
+                       while (prev->pnext)
+                               prev = prev->pnext;
+                       prev->pnext = ssid;
+                       return 0;
+               }
+       }
+
+       /* First network for this priority - add a new priority list */
+       nlist = os_realloc(config->pssid,
+                          (config->num_prio + 1) * sizeof(struct wpa_ssid *));
+       if (nlist == NULL)
+               return -1;
+
+       for (prio = 0; prio < config->num_prio; prio++) {
+               if (nlist[prio]->priority < ssid->priority)
+                       break;
+       }
+
+       os_memmove(&nlist[prio + 1], &nlist[prio],
+                  (config->num_prio - prio) * sizeof(struct wpa_ssid *));
+
+       nlist[prio] = ssid;
+       config->num_prio++;
+       config->pssid = nlist;
+
+       return 0;
+}
+
+
+/**
+ * wpa_config_update_prio_list - Update network priority list
+ * @config: Configuration data from wpa_config_read()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to update the priority list of networks in the
+ * configuration when a network is being added or removed. This is also called
+ * if a priority for a network is changed.
+ */
+int wpa_config_update_prio_list(struct wpa_config *config)
+{
+       struct wpa_ssid *ssid;
+       int ret = 0;
+
+       os_free(config->pssid);
+       config->pssid = NULL;
+       config->num_prio = 0;
+
+       ssid = config->ssid;
+       while (ssid) {
+               ssid->pnext = NULL;
+               if (wpa_config_add_prio_network(config, ssid) < 0)
+                       ret = -1;
+               ssid = ssid->next;
+       }
+
+       return ret;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void eap_peer_config_free(struct eap_peer_config *eap)
+{
+       os_free(eap->eap_methods);
+       os_free(eap->identity);
+       os_free(eap->anonymous_identity);
+       os_free(eap->password);
+       os_free(eap->ca_cert);
+       os_free(eap->ca_path);
+       os_free(eap->client_cert);
+       os_free(eap->private_key);
+       os_free(eap->private_key_passwd);
+       os_free(eap->dh_file);
+       os_free(eap->subject_match);
+       os_free(eap->altsubject_match);
+       os_free(eap->ca_cert2);
+       os_free(eap->ca_path2);
+       os_free(eap->client_cert2);
+       os_free(eap->private_key2);
+       os_free(eap->private_key2_passwd);
+       os_free(eap->dh_file2);
+       os_free(eap->subject_match2);
+       os_free(eap->altsubject_match2);
+       os_free(eap->phase1);
+       os_free(eap->phase2);
+       os_free(eap->pcsc);
+       os_free(eap->pin);
+       os_free(eap->engine_id);
+       os_free(eap->key_id);
+       os_free(eap->cert_id);
+       os_free(eap->ca_cert_id);
+       os_free(eap->key2_id);
+       os_free(eap->cert2_id);
+       os_free(eap->ca_cert2_id);
+       os_free(eap->pin2);
+       os_free(eap->engine2_id);
+       os_free(eap->otp);
+       os_free(eap->pending_req_otp);
+       os_free(eap->pac_file);
+       os_free(eap->new_password);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+/**
+ * wpa_config_free_ssid - Free network/ssid configuration data
+ * @ssid: Configuration data for the network
+ *
+ * This function frees all resources allocated for the network configuration
+ * data.
+ */
+void wpa_config_free_ssid(struct wpa_ssid *ssid)
+{
+       os_free(ssid->ssid);
+       os_free(ssid->passphrase);
+#ifdef IEEE8021X_EAPOL
+       eap_peer_config_free(&ssid->eap);
+#endif /* IEEE8021X_EAPOL */
+       os_free(ssid->id_str);
+       os_free(ssid->scan_freq);
+       os_free(ssid->freq_list);
+       os_free(ssid->bgscan);
+       os_free(ssid);
+}
+
+
+/**
+ * wpa_config_free - Free configuration data
+ * @config: Configuration data from wpa_config_read()
+ *
+ * This function frees all resources allocated for the configuration data by
+ * wpa_config_read().
+ */
+void wpa_config_free(struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       struct wpa_config_blob *blob, *prevblob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+       struct wpa_ssid *ssid, *prev = NULL;
+       ssid = config->ssid;
+       while (ssid) {
+               prev = ssid;
+               ssid = ssid->next;
+               wpa_config_free_ssid(prev);
+       }
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       blob = config->blobs;
+       prevblob = NULL;
+       while (blob) {
+               prevblob = blob;
+               blob = blob->next;
+               wpa_config_free_blob(prevblob);
+       }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+       os_free(config->ctrl_interface);
+       os_free(config->ctrl_interface_group);
+       os_free(config->opensc_engine_path);
+       os_free(config->pkcs11_engine_path);
+       os_free(config->pkcs11_module_path);
+       os_free(config->driver_param);
+       os_free(config->device_name);
+       os_free(config->manufacturer);
+       os_free(config->model_name);
+       os_free(config->model_number);
+       os_free(config->serial_number);
+       os_free(config->device_type);
+       os_free(config->config_methods);
+       os_free(config->pssid);
+       os_free(config);
+}
+
+
+/**
+ * wpa_config_get_network - Get configured network based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: Network configuration or %NULL if not found
+ */
+struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id)
+{
+       struct wpa_ssid *ssid;
+
+       ssid = config->ssid;
+       while (ssid) {
+               if (id == ssid->id)
+                       break;
+               ssid = ssid->next;
+       }
+
+       return ssid;
+}
+
+
+/**
+ * wpa_config_add_network - Add a new network with empty configuration
+ * @config: Configuration data from wpa_config_read()
+ * Returns: The new network configuration or %NULL if operation failed
+ */
+struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
+{
+       int id;
+       struct wpa_ssid *ssid, *last = NULL;
+
+       id = -1;
+       ssid = config->ssid;
+       while (ssid) {
+               if (ssid->id > id)
+                       id = ssid->id;
+               last = ssid;
+               ssid = ssid->next;
+       }
+       id++;
+
+       ssid = os_zalloc(sizeof(*ssid));
+       if (ssid == NULL)
+               return NULL;
+       ssid->id = id;
+       if (last)
+               last->next = ssid;
+       else
+               config->ssid = ssid;
+
+       wpa_config_update_prio_list(config);
+
+       return ssid;
+}
+
+
+/**
+ * wpa_config_remove_network - Remove a configured network based on id
+ * @config: Configuration data from wpa_config_read()
+ * @id: Unique network id to search for
+ * Returns: 0 on success, or -1 if the network was not found
+ */
+int wpa_config_remove_network(struct wpa_config *config, int id)
+{
+       struct wpa_ssid *ssid, *prev = NULL;
+
+       ssid = config->ssid;
+       while (ssid) {
+               if (id == ssid->id)
+                       break;
+               prev = ssid;
+               ssid = ssid->next;
+       }
+
+       if (ssid == NULL)
+               return -1;
+
+       if (prev)
+               prev->next = ssid->next;
+       else
+               config->ssid = ssid->next;
+
+       wpa_config_update_prio_list(config);
+       wpa_config_free_ssid(ssid);
+       return 0;
+}
+
+
+/**
+ * wpa_config_set_network_defaults - Set network default values
+ * @ssid: Pointer to network configuration data
+ */
+void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
+{
+       ssid->proto = DEFAULT_PROTO;
+       ssid->pairwise_cipher = DEFAULT_PAIRWISE;
+       ssid->group_cipher = DEFAULT_GROUP;
+       ssid->key_mgmt = DEFAULT_KEY_MGMT;
+#ifdef IEEE8021X_EAPOL
+       ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
+       ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
+       ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+/**
+ * wpa_config_set - Set a variable in network configuration
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * @value: Variable value
+ * @line: Line number in configuration file or 0 if not used
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function can be used to set network configuration variables based on
+ * both the configuration file and management interface input. The value
+ * parameter must be in the same format as the text-based configuration file is
+ * using. For example, strings are using double quotation marks.
+ */
+int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
+                  int line)
+{
+       size_t i;
+       int ret = 0;
+
+       if (ssid == NULL || var == NULL || value == NULL)
+               return -1;
+
+       for (i = 0; i < NUM_SSID_FIELDS; i++) {
+               const struct parse_data *field = &ssid_fields[i];
+               if (os_strcmp(var, field->name) != 0)
+                       continue;
+
+               if (field->parser(field, ssid, line, value)) {
+                       if (line) {
+                               wpa_printf(MSG_ERROR, "Line %d: failed to "
+                                          "parse %s '%s'.", line, var, value);
+                       }
+                       ret = -1;
+               }
+               break;
+       }
+       if (i == NUM_SSID_FIELDS) {
+               if (line) {
+                       wpa_printf(MSG_ERROR, "Line %d: unknown network field "
+                                  "'%s'.", line, var);
+               }
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+/**
+ * wpa_config_get_all - Get all options from network configuration
+ * @ssid: Pointer to network configuration data
+ * @get_keys: Determines if keys/passwords will be included in returned list
+ * Returns: %NULL terminated list of all set keys and their values in the form
+ * of [key1, val1, key2, val2, ... , NULL]
+ *
+ * This function can be used to get list of all configured network properties.
+ * The caller is responsible for freeing the returned list and all its
+ * elements.
+ */
+char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
+{
+       const struct parse_data *field;
+       char *key, *value;
+       size_t i;
+       char **props;
+       int fields_num;
+
+       props = os_zalloc(sizeof(char *) * ((2 * NUM_SSID_FIELDS) + 1));
+       if (!props)
+               return NULL;
+
+       fields_num = 0;
+       for (i = 0; i < NUM_SSID_FIELDS; i++) {
+               field = &ssid_fields[i];
+               if (field->key_data && !get_keys)
+                       continue;
+               value = field->writer(field, ssid);
+               if (value == NULL)
+                       continue;
+               if (os_strlen(value) == 0) {
+                       os_free(value);
+                       continue;
+               }
+
+               key = os_strdup(field->name);
+               if (key == NULL) {
+                       os_free(value);
+                       goto err;
+               }
+
+               props[fields_num * 2] = key;
+               props[fields_num * 2 + 1] = value;
+
+               fields_num++;
+       }
+
+       return props;
+
+err:
+       value = *props;
+       while (value)
+               os_free(value++);
+       os_free(props);
+       return NULL;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+/**
+ * wpa_config_get - Get a variable in network configuration
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * Returns: Value of the variable or %NULL on failure
+ *
+ * This function can be used to get network configuration variables. The
+ * returned value is a copy of the configuration variable in text format, i.e,.
+ * the same format that the text-based configuration file and wpa_config_set()
+ * are using for the value. The caller is responsible for freeing the returned
+ * value.
+ */
+char * wpa_config_get(struct wpa_ssid *ssid, const char *var)
+{
+       size_t i;
+
+       if (ssid == NULL || var == NULL)
+               return NULL;
+
+       for (i = 0; i < NUM_SSID_FIELDS; i++) {
+               const struct parse_data *field = &ssid_fields[i];
+               if (os_strcmp(var, field->name) == 0)
+                       return field->writer(field, ssid);
+       }
+
+       return NULL;
+}
+
+
+/**
+ * wpa_config_get_no_key - Get a variable in network configuration (no keys)
+ * @ssid: Pointer to network configuration data
+ * @var: Variable name, e.g., "ssid"
+ * Returns: Value of the variable or %NULL on failure
+ *
+ * This function can be used to get network configuration variable like
+ * wpa_config_get(). The only difference is that this functions does not expose
+ * key/password material from the configuration. In case a key/password field
+ * is requested, the returned value is an empty string or %NULL if the variable
+ * is not set or "*" if the variable is set (regardless of its value). The
+ * returned value is a copy of the configuration variable in text format, i.e,.
+ * the same format that the text-based configuration file and wpa_config_set()
+ * are using for the value. The caller is responsible for freeing the returned
+ * value.
+ */
+char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
+{
+       size_t i;
+
+       if (ssid == NULL || var == NULL)
+               return NULL;
+
+       for (i = 0; i < NUM_SSID_FIELDS; i++) {
+               const struct parse_data *field = &ssid_fields[i];
+               if (os_strcmp(var, field->name) == 0) {
+                       char *res = field->writer(field, ssid);
+                       if (field->key_data) {
+                               if (res && res[0]) {
+                                       wpa_printf(MSG_DEBUG, "Do not allow "
+                                                  "key_data field to be "
+                                                  "exposed");
+                                       os_free(res);
+                                       return os_strdup("*");
+                               }
+
+                               os_free(res);
+                               return NULL;
+                       }
+                       return res;
+               }
+       }
+
+       return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+/**
+ * wpa_config_update_psk - Update WPA PSK based on passphrase and SSID
+ * @ssid: Pointer to network configuration data
+ *
+ * This function must be called to update WPA PSK when either SSID or the
+ * passphrase has changed for the network configuration.
+ */
+void wpa_config_update_psk(struct wpa_ssid *ssid)
+{
+#ifndef CONFIG_NO_PBKDF2
+       pbkdf2_sha1(ssid->passphrase,
+                   (char *) ssid->ssid, ssid->ssid_len, 4096,
+                   ssid->psk, PMK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
+                       ssid->psk, PMK_LEN);
+       ssid->psk_set = 1;
+#endif /* CONFIG_NO_PBKDF2 */
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+/**
+ * wpa_config_get_blob - Get a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @name: Name of the blob
+ * Returns: Pointer to blob data or %NULL if not found
+ */
+const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
+                                                  const char *name)
+{
+       struct wpa_config_blob *blob = config->blobs;
+
+       while (blob) {
+               if (os_strcmp(blob->name, name) == 0)
+                       return blob;
+               blob = blob->next;
+       }
+       return NULL;
+}
+
+
+/**
+ * wpa_config_set_blob - Set or add a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @blob: New value for the blob
+ *
+ * Adds a new configuration blob or replaces the current value of an existing
+ * blob.
+ */
+void wpa_config_set_blob(struct wpa_config *config,
+                        struct wpa_config_blob *blob)
+{
+       wpa_config_remove_blob(config, blob->name);
+       blob->next = config->blobs;
+       config->blobs = blob;
+}
+
+
+/**
+ * wpa_config_free_blob - Free blob data
+ * @blob: Pointer to blob to be freed
+ */
+void wpa_config_free_blob(struct wpa_config_blob *blob)
+{
+       if (blob) {
+               os_free(blob->name);
+               os_free(blob->data);
+               os_free(blob);
+       }
+}
+
+
+/**
+ * wpa_config_remove_blob - Remove a named configuration blob
+ * @config: Configuration data from wpa_config_read()
+ * @name: Name of the blob to remove
+ * Returns: 0 if blob was removed or -1 if blob was not found
+ */
+int wpa_config_remove_blob(struct wpa_config *config, const char *name)
+{
+       struct wpa_config_blob *pos = config->blobs, *prev = NULL;
+
+       while (pos) {
+               if (os_strcmp(pos->name, name) == 0) {
+                       if (prev)
+                               prev->next = pos->next;
+                       else
+                               config->blobs = pos->next;
+                       wpa_config_free_blob(pos);
+                       return 0;
+               }
+               prev = pos;
+               pos = pos->next;
+       }
+
+       return -1;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+/**
+ * wpa_config_alloc_empty - Allocate an empty configuration
+ * @ctrl_interface: Control interface parameters, e.g., path to UNIX domain
+ * socket
+ * @driver_param: Driver parameters
+ * Returns: Pointer to allocated configuration data or %NULL on failure
+ */
+struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+                                          const char *driver_param)
+{
+       struct wpa_config *config;
+
+       config = os_zalloc(sizeof(*config));
+       if (config == NULL)
+               return NULL;
+       config->eapol_version = DEFAULT_EAPOL_VERSION;
+       config->ap_scan = DEFAULT_AP_SCAN;
+       config->fast_reauth = DEFAULT_FAST_REAUTH;
+       config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
+
+       if (ctrl_interface)
+               config->ctrl_interface = os_strdup(ctrl_interface);
+       if (driver_param)
+               config->driver_param = os_strdup(driver_param);
+
+       return config;
+}
+
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+/**
+ * wpa_config_debug_dump_networks - Debug dump of configured networks
+ * @config: Configuration data from wpa_config_read()
+ */
+void wpa_config_debug_dump_networks(struct wpa_config *config)
+{
+       int prio;
+       struct wpa_ssid *ssid;
+
+       for (prio = 0; prio < config->num_prio; prio++) {
+               ssid = config->pssid[prio];
+               wpa_printf(MSG_DEBUG, "Priority group %d",
+                          ssid->priority);
+               while (ssid) {
+                       wpa_printf(MSG_DEBUG, "   id=%d ssid='%s'",
+                                  ssid->id,
+                                  wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+                       ssid = ssid->pnext;
+               }
+       }
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
diff --git a/wpa_supplicant/config.h b/wpa_supplicant/config.h
new file mode 100644 (file)
index 0000000..754e4be
--- /dev/null
@@ -0,0 +1,416 @@
+/*
+ * WPA Supplicant / Configuration file structures
+ * 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 CONFIG_H
+#define CONFIG_H
+
+#define DEFAULT_EAPOL_VERSION 1
+#ifdef CONFIG_NO_SCAN_PROCESSING
+#define DEFAULT_AP_SCAN 2
+#else /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_AP_SCAN 1
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_FAST_REAUTH 1
+#define DEFAULT_BSS_MAX_COUNT 200
+
+#include "config_ssid.h"
+
+
+/**
+ * struct wpa_config - wpa_supplicant configuration data
+ *
+ * This data structure is presents the per-interface (radio) configuration
+ * data. In many cases, there is only one struct wpa_config instance, but if
+ * more than one network interface is being controlled, one instance is used
+ * for each.
+ */
+struct wpa_config {
+       /**
+        * ssid - Head of the global network list
+        *
+        * This is the head for the list of all the configured networks.
+        */
+       struct wpa_ssid *ssid;
+
+       /**
+        * pssid - Per-priority network lists (in priority order)
+        */
+       struct wpa_ssid **pssid;
+
+       /**
+        * num_prio - Number of different priorities used in the pssid lists
+        *
+        * This indicates how many per-priority network lists are included in
+        * pssid.
+        */
+       int num_prio;
+
+       /**
+        * eapol_version - IEEE 802.1X/EAPOL version number
+        *
+        * wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which
+        * defines EAPOL version 2. However, there are many APs that do not
+        * handle the new version number correctly (they seem to drop the
+        * frames completely). In order to make wpa_supplicant interoperate
+        * with these APs, the version number is set to 1 by default. This
+        * configuration value can be used to set it to the new version (2).
+        */
+       int eapol_version;
+
+       /**
+        * ap_scan - AP scanning/selection
+        *
+        * By default, wpa_supplicant requests driver to perform AP
+        * scanning and then uses the scan results to select a
+        * suitable AP. Another alternative is to allow the driver to
+        * take care of AP scanning and selection and use
+        * wpa_supplicant just to process EAPOL frames based on IEEE
+        * 802.11 association information from the driver.
+        *
+        * 1: wpa_supplicant initiates scanning and AP selection (default).
+        *
+        * 0: Driver takes care of scanning, AP selection, and IEEE 802.11
+        * association parameters (e.g., WPA IE generation); this mode can
+        * also be used with non-WPA drivers when using IEEE 802.1X mode;
+        * do not try to associate with APs (i.e., external program needs
+        * to control association). This mode must also be used when using
+        * wired Ethernet drivers.
+        *
+        * 2: like 0, but associate with APs using security policy and SSID
+        * (but not BSSID); this can be used, e.g., with ndiswrapper and NDIS
+        * drivers to enable operation with hidden SSIDs and optimized roaming;
+        * in this mode, the network blocks in the configuration are tried
+        * one by one until the driver reports successful association; each
+        * network block should have explicit security policy (i.e., only one
+        * option in the lists) for key_mgmt, pairwise, group, proto variables.
+        */
+       int ap_scan;
+
+       /**
+        * ctrl_interface - Parameters for the control interface
+        *
+        * If this is specified, %wpa_supplicant will open a control interface
+        * that is available for external programs to manage %wpa_supplicant.
+        * The meaning of this string depends on which control interface
+        * mechanism is used. For all cases, the existance of this parameter
+        * in configuration is used to determine whether the control interface
+        * is enabled.
+        *
+        * For UNIX domain sockets (default on Linux and BSD): This is a
+        * directory that will be created for UNIX domain sockets for listening
+        * to requests from external programs (CLI/GUI, etc.) for status
+        * information and configuration. The socket file will be named based
+        * on the interface name, so multiple %wpa_supplicant processes can be
+        * run at the same time if more than one interface is used.
+        * /var/run/wpa_supplicant is the recommended directory for sockets and
+        * by default, wpa_cli will use it when trying to connect with
+        * %wpa_supplicant.
+        *
+        * Access control for the control interface can be configured
+        * by setting the directory to allow only members of a group
+        * to use sockets. This way, it is possible to run
+        * %wpa_supplicant as root (since it needs to change network
+        * configuration and open raw sockets) and still allow GUI/CLI
+        * components to be run as non-root users. However, since the
+        * control interface can be used to change the network
+        * configuration, this access needs to be protected in many
+        * cases. By default, %wpa_supplicant is configured to use gid
+        * 0 (root). If you want to allow non-root users to use the
+        * control interface, add a new group and change this value to
+        * match with that group. Add users that should have control
+        * interface access to this group.
+        *
+        * When configuring both the directory and group, use following format:
+        * DIR=/var/run/wpa_supplicant GROUP=wheel
+        * DIR=/var/run/wpa_supplicant GROUP=0
+        * (group can be either group name or gid)
+        *
+        * For UDP connections (default on Windows): The value will be ignored.
+        * This variable is just used to select that the control interface is
+        * to be created. The value can be set to, e.g., udp
+        * (ctrl_interface=udp).
+        *
+        * For Windows Named Pipe: This value can be used to set the security
+        * descriptor for controlling access to the control interface. Security
+        * descriptor can be set using Security Descriptor String Format (see
+        * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthz/security/security_descriptor_string_format.asp).
+        * The descriptor string needs to be prefixed with SDDL=. For example,
+        * ctrl_interface=SDDL=D: would set an empty DACL (which will reject
+        * all connections).
+        */
+       char *ctrl_interface;
+
+       /**
+        * ctrl_interface_group - Control interface group (DEPRECATED)
+        *
+        * This variable is only used for backwards compatibility. Group for
+        * UNIX domain sockets should now be specified using GROUP=group in
+        * ctrl_interface variable.
+        */
+       char *ctrl_interface_group;
+
+       /**
+        * fast_reauth - EAP fast re-authentication (session resumption)
+        *
+        * By default, fast re-authentication is enabled for all EAP methods
+        * that support it. This variable can be used to disable fast
+        * re-authentication (by setting fast_reauth=0). Normally, there is no
+        * need to disable fast re-authentication.
+        */
+       int fast_reauth;
+
+       /**
+        * opensc_engine_path - Path to the OpenSSL engine for opensc
+        *
+        * This is an OpenSSL specific configuration option for loading OpenSC
+        * engine (engine_opensc.so); if %NULL, this engine is not loaded.
+        */
+       char *opensc_engine_path;
+
+       /**
+        * pkcs11_engine_path - Path to the OpenSSL engine for PKCS#11
+        *
+        * This is an OpenSSL specific configuration option for loading PKCS#11
+        * engine (engine_pkcs11.so); if %NULL, this engine is not loaded.
+        */
+       char *pkcs11_engine_path;
+
+       /**
+        * pkcs11_module_path - Path to the OpenSSL OpenSC/PKCS#11 module
+        *
+        * This is an OpenSSL specific configuration option for configuring
+        * path to OpenSC/PKCS#11 engine (opensc-pkcs11.so); if %NULL, this
+        * module is not loaded.
+        */
+       char *pkcs11_module_path;
+
+       /**
+        * driver_param - Driver interface parameters
+        *
+        * This text string is passed to the selected driver interface with the
+        * optional struct wpa_driver_ops::set_param() handler. This can be
+        * used to configure driver specific options without having to add new
+        * driver interface functionality.
+        */
+       char *driver_param;
+
+       /**
+        * dot11RSNAConfigPMKLifetime - Maximum lifetime of a PMK
+        *
+        * dot11 MIB variable for the maximum lifetime of a PMK in the PMK
+        * cache (unit: seconds).
+        */
+       unsigned int dot11RSNAConfigPMKLifetime;
+
+       /**
+        * dot11RSNAConfigPMKReauthThreshold - PMK re-authentication threshold
+        *
+        * dot11 MIB variable for the percentage of the PMK lifetime
+        * that should expire before an IEEE 802.1X reauthentication occurs.
+        */
+       unsigned int dot11RSNAConfigPMKReauthThreshold;
+
+       /**
+        * dot11RSNAConfigSATimeout - Security association timeout
+        *
+        * dot11 MIB variable for the maximum time a security association
+        * shall take to set up (unit: seconds).
+        */
+       unsigned int dot11RSNAConfigSATimeout;
+
+       /**
+        * update_config - Is wpa_supplicant allowed to update configuration
+        *
+        * This variable control whether wpa_supplicant is allow to re-write
+        * its configuration with wpa_config_write(). If this is zero,
+        * configuration data is only changed in memory and the external data
+        * is not overriden. If this is non-zero, wpa_supplicant will update
+        * the configuration data (e.g., a file) whenever configuration is
+        * changed. This update may replace the old configuration which can
+        * remove comments from it in case of a text file configuration.
+        */
+       int update_config;
+
+       /**
+        * blobs - Configuration blobs
+        */
+       struct wpa_config_blob *blobs;
+
+       /**
+        * uuid - Universally Unique IDentifier (UUID; see RFC 4122) for WPS
+        */
+       u8 uuid[16];
+
+       /**
+        * device_name - Device Name (WPS)
+        * User-friendly description of device; up to 32 octets encoded in
+        * UTF-8
+        */
+       char *device_name;
+
+       /**
+        * manufacturer - Manufacturer (WPS)
+        * The manufacturer of the device (up to 64 ASCII characters)
+        */
+       char *manufacturer;
+
+       /**
+        * model_name - Model Name (WPS)
+        * Model of the device (up to 32 ASCII characters)
+        */
+       char *model_name;
+
+       /**
+        * model_number - Model Number (WPS)
+        * Additional device description (up to 32 ASCII characters)
+        */
+       char *model_number;
+
+       /**
+        * serial_number - Serial Number (WPS)
+        * Serial number of the device (up to 32 characters)
+        */
+       char *serial_number;
+
+       /**
+        * device_type - Primary Device Type (WPS)
+        * 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)
+        */
+       char *device_type;
+
+       /**
+        * config_methods - Config Methods
+        *
+        * This is a space-separated list of supported WPS configuration
+        * methods. For example, "label display push_button keypad".
+        * Available methods: usba ethernet label display ext_nfc_token
+        * int_nfc_token nfc_interface push_button keypad.
+        */
+       char *config_methods;
+
+       /**
+        * os_version - OS Version (WPS)
+        * 4-octet operating system version number
+        */
+       u8 os_version[4];
+
+       /**
+        * country - Country code
+        *
+        * This is the ISO/IEC alpha2 country code for which we are operating
+        * in
+        */
+       char country[2];
+
+       /**
+        * wps_cred_processing - Credential processing
+        *
+        *   0 = process received credentials internally
+        *   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)
+        */
+       int wps_cred_processing;
+
+       /**
+        * bss_max_count - Maximum number of BSS entries to keep in memory
+        */
+       unsigned int bss_max_count;
+
+       /**
+        * filter_ssids - SSID-based scan result filtering
+        *
+        *   0 = do not filter scan results
+        *   1 = only include configured SSIDs in scan results/BSS table
+        */
+       int filter_ssids;
+};
+
+
+/* Prototypes for common functions from config.c */
+
+void wpa_config_free(struct wpa_config *ssid);
+void wpa_config_free_ssid(struct wpa_ssid *ssid);
+struct wpa_ssid * wpa_config_get_network(struct wpa_config *config, int id);
+struct wpa_ssid * wpa_config_add_network(struct wpa_config *config);
+int wpa_config_remove_network(struct wpa_config *config, int id);
+void wpa_config_set_network_defaults(struct wpa_ssid *ssid);
+int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
+                  int line);
+char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
+char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
+char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
+void wpa_config_update_psk(struct wpa_ssid *ssid);
+int wpa_config_add_prio_network(struct wpa_config *config,
+                               struct wpa_ssid *ssid);
+int wpa_config_update_prio_list(struct wpa_config *config);
+const struct wpa_config_blob * wpa_config_get_blob(struct wpa_config *config,
+                                                  const char *name);
+void wpa_config_set_blob(struct wpa_config *config,
+                        struct wpa_config_blob *blob);
+void wpa_config_free_blob(struct wpa_config_blob *blob);
+int wpa_config_remove_blob(struct wpa_config *config, const char *name);
+
+struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
+                                          const char *driver_param);
+#ifndef CONFIG_NO_STDOUT_DEBUG
+void wpa_config_debug_dump_networks(struct wpa_config *config);
+#else /* CONFIG_NO_STDOUT_DEBUG */
+#define wpa_config_debug_dump_networks(c) do { } while (0)
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
+/* Prototypes for backend specific functions from the selected config_*.c */
+
+/**
+ * wpa_config_read - Read and parse configuration database
+ * @name: Name of the configuration (e.g., path and file name for the
+ * configuration file)
+ * Returns: Pointer to allocated configuration data or %NULL on failure
+ *
+ * This function reads configuration data, parses its contents, and allocates
+ * data structures needed for storing configuration information. The allocated
+ * data can be freed with wpa_config_free().
+ *
+ * Each configuration backend needs to implement this function.
+ */
+struct wpa_config * wpa_config_read(const char *name);
+
+/**
+ * wpa_config_write - Write or update configuration data
+ * @name: Name of the configuration (e.g., path and file name for the
+ * configuration file)
+ * @config: Configuration data from wpa_config_read()
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function write all configuration data into an external database (e.g.,
+ * a text file) in a format that can be read with wpa_config_read(). This can
+ * be used to allow wpa_supplicant to update its configuration, e.g., when a
+ * new network is added or a password is changed.
+ *
+ * Each configuration backend needs to implement this function.
+ */
+int wpa_config_write(const char *name, struct wpa_config *config);
+
+#endif /* CONFIG_H */
diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c
new file mode 100644 (file)
index 0000000..5f07045
--- /dev/null
@@ -0,0 +1,947 @@
+/*
+ * WPA Supplicant / Configuration backend: text file
+ * Copyright (c) 2003-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.
+ *
+ * This file implements a configuration backend for text files. All the
+ * configuration information is stored in a text file that uses a format
+ * described in the sample configuration file, wpa_supplicant.conf.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "config.h"
+#include "base64.h"
+#include "uuid.h"
+#include "eap_peer/eap_methods.h"
+
+
+/**
+ * wpa_config_get_line - Read the next configuration file line
+ * @s: Buffer for the line
+ * @size: The buffer length
+ * @stream: File stream to read from
+ * @line: Pointer to a variable storing the file line number
+ * @_pos: Buffer for the pointer to the beginning of data on the text line or
+ * %NULL if not needed (returned value used instead)
+ * Returns: Pointer to the beginning of data on the text line or %NULL if no
+ * more text lines are available.
+ *
+ * This function reads the next non-empty line from the configuration file and
+ * removes comments. The returned string is guaranteed to be null-terminated.
+ */
+static char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+                                 char **_pos)
+{
+       char *pos, *end, *sstart;
+
+       while (fgets(s, size, stream)) {
+               (*line)++;
+               s[size - 1] = '\0';
+               pos = s;
+
+               /* Skip white space from the beginning of line. */
+               while (*pos == ' ' || *pos == '\t' || *pos == '\r')
+                       pos++;
+
+               /* Skip comment lines and empty lines */
+               if (*pos == '#' || *pos == '\n' || *pos == '\0')
+                       continue;
+
+               /*
+                * Remove # comments unless they are within a double quoted
+                * string.
+                */
+               sstart = os_strchr(pos, '"');
+               if (sstart)
+                       sstart = os_strrchr(sstart + 1, '"');
+               if (!sstart)
+                       sstart = pos;
+               end = os_strchr(sstart, '#');
+               if (end)
+                       *end-- = '\0';
+               else
+                       end = pos + os_strlen(pos) - 1;
+
+               /* Remove trailing white space. */
+               while (end > pos &&
+                      (*end == '\n' || *end == ' ' || *end == '\t' ||
+                       *end == '\r'))
+                       *end-- = '\0';
+
+               if (*pos == '\0')
+                       continue;
+
+               if (_pos)
+                       *_pos = pos;
+               return pos;
+       }
+
+       if (_pos)
+               *_pos = NULL;
+       return NULL;
+}
+
+
+static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
+{
+       int errors = 0;
+
+       if (ssid->passphrase) {
+               if (ssid->psk_set) {
+                       wpa_printf(MSG_ERROR, "Line %d: both PSK and "
+                                  "passphrase configured.", line);
+                       errors++;
+               }
+               wpa_config_update_psk(ssid);
+       }
+
+       if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
+                              WPA_KEY_MGMT_PSK_SHA256)) &&
+           !ssid->psk_set) {
+               wpa_printf(MSG_ERROR, "Line %d: WPA-PSK accepted for key "
+                          "management, but no PSK configured.", line);
+               errors++;
+       }
+
+       if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
+           !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+           !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
+               /* Group cipher cannot be stronger than the pairwise cipher. */
+               wpa_printf(MSG_DEBUG, "Line %d: removed CCMP from group cipher"
+                          " list since it was not allowed for pairwise "
+                          "cipher", line);
+               ssid->group_cipher &= ~WPA_CIPHER_CCMP;
+       }
+
+       return errors;
+}
+
+
+static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
+{
+       struct wpa_ssid *ssid;
+       int errors = 0, end = 0;
+       char buf[256], *pos, *pos2;
+
+       wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new network block",
+                  *line);
+       ssid = os_zalloc(sizeof(*ssid));
+       if (ssid == NULL)
+               return NULL;
+       ssid->id = id;
+
+       wpa_config_set_network_defaults(ssid);
+
+       while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+               if (os_strcmp(pos, "}") == 0) {
+                       end = 1;
+                       break;
+               }
+
+               pos2 = os_strchr(pos, '=');
+               if (pos2 == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid SSID line "
+                                  "'%s'.", *line, pos);
+                       errors++;
+                       continue;
+               }
+
+               *pos2++ = '\0';
+               if (*pos2 == '"') {
+                       if (os_strchr(pos2 + 1, '"') == NULL) {
+                               wpa_printf(MSG_ERROR, "Line %d: invalid "
+                                          "quotation '%s'.", *line, pos2);
+                               errors++;
+                               continue;
+                       }
+               }
+
+               if (wpa_config_set(ssid, pos, pos2, *line) < 0)
+                       errors++;
+       }
+
+       if (!end) {
+               wpa_printf(MSG_ERROR, "Line %d: network block was not "
+                          "terminated properly.", *line);
+               errors++;
+       }
+
+       errors += wpa_config_validate_network(ssid, *line);
+
+       if (errors) {
+               wpa_config_free_ssid(ssid);
+               ssid = NULL;
+       }
+
+       return ssid;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static struct wpa_config_blob * wpa_config_read_blob(FILE *f, int *line,
+                                                    const char *name)
+{
+       struct wpa_config_blob *blob;
+       char buf[256], *pos;
+       unsigned char *encoded = NULL, *nencoded;
+       int end = 0;
+       size_t encoded_len = 0, len;
+
+       wpa_printf(MSG_MSGDUMP, "Line: %d - start of a new named blob '%s'",
+                  *line, name);
+
+       while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
+               if (os_strcmp(pos, "}") == 0) {
+                       end = 1;
+                       break;
+               }
+
+               len = os_strlen(pos);
+               nencoded = os_realloc(encoded, encoded_len + len);
+               if (nencoded == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: not enough memory for "
+                                  "blob", *line);
+                       os_free(encoded);
+                       return NULL;
+               }
+               encoded = nencoded;
+               os_memcpy(encoded + encoded_len, pos, len);
+               encoded_len += len;
+       }
+
+       if (!end) {
+               wpa_printf(MSG_ERROR, "Line %d: blob was not terminated "
+                          "properly", *line);
+               os_free(encoded);
+               return NULL;
+       }
+
+       blob = os_zalloc(sizeof(*blob));
+       if (blob == NULL) {
+               os_free(encoded);
+               return NULL;
+       }
+       blob->name = os_strdup(name);
+       blob->data = base64_decode(encoded, encoded_len, &blob->len);
+       os_free(encoded);
+
+       if (blob->name == NULL || blob->data == NULL) {
+               wpa_config_free_blob(blob);
+               return NULL;
+       }
+
+       return blob;
+}
+
+
+static int wpa_config_process_blob(struct wpa_config *config, FILE *f,
+                                  int *line, char *bname)
+{
+       char *name_end;
+       struct wpa_config_blob *blob;
+
+       name_end = os_strchr(bname, '=');
+       if (name_end == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: no blob name terminator",
+                          *line);
+               return -1;
+       }
+       *name_end = '\0';
+
+       blob = wpa_config_read_blob(f, line, bname);
+       if (blob == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: failed to read blob %s",
+                          *line, bname);
+               return -1;
+       }
+       wpa_config_set_blob(config, blob);
+       return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+struct global_parse_data {
+       char *name;
+       int (*parser)(const struct global_parse_data *data,
+                     struct wpa_config *config, int line, const char *value);
+       void *param1, *param2, *param3;
+};
+
+
+static int wpa_config_parse_int(const struct global_parse_data *data,
+                               struct wpa_config *config, int line,
+                               const char *pos)
+{
+       int *dst;
+       dst = (int *) (((u8 *) config) + (long) data->param1);
+       *dst = atoi(pos);
+       wpa_printf(MSG_DEBUG, "%s=%d", data->name, *dst);
+
+       if (data->param2 && *dst < (long) data->param2) {
+               wpa_printf(MSG_ERROR, "Line %d: too small %s (value=%d "
+                          "min_value=%ld)", line, data->name, *dst,
+                          (long) data->param2);
+               *dst = (long) data->param2;
+               return -1;
+       }
+
+       if (data->param3 && *dst > (long) data->param3) {
+               wpa_printf(MSG_ERROR, "Line %d: too large %s (value=%d "
+                          "max_value=%ld)", line, data->name, *dst,
+                          (long) data->param3);
+               *dst = (long) data->param3;
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_config_parse_str(const struct global_parse_data *data,
+                               struct wpa_config *config, int line,
+                               const char *pos)
+{
+       size_t len;
+       char **dst, *tmp;
+
+       len = os_strlen(pos);
+       if (data->param2 && len < (size_t) data->param2) {
+               wpa_printf(MSG_ERROR, "Line %d: too short %s (len=%lu "
+                          "min_len=%ld)", line, data->name,
+                          (unsigned long) len, (long) data->param2);
+               return -1;
+       }
+
+       if (data->param3 && len > (size_t) data->param3) {
+               wpa_printf(MSG_ERROR, "Line %d: too long %s (len=%lu "
+                          "max_len=%ld)", line, data->name,
+                          (unsigned long) len, (long) data->param3);
+               return -1;
+       }
+
+       tmp = os_strdup(pos);
+       if (tmp == NULL)
+               return -1;
+
+       dst = (char **) (((u8 *) config) + (long) data->param1);
+       os_free(*dst);
+       *dst = tmp;
+       wpa_printf(MSG_DEBUG, "%s='%s'", data->name, *dst);
+
+       return 0;
+}
+
+
+static int wpa_config_process_country(const struct global_parse_data *data,
+                                     struct wpa_config *config, int line,
+                                     const char *pos)
+{
+       if (!pos[0] || !pos[1]) {
+               wpa_printf(MSG_DEBUG, "Invalid country set");
+               return -1;
+       }
+       config->country[0] = pos[0];
+       config->country[1] = pos[1];
+       wpa_printf(MSG_DEBUG, "country='%c%c'",
+                  config->country[0], config->country[1]);
+       return 0;
+}
+
+
+static int wpa_config_process_load_dynamic_eap(
+       const struct global_parse_data *data, struct wpa_config *config,
+       int line, const char *so)
+{
+       int ret;
+       wpa_printf(MSG_DEBUG, "load_dynamic_eap=%s", so);
+       ret = eap_peer_method_load(so);
+       if (ret == -2) {
+               wpa_printf(MSG_DEBUG, "This EAP type was already loaded - not "
+                          "reloading.");
+       } else if (ret) {
+               wpa_printf(MSG_ERROR, "Line %d: Failed to load dynamic EAP "
+                          "method '%s'.", line, so);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS
+
+static int wpa_config_process_uuid(const struct global_parse_data *data,
+                                  struct wpa_config *config, int line,
+                                  const char *pos)
+{
+       char buf[40];
+       if (uuid_str2bin(pos, config->uuid)) {
+               wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+               return -1;
+       }
+       uuid_bin2str(config->uuid, buf, sizeof(buf));
+       wpa_printf(MSG_DEBUG, "uuid=%s", buf);
+       return 0;
+}
+
+
+static int wpa_config_process_os_version(const struct global_parse_data *data,
+                                        struct wpa_config *config, int line,
+                                        const char *pos)
+{
+       if (hexstr2bin(pos, config->os_version, 4)) {
+               wpa_printf(MSG_ERROR, "Line %d: invalid os_version", line);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "os_version=%08x",
+                  WPA_GET_BE32(config->os_version));
+       return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_config structure */
+#define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
+
+#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
+#define _INT(f) #f, wpa_config_parse_int, OFFSET(f)
+#define INT(f) _INT(f), NULL, NULL
+#define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
+#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
+#define STR(f) _STR(f), NULL, NULL
+#define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
+
+static const struct global_parse_data global_fields[] = {
+#ifdef CONFIG_CTRL_IFACE
+       { STR(ctrl_interface) },
+       { STR(ctrl_interface_group) } /* deprecated */,
+#endif /* CONFIG_CTRL_IFACE */
+       { INT_RANGE(eapol_version, 1, 2) },
+       { INT(ap_scan) },
+       { INT(fast_reauth) },
+       { STR(opensc_engine_path) },
+       { STR(pkcs11_engine_path) },
+       { STR(pkcs11_module_path) },
+       { STR(driver_param) },
+       { INT(dot11RSNAConfigPMKLifetime) },
+       { INT(dot11RSNAConfigPMKReauthThreshold) },
+       { INT(dot11RSNAConfigSATimeout) },
+#ifndef CONFIG_NO_CONFIG_WRITE
+       { INT(update_config) },
+#endif /* CONFIG_NO_CONFIG_WRITE */
+       { FUNC_NO_VAR(load_dynamic_eap) },
+#ifdef CONFIG_WPS
+       { FUNC(uuid) },
+       { STR_RANGE(device_name, 0, 32) },
+       { STR_RANGE(manufacturer, 0, 64) },
+       { STR_RANGE(model_name, 0, 32) },
+       { STR_RANGE(model_number, 0, 32) },
+       { STR_RANGE(serial_number, 0, 32) },
+       { STR(device_type) },
+       { FUNC(os_version) },
+       { STR(config_methods) },
+       { INT_RANGE(wps_cred_processing, 0, 2) },
+#endif /* CONFIG_WPS */
+       { FUNC(country) },
+       { INT(bss_max_count) },
+       { INT_RANGE(filter_ssids, 0, 1) }
+};
+
+#undef FUNC
+#undef _INT
+#undef INT
+#undef INT_RANGE
+#undef _STR
+#undef STR
+#undef STR_RANGE
+#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+
+
+static int wpa_config_process_global(struct wpa_config *config, char *pos,
+                                    int line)
+{
+       size_t i;
+       int ret = 0;
+
+       for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+               const struct global_parse_data *field = &global_fields[i];
+               size_t flen = os_strlen(field->name);
+               if (os_strncmp(pos, field->name, flen) != 0 ||
+                   pos[flen] != '=')
+                       continue;
+
+               if (field->parser(field, config, line, pos + flen + 1)) {
+                       wpa_printf(MSG_ERROR, "Line %d: failed to "
+                                  "parse '%s'.", line, pos);
+                       ret = -1;
+               }
+               break;
+       }
+       if (i == NUM_GLOBAL_FIELDS) {
+               wpa_printf(MSG_ERROR, "Line %d: unknown global field '%s'.",
+                          line, pos);
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+struct wpa_config * wpa_config_read(const char *name)
+{
+       FILE *f;
+       char buf[256], *pos;
+       int errors = 0, line = 0;
+       struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
+       struct wpa_config *config;
+       int id = 0;
+
+       config = wpa_config_alloc_empty(NULL, NULL);
+       if (config == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
+       f = fopen(name, "r");
+       if (f == NULL) {
+               os_free(config);
+               return NULL;
+       }
+
+       while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
+               if (os_strcmp(pos, "network={") == 0) {
+                       ssid = wpa_config_read_network(f, &line, id++);
+                       if (ssid == NULL) {
+                               wpa_printf(MSG_ERROR, "Line %d: failed to "
+                                          "parse network block.", line);
+                               errors++;
+                               continue;
+                       }
+                       if (head == NULL) {
+                               head = tail = ssid;
+                       } else {
+                               tail->next = ssid;
+                               tail = ssid;
+                       }
+                       if (wpa_config_add_prio_network(config, ssid)) {
+                               wpa_printf(MSG_ERROR, "Line %d: failed to add "
+                                          "network block to priority list.",
+                                          line);
+                               errors++;
+                               continue;
+                       }
+#ifndef CONFIG_NO_CONFIG_BLOBS
+               } else if (os_strncmp(pos, "blob-base64-", 12) == 0) {
+                       if (wpa_config_process_blob(config, f, &line, pos + 12)
+                           < 0) {
+                               errors++;
+                               continue;
+                       }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+               } else if (wpa_config_process_global(config, pos, line) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid configuration "
+                                  "line '%s'.", line, pos);
+                       errors++;
+                       continue;
+               }
+       }
+
+       fclose(f);
+
+       config->ssid = head;
+       wpa_config_debug_dump_networks(config);
+
+       if (errors) {
+               wpa_config_free(config);
+               config = NULL;
+               head = NULL;
+       }
+
+       return config;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+
+static void write_str(FILE *f, const char *field, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, field);
+       if (value == NULL)
+               return;
+       fprintf(f, "\t%s=%s\n", field, value);
+       os_free(value);
+}
+
+
+static void write_int(FILE *f, const char *field, int value, int def)
+{
+       if (value == def)
+               return;
+       fprintf(f, "\t%s=%d\n", field, value);
+}
+
+
+static void write_bssid(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, "bssid");
+       if (value == NULL)
+               return;
+       fprintf(f, "\tbssid=%s\n", value);
+       os_free(value);
+}
+
+
+static void write_psk(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, "psk");
+       if (value == NULL)
+               return;
+       fprintf(f, "\tpsk=%s\n", value);
+       os_free(value);
+}
+
+
+static void write_proto(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->proto == DEFAULT_PROTO)
+               return;
+
+       value = wpa_config_get(ssid, "proto");
+       if (value == NULL)
+               return;
+       if (value[0])
+               fprintf(f, "\tproto=%s\n", value);
+       os_free(value);
+}
+
+
+static void write_key_mgmt(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
+               return;
+
+       value = wpa_config_get(ssid, "key_mgmt");
+       if (value == NULL)
+               return;
+       if (value[0])
+               fprintf(f, "\tkey_mgmt=%s\n", value);
+       os_free(value);
+}
+
+
+static void write_pairwise(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
+               return;
+
+       value = wpa_config_get(ssid, "pairwise");
+       if (value == NULL)
+               return;
+       if (value[0])
+               fprintf(f, "\tpairwise=%s\n", value);
+       os_free(value);
+}
+
+
+static void write_group(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->group_cipher == DEFAULT_GROUP)
+               return;
+
+       value = wpa_config_get(ssid, "group");
+       if (value == NULL)
+               return;
+       if (value[0])
+               fprintf(f, "\tgroup=%s\n", value);
+       os_free(value);
+}
+
+
+static void write_auth_alg(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->auth_alg == 0)
+               return;
+
+       value = wpa_config_get(ssid, "auth_alg");
+       if (value == NULL)
+               return;
+       if (value[0])
+               fprintf(f, "\tauth_alg=%s\n", value);
+       os_free(value);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void write_eap(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       value = wpa_config_get(ssid, "eap");
+       if (value == NULL)
+               return;
+
+       if (value[0])
+               fprintf(f, "\teap=%s\n", value);
+       os_free(value);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
+{
+       char field[20], *value;
+       int res;
+
+       res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
+       if (res < 0 || (size_t) res >= sizeof(field))
+               return;
+       value = wpa_config_get(ssid, field);
+       if (value) {
+               fprintf(f, "\t%s=%s\n", field, value);
+               os_free(value);
+       }
+}
+
+
+static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
+{
+       int i;
+
+#define STR(t) write_str(f, #t, ssid)
+#define INT(t) write_int(f, #t, ssid->t, 0)
+#define INTe(t) write_int(f, #t, ssid->eap.t, 0)
+#define INT_DEF(t, def) write_int(f, #t, ssid->t, def)
+#define INT_DEFe(t, def) write_int(f, #t, ssid->eap.t, def)
+
+       STR(ssid);
+       INT(scan_ssid);
+       write_bssid(f, ssid);
+       write_psk(f, ssid);
+       write_proto(f, ssid);
+       write_key_mgmt(f, ssid);
+       write_pairwise(f, ssid);
+       write_group(f, ssid);
+       write_auth_alg(f, ssid);
+#ifdef IEEE8021X_EAPOL
+       write_eap(f, ssid);
+       STR(identity);
+       STR(anonymous_identity);
+       STR(password);
+       STR(ca_cert);
+       STR(ca_path);
+       STR(client_cert);
+       STR(private_key);
+       STR(private_key_passwd);
+       STR(dh_file);
+       STR(subject_match);
+       STR(altsubject_match);
+       STR(ca_cert2);
+       STR(ca_path2);
+       STR(client_cert2);
+       STR(private_key2);
+       STR(private_key2_passwd);
+       STR(dh_file2);
+       STR(subject_match2);
+       STR(altsubject_match2);
+       STR(phase1);
+       STR(phase2);
+       STR(pcsc);
+       STR(pin);
+       STR(engine_id);
+       STR(key_id);
+       STR(cert_id);
+       STR(ca_cert_id);
+       STR(key2_id);
+       STR(pin2);
+       STR(engine2_id);
+       STR(cert2_id);
+       STR(ca_cert2_id);
+       INTe(engine);
+       INTe(engine2);
+       INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+#endif /* IEEE8021X_EAPOL */
+       for (i = 0; i < 4; i++)
+               write_wep_key(f, i, ssid);
+       INT(wep_tx_keyidx);
+       INT(priority);
+#ifdef IEEE8021X_EAPOL
+       INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
+       STR(pac_file);
+       INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+#endif /* IEEE8021X_EAPOL */
+       INT(mode);
+       INT(proactive_key_caching);
+       INT(disabled);
+       INT(peerkey);
+#ifdef CONFIG_IEEE80211W
+       INT(ieee80211w);
+#endif /* CONFIG_IEEE80211W */
+       STR(id_str);
+
+#undef STR
+#undef INT
+#undef INT_DEF
+}
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpa_config_write_blob(FILE *f, struct wpa_config_blob *blob)
+{
+       unsigned char *encoded;
+
+       encoded = base64_encode(blob->data, blob->len, NULL);
+       if (encoded == NULL)
+               return -1;
+
+       fprintf(f, "\nblob-base64-%s={\n%s}\n", blob->name, encoded);
+       os_free(encoded);
+       return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static void wpa_config_write_global(FILE *f, struct wpa_config *config)
+{
+#ifdef CONFIG_CTRL_IFACE
+       if (config->ctrl_interface)
+               fprintf(f, "ctrl_interface=%s\n", config->ctrl_interface);
+       if (config->ctrl_interface_group)
+               fprintf(f, "ctrl_interface_group=%s\n",
+                       config->ctrl_interface_group);
+#endif /* CONFIG_CTRL_IFACE */
+       if (config->eapol_version != DEFAULT_EAPOL_VERSION)
+               fprintf(f, "eapol_version=%d\n", config->eapol_version);
+       if (config->ap_scan != DEFAULT_AP_SCAN)
+               fprintf(f, "ap_scan=%d\n", config->ap_scan);
+       if (config->fast_reauth != DEFAULT_FAST_REAUTH)
+               fprintf(f, "fast_reauth=%d\n", config->fast_reauth);
+       if (config->opensc_engine_path)
+               fprintf(f, "opensc_engine_path=%s\n",
+                       config->opensc_engine_path);
+       if (config->pkcs11_engine_path)
+               fprintf(f, "pkcs11_engine_path=%s\n",
+                       config->pkcs11_engine_path);
+       if (config->pkcs11_module_path)
+               fprintf(f, "pkcs11_module_path=%s\n",
+                       config->pkcs11_module_path);
+       if (config->driver_param)
+               fprintf(f, "driver_param=%s\n", config->driver_param);
+       if (config->dot11RSNAConfigPMKLifetime)
+               fprintf(f, "dot11RSNAConfigPMKLifetime=%d\n",
+                       config->dot11RSNAConfigPMKLifetime);
+       if (config->dot11RSNAConfigPMKReauthThreshold)
+               fprintf(f, "dot11RSNAConfigPMKReauthThreshold=%d\n",
+                       config->dot11RSNAConfigPMKReauthThreshold);
+       if (config->dot11RSNAConfigSATimeout)
+               fprintf(f, "dot11RSNAConfigSATimeout=%d\n",
+                       config->dot11RSNAConfigSATimeout);
+       if (config->update_config)
+               fprintf(f, "update_config=%d\n", config->update_config);
+#ifdef CONFIG_WPS
+       if (!is_nil_uuid(config->uuid)) {
+               char buf[40];
+               uuid_bin2str(config->uuid, buf, sizeof(buf));
+               fprintf(f, "uuid=%s\n", buf);
+       }
+       if (config->device_name)
+               fprintf(f, "device_name=%s\n", config->device_name);
+       if (config->manufacturer)
+               fprintf(f, "manufacturer=%s\n", config->manufacturer);
+       if (config->model_name)
+               fprintf(f, "model_name=%s\n", config->model_name);
+       if (config->model_number)
+               fprintf(f, "model_number=%s\n", config->model_number);
+       if (config->serial_number)
+               fprintf(f, "serial_number=%s\n", config->serial_number);
+       if (config->device_type)
+               fprintf(f, "device_type=%s\n", config->device_type);
+       if (WPA_GET_BE32(config->os_version))
+               fprintf(f, "os_version=%08x\n",
+                       WPA_GET_BE32(config->os_version));
+       if (config->config_methods)
+               fprintf(f, "config_methods=%s\n", config->config_methods);
+       if (config->wps_cred_processing)
+               fprintf(f, "wps_cred_processing=%d\n",
+                       config->wps_cred_processing);
+#endif /* CONFIG_WPS */
+       if (config->country[0] && config->country[1]) {
+               fprintf(f, "country=%c%c\n",
+                       config->country[0], config->country[1]);
+       }
+       if (config->bss_max_count != DEFAULT_BSS_MAX_COUNT)
+               fprintf(f, "bss_max_count=%u\n", config->bss_max_count);
+       if (config->filter_ssids)
+               fprintf(f, "filter_ssids=%d\n", config->filter_ssids);
+}
+
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_WRITE
+       FILE *f;
+       struct wpa_ssid *ssid;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       struct wpa_config_blob *blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+       f = fopen(name, "w");
+       if (f == NULL) {
+               wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name);
+               return -1;
+       }
+
+       wpa_config_write_global(f, config);
+
+       for (ssid = config->ssid; ssid; ssid = ssid->next) {
+               if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+                       continue; /* do not save temporary WPS networks */
+               fprintf(f, "\nnetwork={\n");
+               wpa_config_write_network(f, ssid);
+               fprintf(f, "}\n");
+       }
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       for (blob = config->blobs; blob; blob = blob->next) {
+               ret = wpa_config_write_blob(f, blob);
+               if (ret)
+                       break;
+       }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+       fclose(f);
+
+       wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully",
+                  name, ret ? "un" : "");
+       return ret;
+#else /* CONFIG_NO_CONFIG_WRITE */
+       return -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
diff --git a/wpa_supplicant/config_none.c b/wpa_supplicant/config_none.c
new file mode 100644 (file)
index 0000000..2e9ccc0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * WPA Supplicant / Configuration backend: empty starting point
+ * 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.
+ *
+ * This file implements dummy example of a configuration backend. None of the
+ * functions are actually implemented so this can be used as a simple
+ * compilation test or a starting point for a new configuration backend.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "config.h"
+#include "base64.h"
+
+
+struct wpa_config * wpa_config_read(const char *name)
+{
+       struct wpa_config *config;
+
+       config = wpa_config_alloc_empty(NULL, NULL);
+       if (config == NULL)
+               return NULL;
+       /* TODO: fill in configuration data */
+       return config;
+}
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+       struct wpa_ssid *ssid;
+       struct wpa_config_blob *blob;
+
+       wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+       /* TODO: write global config parameters */
+
+
+       for (ssid = config->ssid; ssid; ssid = ssid->next) {
+               /* TODO: write networks */
+       }
+
+       for (blob = config->blobs; blob; blob = blob->next) {
+               /* TODO: write blobs */
+       }
+
+       return 0;
+}
diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h
new file mode 100644 (file)
index 0000000..25e87aa
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * WPA Supplicant / Network configuration structures
+ * Copyright (c) 2003-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 CONFIG_SSID_H
+#define CONFIG_SSID_H
+
+#include "common/defs.h"
+#include "eap_peer/eap_config.h"
+
+#define MAX_SSID_LEN 32
+
+
+#define DEFAULT_EAP_WORKAROUND ((unsigned int) -1)
+#define DEFAULT_EAPOL_FLAGS (EAPOL_FLAG_REQUIRE_KEY_UNICAST | \
+                            EAPOL_FLAG_REQUIRE_KEY_BROADCAST)
+#define DEFAULT_PROTO (WPA_PROTO_WPA | WPA_PROTO_RSN)
+#define DEFAULT_KEY_MGMT (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X)
+#define DEFAULT_PAIRWISE (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP)
+#define DEFAULT_GROUP (WPA_CIPHER_CCMP | WPA_CIPHER_TKIP | \
+                      WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)
+#define DEFAULT_FRAGMENT_SIZE 1398
+
+/**
+ * struct wpa_ssid - Network configuration data
+ *
+ * This structure includes all the configuration variables for a network. This
+ * data is included in the per-interface configuration data as an element of
+ * the network list, struct wpa_config::ssid. Each network block in the
+ * configuration is mapped to a struct wpa_ssid instance.
+ */
+struct wpa_ssid {
+       /**
+        * next - Next network in global list
+        *
+        * This pointer can be used to iterate over all networks. The head of
+        * this list is stored in the ssid field of struct wpa_config.
+        */
+       struct wpa_ssid *next;
+
+       /**
+        * pnext - Next network in per-priority list
+        *
+        * This pointer can be used to iterate over all networks in the same
+        * priority class. The heads of these list are stored in the pssid
+        * fields of struct wpa_config.
+        */
+       struct wpa_ssid *pnext;
+
+       /**
+        * id - Unique id for the network
+        *
+        * This identifier is used as a unique identifier for each network
+        * block when using the control interface. Each network is allocated an
+        * id when it is being created, either when reading the configuration
+        * file or when a new network is added through the control interface.
+        */
+       int id;
+
+       /**
+        * priority - Priority group
+        *
+        * By default, all networks will get same priority group (0). If some
+        * of the networks are more desirable, this field can be used to change
+        * the order in which wpa_supplicant goes through the networks when
+        * selecting a BSS. The priority groups will be iterated in decreasing
+        * priority (i.e., the larger the priority value, the sooner the
+        * network is matched against the scan results). Within each priority
+        * group, networks will be selected based on security policy, signal
+        * strength, etc.
+        *
+        * Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are
+        * not using this priority to select the order for scanning. Instead,
+        * they try the networks in the order that used in the configuration
+        * file.
+        */
+       int priority;
+
+       /**
+        * ssid - Service set identifier (network name)
+        *
+        * This is the SSID for the network. For wireless interfaces, this is
+        * used to select which network will be used. If set to %NULL (or
+        * ssid_len=0), any SSID can be used. For wired interfaces, this must
+        * be set to %NULL. Note: SSID may contain any characters, even nul
+        * (ASCII 0) and as such, this should not be assumed to be a nul
+        * terminated string. ssid_len defines how many characters are valid
+        * and the ssid field is not guaranteed to be nul terminated.
+        */
+       u8 *ssid;
+
+       /**
+        * ssid_len - Length of the SSID
+        */
+       size_t ssid_len;
+
+       /**
+        * bssid - BSSID
+        *
+        * If set, this network block is used only when associating with the AP
+        * using the configured BSSID
+        */
+       u8 bssid[ETH_ALEN];
+
+       /**
+        * bssid_set - Whether BSSID is configured for this network
+        */
+       int bssid_set;
+
+       /**
+        * psk - WPA pre-shared key (256 bits)
+        */
+       u8 psk[32];
+
+       /**
+        * psk_set - Whether PSK field is configured
+        */
+       int psk_set;
+
+       /**
+        * passphrase - WPA ASCII passphrase
+        *
+        * If this is set, psk will be generated using the SSID and passphrase
+        * configured for the network. ASCII passphrase must be between 8 and
+        * 63 characters (inclusive).
+        */
+       char *passphrase;
+
+       /**
+        * pairwise_cipher - Bitfield of allowed pairwise ciphers, WPA_CIPHER_*
+        */
+       int pairwise_cipher;
+
+       /**
+        * group_cipher - Bitfield of allowed group ciphers, WPA_CIPHER_*
+        */
+       int group_cipher;
+
+       /**
+        * key_mgmt - Bitfield of allowed key management protocols
+        *
+        * WPA_KEY_MGMT_*
+        */
+       int key_mgmt;
+
+       /**
+        * proto - Bitfield of allowed protocols, WPA_PROTO_*
+        */
+       int proto;
+
+       /**
+        * auth_alg -  Bitfield of allowed authentication algorithms
+        *
+        * WPA_AUTH_ALG_*
+        */
+       int auth_alg;
+
+       /**
+        * scan_ssid - Scan this SSID with Probe Requests
+        *
+        * scan_ssid can be used to scan for APs using hidden SSIDs.
+        * Note: Many drivers do not support this. ap_mode=2 can be used with
+        * such drivers to use hidden SSIDs.
+        */
+       int scan_ssid;
+
+#ifdef IEEE8021X_EAPOL
+#define EAPOL_FLAG_REQUIRE_KEY_UNICAST BIT(0)
+#define EAPOL_FLAG_REQUIRE_KEY_BROADCAST BIT(1)
+       /**
+        * eapol_flags - Bit field of IEEE 802.1X/EAPOL options (EAPOL_FLAG_*)
+        */
+       int eapol_flags;
+
+       /**
+        * eap - EAP peer configuration for this network
+        */
+       struct eap_peer_config eap;
+#endif /* IEEE8021X_EAPOL */
+
+#define NUM_WEP_KEYS 4
+#define MAX_WEP_KEY_LEN 16
+       /**
+        * wep_key - WEP keys
+        */
+       u8 wep_key[NUM_WEP_KEYS][MAX_WEP_KEY_LEN];
+
+       /**
+        * wep_key_len - WEP key lengths
+        */
+       size_t wep_key_len[NUM_WEP_KEYS];
+
+       /**
+        * wep_tx_keyidx - Default key index for TX frames using WEP
+        */
+       int wep_tx_keyidx;
+
+       /**
+        * proactive_key_caching - Enable proactive key caching
+        *
+        * This field can be used to enable proactive key caching which is also
+        * known as opportunistic PMKSA caching for WPA2. This is disabled (0)
+        * by default. Enable by setting this to 1.
+        *
+        * Proactive key caching is used to make supplicant assume that the APs
+        * are using the same PMK and generate PMKSA cache entries without
+        * doing RSN pre-authentication. This requires support from the AP side
+        * and is normally used with wireless switches that co-locate the
+        * authenticator.
+        */
+       int proactive_key_caching;
+
+       /**
+        * mixed_cell - Whether mixed cells are allowed
+        *
+        * This option can be used to configure whether so called mixed cells,
+        * i.e., networks that use both plaintext and encryption in the same
+        * SSID, are allowed. This is disabled (0) by default. Enable by
+        * setting this to 1.
+        */
+       int mixed_cell;
+
+#ifdef IEEE8021X_EAPOL
+
+       /**
+        * leap - Number of EAP methods using LEAP
+        *
+        * This field should be set to 1 if LEAP is enabled. This is used to
+        * select IEEE 802.11 authentication algorithm.
+        */
+       int leap;
+
+       /**
+        * non_leap - Number of EAP methods not using LEAP
+        *
+        * This field should be set to >0 if any EAP method other than LEAP is
+        * enabled. This is used to select IEEE 802.11 authentication
+        * algorithm.
+        */
+       int non_leap;
+
+       /**
+        * eap_workaround - EAP workarounds enabled
+        *
+        * wpa_supplicant supports number of "EAP workarounds" to work around
+        * interoperability issues with incorrectly behaving authentication
+        * servers. This is recommended to be enabled by default because some
+        * of the issues are present in large number of authentication servers.
+        *
+        * Strict EAP conformance mode can be configured by disabling
+        * workarounds with eap_workaround = 0.
+        */
+       unsigned int eap_workaround;
+
+#endif /* IEEE8021X_EAPOL */
+
+       /**
+        * mode - IEEE 802.11 operation mode (Infrastucture/IBSS)
+        *
+        * 0 = infrastructure (Managed) mode, i.e., associate with an AP.
+        *
+        * 1 = IBSS (ad-hoc, peer-to-peer)
+        *
+        * 2 = AP (access point)
+        *
+        * Note: IBSS can only be used with key_mgmt NONE (plaintext and
+        * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
+        * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
+        * following network block options: proto=WPA, key_mgmt=WPA-NONE,
+        * pairwise=NONE, group=TKIP (or CCMP, but not both), and psk must also
+        * be set (either directly or using ASCII passphrase).
+        */
+       enum wpas_mode {
+               WPAS_MODE_INFRA = 0,
+               WPAS_MODE_IBSS = 1,
+               WPAS_MODE_AP = 2,
+       } mode;
+
+       /**
+        * disabled - Whether this network is currently disabled
+        *
+        * 0 = this network can be used (default).
+        * 1 = this network block is disabled (can be enabled through
+        * ctrl_iface, e.g., with wpa_cli or wpa_gui).
+        */
+       int disabled;
+
+       /**
+        * peerkey -  Whether PeerKey handshake for direct links is allowed
+        *
+        * This is only used when both RSN/WPA2 and IEEE 802.11e (QoS) are
+        * enabled.
+        *
+        * 0 = disabled (default)
+        * 1 = enabled
+        */
+       int peerkey;
+
+       /**
+        * id_str - Network identifier string for external scripts
+        *
+        * This value is passed to external ctrl_iface monitors in
+        * WPA_EVENT_CONNECTED event and wpa_cli sets this as WPA_ID_STR
+        * environment variable for action scripts.
+        */
+       char *id_str;
+
+#ifdef CONFIG_IEEE80211W
+       /**
+        * ieee80211w - Whether management frame protection is enabled
+        *
+        * This value is used to configure policy for management frame
+        * protection (IEEE 802.11w). 0 = disabled, 1 = optional, 2 = required.
+        */
+       enum mfp_options ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+
+       /**
+        * frequency - Channel frequency in megahertz (MHz) for IBSS
+        *
+        * This value is used to configure the initial channel for IBSS (adhoc)
+        * networks, e.g., 2412 = IEEE 802.11b/g channel 1. It is ignored in
+        * the infrastructure mode. In addition, this value is only used by the
+        * station that creates the IBSS. If an IBSS network with the
+        * configured SSID is already present, the frequency of the network
+        * will be used instead of this configured value.
+        */
+       int frequency;
+
+       /**
+        * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
+        *
+        * This value can be used to enforce rekeying of PTK to mitigate some
+        * attacks against TKIP deficiencies.
+        */
+       int wpa_ptk_rekey;
+
+       /**
+        * scan_freq - Array of frequencies to scan or %NULL for all
+        *
+        * This is an optional zero-terminated array of frequencies in
+        * megahertz (MHz) to include in scan requests when searching for this
+        * network. This can be used to speed up scanning when the network is
+        * known to not use all possible channels.
+        */
+       int *scan_freq;
+
+       /**
+        * bgscan - Background scan and roaming parameters or %NULL if none
+        *
+        * This is an optional set of parameters for background scanning and
+        * roaming within a network (ESS) in following format:
+        * <bgscan module name>:<module parameters>
+        */
+       char *bgscan;
+
+       /**
+        * freq_list - Array of allowed frequencies or %NULL for all
+        *
+        * This is an optional zero-terminated array of frequencies in
+        * megahertz (MHz) to allow for selecting the BSS. If set, scan results
+        * that do not match any of the specified frequencies are not
+        * considered when selecting a BSS.
+        */
+       int *freq_list;
+};
+
+#endif /* CONFIG_SSID_H */
diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c
new file mode 100644 (file)
index 0000000..9a7825a
--- /dev/null
@@ -0,0 +1,995 @@
+/*
+ * WPA Supplicant / Configuration backend: Windows registry
+ * Copyright (c) 2003-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.
+ *
+ * This file implements a configuration backend for Windows registry. All the
+ * configuration information is stored in the registry and the format for
+ * network configuration fields is same as described in the sample
+ * configuration file, wpa_supplicant.conf.
+ *
+ * Configuration data is in
+ * \a HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant\\configs
+ * key. Each configuration profile has its own key under this. In terms of text
+ * files, each profile would map to a separate text file with possibly multiple
+ * networks. Under each profile, there is a networks key that lists all
+ * networks as a subkey. Each network has set of values in the same way as
+ * network block in the configuration file. In addition, blobs subkey has
+ * possible blobs as values.
+ *
+ * Example network configuration block:
+ * \verbatim
+HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000
+   ssid="example"
+   key_mgmt=WPA-PSK
+\endverbatim
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "uuid.h"
+#include "config.h"
+
+#ifndef WPA_KEY_ROOT
+#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
+#endif
+#ifndef WPA_KEY_PREFIX
+#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
+#endif
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+static int wpa_config_read_blobs(struct wpa_config *config, HKEY hk)
+{
+       struct wpa_config_blob *blob;
+       int errors = 0;
+       HKEY bhk;
+       LONG ret;
+       DWORD i;
+
+       ret = RegOpenKeyEx(hk, TEXT("blobs"), 0, KEY_QUERY_VALUE, &bhk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config "
+                          "blobs key");
+               return 0; /* assume no blobs */
+       }
+
+       for (i = 0; ; i++) {
+#define TNAMELEN 255
+               TCHAR name[TNAMELEN];
+               char data[4096];
+               DWORD namelen, datalen, type;
+
+               namelen = TNAMELEN;
+               datalen = sizeof(data);
+               ret = RegEnumValue(bhk, i, name, &namelen, NULL, &type,
+                                  (LPBYTE) data, &datalen);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "RegEnumValue failed: 0x%x",
+                                  (unsigned int) ret);
+                       break;
+               }
+
+               if (namelen >= TNAMELEN)
+                       namelen = TNAMELEN - 1;
+               name[namelen] = TEXT('\0');
+               wpa_unicode2ascii_inplace(name);
+
+               if (datalen >= sizeof(data))
+                       datalen = sizeof(data) - 1;
+
+               wpa_printf(MSG_MSGDUMP, "blob %d: field='%s' len %d",
+                          (int) i, name, (int) datalen);
+
+               blob = os_zalloc(sizeof(*blob));
+               if (blob == NULL) {
+                       errors++;
+                       break;
+               }
+               blob->name = os_strdup((char *) name);
+               blob->data = os_malloc(datalen);
+               if (blob->name == NULL || blob->data == NULL) {
+                       wpa_config_free_blob(blob);
+                       errors++;
+                       break;
+               }
+               os_memcpy(blob->data, data, datalen);
+               blob->len = datalen;
+
+               wpa_config_set_blob(config, blob);
+       }
+
+       RegCloseKey(bhk);
+
+       return errors ? -1 : 0;
+}
+
+
+static int wpa_config_read_reg_dword(HKEY hk, const TCHAR *name, int *_val)
+{
+       DWORD val, buflen;
+       LONG ret;
+
+       buflen = sizeof(val);
+       ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) &val, &buflen);
+       if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+               wpa_printf(MSG_DEBUG, TSTR "=%d", name, (int) val);
+               *_val = val;
+               return 0;
+       }
+
+       return -1;
+}
+
+
+static char * wpa_config_read_reg_string(HKEY hk, const TCHAR *name)
+{
+       DWORD buflen;
+       LONG ret;
+       TCHAR *val;
+
+       buflen = 0;
+       ret = RegQueryValueEx(hk, name, NULL, NULL, NULL, &buflen);
+       if (ret != ERROR_SUCCESS)
+               return NULL;
+       val = os_malloc(buflen);
+       if (val == NULL)
+               return NULL;
+
+       ret = RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE) val, &buflen);
+       if (ret != ERROR_SUCCESS) {
+               os_free(val);
+               return NULL;
+       }
+
+       wpa_unicode2ascii_inplace(val);
+       wpa_printf(MSG_DEBUG, TSTR "=%s", name, (char *) val);
+       return (char *) val;
+}
+
+
+#ifdef CONFIG_WPS
+static int wpa_config_read_global_uuid(struct wpa_config *config, HKEY hk)
+{
+       char *str;
+       int ret = 0;
+
+       str = wpa_config_read_reg_string(hk, TEXT("uuid"));
+       if (str == NULL)
+               return 0;
+
+       if (uuid_str2bin(str, config->uuid))
+               ret = -1;
+
+       os_free(str);
+
+       return ret;
+}
+
+
+static int wpa_config_read_global_os_version(struct wpa_config *config,
+                                            HKEY hk)
+{
+       char *str;
+       int ret = 0;
+
+       str = wpa_config_read_reg_string(hk, TEXT("os_version"));
+       if (str == NULL)
+               return 0;
+
+       if (hexstr2bin(str, config->os_version, 4))
+               ret = -1;
+
+       os_free(str);
+
+       return ret;
+}
+#endif /* CONFIG_WPS */
+
+
+static int wpa_config_read_global(struct wpa_config *config, HKEY hk)
+{
+       int errors = 0;
+
+       wpa_config_read_reg_dword(hk, TEXT("ap_scan"), &config->ap_scan);
+       wpa_config_read_reg_dword(hk, TEXT("fast_reauth"),
+                                 &config->fast_reauth);
+       wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"),
+                                 (int *) &config->dot11RSNAConfigPMKLifetime);
+       wpa_config_read_reg_dword(hk,
+                                 TEXT("dot11RSNAConfigPMKReauthThreshold"),
+                                 (int *)
+                                 &config->dot11RSNAConfigPMKReauthThreshold);
+       wpa_config_read_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"),
+                                 (int *) &config->dot11RSNAConfigSATimeout);
+       wpa_config_read_reg_dword(hk, TEXT("update_config"),
+                                 &config->update_config);
+
+       if (wpa_config_read_reg_dword(hk, TEXT("eapol_version"),
+                                     &config->eapol_version) == 0) {
+               if (config->eapol_version < 1 ||
+                   config->eapol_version > 2) {
+                       wpa_printf(MSG_ERROR, "Invalid EAPOL version (%d)",
+                                  config->eapol_version);
+                       errors++;
+               }
+       }
+
+       config->ctrl_interface = wpa_config_read_reg_string(
+               hk, TEXT("ctrl_interface"));
+
+#ifdef CONFIG_WPS
+       if (wpa_config_read_global_uuid(config, hk))
+               errors++;
+       config->device_name = wpa_config_read_reg_string(
+               hk, TEXT("device_name"));
+       config->manufacturer = wpa_config_read_reg_string(
+               hk, TEXT("manufacturer"));
+       config->model_name = wpa_config_read_reg_string(
+               hk, TEXT("model_name"));
+       config->serial_number = wpa_config_read_reg_string(
+               hk, TEXT("serial_number"));
+       config->device_type = wpa_config_read_reg_string(
+               hk, TEXT("device_type"));
+       config->config_methods = wpa_config_read_reg_string(
+               hk, TEXT("config_methods"));
+       if (wpa_config_read_global_os_version(config, hk))
+               errors++;
+       wpa_config_read_reg_dword(hk, TEXT("wps_cred_processing"),
+                                 &config->wps_cred_processing);
+#endif /* CONFIG_WPS */
+
+       wpa_config_read_reg_dword(hk, TEXT("bss_max_count"),
+                                 (int *) &config->bss_max_count);
+       wpa_config_read_reg_dword(hk, TEXT("filter_ssids"),
+                                 &config->filter_ssids);
+
+       return errors ? -1 : 0;
+}
+
+
+static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw,
+                                                int id)
+{
+       HKEY nhk;
+       LONG ret;
+       DWORD i;
+       struct wpa_ssid *ssid;
+       int errors = 0;
+
+       ret = RegOpenKeyEx(hk, netw, 0, KEY_QUERY_VALUE, &nhk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "Could not open wpa_supplicant config "
+                          "network '" TSTR "'", netw);
+               return NULL;
+       }
+
+       wpa_printf(MSG_MSGDUMP, "Start of a new network '" TSTR "'", netw);
+       ssid = os_zalloc(sizeof(*ssid));
+       if (ssid == NULL) {
+               RegCloseKey(nhk);
+               return NULL;
+       }
+       ssid->id = id;
+
+       wpa_config_set_network_defaults(ssid);
+
+       for (i = 0; ; i++) {
+               TCHAR name[255], data[1024];
+               DWORD namelen, datalen, type;
+
+               namelen = 255;
+               datalen = sizeof(data);
+               ret = RegEnumValue(nhk, i, name, &namelen, NULL, &type,
+                                  (LPBYTE) data, &datalen);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_ERROR, "RegEnumValue failed: 0x%x",
+                                  (unsigned int) ret);
+                       break;
+               }
+
+               if (namelen >= 255)
+                       namelen = 255 - 1;
+               name[namelen] = TEXT('\0');
+
+               if (datalen >= 1024)
+                       datalen = 1024 - 1;
+               data[datalen] = TEXT('\0');
+
+               wpa_unicode2ascii_inplace(name);
+               wpa_unicode2ascii_inplace(data);
+               if (wpa_config_set(ssid, (char *) name, (char *) data, 0) < 0)
+                       errors++;
+       }
+
+       RegCloseKey(nhk);
+
+       if (ssid->passphrase) {
+               if (ssid->psk_set) {
+                       wpa_printf(MSG_ERROR, "Both PSK and passphrase "
+                                  "configured for network '" TSTR "'.", netw);
+                       errors++;
+               }
+               wpa_config_update_psk(ssid);
+       }
+
+       if ((ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK |
+                              WPA_KEY_MGMT_PSK_SHA256)) &&
+           !ssid->psk_set) {
+               wpa_printf(MSG_ERROR, "WPA-PSK accepted for key management, "
+                          "but no PSK configured for network '" TSTR "'.",
+                          netw);
+               errors++;
+       }
+
+       if ((ssid->group_cipher & WPA_CIPHER_CCMP) &&
+           !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+           !(ssid->pairwise_cipher & WPA_CIPHER_NONE)) {
+               /* Group cipher cannot be stronger than the pairwise cipher. */
+               wpa_printf(MSG_DEBUG, "Removed CCMP from group cipher "
+                          "list since it was not allowed for pairwise "
+                          "cipher for network '" TSTR "'.", netw);
+               ssid->group_cipher &= ~WPA_CIPHER_CCMP;
+       }
+
+       if (errors) {
+               wpa_config_free_ssid(ssid);
+               ssid = NULL;
+       }
+
+       return ssid;
+}
+
+
+static int wpa_config_read_networks(struct wpa_config *config, HKEY hk)
+{
+       HKEY nhk;
+       struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
+       int errors = 0;
+       LONG ret;
+       DWORD i;
+
+       ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_ENUMERATE_SUB_KEYS,
+                          &nhk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "Could not open wpa_supplicant networks "
+                          "registry key");
+               return -1;
+       }
+
+       for (i = 0; ; i++) {
+               TCHAR name[255];
+               DWORD namelen;
+
+               namelen = 255;
+               ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL,
+                                  NULL);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x",
+                                  (unsigned int) ret);
+                       break;
+               }
+
+               if (namelen >= 255)
+                       namelen = 255 - 1;
+               name[namelen] = '\0';
+
+               ssid = wpa_config_read_network(nhk, name, i);
+               if (ssid == NULL) {
+                       wpa_printf(MSG_ERROR, "Failed to parse network "
+                                  "profile '%s'.", name);
+                       errors++;
+                       continue;
+               }
+               if (head == NULL) {
+                       head = tail = ssid;
+               } else {
+                       tail->next = ssid;
+                       tail = ssid;
+               }
+               if (wpa_config_add_prio_network(config, ssid)) {
+                       wpa_printf(MSG_ERROR, "Failed to add network profile "
+                                  "'%s' to priority list.", name);
+                       errors++;
+                       continue;
+               }
+       }
+
+       RegCloseKey(nhk);
+
+       config->ssid = head;
+
+       return errors ? -1 : 0;
+}
+
+
+struct wpa_config * wpa_config_read(const char *name)
+{
+       TCHAR buf[256];
+       int errors = 0;
+       struct wpa_config *config;
+       HKEY hk;
+       LONG ret;
+
+       config = wpa_config_alloc_empty(NULL, NULL);
+       if (config == NULL)
+               return NULL;
+       wpa_printf(MSG_DEBUG, "Reading configuration profile '%s'", name);
+
+#ifdef UNICODE
+       _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name);
+#else /* UNICODE */
+       os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name);
+#endif /* UNICODE */
+
+       ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_QUERY_VALUE, &hk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "Could not open wpa_supplicant "
+                          "configuration registry HKLM\\" TSTR, buf);
+               os_free(config);
+               return NULL;
+       }
+
+       if (wpa_config_read_global(config, hk))
+               errors++;
+
+       if (wpa_config_read_networks(config, hk))
+               errors++;
+
+       if (wpa_config_read_blobs(config, hk))
+               errors++;
+
+       wpa_config_debug_dump_networks(config);
+
+       RegCloseKey(hk);
+
+       if (errors) {
+               wpa_config_free(config);
+               config = NULL;
+       }
+
+       return config;
+}
+
+
+static int wpa_config_write_reg_dword(HKEY hk, const TCHAR *name, int val,
+                                     int def)
+{
+       LONG ret;
+       DWORD _val = val;
+
+       if (val == def) {
+               RegDeleteValue(hk, name);
+               return 0;
+       }
+
+       ret = RegSetValueEx(hk, name, 0, REG_DWORD, (LPBYTE) &_val,
+                           sizeof(_val));
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WINREG: Failed to set %s=%d: error %d",
+                          name, val, (int) GetLastError());
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_config_write_reg_string(HKEY hk, const char *name,
+                                      const char *val)
+{
+       LONG ret;
+       TCHAR *_name, *_val;
+
+       _name = wpa_strdup_tchar(name);
+       if (_name == NULL)
+               return -1;
+
+       if (val == NULL) {
+               RegDeleteValue(hk, _name);
+               os_free(_name);
+               return 0;
+       }
+
+       _val = wpa_strdup_tchar(val);
+       if (_val == NULL) {
+               os_free(_name);
+               return -1;
+       }
+       ret = RegSetValueEx(hk, _name, 0, REG_SZ, (BYTE *) _val,
+                           (os_strlen(val) + 1) * sizeof(TCHAR));
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WINREG: Failed to set %s='%s': "
+                          "error %d", name, val, (int) GetLastError());
+               os_free(_name);
+               os_free(_val);
+               return -1;
+       }
+
+       os_free(_name);
+       os_free(_val);
+       return 0;
+}
+
+
+static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
+{
+#ifdef CONFIG_CTRL_IFACE
+       wpa_config_write_reg_string(hk, "ctrl_interface",
+                                   config->ctrl_interface);
+#endif /* CONFIG_CTRL_IFACE */
+
+       wpa_config_write_reg_dword(hk, TEXT("eapol_version"),
+                                  config->eapol_version,
+                                  DEFAULT_EAPOL_VERSION);
+       wpa_config_write_reg_dword(hk, TEXT("ap_scan"), config->ap_scan,
+                                  DEFAULT_AP_SCAN);
+       wpa_config_write_reg_dword(hk, TEXT("fast_reauth"),
+                                  config->fast_reauth, DEFAULT_FAST_REAUTH);
+       wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigPMKLifetime"),
+                                  config->dot11RSNAConfigPMKLifetime, 0);
+       wpa_config_write_reg_dword(hk,
+                                  TEXT("dot11RSNAConfigPMKReauthThreshold"),
+                                  config->dot11RSNAConfigPMKReauthThreshold,
+                                  0);
+       wpa_config_write_reg_dword(hk, TEXT("dot11RSNAConfigSATimeout"),
+                                  config->dot11RSNAConfigSATimeout, 0);
+       wpa_config_write_reg_dword(hk, TEXT("update_config"),
+                                  config->update_config,
+                                  0);
+#ifdef CONFIG_WPS
+       if (!is_nil_uuid(config->uuid)) {
+               char buf[40];
+               uuid_bin2str(config->uuid, buf, sizeof(buf));
+               wpa_config_write_reg_string(hk, "uuid", buf);
+       }
+       wpa_config_write_reg_string(hk, "device_name", config->device_name);
+       wpa_config_write_reg_string(hk, "manufacturer", config->manufacturer);
+       wpa_config_write_reg_string(hk, "model_name", config->model_name);
+       wpa_config_write_reg_string(hk, "model_number", config->model_number);
+       wpa_config_write_reg_string(hk, "serial_number",
+                                   config->serial_number);
+       wpa_config_write_reg_string(hk, "device_type", config->device_type);
+       wpa_config_write_reg_string(hk, "config_methods",
+                                   config->config_methods);
+       if (WPA_GET_BE32(config->os_version)) {
+               char vbuf[10];
+               os_snprintf(vbuf, sizeof(vbuf), "%08x",
+                           WPA_GET_BE32(config->os_version));
+               wpa_config_write_reg_string(hk, "os_version", vbuf);
+       }
+       wpa_config_write_reg_dword(hk, TEXT("wps_cred_processing"),
+                                  config->wps_cred_processing, 0);
+#endif /* CONFIG_WPS */
+
+       wpa_config_write_reg_dword(hk, TEXT("bss_max_count"),
+                                  config->bss_max_count,
+                                  DEFAULT_BSS_MAX_COUNT);
+       wpa_config_write_reg_dword(hk, TEXT("filter_ssids"),
+                                  config->filter_ssids, 0);
+
+       return 0;
+}
+
+
+static int wpa_config_delete_subkeys(HKEY hk, const TCHAR *key)
+{
+       HKEY nhk;
+       int i, errors = 0;
+       LONG ret;
+
+       ret = RegOpenKeyEx(hk, key, 0, KEY_ENUMERATE_SUB_KEYS | DELETE, &nhk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "WINREG: Could not open key '" TSTR
+                          "' for subkey deletion: error 0x%x (%d)", key,
+                          (unsigned int) ret, (int) GetLastError());
+               return 0;
+       }
+
+       for (i = 0; ; i++) {
+               TCHAR name[255];
+               DWORD namelen;
+
+               namelen = 255;
+               ret = RegEnumKeyEx(nhk, i, name, &namelen, NULL, NULL, NULL,
+                                  NULL);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "RegEnumKeyEx failed: 0x%x (%d)",
+                                  (unsigned int) ret, (int) GetLastError());
+                       break;
+               }
+
+               if (namelen >= 255)
+                       namelen = 255 - 1;
+               name[namelen] = TEXT('\0');
+
+               ret = RegDeleteKey(nhk, name);
+               if (ret != ERROR_SUCCESS) {
+                       wpa_printf(MSG_DEBUG, "RegDeleteKey failed: 0x%x (%d)",
+                                  (unsigned int) ret, (int) GetLastError());
+                       errors++;
+               }
+       }
+
+       RegCloseKey(nhk);
+
+       return errors ? -1 : 0;
+}
+
+
+static void write_str(HKEY hk, const char *field, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, field);
+       if (value == NULL)
+               return;
+       wpa_config_write_reg_string(hk, field, value);
+       os_free(value);
+}
+
+
+static void write_int(HKEY hk, const char *field, int value, int def)
+{
+       char val[20];
+       if (value == def)
+               return;
+       os_snprintf(val, sizeof(val), "%d", value);
+       wpa_config_write_reg_string(hk, field, val);
+}
+
+
+static void write_bssid(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, "bssid");
+       if (value == NULL)
+               return;
+       wpa_config_write_reg_string(hk, "bssid", value);
+       os_free(value);
+}
+
+
+static void write_psk(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, "psk");
+       if (value == NULL)
+               return;
+       wpa_config_write_reg_string(hk, "psk", value);
+       os_free(value);
+}
+
+
+static void write_proto(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->proto == DEFAULT_PROTO)
+               return;
+
+       value = wpa_config_get(ssid, "proto");
+       if (value == NULL)
+               return;
+       if (value[0])
+               wpa_config_write_reg_string(hk, "proto", value);
+       os_free(value);
+}
+
+
+static void write_key_mgmt(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->key_mgmt == DEFAULT_KEY_MGMT)
+               return;
+
+       value = wpa_config_get(ssid, "key_mgmt");
+       if (value == NULL)
+               return;
+       if (value[0])
+               wpa_config_write_reg_string(hk, "key_mgmt", value);
+       os_free(value);
+}
+
+
+static void write_pairwise(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->pairwise_cipher == DEFAULT_PAIRWISE)
+               return;
+
+       value = wpa_config_get(ssid, "pairwise");
+       if (value == NULL)
+               return;
+       if (value[0])
+               wpa_config_write_reg_string(hk, "pairwise", value);
+       os_free(value);
+}
+
+
+static void write_group(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->group_cipher == DEFAULT_GROUP)
+               return;
+
+       value = wpa_config_get(ssid, "group");
+       if (value == NULL)
+               return;
+       if (value[0])
+               wpa_config_write_reg_string(hk, "group", value);
+       os_free(value);
+}
+
+
+static void write_auth_alg(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       if (ssid->auth_alg == 0)
+               return;
+
+       value = wpa_config_get(ssid, "auth_alg");
+       if (value == NULL)
+               return;
+       if (value[0])
+               wpa_config_write_reg_string(hk, "auth_alg", value);
+       os_free(value);
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static void write_eap(HKEY hk, struct wpa_ssid *ssid)
+{
+       char *value;
+
+       value = wpa_config_get(ssid, "eap");
+       if (value == NULL)
+               return;
+
+       if (value[0])
+               wpa_config_write_reg_string(hk, "eap", value);
+       os_free(value);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+static void write_wep_key(HKEY hk, int idx, struct wpa_ssid *ssid)
+{
+       char field[20], *value;
+
+       os_snprintf(field, sizeof(field), "wep_key%d", idx);
+       value = wpa_config_get(ssid, field);
+       if (value) {
+               wpa_config_write_reg_string(hk, field, value);
+               os_free(value);
+       }
+}
+
+
+static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
+{
+       int i, errors = 0;
+       HKEY nhk, netw;
+       LONG ret;
+       TCHAR name[5];
+
+       ret = RegOpenKeyEx(hk, TEXT("networks"), 0, KEY_CREATE_SUB_KEY, &nhk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "WINREG: Could not open networks key "
+                          "for subkey addition: error 0x%x (%d)",
+                          (unsigned int) ret, (int) GetLastError());
+               return 0;
+       }
+
+#ifdef UNICODE
+       wsprintf(name, L"%04d", id);
+#else /* UNICODE */
+       os_snprintf(name, sizeof(name), "%04d", id);
+#endif /* UNICODE */
+       ret = RegCreateKeyEx(nhk, name, 0, NULL, 0, KEY_WRITE, NULL, &netw,
+                            NULL);
+       RegCloseKey(nhk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "WINREG: Could not add network key '%s':"
+                          " error 0x%x (%d)",
+                          name, (unsigned int) ret, (int) GetLastError());
+               return -1;
+       }
+
+#define STR(t) write_str(netw, #t, ssid)
+#define INT(t) write_int(netw, #t, ssid->t, 0)
+#define INTe(t) write_int(netw, #t, ssid->eap.t, 0)
+#define INT_DEF(t, def) write_int(netw, #t, ssid->t, def)
+#define INT_DEFe(t, def) write_int(netw, #t, ssid->eap.t, def)
+
+       STR(ssid);
+       INT(scan_ssid);
+       write_bssid(netw, ssid);
+       write_psk(netw, ssid);
+       write_proto(netw, ssid);
+       write_key_mgmt(netw, ssid);
+       write_pairwise(netw, ssid);
+       write_group(netw, ssid);
+       write_auth_alg(netw, ssid);
+#ifdef IEEE8021X_EAPOL
+       write_eap(netw, ssid);
+       STR(identity);
+       STR(anonymous_identity);
+       STR(password);
+       STR(ca_cert);
+       STR(ca_path);
+       STR(client_cert);
+       STR(private_key);
+       STR(private_key_passwd);
+       STR(dh_file);
+       STR(subject_match);
+       STR(altsubject_match);
+       STR(ca_cert2);
+       STR(ca_path2);
+       STR(client_cert2);
+       STR(private_key2);
+       STR(private_key2_passwd);
+       STR(dh_file2);
+       STR(subject_match2);
+       STR(altsubject_match2);
+       STR(phase1);
+       STR(phase2);
+       STR(pcsc);
+       STR(pin);
+       STR(engine_id);
+       STR(key_id);
+       STR(cert_id);
+       STR(ca_cert_id);
+       STR(key2_id);
+       STR(pin2);
+       STR(engine2_id);
+       STR(cert2_id);
+       STR(ca_cert2_id);
+       INTe(engine);
+       INTe(engine2);
+       INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+#endif /* IEEE8021X_EAPOL */
+       for (i = 0; i < 4; i++)
+               write_wep_key(netw, i, ssid);
+       INT(wep_tx_keyidx);
+       INT(priority);
+#ifdef IEEE8021X_EAPOL
+       INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
+       STR(pac_file);
+       INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+#endif /* IEEE8021X_EAPOL */
+       INT(mode);
+       INT(proactive_key_caching);
+       INT(disabled);
+       INT(peerkey);
+#ifdef CONFIG_IEEE80211W
+       INT(ieee80211w);
+#endif /* CONFIG_IEEE80211W */
+       STR(id_str);
+
+#undef STR
+#undef INT
+#undef INT_DEF
+
+       RegCloseKey(netw);
+
+       return errors ? -1 : 0;
+}
+
+
+static int wpa_config_write_blob(HKEY hk, struct wpa_config_blob *blob)
+{
+       HKEY bhk;
+       LONG ret;
+       TCHAR *name;
+
+       ret = RegCreateKeyEx(hk, TEXT("blobs"), 0, NULL, 0, KEY_WRITE, NULL,
+                            &bhk, NULL);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "WINREG: Could not add blobs key: "
+                          "error 0x%x (%d)",
+                          (unsigned int) ret, (int) GetLastError());
+               return -1;
+       }
+
+       name = wpa_strdup_tchar(blob->name);
+       ret = RegSetValueEx(bhk, name, 0, REG_BINARY, blob->data,
+                           blob->len);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "WINREG: Failed to set blob %s': "
+                          "error 0x%x (%d)", blob->name, (unsigned int) ret,
+                          (int) GetLastError());
+               RegCloseKey(bhk);
+               os_free(name);
+               return -1;
+       }
+       os_free(name);
+
+       RegCloseKey(bhk);
+
+       return 0;
+}
+
+
+int wpa_config_write(const char *name, struct wpa_config *config)
+{
+       TCHAR buf[256];
+       HKEY hk;
+       LONG ret;
+       int errors = 0;
+       struct wpa_ssid *ssid;
+       struct wpa_config_blob *blob;
+       int id;
+
+       wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
+
+#ifdef UNICODE
+       _snwprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%S"), name);
+#else /* UNICODE */
+       os_snprintf(buf, 256, WPA_KEY_PREFIX TEXT("\\configs\\%s"), name);
+#endif /* UNICODE */
+
+       ret = RegOpenKeyEx(WPA_KEY_ROOT, buf, 0, KEY_SET_VALUE | DELETE, &hk);
+       if (ret != ERROR_SUCCESS) {
+               wpa_printf(MSG_ERROR, "Could not open wpa_supplicant "
+                          "configuration registry %s: error %d", buf,
+                          (int) GetLastError());
+               return -1;
+       }
+
+       if (wpa_config_write_global(config, hk)) {
+               wpa_printf(MSG_ERROR, "Failed to write global configuration "
+                          "data");
+               errors++;
+       }
+
+       wpa_config_delete_subkeys(hk, TEXT("networks"));
+       for (ssid = config->ssid, id = 0; ssid; ssid = ssid->next, id++) {
+               if (ssid->key_mgmt == WPA_KEY_MGMT_WPS)
+                       continue; /* do not save temporary WPS networks */
+               if (wpa_config_write_network(hk, ssid, id))
+                       errors++;
+       }
+
+       RegDeleteKey(hk, TEXT("blobs"));
+       for (blob = config->blobs; blob; blob = blob->next) {
+               if (wpa_config_write_blob(hk, blob))
+                       errors++;
+       }
+
+       RegCloseKey(hk);
+
+       wpa_printf(MSG_DEBUG, "Configuration '%s' written %ssuccessfully",
+                  name, errors ? "un" : "");
+       return errors ? -1 : 0;
+}
diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c
new file mode 100644 (file)
index 0000000..19fea29
--- /dev/null
@@ -0,0 +1,2142 @@
+/*
+ * WPA Supplicant / Control interface (shared code for all backends)
+ * Copyright (c) 2004-2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "eap_peer/eap.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "l2_packet/l2_packet.h"
+#include "wps/wps.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "ap.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+#include "ctrl_iface.h"
+
+extern struct wpa_driver_ops *wpa_drivers[];
+
+static int wpa_supplicant_global_iface_list(struct wpa_global *global,
+                                           char *buf, int len);
+static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
+                                                 char *buf, int len);
+
+
+static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
+                                        char *cmd)
+{
+       char *value;
+       int ret = 0;
+
+       value = os_strchr(cmd, ' ');
+       if (value == NULL)
+               return -1;
+       *value++ = '\0';
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE SET '%s'='%s'", cmd, value);
+       if (os_strcasecmp(cmd, "EAPOL::heldPeriod") == 0) {
+               eapol_sm_configure(wpa_s->eapol,
+                                  atoi(value), -1, -1, -1);
+       } else if (os_strcasecmp(cmd, "EAPOL::authPeriod") == 0) {
+               eapol_sm_configure(wpa_s->eapol,
+                                  -1, atoi(value), -1, -1);
+       } else if (os_strcasecmp(cmd, "EAPOL::startPeriod") == 0) {
+               eapol_sm_configure(wpa_s->eapol,
+                                  -1, -1, atoi(value), -1);
+       } else if (os_strcasecmp(cmd, "EAPOL::maxStart") == 0) {
+               eapol_sm_configure(wpa_s->eapol,
+                                  -1, -1, -1, atoi(value));
+       } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKLifetime") == 0) {
+               if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
+                                    atoi(value)))
+                       ret = -1;
+       } else if (os_strcasecmp(cmd, "dot11RSNAConfigPMKReauthThreshold") ==
+                  0) {
+               if (wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
+                                    atoi(value)))
+                       ret = -1;
+       } else if (os_strcasecmp(cmd, "dot11RSNAConfigSATimeout") == 0) {
+               if (wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, atoi(value)))
+                       ret = -1;
+       } else
+               ret = -1;
+
+       return ret;
+}
+
+
+#ifdef IEEE8021X_EAPOL
+static int wpa_supplicant_ctrl_iface_preauth(struct wpa_supplicant *wpa_s,
+                                            char *addr)
+{
+       u8 bssid[ETH_ALEN];
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       if (hwaddr_aton(addr, bssid)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH: invalid address "
+                          "'%s'", addr);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE PREAUTH " MACSTR, MAC2STR(bssid));
+       rsn_preauth_deinit(wpa_s->wpa);
+       if (rsn_preauth_init(wpa_s->wpa, bssid, ssid ? &ssid->eap : NULL))
+               return -1;
+
+       return 0;
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifdef CONFIG_PEERKEY
+/* MLME-STKSTART.request(peer) */
+static int wpa_supplicant_ctrl_iface_stkstart(
+       struct wpa_supplicant *wpa_s, char *addr)
+{
+       u8 peer[ETH_ALEN];
+
+       if (hwaddr_aton(addr, peer)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART: invalid "
+                          "address '%s'", addr);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE STKSTART " MACSTR,
+                  MAC2STR(peer));
+
+       return wpa_sm_stkstart(wpa_s->wpa, peer);
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_IEEE80211R
+static int wpa_supplicant_ctrl_iface_ft_ds(
+       struct wpa_supplicant *wpa_s, char *addr)
+{
+       u8 target_ap[ETH_ALEN];
+       struct wpa_bss *bss;
+       const u8 *mdie;
+
+       if (hwaddr_aton(addr, target_ap)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS: invalid "
+                          "address '%s'", addr);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE FT_DS " MACSTR, MAC2STR(target_ap));
+
+       bss = wpa_bss_get_bssid(wpa_s, target_ap);
+       if (bss)
+               mdie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+       else
+               mdie = NULL;
+
+       return wpa_ft_start_over_ds(wpa_s->wpa, target_ap, mdie);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_WPS
+static int wpa_supplicant_ctrl_iface_wps_pbc(struct wpa_supplicant *wpa_s,
+                                            char *cmd)
+{
+       u8 bssid[ETH_ALEN], *_bssid = bssid;
+
+       if (cmd == NULL || os_strcmp(cmd, "any") == 0)
+               _bssid = NULL;
+       else if (hwaddr_aton(cmd, bssid)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PBC: invalid BSSID '%s'",
+                          cmd);
+               return -1;
+       }
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface)
+               return wpa_supplicant_ap_wps_pbc(wpa_s, _bssid);
+#endif /* CONFIG_AP */
+
+       return wpas_wps_start_pbc(wpa_s, _bssid);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
+                                            char *cmd, char *buf,
+                                            size_t buflen)
+{
+       u8 bssid[ETH_ALEN], *_bssid = bssid;
+       char *pin;
+       int ret;
+
+       pin = os_strchr(cmd, ' ');
+       if (pin)
+               *pin++ = '\0';
+
+       if (os_strcmp(cmd, "any") == 0)
+               _bssid = NULL;
+       else if (hwaddr_aton(cmd, bssid)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_PIN: invalid BSSID '%s'",
+                          cmd);
+               return -1;
+       }
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface)
+               return wpa_supplicant_ap_wps_pin(wpa_s, _bssid, pin,
+                                                buf, buflen);
+#endif /* CONFIG_AP */
+
+       if (pin) {
+               ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+               if (ret < 0)
+                       return -1;
+               ret = os_snprintf(buf, buflen, "%s", pin);
+               if (ret < 0 || (size_t) ret >= buflen)
+                       return -1;
+               return ret;
+       }
+
+       ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+       if (ret < 0)
+               return -1;
+
+       /* Return the generated PIN */
+       ret = os_snprintf(buf, buflen, "%08d", ret);
+       if (ret < 0 || (size_t) ret >= buflen)
+               return -1;
+       return ret;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
+                                            char *cmd)
+{
+       char *path, *method, *name;
+
+       path = os_strchr(cmd, ' ');
+       if (path == NULL)
+               return -1;
+       *path++ = '\0';
+
+       method = os_strchr(path, ' ');
+       if (method == NULL)
+               return -1;
+       *method++ = '\0';
+
+       name = os_strchr(method, ' ');
+       if (name != NULL)
+               *name++ = '\0';
+
+       return wpas_wps_start_oob(wpa_s, cmd, path, method, name);
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+static int wpa_supplicant_ctrl_iface_wps_reg(struct wpa_supplicant *wpa_s,
+                                            char *cmd)
+{
+       u8 bssid[ETH_ALEN], *_bssid = bssid;
+       char *pin;
+       char *new_ssid;
+       char *new_auth;
+       char *new_encr;
+       char *new_key;
+       struct wps_new_ap_settings ap;
+
+       pin = os_strchr(cmd, ' ');
+       if (pin == NULL)
+               return -1;
+       *pin++ = '\0';
+
+       if (os_strcmp(cmd, "any") == 0)
+               _bssid = NULL;
+       else if (hwaddr_aton(cmd, bssid)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE WPS_REG: invalid BSSID '%s'",
+                          cmd);
+               return -1;
+       }
+
+       new_ssid = os_strchr(pin, ' ');
+       if (new_ssid == NULL)
+               return wpas_wps_start_reg(wpa_s, _bssid, pin, NULL);
+       *new_ssid++ = '\0';
+
+       new_auth = os_strchr(new_ssid, ' ');
+       if (new_auth == NULL)
+               return -1;
+       *new_auth++ = '\0';
+
+       new_encr = os_strchr(new_auth, ' ');
+       if (new_encr == NULL)
+               return -1;
+       *new_encr++ = '\0';
+
+       new_key = os_strchr(new_encr, ' ');
+       if (new_key == NULL)
+               return -1;
+       *new_key++ = '\0';
+
+       os_memset(&ap, 0, sizeof(ap));
+       ap.ssid_hex = new_ssid;
+       ap.auth = new_auth;
+       ap.encr = new_encr;
+       ap.key_hex = new_key;
+       return wpas_wps_start_reg(wpa_s, _bssid, pin, &ap);
+}
+
+
+#ifdef CONFIG_WPS_ER
+static int wpa_supplicant_ctrl_iface_wps_er_pin(struct wpa_supplicant *wpa_s,
+                                               char *cmd)
+{
+       char *uuid = cmd, *pin;
+       pin = os_strchr(uuid, ' ');
+       if (pin == NULL)
+               return -1;
+       *pin++ = '\0';
+       return wpas_wps_er_add_pin(wpa_s, uuid, pin);
+}
+
+
+static int wpa_supplicant_ctrl_iface_wps_er_learn(struct wpa_supplicant *wpa_s,
+                                                 char *cmd)
+{
+       char *uuid = cmd, *pin;
+       pin = os_strchr(uuid, ' ');
+       if (pin == NULL)
+               return -1;
+       *pin++ = '\0';
+       return wpas_wps_er_learn(wpa_s, uuid, pin);
+}
+#endif /* CONFIG_WPS_ER */
+
+#endif /* CONFIG_WPS */
+
+
+#ifdef CONFIG_IBSS_RSN
+static int wpa_supplicant_ctrl_iface_ibss_rsn(
+       struct wpa_supplicant *wpa_s, char *addr)
+{
+       u8 peer[ETH_ALEN];
+
+       if (hwaddr_aton(addr, peer)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN: invalid "
+                          "address '%s'", addr);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE IBSS_RSN " MACSTR,
+                  MAC2STR(peer));
+
+       return ibss_rsn_start(wpa_s->ibss_rsn, peer);
+}
+#endif /* CONFIG_IBSS_RSN */
+
+
+static int wpa_supplicant_ctrl_iface_ctrl_rsp(struct wpa_supplicant *wpa_s,
+                                             char *rsp)
+{
+#ifdef IEEE8021X_EAPOL
+       char *pos, *id_pos;
+       int id;
+       struct wpa_ssid *ssid;
+       struct eap_peer_config *eap;
+
+       pos = os_strchr(rsp, '-');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+       id_pos = pos;
+       pos = os_strchr(pos, ':');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+       id = atoi(id_pos);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: field=%s id=%d", rsp, id);
+       wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+                             (u8 *) pos, os_strlen(pos));
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+                          "to update", id);
+               return -1;
+       }
+       eap = &ssid->eap;
+
+       if (os_strcmp(rsp, "IDENTITY") == 0) {
+               os_free(eap->identity);
+               eap->identity = (u8 *) os_strdup(pos);
+               eap->identity_len = os_strlen(pos);
+               eap->pending_req_identity = 0;
+               if (ssid == wpa_s->current_ssid)
+                       wpa_s->reassociate = 1;
+       } else if (os_strcmp(rsp, "PASSWORD") == 0) {
+               os_free(eap->password);
+               eap->password = (u8 *) os_strdup(pos);
+               eap->password_len = os_strlen(pos);
+               eap->pending_req_password = 0;
+               if (ssid == wpa_s->current_ssid)
+                       wpa_s->reassociate = 1;
+       } else if (os_strcmp(rsp, "NEW_PASSWORD") == 0) {
+               os_free(eap->new_password);
+               eap->new_password = (u8 *) os_strdup(pos);
+               eap->new_password_len = os_strlen(pos);
+               eap->pending_req_new_password = 0;
+               if (ssid == wpa_s->current_ssid)
+                       wpa_s->reassociate = 1;
+       } else if (os_strcmp(rsp, "PIN") == 0) {
+               os_free(eap->pin);
+               eap->pin = os_strdup(pos);
+               eap->pending_req_pin = 0;
+               if (ssid == wpa_s->current_ssid)
+                       wpa_s->reassociate = 1;
+       } else if (os_strcmp(rsp, "OTP") == 0) {
+               os_free(eap->otp);
+               eap->otp = (u8 *) os_strdup(pos);
+               eap->otp_len = os_strlen(pos);
+               os_free(eap->pending_req_otp);
+               eap->pending_req_otp = NULL;
+               eap->pending_req_otp_len = 0;
+       } else if (os_strcmp(rsp, "PASSPHRASE") == 0) {
+               os_free(eap->private_key_passwd);
+               eap->private_key_passwd = (u8 *) os_strdup(pos);
+               eap->pending_req_passphrase = 0;
+               if (ssid == wpa_s->current_ssid)
+                       wpa_s->reassociate = 1;
+       } else {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", rsp);
+               return -1;
+       }
+
+       return 0;
+#else /* IEEE8021X_EAPOL */
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
+       return -1;
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
+                                           const char *params,
+                                           char *buf, size_t buflen)
+{
+       char *pos, *end, tmp[30];
+       int res, verbose, ret;
+
+       verbose = os_strcmp(params, "-VERBOSE") == 0;
+       pos = buf;
+       end = buf + buflen;
+       if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
+               struct wpa_ssid *ssid = wpa_s->current_ssid;
+               ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
+                                 MAC2STR(wpa_s->bssid));
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               if (ssid) {
+                       u8 *_ssid = ssid->ssid;
+                       size_t ssid_len = ssid->ssid_len;
+                       u8 ssid_buf[MAX_SSID_LEN];
+                       if (ssid_len == 0) {
+                               int _res = wpa_drv_get_ssid(wpa_s, ssid_buf);
+                               if (_res < 0)
+                                       ssid_len = 0;
+                               else
+                                       ssid_len = _res;
+                               _ssid = ssid_buf;
+                       }
+                       ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
+                                         wpa_ssid_txt(_ssid, ssid_len),
+                                         ssid->id);
+                       if (ret < 0 || ret >= end - pos)
+                               return pos - buf;
+                       pos += ret;
+
+                       if (ssid->id_str) {
+                               ret = os_snprintf(pos, end - pos,
+                                                 "id_str=%s\n",
+                                                 ssid->id_str);
+                               if (ret < 0 || ret >= end - pos)
+                                       return pos - buf;
+                               pos += ret;
+                       }
+
+                       switch (ssid->mode) {
+                       case WPAS_MODE_INFRA:
+                               ret = os_snprintf(pos, end - pos,
+                                                 "mode=station\n");
+                               break;
+                       case WPAS_MODE_IBSS:
+                               ret = os_snprintf(pos, end - pos,
+                                                 "mode=IBSS\n");
+                               break;
+                       case WPAS_MODE_AP:
+                               ret = os_snprintf(pos, end - pos,
+                                                 "mode=AP\n");
+                               break;
+                       default:
+                               ret = 0;
+                               break;
+                       }
+                       if (ret < 0 || ret >= end - pos)
+                               return pos - buf;
+                       pos += ret;
+               }
+
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface) {
+                       pos += ap_ctrl_iface_wpa_get_status(wpa_s, pos,
+                                                           end - pos,
+                                                           verbose);
+               } else
+#endif /* CONFIG_AP */
+               pos += wpa_sm_get_status(wpa_s->wpa, pos, end - pos, verbose);
+       }
+       ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
+                         wpa_supplicant_state_txt(wpa_s->wpa_state));
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       if (wpa_s->l2 &&
+           l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
+               ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               res = eapol_sm_get_status(wpa_s->eapol, pos, end - pos,
+                                         verbose);
+               if (res >= 0)
+                       pos += res;
+       }
+
+       res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
+       if (res >= 0)
+               pos += res;
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bssid(struct wpa_supplicant *wpa_s,
+                                          char *cmd)
+{
+       char *pos;
+       int id;
+       struct wpa_ssid *ssid;
+       u8 bssid[ETH_ALEN];
+
+       /* cmd: "<network id> <BSSID>" */
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+       id = atoi(cmd);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: id=%d bssid='%s'", id, pos);
+       if (hwaddr_aton(pos, bssid)) {
+               wpa_printf(MSG_DEBUG ,"CTRL_IFACE: invalid BSSID '%s'", pos);
+               return -1;
+       }
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find SSID id=%d "
+                          "to update", id);
+               return -1;
+       }
+
+       os_memcpy(ssid->bssid, bssid, ETH_ALEN);
+       ssid->bssid_set = !is_zero_ether_addr(bssid);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_list_networks(
+       struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+       char *pos, *end;
+       struct wpa_ssid *ssid;
+       int ret;
+
+       pos = buf;
+       end = buf + buflen;
+       ret = os_snprintf(pos, end - pos,
+                         "network id / ssid / bssid / flags\n");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       ssid = wpa_s->conf->ssid;
+       while (ssid) {
+               ret = os_snprintf(pos, end - pos, "%d\t%s",
+                                 ssid->id,
+                                 wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               if (ssid->bssid_set) {
+                       ret = os_snprintf(pos, end - pos, "\t" MACSTR,
+                                         MAC2STR(ssid->bssid));
+               } else {
+                       ret = os_snprintf(pos, end - pos, "\tany");
+               }
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               ret = os_snprintf(pos, end - pos, "\t%s%s",
+                                 ssid == wpa_s->current_ssid ?
+                                 "[CURRENT]" : "",
+                                 ssid->disabled ? "[DISABLED]" : "");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               ret = os_snprintf(pos, end - pos, "\n");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+
+               ssid = ssid->next;
+       }
+
+       return pos - buf;
+}
+
+
+static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
+{
+       int first = 1, ret;
+       ret = os_snprintf(pos, end - pos, "-");
+       if (ret < 0 || ret >= end - pos)
+               return pos;
+       pos += ret;
+       if (cipher & WPA_CIPHER_NONE) {
+               ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (cipher & WPA_CIPHER_WEP40) {
+               ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (cipher & WPA_CIPHER_WEP104) {
+               ret = os_snprintf(pos, end - pos, "%sWEP104",
+                                 first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (cipher & WPA_CIPHER_TKIP) {
+               ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (cipher & WPA_CIPHER_CCMP) {
+               ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       return pos;
+}
+
+
+static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
+                                   const u8 *ie, size_t ie_len)
+{
+       struct wpa_ie_data data;
+       int first, ret;
+
+       ret = os_snprintf(pos, end - pos, "[%s-", proto);
+       if (ret < 0 || ret >= end - pos)
+               return pos;
+       pos += ret;
+
+       if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
+               ret = os_snprintf(pos, end - pos, "?]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               return pos;
+       }
+
+       first = 1;
+       if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
+               ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
+               ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
+               ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+#ifdef CONFIG_IEEE80211R
+       if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+               ret = os_snprintf(pos, end - pos, "%sFT/EAP",
+                                 first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+               ret = os_snprintf(pos, end - pos, "%sFT/PSK",
+                                 first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+               ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
+                                 first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+       if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+               ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
+                                 first ? "" : "+");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+               first = 0;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
+
+       if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
+               ret = os_snprintf(pos, end - pos, "-preauth");
+               if (ret < 0 || ret >= end - pos)
+                       return pos;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "]");
+       if (ret < 0 || ret >= end - pos)
+               return pos;
+       pos += ret;
+
+       return pos;
+}
+
+
+#ifdef CONFIG_WPS
+static char * wpa_supplicant_wps_ie_txt_buf(char *pos, char *end,
+                                           struct wpabuf *wps_ie)
+{
+       int ret;
+       const char *txt;
+
+       if (wps_ie == NULL)
+               return pos;
+       if (wps_is_selected_pbc_registrar(wps_ie))
+               txt = "[WPS-PBC]";
+       else if (wps_is_selected_pin_registrar(wps_ie))
+               txt = "[WPS-PIN]";
+       else
+               txt = "[WPS]";
+
+       ret = os_snprintf(pos, end - pos, "%s", txt);
+       if (ret >= 0 && ret < end - pos)
+               pos += ret;
+       wpabuf_free(wps_ie);
+       return pos;
+}
+#endif /* CONFIG_WPS */
+
+
+static char * wpa_supplicant_wps_ie_txt(char *pos, char *end,
+                                       const struct wpa_bss *bss)
+{
+#ifdef CONFIG_WPS
+       struct wpabuf *wps_ie;
+       wps_ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+       return wpa_supplicant_wps_ie_txt_buf(pos, end, wps_ie);
+#else /* CONFIG_WPS */
+       return pos;
+#endif /* CONFIG_WPS */
+}
+
+
+/* Format one result on one text line into a buffer. */
+static int wpa_supplicant_ctrl_iface_scan_result(
+       const struct wpa_bss *bss, char *buf, size_t buflen)
+{
+       char *pos, *end;
+       int ret;
+       const u8 *ie, *ie2;
+
+       pos = buf;
+       end = buf + buflen;
+
+       ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
+                         MAC2STR(bss->bssid), bss->freq, bss->level);
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+       ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       if (ie)
+               pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
+       ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+       if (ie2)
+               pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
+       pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+       if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
+               ret = os_snprintf(pos, end - pos, "[WEP]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+       if (bss->caps & IEEE80211_CAP_IBSS) {
+               ret = os_snprintf(pos, end - pos, "[IBSS]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+       if (bss->caps & IEEE80211_CAP_ESS) {
+               ret = os_snprintf(pos, end - pos, "[ESS]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "\t%s",
+                         wpa_ssid_txt(bss->ssid, bss->ssid_len));
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos, "\n");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_scan_results(
+       struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+       char *pos, *end;
+       struct wpa_bss *bss;
+       int ret;
+
+       pos = buf;
+       end = buf + buflen;
+       ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
+                         "flags / ssid\n");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+               ret = wpa_supplicant_ctrl_iface_scan_result(bss, pos,
+                                                           end - pos);
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_select_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id;
+       struct wpa_ssid *ssid;
+
+       /* cmd: "<network id>" or "any" */
+       if (os_strcmp(cmd, "any") == 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
+               ssid = NULL;
+       } else {
+               id = atoi(cmd);
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK id=%d", id);
+
+               ssid = wpa_config_get_network(wpa_s->conf, id);
+               if (ssid == NULL) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+                                  "network id=%d", id);
+                       return -1;
+               }
+       }
+
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_enable_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id;
+       struct wpa_ssid *ssid;
+
+       /* cmd: "<network id>" or "all" */
+       if (os_strcmp(cmd, "all") == 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK all");
+               ssid = NULL;
+       } else {
+               id = atoi(cmd);
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: ENABLE_NETWORK id=%d", id);
+
+               ssid = wpa_config_get_network(wpa_s->conf, id);
+               if (ssid == NULL) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+                                  "network id=%d", id);
+                       return -1;
+               }
+       }
+       wpa_supplicant_enable_network(wpa_s, ssid);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_disable_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id;
+       struct wpa_ssid *ssid;
+
+       /* cmd: "<network id>" or "all" */
+       if (os_strcmp(cmd, "all") == 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK all");
+               ssid = NULL;
+       } else {
+               id = atoi(cmd);
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: DISABLE_NETWORK id=%d", id);
+
+               ssid = wpa_config_get_network(wpa_s->conf, id);
+               if (ssid == NULL) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+                                  "network id=%d", id);
+                       return -1;
+               }
+       }
+       wpa_supplicant_disable_network(wpa_s, ssid);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_add_network(
+       struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+       struct wpa_ssid *ssid;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: ADD_NETWORK");
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return -1;
+
+       wpas_notify_network_added(wpa_s, ssid);
+
+       ssid->disabled = 1;
+       wpa_config_set_network_defaults(ssid);
+
+       ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
+       if (ret < 0 || (size_t) ret >= buflen)
+               return -1;
+       return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_remove_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id;
+       struct wpa_ssid *ssid;
+
+       /* cmd: "<network id>" or "all" */
+       if (os_strcmp(cmd, "all") == 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK all");
+               ssid = wpa_s->conf->ssid;
+               while (ssid) {
+                       struct wpa_ssid *remove_ssid = ssid;
+                       id = ssid->id;
+                       ssid = ssid->next;
+                       wpas_notify_network_removed(wpa_s, remove_ssid);
+                       wpa_config_remove_network(wpa_s->conf, id);
+               }
+               if (wpa_s->current_ssid) {
+                       eapol_sm_invalidate_cached_session(wpa_s->eapol);
+                       wpa_supplicant_disassociate(wpa_s,
+                                                   WLAN_REASON_DEAUTH_LEAVING);
+               }
+               return 0;
+       }
+
+       id = atoi(cmd);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_NETWORK id=%d", id);
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL ||
+           wpa_config_remove_network(wpa_s->conf, id) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+                          "id=%d", id);
+               return -1;
+       }
+
+       if (ssid == wpa_s->current_ssid) {
+               /*
+                * Invalidate the EAP session cache if the current network is
+                * removed.
+                */
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
+
+               wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id;
+       struct wpa_ssid *ssid;
+       char *name, *value;
+
+       /* cmd: "<network id> <variable name> <value>" */
+       name = os_strchr(cmd, ' ');
+       if (name == NULL)
+               return -1;
+       *name++ = '\0';
+
+       value = os_strchr(name, ' ');
+       if (value == NULL)
+               return -1;
+       *value++ = '\0';
+
+       id = atoi(cmd);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: SET_NETWORK id=%d name='%s'",
+                  id, name);
+       wpa_hexdump_ascii_key(MSG_DEBUG, "CTRL_IFACE: value",
+                             (u8 *) value, os_strlen(value));
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+                          "id=%d", id);
+               return -1;
+       }
+
+       if (wpa_config_set(ssid, name, value, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
+                          "variable '%s'", name);
+               return -1;
+       }
+
+       if (wpa_s->current_ssid == ssid) {
+               /*
+                * Invalidate the EAP session cache if anything in the current
+                * configuration changes.
+                */
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
+       }
+
+       if ((os_strcmp(name, "psk") == 0 &&
+            value[0] == '"' && ssid->ssid_len) ||
+           (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
+               wpa_config_update_psk(ssid);
+       else if (os_strcmp(name, "priority") == 0)
+               wpa_config_update_prio_list(wpa_s->conf);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_network(
+       struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
+{
+       int id;
+       size_t res;
+       struct wpa_ssid *ssid;
+       char *name, *value;
+
+       /* cmd: "<network id> <variable name>" */
+       name = os_strchr(cmd, ' ');
+       if (name == NULL || buflen == 0)
+               return -1;
+       *name++ = '\0';
+
+       id = atoi(cmd);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_NETWORK id=%d name='%s'",
+                  id, name);
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find network "
+                          "id=%d", id);
+               return -1;
+       }
+
+       value = wpa_config_get_no_key(ssid, name);
+       if (value == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+                          "variable '%s'", name);
+               return -1;
+       }
+
+       res = os_strlcpy(buf, value, buflen);
+       if (res >= buflen) {
+               os_free(value);
+               return -1;
+       }
+
+       os_free(value);
+
+       return res;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
+{
+       int ret;
+
+       if (!wpa_s->conf->update_config) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed "
+                          "to update configuration (update_config=0)");
+               return -1;
+       }
+
+       ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to "
+                          "update configuration");
+       } else {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration"
+                          " updated");
+       }
+
+       return ret;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+static int ctrl_iface_get_capability_pairwise(int res, char *strict,
+                                             struct wpa_driver_capa *capa,
+                                             char *buf, size_t buflen)
+{
+       int ret, first = 1;
+       char *pos, *end;
+       size_t len;
+
+       pos = buf;
+       end = pos + buflen;
+
+       if (res < 0) {
+               if (strict)
+                       return 0;
+               len = os_strlcpy(buf, "CCMP TKIP NONE", buflen);
+               if (len >= buflen)
+                       return -1;
+               return len;
+       }
+
+       if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
+               ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
+               ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+               ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_group(int res, char *strict,
+                                          struct wpa_driver_capa *capa,
+                                          char *buf, size_t buflen)
+{
+       int ret, first = 1;
+       char *pos, *end;
+       size_t len;
+
+       pos = buf;
+       end = pos + buflen;
+
+       if (res < 0) {
+               if (strict)
+                       return 0;
+               len = os_strlcpy(buf, "CCMP TKIP WEP104 WEP40", buflen);
+               if (len >= buflen)
+                       return -1;
+               return len;
+       }
+
+       if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
+               ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
+               ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) {
+               ret = os_snprintf(pos, end - pos, "%sWEP104",
+                                 first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) {
+               ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
+                                             struct wpa_driver_capa *capa,
+                                             char *buf, size_t buflen)
+{
+       int ret;
+       char *pos, *end;
+       size_t len;
+
+       pos = buf;
+       end = pos + buflen;
+
+       if (res < 0) {
+               if (strict)
+                       return 0;
+               len = os_strlcpy(buf, "WPA-PSK WPA-EAP IEEE8021X WPA-NONE "
+                                "NONE", buflen);
+               if (len >= buflen)
+                       return -1;
+               return len;
+       }
+
+       ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                             WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+               ret = os_snprintf(pos, end - pos, " WPA-EAP");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+                             WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+               ret = os_snprintf(pos, end - pos, " WPA-PSK");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+               ret = os_snprintf(pos, end - pos, " WPA-NONE");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_proto(int res, char *strict,
+                                          struct wpa_driver_capa *capa,
+                                          char *buf, size_t buflen)
+{
+       int ret, first = 1;
+       char *pos, *end;
+       size_t len;
+
+       pos = buf;
+       end = pos + buflen;
+
+       if (res < 0) {
+               if (strict)
+                       return 0;
+               len = os_strlcpy(buf, "RSN WPA", buflen);
+               if (len >= buflen)
+                       return -1;
+               return len;
+       }
+
+       if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                             WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+               ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                             WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
+               ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       return pos - buf;
+}
+
+
+static int ctrl_iface_get_capability_auth_alg(int res, char *strict,
+                                             struct wpa_driver_capa *capa,
+                                             char *buf, size_t buflen)
+{
+       int ret, first = 1;
+       char *pos, *end;
+       size_t len;
+
+       pos = buf;
+       end = pos + buflen;
+
+       if (res < 0) {
+               if (strict)
+                       return 0;
+               len = os_strlcpy(buf, "OPEN SHARED LEAP", buflen);
+               if (len >= buflen)
+                       return -1;
+               return len;
+       }
+
+       if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
+               ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
+               ret = os_snprintf(pos, end - pos, "%sSHARED",
+                                 first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
+               ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " ");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+               first = 0;
+       }
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_get_capability(
+       struct wpa_supplicant *wpa_s, const char *_field, char *buf,
+       size_t buflen)
+{
+       struct wpa_driver_capa capa;
+       int res;
+       char *strict;
+       char field[30];
+       size_t len;
+
+       /* Determine whether or not strict checking was requested */
+       len = os_strlcpy(field, _field, sizeof(field));
+       if (len >= sizeof(field))
+               return -1;
+       strict = os_strchr(field, ' ');
+       if (strict != NULL) {
+               *strict++ = '\0';
+               if (os_strcmp(strict, "strict") != 0)
+                       return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CAPABILITY '%s' %s",
+               field, strict ? strict : "");
+
+       if (os_strcmp(field, "eap") == 0) {
+               return eap_get_names(buf, buflen);
+       }
+
+       res = wpa_drv_get_capa(wpa_s, &capa);
+
+       if (os_strcmp(field, "pairwise") == 0)
+               return ctrl_iface_get_capability_pairwise(res, strict, &capa,
+                                                         buf, buflen);
+
+       if (os_strcmp(field, "group") == 0)
+               return ctrl_iface_get_capability_group(res, strict, &capa,
+                                                      buf, buflen);
+
+       if (os_strcmp(field, "key_mgmt") == 0)
+               return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
+                                                         buf, buflen);
+
+       if (os_strcmp(field, "proto") == 0)
+               return ctrl_iface_get_capability_proto(res, strict, &capa,
+                                                      buf, buflen);
+
+       if (os_strcmp(field, "auth_alg") == 0)
+               return ctrl_iface_get_capability_auth_alg(res, strict, &capa,
+                                                         buf, buflen);
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
+                  field);
+
+       return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
+                                        const char *cmd, char *buf,
+                                        size_t buflen)
+{
+       u8 bssid[ETH_ALEN];
+       size_t i;
+       struct wpa_bss *bss;
+       int ret;
+       char *pos, *end;
+       const u8 *ie, *ie2;
+
+       if (os_strcmp(cmd, "FIRST") == 0)
+               bss = dl_list_first(&wpa_s->bss, struct wpa_bss, list);
+       else if (os_strncmp(cmd, "ID-", 3) == 0) {
+               i = atoi(cmd + 3);
+               bss = wpa_bss_get_id(wpa_s, i);
+       } else if (os_strncmp(cmd, "NEXT-", 5) == 0) {
+               i = atoi(cmd + 5);
+               bss = wpa_bss_get_id(wpa_s, i);
+               if (bss) {
+                       struct dl_list *next = bss->list_id.next;
+                       if (next == &wpa_s->bss_id)
+                               bss = NULL;
+                       else
+                               bss = dl_list_entry(next, struct wpa_bss,
+                                                   list_id);
+               }
+       } else if (hwaddr_aton(cmd, bssid) == 0)
+               bss = wpa_bss_get_bssid(wpa_s, bssid);
+       else {
+               struct wpa_bss *tmp;
+               i = atoi(cmd);
+               bss = NULL;
+               dl_list_for_each(tmp, &wpa_s->bss_id, struct wpa_bss, list_id)
+               {
+                       if (i-- == 0) {
+                               bss = tmp;
+                               break;
+                       }
+               }
+       }
+
+       if (bss == NULL)
+               return 0;
+
+       pos = buf;
+       end = buf + buflen;
+       ret = os_snprintf(pos, end - pos,
+                         "id=%u\n"
+                         "bssid=" MACSTR "\n"
+                         "freq=%d\n"
+                         "beacon_int=%d\n"
+                         "capabilities=0x%04x\n"
+                         "qual=%d\n"
+                         "noise=%d\n"
+                         "level=%d\n"
+                         "tsf=%016llu\n"
+                         "ie=",
+                         bss->id,
+                         MAC2STR(bss->bssid), bss->freq, bss->beacon_int,
+                         bss->caps, bss->qual, bss->noise, bss->level,
+                         (unsigned long long) bss->tsf);
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       ie = (const u8 *) (bss + 1);
+       for (i = 0; i < bss->ie_len; i++) {
+               ret = os_snprintf(pos, end - pos, "%02x", *ie++);
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "\n");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos, "flags=");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       if (ie)
+               pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
+       ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+       if (ie2)
+               pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
+       pos = wpa_supplicant_wps_ie_txt(pos, end, bss);
+       if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
+               ret = os_snprintf(pos, end - pos, "[WEP]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+       if (bss->caps & IEEE80211_CAP_IBSS) {
+               ret = os_snprintf(pos, end - pos, "[IBSS]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+       if (bss->caps & IEEE80211_CAP_ESS) {
+               ret = os_snprintf(pos, end - pos, "[ESS]");
+               if (ret < 0 || ret >= end - pos)
+                       return pos - buf;
+               pos += ret;
+       }
+
+       ret = os_snprintf(pos, end - pos, "\n");
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos, "ssid=%s\n",
+                         wpa_ssid_txt(bss->ssid, bss->ssid_len));
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+
+#ifdef CONFIG_WPS
+       ie = (const u8 *) (bss + 1);
+       ret = wpas_wps_scan_result_text(ie, bss->ie_len, pos, end);
+       if (ret < 0 || ret >= end - pos)
+               return pos - buf;
+       pos += ret;
+#endif /* CONFIG_WPS */
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_ctrl_iface_ap_scan(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int ap_scan = atoi(cmd);
+       return wpa_supplicant_set_ap_scan(wpa_s, ap_scan);
+}
+
+
+static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
+{
+       u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
+
+       wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
+       /* MLME-DELETEKEYS.request */
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+#ifdef CONFIG_IEEE80211W
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+#endif /* CONFIG_IEEE80211W */
+
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, wpa_s->bssid, 0, 0, NULL, 0, NULL,
+                       0);
+       /* MLME-SETPROTECTION.request(None) */
+       wpa_drv_mlme_setprotection(wpa_s, wpa_s->bssid,
+                                  MLME_SETPROTECTION_PROTECT_TYPE_NONE,
+                                  MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+       wpa_sm_drop_sa(wpa_s->wpa);
+}
+
+
+static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
+                                         char *addr)
+{
+       u8 bssid[ETH_ALEN];
+       struct wpa_bss *bss;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       if (hwaddr_aton(addr, bssid)) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: invalid "
+                          "address '%s'", addr);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM " MACSTR, MAC2STR(bssid));
+
+       bss = wpa_bss_get_bssid(wpa_s, bssid);
+       if (!bss) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: Target AP not found "
+                          "from BSS table");
+               return -1;
+       }
+
+       /*
+        * TODO: Find best network configuration block from configuration to
+        * allow roaming to other networks
+        */
+
+       if (!ssid) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE ROAM: No network "
+                          "configuration known for the target AP");
+               return -1;
+       }
+
+       wpa_s->reassociate = 1;
+       wpa_supplicant_connect(wpa_s, bss, ssid);
+
+       return 0;
+}
+
+
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+                                        char *buf, size_t *resp_len)
+{
+       char *reply;
+       const int reply_size = 2048;
+       int ctrl_rsp = 0;
+       int reply_len;
+
+       if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
+           os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+               wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
+                                     (const u8 *) buf, os_strlen(buf));
+       } else {
+               wpa_hexdump_ascii(MSG_DEBUG, "RX ctrl_iface",
+                                 (const u8 *) buf, os_strlen(buf));
+       }
+
+       reply = os_malloc(reply_size);
+       if (reply == NULL) {
+               *resp_len = 1;
+               return NULL;
+       }
+
+       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 = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
+               if (reply_len >= 0) {
+                       int res;
+                       res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
+                                              reply_size - reply_len);
+                       if (res < 0)
+                               reply_len = -1;
+                       else
+                               reply_len += res;
+               }
+       } else if (os_strncmp(buf, "STATUS", 6) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_status(
+                       wpa_s, buf + 6, reply, reply_size);
+       } else if (os_strcmp(buf, "PMKSA") == 0) {
+               reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply,
+                                                   reply_size);
+       } else if (os_strncmp(buf, "SET ", 4) == 0) {
+               if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "LOGON") == 0) {
+               eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+       } else if (os_strcmp(buf, "LOGOFF") == 0) {
+               eapol_sm_notify_logoff(wpa_s->eapol, TRUE);
+       } else if (os_strcmp(buf, "REASSOCIATE") == 0) {
+               wpa_s->disconnected = 0;
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       } else if (os_strcmp(buf, "RECONNECT") == 0) {
+               if (wpa_s->disconnected) {
+                       wpa_s->disconnected = 0;
+                       wpa_s->reassociate = 1;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
+#ifdef IEEE8021X_EAPOL
+       } else if (os_strncmp(buf, "PREAUTH ", 8) == 0) {
+               if (wpa_supplicant_ctrl_iface_preauth(wpa_s, buf + 8))
+                       reply_len = -1;
+#endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_PEERKEY
+       } else if (os_strncmp(buf, "STKSTART ", 9) == 0) {
+               if (wpa_supplicant_ctrl_iface_stkstart(wpa_s, buf + 9))
+                       reply_len = -1;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211R
+       } else if (os_strncmp(buf, "FT_DS ", 6) == 0) {
+               if (wpa_supplicant_ctrl_iface_ft_ds(wpa_s, buf + 6))
+                       reply_len = -1;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+       } else if (os_strcmp(buf, "WPS_PBC") == 0) {
+               if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, NULL))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WPS_PBC ", 8) == 0) {
+               if (wpa_supplicant_ctrl_iface_wps_pbc(wpa_s, buf + 8))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
+                                                             reply,
+                                                             reply_size);
+#ifdef CONFIG_WPS_OOB
+       } else if (os_strncmp(buf, "WPS_OOB ", 8) == 0) {
+               if (wpa_supplicant_ctrl_iface_wps_oob(wpa_s, buf + 8))
+                       reply_len = -1;
+#endif /* CONFIG_WPS_OOB */
+       } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
+               if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))
+                       reply_len = -1;
+#ifdef CONFIG_WPS_ER
+       } else if (os_strcmp(buf, "WPS_ER_START") == 0) {
+               if (wpas_wps_er_start(wpa_s))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
+               if (wpas_wps_er_stop(wpa_s))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
+               if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WPS_ER_PBC ", 11) == 0) {
+               if (wpas_wps_er_pbc(wpa_s, buf + 11))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WPS_ER_LEARN ", 13) == 0) {
+               if (wpa_supplicant_ctrl_iface_wps_er_learn(wpa_s, buf + 13))
+                       reply_len = -1;
+#endif /* CONFIG_WPS_ER */
+#endif /* CONFIG_WPS */
+#ifdef CONFIG_IBSS_RSN
+       } else if (os_strncmp(buf, "IBSS_RSN ", 9) == 0) {
+               if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
+                       reply_len = -1;
+#endif /* CONFIG_IBSS_RSN */
+       } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
+       {
+               if (wpa_supplicant_ctrl_iface_ctrl_rsp(
+                           wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
+                       reply_len = -1;
+               else
+                       ctrl_rsp = 1;
+       } else if (os_strcmp(buf, "RECONFIGURE") == 0) {
+               if (wpa_supplicant_reload_configuration(wpa_s))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "TERMINATE") == 0) {
+               wpa_supplicant_terminate_proc(wpa_s->global);
+       } else if (os_strncmp(buf, "BSSID ", 6) == 0) {
+               if (wpa_supplicant_ctrl_iface_bssid(wpa_s, buf + 6))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_list_networks(
+                       wpa_s, reply, reply_size);
+       } else if (os_strcmp(buf, "DISCONNECT") == 0) {
+               wpa_s->reassociate = 0;
+               wpa_s->disconnected = 1;
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+       } else if (os_strcmp(buf, "SCAN") == 0) {
+               wpa_s->scan_req = 2;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_scan_results(
+                       wpa_s, reply, reply_size);
+       } else if (os_strncmp(buf, "SELECT_NETWORK ", 15) == 0) {
+               if (wpa_supplicant_ctrl_iface_select_network(wpa_s, buf + 15))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "ENABLE_NETWORK ", 15) == 0) {
+               if (wpa_supplicant_ctrl_iface_enable_network(wpa_s, buf + 15))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DISABLE_NETWORK ", 16) == 0) {
+               if (wpa_supplicant_ctrl_iface_disable_network(wpa_s, buf + 16))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "ADD_NETWORK") == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_add_network(
+                       wpa_s, reply, reply_size);
+       } else if (os_strncmp(buf, "REMOVE_NETWORK ", 15) == 0) {
+               if (wpa_supplicant_ctrl_iface_remove_network(wpa_s, buf + 15))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+               if (wpa_supplicant_ctrl_iface_set_network(wpa_s, buf + 12))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_get_network(
+                       wpa_s, buf + 12, reply, reply_size);
+#ifndef CONFIG_NO_CONFIG_WRITE
+       } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
+               if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
+                       reply_len = -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+       } else if (os_strncmp(buf, "GET_CAPABILITY ", 15) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_get_capability(
+                       wpa_s, buf + 15, reply, reply_size);
+       } else if (os_strncmp(buf, "AP_SCAN ", 8) == 0) {
+               if (wpa_supplicant_ctrl_iface_ap_scan(wpa_s, buf + 8))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
+               reply_len = wpa_supplicant_global_iface_list(
+                       wpa_s->global, reply, reply_size);
+       } else if (os_strcmp(buf, "INTERFACES") == 0) {
+               reply_len = wpa_supplicant_global_iface_interfaces(
+                       wpa_s->global, reply, reply_size);
+       } else if (os_strncmp(buf, "BSS ", 4) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_bss(
+                       wpa_s, buf + 4, reply, reply_size);
+#ifdef CONFIG_AP
+       } else if (os_strcmp(buf, "STA-FIRST") == 0) {
+               reply_len = ap_ctrl_iface_sta_first(wpa_s, reply, reply_size);
+       } else if (os_strncmp(buf, "STA ", 4) == 0) {
+               reply_len = ap_ctrl_iface_sta(wpa_s, buf + 4, reply,
+                                             reply_size);
+       } else if (os_strncmp(buf, "STA-NEXT ", 9) == 0) {
+               reply_len = ap_ctrl_iface_sta_next(wpa_s, buf + 9, reply,
+                                                  reply_size);
+#endif /* CONFIG_AP */
+       } else if (os_strcmp(buf, "SUSPEND") == 0) {
+               wpas_notify_suspend(wpa_s->global);
+       } else if (os_strcmp(buf, "RESUME") == 0) {
+               wpas_notify_resume(wpa_s->global);
+       } else if (os_strcmp(buf, "DROP_SA") == 0) {
+               wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
+       } else if (os_strncmp(buf, "ROAM ", 5) == 0) {
+               if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
+                       reply_len = -1;
+       } else {
+               os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+               reply_len = 16;
+       }
+
+       if (reply_len < 0) {
+               os_memcpy(reply, "FAIL\n", 5);
+               reply_len = 5;
+       }
+
+       if (ctrl_rsp)
+               eapol_sm_notify_ctrl_response(wpa_s->eapol);
+
+       *resp_len = reply_len;
+       return reply;
+}
+
+
+static int wpa_supplicant_global_iface_add(struct wpa_global *global,
+                                          char *cmd)
+{
+       struct wpa_interface iface;
+       char *pos;
+
+       /*
+        * <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB<driver_param>
+        * TAB<bridge_ifname>
+        */
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_ADD '%s'", cmd);
+
+       os_memset(&iface, 0, sizeof(iface));
+
+       do {
+               iface.ifname = pos = cmd;
+               pos = os_strchr(pos, '\t');
+               if (pos)
+                       *pos++ = '\0';
+               if (iface.ifname[0] == '\0')
+                       return -1;
+               if (pos == NULL)
+                       break;
+
+               iface.confname = pos;
+               pos = os_strchr(pos, '\t');
+               if (pos)
+                       *pos++ = '\0';
+               if (iface.confname[0] == '\0')
+                       iface.confname = NULL;
+               if (pos == NULL)
+                       break;
+
+               iface.driver = pos;
+               pos = os_strchr(pos, '\t');
+               if (pos)
+                       *pos++ = '\0';
+               if (iface.driver[0] == '\0')
+                       iface.driver = NULL;
+               if (pos == NULL)
+                       break;
+
+               iface.ctrl_interface = pos;
+               pos = os_strchr(pos, '\t');
+               if (pos)
+                       *pos++ = '\0';
+               if (iface.ctrl_interface[0] == '\0')
+                       iface.ctrl_interface = NULL;
+               if (pos == NULL)
+                       break;
+
+               iface.driver_param = pos;
+               pos = os_strchr(pos, '\t');
+               if (pos)
+                       *pos++ = '\0';
+               if (iface.driver_param[0] == '\0')
+                       iface.driver_param = NULL;
+               if (pos == NULL)
+                       break;
+
+               iface.bridge_ifname = pos;
+               pos = os_strchr(pos, '\t');
+               if (pos)
+                       *pos++ = '\0';
+               if (iface.bridge_ifname[0] == '\0')
+                       iface.bridge_ifname = NULL;
+               if (pos == NULL)
+                       break;
+       } while (0);
+
+       if (wpa_supplicant_get_iface(global, iface.ifname))
+               return -1;
+
+       return wpa_supplicant_add_iface(global, &iface) ? 0 : -1;
+}
+
+
+static int wpa_supplicant_global_iface_remove(struct wpa_global *global,
+                                             char *cmd)
+{
+       struct wpa_supplicant *wpa_s;
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE GLOBAL INTERFACE_REMOVE '%s'", cmd);
+
+       wpa_s = wpa_supplicant_get_iface(global, cmd);
+       if (wpa_s == NULL)
+               return -1;
+       return wpa_supplicant_remove_iface(global, wpa_s);
+}
+
+
+static void wpa_free_iface_info(struct wpa_interface_info *iface)
+{
+       struct wpa_interface_info *prev;
+
+       while (iface) {
+               prev = iface;
+               iface = iface->next;
+
+               os_free(prev->ifname);
+               os_free(prev->desc);
+               os_free(prev);
+       }
+}
+
+
+static int wpa_supplicant_global_iface_list(struct wpa_global *global,
+                                           char *buf, int len)
+{
+       int i, res;
+       struct wpa_interface_info *iface = NULL, *last = NULL, *tmp;
+       char *pos, *end;
+
+       for (i = 0; wpa_drivers[i]; i++) {
+               struct wpa_driver_ops *drv = wpa_drivers[i];
+               if (drv->get_interfaces == NULL)
+                       continue;
+               tmp = drv->get_interfaces(global->drv_priv[i]);
+               if (tmp == NULL)
+                       continue;
+
+               if (last == NULL)
+                       iface = last = tmp;
+               else
+                       last->next = tmp;
+               while (last->next)
+                       last = last->next;
+       }
+
+       pos = buf;
+       end = buf + len;
+       for (tmp = iface; tmp; tmp = tmp->next) {
+               res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
+                                 tmp->drv_name, tmp->ifname,
+                                 tmp->desc ? tmp->desc : "");
+               if (res < 0 || res >= end - pos) {
+                       *pos = '\0';
+                       break;
+               }
+               pos += res;
+       }
+
+       wpa_free_iface_info(iface);
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
+                                                 char *buf, int len)
+{
+       int res;
+       char *pos, *end;
+       struct wpa_supplicant *wpa_s;
+
+       wpa_s = global->ifaces;
+       pos = buf;
+       end = buf + len;
+
+       while (wpa_s) {
+               res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
+               if (res < 0 || res >= end - pos) {
+                       *pos = '\0';
+                       break;
+               }
+               pos += res;
+               wpa_s = wpa_s->next;
+       }
+       return pos - buf;
+}
+
+
+char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
+                                               char *buf, size_t *resp_len)
+{
+       char *reply;
+       const int reply_size = 2048;
+       int reply_len;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "RX global ctrl_iface",
+                         (const u8 *) buf, os_strlen(buf));
+
+       reply = os_malloc(reply_size);
+       if (reply == NULL) {
+               *resp_len = 1;
+               return NULL;
+       }
+
+       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_strncmp(buf, "INTERFACE_ADD ", 14) == 0) {
+               if (wpa_supplicant_global_iface_add(global, buf + 14))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "INTERFACE_REMOVE ", 17) == 0) {
+               if (wpa_supplicant_global_iface_remove(global, buf + 17))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "INTERFACE_LIST") == 0) {
+               reply_len = wpa_supplicant_global_iface_list(
+                       global, reply, reply_size);
+       } else if (os_strcmp(buf, "INTERFACES") == 0) {
+               reply_len = wpa_supplicant_global_iface_interfaces(
+                       global, reply, reply_size);
+       } else if (os_strcmp(buf, "TERMINATE") == 0) {
+               wpa_supplicant_terminate_proc(global);
+       } else if (os_strcmp(buf, "SUSPEND") == 0) {
+               wpas_notify_suspend(global);
+       } else if (os_strcmp(buf, "RESUME") == 0) {
+               wpas_notify_resume(global);
+       } else {
+               os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
+               reply_len = 16;
+       }
+
+       if (reply_len < 0) {
+               os_memcpy(reply, "FAIL\n", 5);
+               reply_len = 5;
+       }
+
+       *resp_len = reply_len;
+       return reply;
+}
diff --git a/wpa_supplicant/ctrl_iface.h b/wpa_supplicant/ctrl_iface.h
new file mode 100644 (file)
index 0000000..051d99a
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * 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 CTRL_IFACE_H
+#define CTRL_IFACE_H
+
+#ifdef CONFIG_CTRL_IFACE
+
+/* Shared functions from ctrl_iface.c; to be called by ctrl_iface backends */
+
+/**
+ * wpa_supplicant_ctrl_iface_process - Process ctrl_iface command
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @buf: Received command buffer (nul terminated string)
+ * @resp_len: Variable to be set to the response length
+ * Returns: Response (*resp_len bytes) or %NULL on failure
+ *
+ * Control interface backends call this function when receiving a message that
+ * they do not process internally, i.e., anything else than ATTACH, DETACH,
+ * and LEVEL. The return response value is then sent to the external program
+ * that sent the command. Caller is responsible for freeing the buffer after
+ * this. If %NULL is returned, *resp_len can be set to two special values:
+ * 1 = send "FAIL\n" response, 2 = send "OK\n" response. If *resp_len has any
+ * other value, no response is sent.
+ */
+char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
+                                        char *buf, size_t *resp_len);
+
+/**
+ * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @buf: Received command buffer (nul terminated string)
+ * @resp_len: Variable to be set to the response length
+ * Returns: Response (*resp_len bytes) or %NULL on failure
+ *
+ * Control interface backends call this function when receiving a message from
+ * the global ctrl_iface connection. The return response value is then sent to
+ * the external program that sent the command. Caller is responsible for
+ * freeing the buffer after this. If %NULL is returned, *resp_len can be set to
+ * two special values: 1 = send "FAIL\n" response, 2 = send "OK\n" response. If
+ * *resp_len has any other value, no response is sent.
+ */
+char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
+                                               char *buf, size_t *resp_len);
+
+
+/* Functions that each ctrl_iface backend must implement */
+
+/**
+ * wpa_supplicant_ctrl_iface_init - Initialize control interface
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: Pointer to private data on success, %NULL on failure
+ *
+ * Initialize the control interface and start receiving commands from external
+ * programs.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s);
+
+/**
+ * wpa_supplicant_ctrl_iface_deinit - Deinitialize control interface
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ *
+ * Deinitialize the control interface that was initialized with
+ * wpa_supplicant_ctrl_iface_init().
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv);
+
+/**
+ * wpa_supplicant_ctrl_iface_wait - Wait for ctrl_iface monitor
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ *
+ * Wait until the first message from an external program using the control
+ * interface is received. This function can be used to delay normal startup
+ * processing to allow control interface programs to attach with
+ * %wpa_supplicant before normal operations are started.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_init - Initialize global control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: Pointer to private data on success, %NULL on failure
+ *
+ * Initialize the global control interface and start receiving commands from
+ * external programs.
+ *
+ * Required to be implemented in each control interface backend.
+ */
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global);
+
+/**
+ * wpa_supplicant_global_ctrl_iface_deinit - Deinitialize global ctrl interface
+ * @priv: Pointer to private data from wpa_supplicant_global_ctrl_iface_init()
+ *
+ * Deinitialize the global control interface that was initialized with
+ * wpa_supplicant_global_ctrl_iface_init().
+ *
+ * Required to be implemented in each control interface backend.
+ */
+void wpa_supplicant_global_ctrl_iface_deinit(
+       struct ctrl_iface_global_priv *priv);
+
+#else /* CONFIG_CTRL_IFACE */
+
+static inline struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+       return (void *) -1;
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv, int level,
+                              char *buf, size_t len)
+{
+}
+
+static inline void
+wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+}
+
+static inline struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+       return (void *) 1;
+}
+
+static inline void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE */
+
+#endif /* CTRL_IFACE_H */
diff --git a/wpa_supplicant/ctrl_iface_named_pipe.c b/wpa_supplicant/ctrl_iface_named_pipe.c
new file mode 100644 (file)
index 0000000..5f7e24d
--- /dev/null
@@ -0,0 +1,835 @@
+/*
+ * WPA Supplicant / Windows Named Pipe -based control interface
+ * Copyright (c) 2004-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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+#include "common/wpa_ctrl.h"
+
+#ifdef __MINGW32_VERSION
+/* mingw-w32api v3.1 does not yet include sddl.h, so define needed parts here
+ */
+#define SDDL_REVISION_1 1
+BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorA(
+       LPCSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
+BOOL WINAPI ConvertStringSecurityDescriptorToSecurityDescriptorW(
+       LPCWSTR, DWORD, PSECURITY_DESCRIPTOR *, PULONG);
+#ifdef UNICODE
+#define ConvertStringSecurityDescriptorToSecurityDescriptor \
+ConvertStringSecurityDescriptorToSecurityDescriptorW
+#else
+#define ConvertStringSecurityDescriptorToSecurityDescriptor \
+ConvertStringSecurityDescriptorToSecurityDescriptorA
+#endif
+#else /* __MINGW32_VERSION */
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+#include <sddl.h>
+#endif /* __MINGW32_VERSION */
+
+#ifndef WPA_SUPPLICANT_NAMED_PIPE
+#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant"
+#endif
+#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE)
+
+/* Per-interface ctrl_iface */
+
+#define REQUEST_BUFSIZE 256
+#define REPLY_BUFSIZE 4096
+
+struct ctrl_iface_priv;
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface clients
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_named_pipe.c and should not be touched directly from other files.
+ */
+struct wpa_ctrl_dst {
+       /* Note: OVERLAPPED must be the first member of struct wpa_ctrl_dst */
+       OVERLAPPED overlap;
+       struct wpa_ctrl_dst *next, *prev;
+       struct ctrl_iface_priv *priv;
+       HANDLE pipe;
+       int attached;
+       int debug_level;
+       int errors;
+       char req_buf[REQUEST_BUFSIZE];
+       char *rsp_buf;
+       int used;
+};
+
+
+struct ctrl_iface_priv {
+       struct wpa_supplicant *wpa_s;
+       struct wpa_ctrl_dst *ctrl_dst;
+       SECURITY_ATTRIBUTES attr;
+       int sec_attr_set;
+};
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+                                          int level, const char *buf,
+                                          size_t len);
+
+static void ctrl_close_pipe(struct wpa_ctrl_dst *dst);
+static void wpa_supplicant_ctrl_iface_receive(void *, void *);
+static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
+                                            LPOVERLAPPED overlap);
+
+struct wpa_global_dst;
+static void global_close_pipe(struct wpa_global_dst *dst);
+static void wpa_supplicant_global_iface_receive(void *eloop_data,
+                                               void *user_ctx);
+static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
+                                              LPOVERLAPPED overlap);
+
+
+static int ctrl_broken_pipe(HANDLE pipe, int used)
+{
+       DWORD err;
+
+       if (PeekNamedPipe(pipe, NULL, 0, NULL, NULL, NULL))
+               return 0;
+
+       err = GetLastError();
+       if (err == ERROR_BROKEN_PIPE || (err == ERROR_BAD_PIPE && used))
+               return 1;
+       return 0;
+}
+
+
+static void ctrl_flush_broken_pipes(struct ctrl_iface_priv *priv)
+{
+       struct wpa_ctrl_dst *dst, *next;
+
+       dst = priv->ctrl_dst;
+
+       while (dst) {
+               next = dst->next;
+               if (ctrl_broken_pipe(dst->pipe, dst->used)) {
+                       wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
+                                  dst);
+                       ctrl_close_pipe(dst);
+               }
+               dst = next;
+       }
+}
+
+
+static int ctrl_open_pipe(struct ctrl_iface_priv *priv)
+{
+       struct wpa_ctrl_dst *dst;
+       DWORD err;
+       TCHAR name[256];
+
+       dst = os_zalloc(sizeof(*dst));
+       if (dst == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
+
+       dst->priv = priv;
+       dst->debug_level = MSG_INFO;
+       dst->pipe = INVALID_HANDLE_VALUE;
+
+       dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+       if (dst->overlap.hEvent == NULL) {
+               wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
+                          (int) GetLastError());
+               goto fail;
+       }
+
+       eloop_register_event(dst->overlap.hEvent,
+                            sizeof(dst->overlap.hEvent),
+                            wpa_supplicant_ctrl_iface_receive, dst, NULL);
+
+#ifdef UNICODE
+       _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"),
+                  priv->wpa_s->ifname);
+#else /* UNICODE */
+       os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
+                   priv->wpa_s->ifname);
+#endif /* UNICODE */
+
+       /* TODO: add support for configuring access list for the pipe */
+       dst->pipe = CreateNamedPipe(name,
+                                   PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                                   PIPE_TYPE_MESSAGE |
+                                   PIPE_READMODE_MESSAGE |
+                                   PIPE_WAIT,
+                                   15, REPLY_BUFSIZE, REQUEST_BUFSIZE,
+                                   1000,
+                                   priv->sec_attr_set ? &priv->attr : NULL);
+       if (dst->pipe == INVALID_HANDLE_VALUE) {
+               wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
+                          (int) GetLastError());
+               goto fail;
+       }
+
+       if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
+               wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
+                          (int) GetLastError());
+               CloseHandle(dst->pipe);
+               os_free(dst);
+               return -1;
+       }
+
+       err = GetLastError();
+       switch (err) {
+       case ERROR_IO_PENDING:
+               wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
+                          "progress");
+               break;
+       case ERROR_PIPE_CONNECTED:
+               wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
+                          "connected");
+               if (SetEvent(dst->overlap.hEvent))
+                       break;
+               /* fall through */
+       default:
+               wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
+                          (int) err);
+               CloseHandle(dst->pipe);
+               os_free(dst);
+               return -1;
+       }
+
+       dst->next = priv->ctrl_dst;
+       if (dst->next)
+               dst->next->prev = dst;
+       priv->ctrl_dst = dst;
+
+       return 0;
+
+fail:
+       ctrl_close_pipe(dst);
+       return -1;
+}
+
+
+static void ctrl_close_pipe(struct wpa_ctrl_dst *dst)
+{
+       wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
+
+       if (dst->overlap.hEvent) {
+               eloop_unregister_event(dst->overlap.hEvent,
+                                      sizeof(dst->overlap.hEvent));
+               CloseHandle(dst->overlap.hEvent);
+       }
+
+       if (dst->pipe != INVALID_HANDLE_VALUE) {
+               /*
+                * Could use FlushFileBuffers() here to guarantee that all data
+                * gets delivered to the client, but that can block, so let's
+                * not do this for now.
+                * FlushFileBuffers(dst->pipe);
+                */
+               CloseHandle(dst->pipe);
+       }
+
+       if (dst->prev)
+               dst->prev->next = dst->next;
+       else
+               dst->priv->ctrl_dst = dst->next;
+       if (dst->next)
+               dst->next->prev = dst->prev;
+
+       os_free(dst->rsp_buf);
+       os_free(dst);
+}
+
+
+static VOID WINAPI ctrl_iface_write_completed(DWORD err, DWORD bytes,
+                                             LPOVERLAPPED overlap)
+{
+       struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
+       wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
+                  "err=%d bytes=%d", dst, (int) err, (int) bytes);
+       if (err) {
+               ctrl_close_pipe(dst);
+               return;
+       }
+
+       os_free(dst->rsp_buf);
+       dst->rsp_buf = NULL;
+
+       if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
+                       &dst->overlap, ctrl_iface_read_completed)) {
+               wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
+                          (int) GetLastError());
+               ctrl_close_pipe(dst);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
+}
+
+
+static void wpa_supplicant_ctrl_iface_rx(struct wpa_ctrl_dst *dst, size_t len)
+{
+       struct wpa_supplicant *wpa_s = dst->priv->wpa_s;
+       char *reply = NULL, *send_buf;
+       size_t reply_len = 0, send_len;
+       int new_attached = 0;
+       char *buf = dst->req_buf;
+
+       dst->used = 1;
+       if (len >= REQUEST_BUFSIZE)
+               len = REQUEST_BUFSIZE - 1;
+       buf[len] = '\0';
+
+       if (os_strcmp(buf, "ATTACH") == 0) {
+               dst->attached = 1;
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached");
+               new_attached = 1;
+               reply_len = 2;
+       } else if (os_strcmp(buf, "DETACH") == 0) {
+               dst->attached = 0;
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached");
+               reply_len = 2;
+       } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", buf + 6);
+               dst->debug_level = atoi(buf + 6);
+               reply_len = 2;
+       } else {
+               reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+                                                         &reply_len);
+       }
+
+       if (reply) {
+               send_buf = reply;
+               send_len = reply_len;
+       } else if (reply_len == 2) {
+               send_buf = "OK\n";
+               send_len = 3;
+       } else {
+               send_buf = "FAIL\n";
+               send_len = 5;
+       }
+
+       os_free(dst->rsp_buf);
+       dst->rsp_buf = os_malloc(send_len);
+       if (dst->rsp_buf == NULL) {
+               ctrl_close_pipe(dst);
+               os_free(reply);
+               return;
+       }
+       os_memcpy(dst->rsp_buf, send_buf, send_len);
+       os_free(reply);
+
+       if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
+                        ctrl_iface_write_completed)) {
+               wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
+                          (int) GetLastError());
+               ctrl_close_pipe(dst);
+       } else {
+               wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
+                          dst);
+       }
+
+       if (new_attached)
+               eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+}
+
+
+static VOID WINAPI ctrl_iface_read_completed(DWORD err, DWORD bytes,
+                                            LPOVERLAPPED overlap)
+{
+       struct wpa_ctrl_dst *dst = (struct wpa_ctrl_dst *) overlap;
+       wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
+                  "bytes=%d", dst, (int) err, (int) bytes);
+       if (err == 0 && bytes > 0)
+               wpa_supplicant_ctrl_iface_rx(dst, bytes);
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(void *eloop_data, void *user_ctx)
+{
+       struct wpa_ctrl_dst *dst = eloop_data;
+       struct ctrl_iface_priv *priv = dst->priv;
+       DWORD bytes;
+
+       wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_ctrl_iface_receive");
+       ResetEvent(dst->overlap.hEvent);
+
+       if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
+               wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
+                          (int) GetLastError());
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
+                  "connected");
+
+       /* Open a new named pipe for the next client. */
+       ctrl_open_pipe(priv);
+
+       /* Use write completion function to start reading a command */
+       ctrl_iface_write_completed(0, 0, &dst->overlap);
+
+       ctrl_flush_broken_pipes(priv);
+}
+
+
+static int ctrl_iface_parse(struct ctrl_iface_priv *priv, const char *params)
+{
+       const char *sddl = NULL;
+       TCHAR *t_sddl;
+
+       if (os_strncmp(params, "SDDL=", 5) == 0)
+               sddl = params + 5;
+       if (!sddl) {
+               sddl = os_strstr(params, " SDDL=");
+               if (sddl)
+                       sddl += 6;
+       }
+
+       if (!sddl)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "CTRL: SDDL='%s'", sddl);
+       os_memset(&priv->attr, 0, sizeof(priv->attr));
+       priv->attr.nLength = sizeof(priv->attr);
+       priv->attr.bInheritHandle = FALSE;
+       t_sddl = wpa_strdup_tchar(sddl);
+       if (t_sddl == NULL)
+               return -1;
+       if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
+                   t_sddl, SDDL_REVISION_1,
+                   (PSECURITY_DESCRIPTOR *) (void *)
+                   &priv->attr.lpSecurityDescriptor,
+                   NULL)) {
+               os_free(t_sddl);
+               wpa_printf(MSG_ERROR, "CTRL: SDDL='%s' - could not convert to "
+                          "security descriptor: %d",
+                          sddl, (int) GetLastError());
+               return -1;
+       }
+       os_free(t_sddl);
+
+       priv->sec_attr_set = 1;
+
+       return 0;
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+                                            const char *txt, size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
+               return;
+       wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+       struct ctrl_iface_priv *priv;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       priv->wpa_s = wpa_s;
+
+       if (wpa_s->conf->ctrl_interface == NULL)
+               return priv;
+
+       if (ctrl_iface_parse(priv, wpa_s->conf->ctrl_interface) < 0) {
+               os_free(priv);
+               return NULL;
+       }
+
+       if (ctrl_open_pipe(priv) < 0) {
+               os_free(priv);
+               return NULL;
+       }
+
+       wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+       return priv;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+       while (priv->ctrl_dst)
+               ctrl_close_pipe(priv->ctrl_dst);
+       if (priv->sec_attr_set)
+               LocalFree(priv->attr.lpSecurityDescriptor);
+       os_free(priv);
+}
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+                                          int level, const char *buf,
+                                          size_t len)
+{
+       struct wpa_ctrl_dst *dst, *next;
+       char levelstr[10];
+       int idx;
+       char *sbuf;
+       int llen;
+       DWORD written;
+
+       dst = priv->ctrl_dst;
+       if (dst == NULL)
+               return;
+
+       os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+
+       llen = os_strlen(levelstr);
+       sbuf = os_malloc(llen + len);
+       if (sbuf == NULL)
+               return;
+
+       os_memcpy(sbuf, levelstr, llen);
+       os_memcpy(sbuf + llen, buf, len);
+
+       idx = 0;
+       while (dst) {
+               next = dst->next;
+               if (dst->attached && level >= dst->debug_level) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %p",
+                                  dst);
+                       if (!WriteFile(dst->pipe, sbuf, llen + len, &written,
+                                      NULL)) {
+                               wpa_printf(MSG_DEBUG, "CTRL: WriteFile to dst "
+                                          "%p failed: %d",
+                                          dst, (int) GetLastError());
+                               dst->errors++;
+                               if (dst->errors > 10)
+                                       ctrl_close_pipe(dst);
+                       } else
+                               dst->errors = 0;
+               }
+               idx++;
+               dst = next;
+       }
+       os_free(sbuf);
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
+                  priv->wpa_s->ifname);
+       if (priv->ctrl_dst == NULL)
+               return;
+       WaitForSingleObject(priv->ctrl_dst->pipe, INFINITE);
+}
+
+
+/* Global ctrl_iface */
+
+struct ctrl_iface_global_priv;
+
+struct wpa_global_dst {
+       /* Note: OVERLAPPED must be the first member of struct wpa_global_dst
+        */
+       OVERLAPPED overlap;
+       struct wpa_global_dst *next, *prev;
+       struct ctrl_iface_global_priv *priv;
+       HANDLE pipe;
+       char req_buf[REQUEST_BUFSIZE];
+       char *rsp_buf;
+       int used;
+};
+
+struct ctrl_iface_global_priv {
+       struct wpa_global *global;
+       struct wpa_global_dst *ctrl_dst;
+};
+
+
+static void global_flush_broken_pipes(struct ctrl_iface_global_priv *priv)
+{
+       struct wpa_global_dst *dst, *next;
+
+       dst = priv->ctrl_dst;
+
+       while (dst) {
+               next = dst->next;
+               if (ctrl_broken_pipe(dst->pipe, dst->used)) {
+                       wpa_printf(MSG_DEBUG, "CTRL: closing broken pipe %p",
+                                  dst);
+                       global_close_pipe(dst);
+               }
+               dst = next;
+       }
+}
+
+
+static int global_open_pipe(struct ctrl_iface_global_priv *priv)
+{
+       struct wpa_global_dst *dst;
+       DWORD err;
+
+       dst = os_zalloc(sizeof(*dst));
+       if (dst == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "CTRL: Open pipe %p", dst);
+
+       dst->priv = priv;
+       dst->pipe = INVALID_HANDLE_VALUE;
+
+       dst->overlap.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
+       if (dst->overlap.hEvent == NULL) {
+               wpa_printf(MSG_ERROR, "CTRL: CreateEvent failed: %d",
+                          (int) GetLastError());
+               goto fail;
+       }
+
+       eloop_register_event(dst->overlap.hEvent,
+                            sizeof(dst->overlap.hEvent),
+                            wpa_supplicant_global_iface_receive, dst, NULL);
+
+       /* TODO: add support for configuring access list for the pipe */
+       dst->pipe = CreateNamedPipe(NAMED_PIPE_PREFIX,
+                                   PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
+                                   PIPE_TYPE_MESSAGE |
+                                   PIPE_READMODE_MESSAGE |
+                                   PIPE_WAIT,
+                                   10, REPLY_BUFSIZE, REQUEST_BUFSIZE,
+                                   1000, NULL);
+       if (dst->pipe == INVALID_HANDLE_VALUE) {
+               wpa_printf(MSG_ERROR, "CTRL: CreateNamedPipe failed: %d",
+                          (int) GetLastError());
+               goto fail;
+       }
+
+       if (ConnectNamedPipe(dst->pipe, &dst->overlap)) {
+               wpa_printf(MSG_ERROR, "CTRL: ConnectNamedPipe failed: %d",
+                          (int) GetLastError());
+               CloseHandle(dst->pipe);
+               os_free(dst);
+               return -1;
+       }
+
+       err = GetLastError();
+       switch (err) {
+       case ERROR_IO_PENDING:
+               wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: connection in "
+                          "progress");
+               break;
+       case ERROR_PIPE_CONNECTED:
+               wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe: already "
+                          "connected");
+               if (SetEvent(dst->overlap.hEvent))
+                       break;
+               /* fall through */
+       default:
+               wpa_printf(MSG_DEBUG, "CTRL: ConnectNamedPipe error: %d",
+                          (int) err);
+               CloseHandle(dst->pipe);
+               os_free(dst);
+               return -1;
+       }
+
+       dst->next = priv->ctrl_dst;
+       if (dst->next)
+               dst->next->prev = dst;
+       priv->ctrl_dst = dst;
+
+       return 0;
+
+fail:
+       global_close_pipe(dst);
+       return -1;
+}
+
+
+static void global_close_pipe(struct wpa_global_dst *dst)
+{
+       wpa_printf(MSG_DEBUG, "CTRL: close pipe %p", dst);
+
+       if (dst->overlap.hEvent) {
+               eloop_unregister_event(dst->overlap.hEvent,
+                                      sizeof(dst->overlap.hEvent));
+               CloseHandle(dst->overlap.hEvent);
+       }
+
+       if (dst->pipe != INVALID_HANDLE_VALUE) {
+               /*
+                * Could use FlushFileBuffers() here to guarantee that all data
+                * gets delivered to the client, but that can block, so let's
+                * not do this for now.
+                * FlushFileBuffers(dst->pipe);
+                */
+               CloseHandle(dst->pipe);
+       }
+
+       if (dst->prev)
+               dst->prev->next = dst->next;
+       else
+               dst->priv->ctrl_dst = dst->next;
+       if (dst->next)
+               dst->next->prev = dst->prev;
+
+       os_free(dst->rsp_buf);
+       os_free(dst);
+}
+
+
+static VOID WINAPI global_iface_write_completed(DWORD err, DWORD bytes,
+                                               LPOVERLAPPED overlap)
+{
+       struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
+       wpa_printf(MSG_DEBUG, "CTRL: Overlapped write completed: dst=%p "
+                  "err=%d bytes=%d", dst, (int) err, (int) bytes);
+       if (err) {
+               global_close_pipe(dst);
+               return;
+       }
+
+       os_free(dst->rsp_buf);
+       dst->rsp_buf = NULL;
+
+       if (!ReadFileEx(dst->pipe, dst->req_buf, sizeof(dst->req_buf),
+                       &dst->overlap, global_iface_read_completed)) {
+               wpa_printf(MSG_DEBUG, "CTRL: ReadFileEx failed: %d",
+                          (int) GetLastError());
+               global_close_pipe(dst);
+               /* FIX: if this was the pipe waiting for new global
+                * connections, at this point there are no open global pipes..
+                * Should try to open a new pipe.. */
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "CTRL: Overlapped read started for %p", dst);
+}
+
+
+static void wpa_supplicant_global_iface_rx(struct wpa_global_dst *dst,
+                                          size_t len)
+{
+       struct wpa_global *global = dst->priv->global;
+       char *reply = NULL, *send_buf;
+       size_t reply_len = 0, send_len;
+       char *buf = dst->req_buf;
+
+       dst->used = 1;
+       if (len >= REQUEST_BUFSIZE)
+               len = REQUEST_BUFSIZE - 1;
+       buf[len] = '\0';
+
+       reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
+                                                        &reply_len);
+       if (reply) {
+               send_buf = reply;
+               send_len = reply_len;
+       } else if (reply_len) {
+               send_buf = "FAIL\n";
+               send_len = 5;
+       } else {
+               os_free(dst->rsp_buf);
+               dst->rsp_buf = NULL;
+               return;
+       }
+
+       os_free(dst->rsp_buf);
+       dst->rsp_buf = os_malloc(send_len);
+       if (dst->rsp_buf == NULL) {
+               global_close_pipe(dst);
+               os_free(reply);
+               return;
+       }
+       os_memcpy(dst->rsp_buf, send_buf, send_len);
+       os_free(reply);
+
+       if (!WriteFileEx(dst->pipe, dst->rsp_buf, send_len, &dst->overlap,
+                        global_iface_write_completed)) {
+               wpa_printf(MSG_DEBUG, "CTRL: WriteFileEx failed: %d",
+                          (int) GetLastError());
+               global_close_pipe(dst);
+       } else {
+               wpa_printf(MSG_DEBUG, "CTRL: Overlapped write started for %p",
+                          dst);
+       }
+}
+
+
+static VOID WINAPI global_iface_read_completed(DWORD err, DWORD bytes,
+                                              LPOVERLAPPED overlap)
+{
+       struct wpa_global_dst *dst = (struct wpa_global_dst *) overlap;
+       wpa_printf(MSG_DEBUG, "CTRL: Overlapped read completed: dst=%p err=%d "
+                  "bytes=%d", dst, (int) err, (int) bytes);
+       if (err == 0 && bytes > 0)
+               wpa_supplicant_global_iface_rx(dst, bytes);
+}
+
+
+static void wpa_supplicant_global_iface_receive(void *eloop_data,
+                                               void *user_ctx)
+{
+       struct wpa_global_dst *dst = eloop_data;
+       struct ctrl_iface_global_priv *priv = dst->priv;
+       DWORD bytes;
+
+       wpa_printf(MSG_DEBUG, "CTRL: wpa_supplicant_global_iface_receive");
+       ResetEvent(dst->overlap.hEvent);
+
+       if (!GetOverlappedResult(dst->pipe, &dst->overlap, &bytes, FALSE)) {
+               wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult failed: %d",
+                          (int) GetLastError());
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "CTRL: GetOverlappedResult: New client "
+                  "connected");
+
+       /* Open a new named pipe for the next client. */
+       if (global_open_pipe(priv) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL: global_open_pipe failed");
+               return;
+       }
+
+       /* Use write completion function to start reading a command */
+       global_iface_write_completed(0, 0, &dst->overlap);
+
+       global_flush_broken_pipes(priv);
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+       struct ctrl_iface_global_priv *priv;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       priv->global = global;
+
+       if (global_open_pipe(priv) < 0) {
+               os_free(priv);
+               return NULL;
+       }
+
+       return priv;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+       while (priv->ctrl_dst)
+               global_close_pipe(priv->ctrl_dst);
+       os_free(priv);
+}
diff --git a/wpa_supplicant/ctrl_iface_udp.c b/wpa_supplicant/ctrl_iface_udp.c
new file mode 100644 (file)
index 0000000..110ca4f
--- /dev/null
@@ -0,0 +1,561 @@
+/*
+ * WPA Supplicant / UDP socket -based control interface
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+#include "common/wpa_ctrl.h"
+
+
+#define COOKIE_LEN 8
+
+/* Per-interface ctrl_iface */
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface monitors
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_udp.c and should not be touched directly from other files.
+ */
+struct wpa_ctrl_dst {
+       struct wpa_ctrl_dst *next;
+       struct sockaddr_in addr;
+       socklen_t addrlen;
+       int debug_level;
+       int errors;
+};
+
+
+struct ctrl_iface_priv {
+       struct wpa_supplicant *wpa_s;
+       int sock;
+       struct wpa_ctrl_dst *ctrl_dst;
+       u8 cookie[COOKIE_LEN];
+};
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+                                          int level, const char *buf,
+                                          size_t len);
+
+
+static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+                                           struct sockaddr_in *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_in));
+       dst->addrlen = fromlen;
+       dst->debug_level = MSG_INFO;
+       dst->next = priv->ctrl_dst;
+       priv->ctrl_dst = dst;
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
+                  inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+                                           struct sockaddr_in *from,
+                                           socklen_t fromlen)
+{
+       struct wpa_ctrl_dst *dst, *prev = NULL;
+
+       dst = priv->ctrl_dst;
+       while (dst) {
+               if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
+                   from->sin_port == dst->addr.sin_port) {
+                       if (prev == NULL)
+                               priv->ctrl_dst = dst->next;
+                       else
+                               prev->next = dst->next;
+                       os_free(dst);
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
+                                  "%s:%d", inet_ntoa(from->sin_addr),
+                                  ntohs(from->sin_port));
+                       return 0;
+               }
+               prev = dst;
+               dst = dst->next;
+       }
+       return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+                                          struct sockaddr_in *from,
+                                          socklen_t fromlen,
+                                          char *level)
+{
+       struct wpa_ctrl_dst *dst;
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+       dst = priv->ctrl_dst;
+       while (dst) {
+               if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
+                   from->sin_port == dst->addr.sin_port) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
+                                  "level %s:%d", inet_ntoa(from->sin_addr),
+                                  ntohs(from->sin_port));
+                       dst->debug_level = atoi(level);
+                       return 0;
+               }
+               dst = dst->next;
+       }
+
+       return -1;
+}
+
+
+static char *
+wpa_supplicant_ctrl_iface_get_cookie(struct ctrl_iface_priv *priv,
+                                    size_t *reply_len)
+{
+       char *reply;
+       reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
+       if (reply == NULL) {
+               *reply_len = 1;
+               return NULL;
+       }
+
+       os_memcpy(reply, "COOKIE=", 7);
+       wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
+                        priv->cookie, COOKIE_LEN);
+
+       *reply_len = 7 + 2 * COOKIE_LEN;
+       return reply;
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
+                                             void *sock_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct ctrl_iface_priv *priv = sock_ctx;
+       char buf[256], *pos;
+       int res;
+       struct sockaddr_in from;
+       socklen_t fromlen = sizeof(from);
+       char *reply = NULL;
+       size_t reply_len = 0;
+       int new_attached = 0;
+       u8 cookie[COOKIE_LEN];
+
+       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               perror("recvfrom(ctrl_iface)");
+               return;
+       }
+       if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
+               /*
+                * The OS networking stack is expected to drop this kind of
+                * frames since the socket is bound to only localhost address.
+                * Just in case, drop the frame if it is coming from any other
+                * address.
+                */
+               wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
+                          "source %s", inet_ntoa(from.sin_addr));
+               return;
+       }
+       buf[res] = '\0';
+
+       if (os_strcmp(buf, "GET_COOKIE") == 0) {
+               reply = wpa_supplicant_ctrl_iface_get_cookie(priv, &reply_len);
+               goto done;
+       }
+
+       /*
+        * Require that the client includes a prefix with the 'cookie' value
+        * fetched with GET_COOKIE command. This is used to verify that the
+        * client has access to a bidirectional link over UDP in order to
+        * avoid attacks using forged localhost IP address even if the OS does
+        * not block such frames from remote destinations.
+        */
+       if (os_strncmp(buf, "COOKIE=", 7) != 0) {
+               wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
+                          "drop request");
+               return;
+       }
+
+       if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
+                          "request - drop request");
+               return;
+       }
+
+       if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
+                          "drop request");
+               return;
+       }
+
+       pos = buf + 7 + 2 * COOKIE_LEN;
+       while (*pos == ' ')
+               pos++;
+
+       if (os_strcmp(pos, "ATTACH") == 0) {
+               if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
+                       reply_len = 1;
+               else {
+                       new_attached = 1;
+                       reply_len = 2;
+               }
+       } else if (os_strcmp(pos, "DETACH") == 0) {
+               if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
+                       reply_len = 1;
+               else
+                       reply_len = 2;
+       } else if (os_strncmp(pos, "LEVEL ", 6) == 0) {
+               if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
+                                                   pos + 6))
+                       reply_len = 1;
+               else
+                       reply_len = 2;
+       } else {
+               reply = wpa_supplicant_ctrl_iface_process(wpa_s, pos,
+                                                         &reply_len);
+       }
+
+ done:
+       if (reply) {
+               sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                      fromlen);
+               os_free(reply);
+       } else if (reply_len == 1) {
+               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+                      fromlen);
+       } else if (reply_len == 2) {
+               sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
+                      fromlen);
+       }
+
+       if (new_attached)
+               eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+                                            const char *txt, size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
+               return;
+       wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+       struct ctrl_iface_priv *priv;
+       struct sockaddr_in addr;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       priv->wpa_s = wpa_s;
+       priv->sock = -1;
+       os_get_random(priv->cookie, COOKIE_LEN);
+
+       if (wpa_s->conf->ctrl_interface == NULL)
+               return priv;
+
+       priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (priv->sock < 0) {
+               perror("socket(PF_INET)");
+               goto fail;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl((127 << 24) | 1);
+       addr.sin_port = htons(WPA_CTRL_IFACE_PORT);
+       if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind(AF_INET)");
+               goto fail;
+       }
+
+       eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
+                                wpa_s, priv);
+       wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+       return priv;
+
+fail:
+       if (priv->sock >= 0)
+               close(priv->sock);
+       os_free(priv);
+       return NULL;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+       struct wpa_ctrl_dst *dst, *prev;
+
+       if (priv->sock > -1) {
+               eloop_unregister_read_sock(priv->sock);
+               if (priv->ctrl_dst) {
+                       /*
+                        * Wait a second before closing the control socket if
+                        * there are any attached monitors in order to allow
+                        * them to receive any pending messages.
+                        */
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
+                                  "monitors to receive messages");
+                       os_sleep(1, 0);
+               }
+               close(priv->sock);
+               priv->sock = -1;
+       }
+
+       dst = priv->ctrl_dst;
+       while (dst) {
+               prev = dst;
+               dst = dst->next;
+               os_free(prev);
+       }
+       os_free(priv);
+}
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+                                          int level, const char *buf,
+                                          size_t len)
+{
+       struct wpa_ctrl_dst *dst, *next;
+       char levelstr[10];
+       int idx;
+       char *sbuf;
+       int llen;
+
+       dst = priv->ctrl_dst;
+       if (priv->sock < 0 || dst == NULL)
+               return;
+
+       os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+
+       llen = os_strlen(levelstr);
+       sbuf = os_malloc(llen + len);
+       if (sbuf == NULL)
+               return;
+
+       os_memcpy(sbuf, levelstr, llen);
+       os_memcpy(sbuf + llen, buf, len);
+
+       idx = 0;
+       while (dst) {
+               next = dst->next;
+               if (level >= dst->debug_level) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
+                                  inet_ntoa(dst->addr.sin_addr),
+                                  ntohs(dst->addr.sin_port));
+                       if (sendto(priv->sock, sbuf, llen + len, 0,
+                                  (struct sockaddr *) &dst->addr,
+                                  sizeof(dst->addr)) < 0) {
+                               perror("sendto(CTRL_IFACE monitor)");
+                               dst->errors++;
+                               if (dst->errors > 10) {
+                                       wpa_supplicant_ctrl_iface_detach(
+                                               priv, &dst->addr,
+                                               dst->addrlen);
+                               }
+                       } else
+                               dst->errors = 0;
+               }
+               idx++;
+               dst = next;
+       }
+       os_free(sbuf);
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor",
+                  priv->wpa_s->ifname);
+       eloop_wait_for_read_sock(priv->sock);
+}
+
+
+/* Global ctrl_iface */
+
+struct ctrl_iface_global_priv {
+       int sock;
+       u8 cookie[COOKIE_LEN];
+};
+
+
+static char *
+wpa_supplicant_global_get_cookie(struct ctrl_iface_global_priv *priv,
+                                size_t *reply_len)
+{
+       char *reply;
+       reply = os_malloc(7 + 2 * COOKIE_LEN + 1);
+       if (reply == NULL) {
+               *reply_len = 1;
+               return NULL;
+       }
+
+       os_memcpy(reply, "COOKIE=", 7);
+       wpa_snprintf_hex(reply + 7, 2 * COOKIE_LEN + 1,
+                        priv->cookie, COOKIE_LEN);
+
+       *reply_len = 7 + 2 * COOKIE_LEN;
+       return reply;
+}
+
+
+static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+                                                    void *sock_ctx)
+{
+       struct wpa_global *global = eloop_ctx;
+       struct ctrl_iface_global_priv *priv = sock_ctx;
+       char buf[256], *pos;
+       int res;
+       struct sockaddr_in from;
+       socklen_t fromlen = sizeof(from);
+       char *reply;
+       size_t reply_len;
+       u8 cookie[COOKIE_LEN];
+
+       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               perror("recvfrom(ctrl_iface)");
+               return;
+       }
+       if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
+               /*
+                * The OS networking stack is expected to drop this kind of
+                * frames since the socket is bound to only localhost address.
+                * Just in case, drop the frame if it is coming from any other
+                * address.
+                */
+               wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected "
+                          "source %s", inet_ntoa(from.sin_addr));
+               return;
+       }
+       buf[res] = '\0';
+
+       if (os_strcmp(buf, "GET_COOKIE") == 0) {
+               reply = wpa_supplicant_global_get_cookie(priv, &reply_len);
+               goto done;
+       }
+
+       if (os_strncmp(buf, "COOKIE=", 7) != 0) {
+               wpa_printf(MSG_DEBUG, "CTLR: No cookie in the request - "
+                          "drop request");
+               return;
+       }
+
+       if (hexstr2bin(buf + 7, cookie, COOKIE_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie format in the "
+                          "request - drop request");
+               return;
+       }
+
+       if (os_memcmp(cookie, priv->cookie, COOKIE_LEN) != 0) {
+               wpa_printf(MSG_DEBUG, "CTLR: Invalid cookie in the request - "
+                          "drop request");
+               return;
+       }
+
+       pos = buf + 7 + 2 * COOKIE_LEN;
+       while (*pos == ' ')
+               pos++;
+
+       reply = wpa_supplicant_global_ctrl_iface_process(global, pos,
+                                                        &reply_len);
+
+ done:
+       if (reply) {
+               sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                      fromlen);
+               os_free(reply);
+       } else if (reply_len) {
+               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+                      fromlen);
+       }
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+       struct ctrl_iface_global_priv *priv;
+       struct sockaddr_in addr;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       priv->sock = -1;
+       os_get_random(priv->cookie, COOKIE_LEN);
+
+       if (global->params.ctrl_interface == NULL)
+               return priv;
+
+       wpa_printf(MSG_DEBUG, "Global control interface '%s'",
+                  global->params.ctrl_interface);
+
+       priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (priv->sock < 0) {
+               perror("socket(PF_INET)");
+               goto fail;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sin_family = AF_INET;
+       addr.sin_addr.s_addr = htonl((127 << 24) | 1);
+       addr.sin_port = htons(WPA_GLOBAL_CTRL_IFACE_PORT);
+       if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind(AF_INET)");
+               goto fail;
+       }
+
+       eloop_register_read_sock(priv->sock,
+                                wpa_supplicant_global_ctrl_iface_receive,
+                                global, priv);
+
+       return priv;
+
+fail:
+       if (priv->sock >= 0)
+               close(priv->sock);
+       os_free(priv);
+       return NULL;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+       if (priv->sock >= 0) {
+               eloop_unregister_read_sock(priv->sock);
+               close(priv->sock);
+       }
+       os_free(priv);
+}
diff --git a/wpa_supplicant/ctrl_iface_unix.c b/wpa_supplicant/ctrl_iface_unix.c
new file mode 100644 (file)
index 0000000..84ac760
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * WPA Supplicant / UNIX domain socket -based control interface
+ * 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 <sys/un.h>
+#include <sys/stat.h>
+#include <grp.h>
+#include <stddef.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "ctrl_iface.h"
+
+/* Per-interface ctrl_iface */
+
+/**
+ * struct wpa_ctrl_dst - Internal data structure of control interface monitors
+ *
+ * This structure is used to store information about registered control
+ * interface monitors into struct wpa_supplicant. This data is private to
+ * ctrl_iface_unix.c and should not be touched directly from other files.
+ */
+struct wpa_ctrl_dst {
+       struct dl_list list;
+       struct sockaddr_un addr;
+       socklen_t addrlen;
+       int debug_level;
+       int errors;
+};
+
+
+struct ctrl_iface_priv {
+       struct wpa_supplicant *wpa_s;
+       int sock;
+       struct dl_list ctrl_dst;
+};
+
+
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+                                          int level, const char *buf,
+                                          size_t len);
+
+
+static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+                                           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;
+       dl_list_add(&priv->ctrl_dst, &dst->list);
+       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
+                   (u8 *) from->sun_path,
+                   fromlen - offsetof(struct sockaddr_un, sun_path));
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+                                           struct sockaddr_un *from,
+                                           socklen_t fromlen)
+{
+       struct wpa_ctrl_dst *dst;
+
+       dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) {
+               if (fromlen == dst->addrlen &&
+                   os_memcmp(from->sun_path, dst->addr.sun_path,
+                             fromlen - offsetof(struct sockaddr_un, sun_path))
+                   == 0) {
+                       dl_list_del(&dst->list);
+                       os_free(dst);
+                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
+                                   (u8 *) from->sun_path,
+                                   fromlen -
+                                   offsetof(struct sockaddr_un, sun_path));
+                       return 0;
+               }
+       }
+       return -1;
+}
+
+
+static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+                                          struct sockaddr_un *from,
+                                          socklen_t fromlen,
+                                          char *level)
+{
+       struct wpa_ctrl_dst *dst;
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
+
+       dl_list_for_each(dst, &priv->ctrl_dst, struct wpa_ctrl_dst, list) {
+               if (fromlen == dst->addrlen &&
+                   os_memcmp(from->sun_path, dst->addr.sun_path,
+                             fromlen - offsetof(struct sockaddr_un, sun_path))
+                   == 0) {
+                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
+                                   "level", (u8 *) from->sun_path,
+                                   fromlen -
+                                   offsetof(struct sockaddr_un, sun_path));
+                       dst->debug_level = atoi(level);
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
+                                             void *sock_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct ctrl_iface_priv *priv = sock_ctx;
+       char buf[256];
+       int res;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+       char *reply = NULL;
+       size_t reply_len = 0;
+       int new_attached = 0;
+
+       res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
+                      (struct sockaddr *) &from, &fromlen);
+       if (res < 0) {
+               perror("recvfrom(ctrl_iface)");
+               return;
+       }
+       buf[res] = '\0';
+
+       if (os_strcmp(buf, "ATTACH") == 0) {
+               if (wpa_supplicant_ctrl_iface_attach(priv, &from, fromlen))
+                       reply_len = 1;
+               else {
+                       new_attached = 1;
+                       reply_len = 2;
+               }
+       } else if (os_strcmp(buf, "DETACH") == 0) {
+               if (wpa_supplicant_ctrl_iface_detach(priv, &from, fromlen))
+                       reply_len = 1;
+               else
+                       reply_len = 2;
+       } else if (os_strncmp(buf, "LEVEL ", 6) == 0) {
+               if (wpa_supplicant_ctrl_iface_level(priv, &from, fromlen,
+                                                   buf + 6))
+                       reply_len = 1;
+               else
+                       reply_len = 2;
+       } else {
+               reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+                                                         &reply_len);
+       }
+
+       if (reply) {
+               sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                      fromlen);
+               os_free(reply);
+       } else if (reply_len == 1) {
+               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+                      fromlen);
+       } else if (reply_len == 2) {
+               sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
+                      fromlen);
+       }
+
+       if (new_attached)
+               eapol_sm_notify_ctrl_attached(wpa_s->eapol);
+}
+
+
+static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
+{
+       char *buf;
+       size_t len;
+       char *pbuf, *dir = NULL, *gid_str = NULL;
+       int res;
+
+       if (wpa_s->conf->ctrl_interface == NULL)
+               return NULL;
+
+       pbuf = os_strdup(wpa_s->conf->ctrl_interface);
+       if (pbuf == NULL)
+               return NULL;
+       if (os_strncmp(pbuf, "DIR=", 4) == 0) {
+               dir = pbuf + 4;
+               gid_str = os_strstr(dir, " GROUP=");
+               if (gid_str) {
+                       *gid_str = '\0';
+                       gid_str += 7;
+               }
+       } else
+               dir = pbuf;
+
+       len = os_strlen(dir) + os_strlen(wpa_s->ifname) + 2;
+       buf = os_malloc(len);
+       if (buf == NULL) {
+               os_free(pbuf);
+               return NULL;
+       }
+
+       res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
+       if (res < 0 || (size_t) res >= len) {
+               os_free(pbuf);
+               os_free(buf);
+               return NULL;
+       }
+#ifdef __CYGWIN__
+       {
+               /* Windows/WinPcap uses interface names that are not suitable
+                * as a file name - convert invalid chars to underscores */
+               char *pos = buf;
+               while (*pos) {
+                       if (*pos == '\\')
+                               *pos = '_';
+                       pos++;
+               }
+       }
+#endif /* __CYGWIN__ */
+       os_free(pbuf);
+       return buf;
+}
+
+
+static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level,
+                                            const char *txt, size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s == NULL || wpa_s->ctrl_iface == NULL)
+               return;
+       wpa_supplicant_ctrl_iface_send(wpa_s->ctrl_iface, level, txt, len);
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+       struct ctrl_iface_priv *priv;
+       struct sockaddr_un addr;
+       char *fname = NULL;
+       gid_t gid = 0;
+       int gid_set = 0;
+       char *buf, *dir = NULL, *gid_str = NULL;
+       struct group *grp;
+       char *endp;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       dl_list_init(&priv->ctrl_dst);
+       priv->wpa_s = wpa_s;
+       priv->sock = -1;
+
+       if (wpa_s->conf->ctrl_interface == NULL)
+               return priv;
+
+       buf = os_strdup(wpa_s->conf->ctrl_interface);
+       if (buf == NULL)
+               goto fail;
+       if (os_strncmp(buf, "DIR=", 4) == 0) {
+               dir = buf + 4;
+               gid_str = os_strstr(dir, " GROUP=");
+               if (gid_str) {
+                       *gid_str = '\0';
+                       gid_str += 7;
+               }
+       } else {
+               dir = buf;
+               gid_str = wpa_s->conf->ctrl_interface_group;
+       }
+
+       if (mkdir(dir, 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 (gid_str) {
+               grp = getgrnam(gid_str);
+               if (grp) {
+                       gid = grp->gr_gid;
+                       gid_set = 1;
+                       wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
+                                  " (from group name '%s')",
+                                  (int) gid, gid_str);
+               } else {
+                       /* Group name not found - try to parse this as gid */
+                       gid = strtol(gid_str, &endp, 10);
+                       if (*gid_str == '\0' || *endp != '\0') {
+                               wpa_printf(MSG_ERROR, "CTRL: Invalid group "
+                                          "'%s'", gid_str);
+                               goto fail;
+                       }
+                       gid_set = 1;
+                       wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+                                  (int) gid);
+               }
+       }
+
+       if (gid_set && chown(dir, -1, gid) < 0) {
+               perror("chown[ctrl_interface]");
+               goto fail;
+       }
+
+       /* Make sure the group can enter and read the directory */
+       if (gid_set &&
+           chmod(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP) < 0) {
+               wpa_printf(MSG_ERROR, "CTRL: chmod[ctrl_interface]: %s",
+                          strerror(errno));
+               goto fail;
+       }
+
+       if (os_strlen(dir) + 1 + os_strlen(wpa_s->ifname) >=
+           sizeof(addr.sun_path)) {
+               wpa_printf(MSG_ERROR, "ctrl_iface path limit exceeded");
+               goto fail;
+       }
+
+       priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (priv->sock < 0) {
+               perror("socket(PF_UNIX)");
+               goto fail;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+       addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+       addr.sun_family = AF_UNIX;
+       fname = wpa_supplicant_ctrl_iface_path(wpa_s);
+       if (fname == NULL)
+               goto fail;
+       os_strlcpy(addr.sun_path, fname, sizeof(addr.sun_path));
+       if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               wpa_printf(MSG_DEBUG, "ctrl_iface bind(PF_UNIX) failed: %s",
+                          strerror(errno));
+               if (connect(priv->sock, (struct sockaddr *) &addr,
+                           sizeof(addr)) < 0) {
+                       wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+                                  " allow connections - assuming it was left"
+                                  "over from forced program termination");
+                       if (unlink(fname) < 0) {
+                               perror("unlink[ctrl_iface]");
+                               wpa_printf(MSG_ERROR, "Could not unlink "
+                                          "existing ctrl_iface socket '%s'",
+                                          fname);
+                               goto fail;
+                       }
+                       if (bind(priv->sock, (struct sockaddr *) &addr,
+                                sizeof(addr)) < 0) {
+                               perror("bind(PF_UNIX)");
+                               goto fail;
+                       }
+                       wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+                                  "ctrl_iface socket '%s'", fname);
+               } else {
+                       wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+                                  "be in use - cannot override it");
+                       wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+                                  "not used anymore", fname);
+                       os_free(fname);
+                       fname = NULL;
+                       goto fail;
+               }
+       }
+
+       if (gid_set && chown(fname, -1, 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);
+
+       eloop_register_read_sock(priv->sock, wpa_supplicant_ctrl_iface_receive,
+                                wpa_s, priv);
+       wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+       os_free(buf);
+       return priv;
+
+fail:
+       if (priv->sock >= 0)
+               close(priv->sock);
+       os_free(priv);
+       if (fname) {
+               unlink(fname);
+               os_free(fname);
+       }
+       os_free(buf);
+       return NULL;
+}
+
+
+void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
+{
+       struct wpa_ctrl_dst *dst, *prev;
+
+       if (priv->sock > -1) {
+               char *fname;
+               char *buf, *dir = NULL, *gid_str = NULL;
+               eloop_unregister_read_sock(priv->sock);
+               if (!dl_list_empty(&priv->ctrl_dst)) {
+                       /*
+                        * Wait a second before closing the control socket if
+                        * there are any attached monitors in order to allow
+                        * them to receive any pending messages.
+                        */
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
+                                  "monitors to receive messages");
+                       os_sleep(1, 0);
+               }
+               close(priv->sock);
+               priv->sock = -1;
+               fname = wpa_supplicant_ctrl_iface_path(priv->wpa_s);
+               if (fname) {
+                       unlink(fname);
+                       os_free(fname);
+               }
+
+               buf = os_strdup(priv->wpa_s->conf->ctrl_interface);
+               if (buf == NULL)
+                       goto free_dst;
+               if (os_strncmp(buf, "DIR=", 4) == 0) {
+                       dir = buf + 4;
+                       gid_str = os_strstr(dir, " GROUP=");
+                       if (gid_str) {
+                               *gid_str = '\0';
+                               gid_str += 7;
+                       }
+               } else
+                       dir = buf;
+
+               if (rmdir(dir) < 0) {
+                       if (errno == ENOTEMPTY) {
+                               wpa_printf(MSG_DEBUG, "Control interface "
+                                          "directory not empty - leaving it "
+                                          "behind");
+                       } else {
+                               perror("rmdir[ctrl_interface]");
+                       }
+               }
+               os_free(buf);
+       }
+
+free_dst:
+       dl_list_for_each_safe(dst, prev, &priv->ctrl_dst, struct wpa_ctrl_dst,
+                             list)
+               os_free(dst);
+       os_free(priv);
+}
+
+
+/**
+ * wpa_supplicant_ctrl_iface_send - Send a control interface packet to monitors
+ * @priv: Pointer to private data from wpa_supplicant_ctrl_iface_init()
+ * @level: Priority level of the message
+ * @buf: Message data
+ * @len: Message length
+ *
+ * Send a packet to all monitor programs attached to the control interface.
+ */
+static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
+                                          int level, const char *buf,
+                                          size_t len)
+{
+       struct wpa_ctrl_dst *dst, *next;
+       char levelstr[10];
+       int idx, res;
+       struct msghdr msg;
+       struct iovec io[2];
+
+       if (priv->sock < 0 || dl_list_empty(&priv->ctrl_dst))
+               return;
+
+       res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
+       if (res < 0 || (size_t) res >= sizeof(levelstr))
+               return;
+       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;
+       dl_list_for_each_safe(dst, next, &priv->ctrl_dst, struct wpa_ctrl_dst,
+                             list) {
+               if (level >= dst->debug_level) {
+                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
+                                   (u8 *) dst->addr.sun_path, dst->addrlen -
+                                   offsetof(struct sockaddr_un, sun_path));
+                       msg.msg_name = (void *) &dst->addr;
+                       msg.msg_namelen = dst->addrlen;
+                       if (sendmsg(priv->sock, &msg, 0) < 0) {
+                               int _errno = errno;
+                               wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
+                                          "%d - %s",
+                                          idx, errno, strerror(errno));
+                               dst->errors++;
+                               if (dst->errors > 1000 ||
+                                   (_errno != ENOBUFS && dst->errors > 10) ||
+                                   _errno == ENOENT) {
+                                       wpa_supplicant_ctrl_iface_detach(
+                                               priv, &dst->addr,
+                                               dst->addrlen);
+                               }
+                       } else
+                               dst->errors = 0;
+               }
+               idx++;
+       }
+}
+
+
+void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
+{
+       char buf[256];
+       int res;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+
+       for (;;) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE - %s - wait for monitor to "
+                          "attach", priv->wpa_s->ifname);
+               eloop_wait_for_read_sock(priv->sock);
+
+               res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0,
+                              (struct sockaddr *) &from, &fromlen);
+               if (res < 0) {
+                       perror("recvfrom(ctrl_iface)");
+                       continue;
+               }
+               buf[res] = '\0';
+
+               if (os_strcmp(buf, "ATTACH") == 0) {
+                       /* handle ATTACH signal of first monitor interface */
+                       if (!wpa_supplicant_ctrl_iface_attach(priv, &from,
+                                                             fromlen)) {
+                               sendto(priv->sock, "OK\n", 3, 0,
+                                      (struct sockaddr *) &from, fromlen);
+                               /* OK to continue */
+                               return;
+                       } else {
+                               sendto(priv->sock, "FAIL\n", 5, 0,
+                                      (struct sockaddr *) &from, fromlen);
+                       }
+               } else {
+                       /* return FAIL for all other signals */
+                       sendto(priv->sock, "FAIL\n", 5, 0,
+                              (struct sockaddr *) &from, fromlen);
+               }
+       }
+}
+
+
+/* Global ctrl_iface */
+
+struct ctrl_iface_global_priv {
+       struct wpa_global *global;
+       int sock;
+};
+
+
+static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
+                                                    void *sock_ctx)
+{
+       struct wpa_global *global = eloop_ctx;
+       char buf[256];
+       int res;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+       char *reply;
+       size_t 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';
+
+       reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
+                                                        &reply_len);
+
+       if (reply) {
+               sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                      fromlen);
+               os_free(reply);
+       } else if (reply_len) {
+               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+                      fromlen);
+       }
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+       struct ctrl_iface_global_priv *priv;
+       struct sockaddr_un addr;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       priv->global = global;
+       priv->sock = -1;
+
+       if (global->params.ctrl_interface == NULL)
+               return priv;
+
+       wpa_printf(MSG_DEBUG, "Global control interface '%s'",
+                  global->params.ctrl_interface);
+
+       priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (priv->sock < 0) {
+               perror("socket(PF_UNIX)");
+               goto fail;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+       addr.sun_len = sizeof(addr);
+#endif /* __FreeBSD__ */
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, global->params.ctrl_interface,
+                  sizeof(addr.sun_path));
+       if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               perror("bind(PF_UNIX)");
+               if (connect(priv->sock, (struct sockaddr *) &addr,
+                           sizeof(addr)) < 0) {
+                       wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
+                                  " allow connections - assuming it was left"
+                                  "over from forced program termination");
+                       if (unlink(global->params.ctrl_interface) < 0) {
+                               perror("unlink[ctrl_iface]");
+                               wpa_printf(MSG_ERROR, "Could not unlink "
+                                          "existing ctrl_iface socket '%s'",
+                                          global->params.ctrl_interface);
+                               goto fail;
+                       }
+                       if (bind(priv->sock, (struct sockaddr *) &addr,
+                                sizeof(addr)) < 0) {
+                               perror("bind(PF_UNIX)");
+                               goto fail;
+                       }
+                       wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+                                  "ctrl_iface socket '%s'",
+                                  global->params.ctrl_interface);
+               } else {
+                       wpa_printf(MSG_INFO, "ctrl_iface exists and seems to "
+                                  "be in use - cannot override it");
+                       wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+                                  "not used anymore",
+                                  global->params.ctrl_interface);
+                       goto fail;
+               }
+       }
+
+       eloop_register_read_sock(priv->sock,
+                                wpa_supplicant_global_ctrl_iface_receive,
+                                global, NULL);
+
+       return priv;
+
+fail:
+       if (priv->sock >= 0)
+               close(priv->sock);
+       os_free(priv);
+       return NULL;
+}
+
+
+void
+wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
+{
+       if (priv->sock >= 0) {
+               eloop_unregister_read_sock(priv->sock);
+               close(priv->sock);
+       }
+       if (priv->global->params.ctrl_interface)
+               unlink(priv->global->params.ctrl_interface);
+       os_free(priv);
+}
diff --git a/wpa_supplicant/dbus/.gitignore b/wpa_supplicant/dbus/.gitignore
new file mode 100644 (file)
index 0000000..6db2468
--- /dev/null
@@ -0,0 +1 @@
+libwpadbus.a
diff --git a/wpa_supplicant/dbus/Makefile b/wpa_supplicant/dbus/Makefile
new file mode 100644 (file)
index 0000000..cfaf58d
--- /dev/null
@@ -0,0 +1,84 @@
+all: libwpadbus.a
+
+clean:
+       rm -f *~ *.o *.d
+       rm -f libwpadbus.a
+
+install:
+       @echo Nothing to be made.
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src -I../../src/utils
+
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+%.o: %.c
+       $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+       @$(E) "  CC " $<
+
+
+ifdef CONFIG_WPS
+CFLAGS += -DCONFIG_WPS
+endif
+
+CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_NEW
+CFLAGS += -DCONFIG_CTRL_IFACE_DBUS
+
+ifndef DBUS_LIBS
+DBUS_LIBS := $(shell pkg-config --libs dbus-1)
+endif
+ifndef DBUS_INCLUDE
+DBUS_INCLUDE := $(shell pkg-config --cflags dbus-1)
+endif
+ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+CFLAGS += -DCONFIG_CTRL_IFACE_DBUS_INTRO
+DBUS_INCLUDE += $(shell xml2-config --cflags)
+DBUS_LIBS += $(shell xml2-config --libs)
+endif
+
+dbus_version=$(subst ., ,$(shell pkg-config --modversion dbus-1))
+DBUS_VERSION_MAJOR=$(word 1,$(dbus_version))
+DBUS_VERSION_MINOR=$(word 2,$(dbus_version))
+ifeq ($(DBUS_VERSION_MAJOR),)
+DBUS_VERSION_MAJOR=0
+endif
+ifeq ($(DBUS_VERSION_MINOR),)
+DBUS_VERSION_MINOR=0
+endif
+DBUS_INCLUDE += -DDBUS_VERSION_MAJOR=$(DBUS_VERSION_MAJOR)
+DBUS_INCLUDE += -DDBUS_VERSION_MINOR=$(DBUS_VERSION_MINOR)
+
+CFLAGS += $(DBUS_INCLUDE)
+
+LIB_OBJS= \
+       dbus_common.o \
+       dbus_old.o \
+       dbus_old_handlers.o \
+       dbus_new.o \
+       dbus_new_handlers.o \
+       dbus_new_helpers.o \
+       dbus_new_introspect.o \
+       dbus_dict_helpers.o
+
+ifdef CONFIG_WPS
+LIB_OBJS += dbus_old_handlers_wps.o
+LIB_OBJS += dbus_new_handlers_wps.o
+endif
+
+libwpadbus.a: $(LIB_OBJS)
+       $(AR) crT $@ $?
+
+-include $(OBJS:%.o=%.d)
diff --git a/wpa_supplicant/dbus/dbus-wpa_supplicant.conf b/wpa_supplicant/dbus/dbus-wpa_supplicant.conf
new file mode 100644 (file)
index 0000000..c091234
--- /dev/null
@@ -0,0 +1,27 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+        <policy user="root">
+                <allow own="fi.epitest.hostap.WPASupplicant"/>
+
+                <allow send_destination="fi.epitest.hostap.WPASupplicant"/>
+                <allow send_interface="fi.epitest.hostap.WPASupplicant"/>
+
+                <allow own="fi.w1.wpa_supplicant1"/>
+
+                <allow send_destination="fi.w1.wpa_supplicant1"/>
+                <allow send_interface="fi.w1.wpa_supplicant1"/>
+                <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
+        </policy>
+        <policy context="default">
+                <deny own="fi.epitest.hostap.WPASupplicant"/>
+                <deny send_destination="fi.epitest.hostap.WPASupplicant"/>
+                <deny send_interface="fi.epitest.hostap.WPASupplicant"/>
+
+                <deny own="fi.w1.wpa_supplicant1"/>
+                <deny send_destination="fi.w1.wpa_supplicant1"/>
+                <deny send_interface="fi.w1.wpa_supplicant1"/>
+                <deny receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
+        </policy>
+</busconfig>
diff --git a/wpa_supplicant/dbus/dbus_common.c b/wpa_supplicant/dbus/dbus_common.c
new file mode 100644 (file)
index 0000000..5850636
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * wpa_supplicant D-Bus control interface - common functionality
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 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 "utils/includes.h"
+#include <dbus/dbus.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "dbus_common.h"
+#include "dbus_common_i.h"
+#include "dbus_new.h"
+#include "dbus_old.h"
+
+
+#ifndef SIGPOLL
+#ifdef SIGIO
+/*
+ * If we do not have SIGPOLL, try to use SIGIO instead. This is needed for
+ * FreeBSD.
+ */
+#define SIGPOLL SIGIO
+#endif
+#endif
+
+
+static void dispatch_data(DBusConnection *con)
+{
+       while (dbus_connection_get_dispatch_status(con) ==
+              DBUS_DISPATCH_DATA_REMAINS)
+               dbus_connection_dispatch(con);
+}
+
+
+/**
+ * dispatch_initial_dbus_messages - Dispatch initial dbus messages after
+ *     claiming bus name
+ * @eloop_ctx: the DBusConnection to dispatch on
+ * @timeout_ctx: unused
+ *
+ * If clients are quick to notice that service claimed its bus name,
+ * there may have been messages that came in before initialization was
+ * all finished.  Dispatch those here.
+ */
+static void dispatch_initial_dbus_messages(void *eloop_ctx, void *timeout_ctx)
+{
+       DBusConnection *con = eloop_ctx;
+       dispatch_data(con);
+}
+
+
+static void process_watch(struct wpas_dbus_priv *priv,
+                         DBusWatch *watch, eloop_event_type type)
+{
+       dbus_connection_ref(priv->con);
+
+       priv->should_dispatch = 0;
+
+       if (type == EVENT_TYPE_READ)
+               dbus_watch_handle(watch, DBUS_WATCH_READABLE);
+       else if (type == EVENT_TYPE_WRITE)
+               dbus_watch_handle(watch, DBUS_WATCH_WRITABLE);
+       else if (type == EVENT_TYPE_EXCEPTION)
+               dbus_watch_handle(watch, DBUS_WATCH_ERROR);
+
+       if (priv->should_dispatch) {
+               dispatch_data(priv->con);
+               priv->should_dispatch = 0;
+       }
+
+       dbus_connection_unref(priv->con);
+}
+
+
+static void process_watch_exception(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_EXCEPTION);
+}
+
+
+static void process_watch_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_READ);
+}
+
+
+static void process_watch_write(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       process_watch(eloop_ctx, sock_ctx, EVENT_TYPE_WRITE);
+}
+
+
+static dbus_bool_t add_watch(DBusWatch *watch, void *data)
+{
+       struct wpas_dbus_priv *priv = data;
+       unsigned int flags;
+       int fd;
+
+       if (!dbus_watch_get_enabled(watch))
+               return TRUE;
+
+       flags = dbus_watch_get_flags(watch);
+       fd = dbus_watch_get_unix_fd(watch);
+
+       eloop_register_sock(fd, EVENT_TYPE_EXCEPTION, process_watch_exception,
+                           priv, watch);
+
+       if (flags & DBUS_WATCH_READABLE) {
+               eloop_register_sock(fd, EVENT_TYPE_READ, process_watch_read,
+                                   priv, watch);
+       }
+       if (flags & DBUS_WATCH_WRITABLE) {
+               eloop_register_sock(fd, EVENT_TYPE_WRITE, process_watch_write,
+                                   priv, watch);
+       }
+
+       dbus_watch_set_data(watch, priv, NULL);
+
+       return TRUE;
+}
+
+
+static void remove_watch(DBusWatch *watch, void *data)
+{
+       unsigned int flags;
+       int fd;
+
+       flags = dbus_watch_get_flags(watch);
+       fd = dbus_watch_get_unix_fd(watch);
+
+       eloop_unregister_sock(fd, EVENT_TYPE_EXCEPTION);
+
+       if (flags & DBUS_WATCH_READABLE)
+               eloop_unregister_sock(fd, EVENT_TYPE_READ);
+       if (flags & DBUS_WATCH_WRITABLE)
+               eloop_unregister_sock(fd, EVENT_TYPE_WRITE);
+
+       dbus_watch_set_data(watch, NULL, NULL);
+}
+
+
+static void watch_toggled(DBusWatch *watch, void *data)
+{
+       if (dbus_watch_get_enabled(watch))
+               add_watch(watch, data);
+       else
+               remove_watch(watch, data);
+}
+
+
+static void process_timeout(void *eloop_ctx, void *sock_ctx)
+{
+       DBusTimeout *timeout = sock_ctx;
+       dbus_timeout_handle(timeout);
+}
+
+
+static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
+{
+       struct wpas_dbus_priv *priv = data;
+       if (!dbus_timeout_get_enabled(timeout))
+               return TRUE;
+
+       eloop_register_timeout(0, dbus_timeout_get_interval(timeout) * 1000,
+                              process_timeout, priv, timeout);
+
+       dbus_timeout_set_data(timeout, priv, NULL);
+
+       return TRUE;
+}
+
+
+static void remove_timeout(DBusTimeout *timeout, void *data)
+{
+       struct wpas_dbus_priv *priv = data;
+       eloop_cancel_timeout(process_timeout, priv, timeout);
+       dbus_timeout_set_data(timeout, NULL, NULL);
+}
+
+
+static void timeout_toggled(DBusTimeout *timeout, void *data)
+{
+       if (dbus_timeout_get_enabled(timeout))
+               add_timeout(timeout, data);
+       else
+               remove_timeout(timeout, data);
+}
+
+
+static void process_wakeup_main(int sig, void *signal_ctx)
+{
+       struct wpas_dbus_priv *priv = signal_ctx;
+
+       if (sig != SIGPOLL || !priv->con)
+               return;
+
+       if (dbus_connection_get_dispatch_status(priv->con) !=
+           DBUS_DISPATCH_DATA_REMAINS)
+               return;
+
+       /* Only dispatch once - we do not want to starve other events */
+       dbus_connection_ref(priv->con);
+       dbus_connection_dispatch(priv->con);
+       dbus_connection_unref(priv->con);
+}
+
+
+/**
+ * wakeup_main - Attempt to wake our mainloop up
+ * @data: dbus control interface private data
+ *
+ * Try to wake up the main eloop so it will process
+ * dbus events that may have happened.
+ */
+static void wakeup_main(void *data)
+{
+       struct wpas_dbus_priv *priv = data;
+
+       /* Use SIGPOLL to break out of the eloop select() */
+       raise(SIGPOLL);
+       priv->should_dispatch = 1;
+}
+
+
+/**
+ * integrate_with_eloop - Register our mainloop integration with dbus
+ * @connection: connection to the system message bus
+ * @priv: a dbus control interface data structure
+ * Returns: 0 on success, -1 on failure
+ */
+static int integrate_with_eloop(struct wpas_dbus_priv *priv)
+{
+       if (!dbus_connection_set_watch_functions(priv->con, add_watch,
+                                                remove_watch, watch_toggled,
+                                                priv, NULL) ||
+           !dbus_connection_set_timeout_functions(priv->con, add_timeout,
+                                                  remove_timeout,
+                                                  timeout_toggled, priv,
+                                                  NULL)) {
+               wpa_printf(MSG_ERROR, "dbus: Failed to set callback "
+                          "functions");
+               return -1;
+       }
+
+       if (eloop_register_signal(SIGPOLL, process_wakeup_main, priv))
+               return -1;
+       dbus_connection_set_wakeup_main_function(priv->con, wakeup_main,
+                                                priv, NULL);
+
+       return 0;
+}
+
+
+static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
+{
+       DBusError error;
+       int ret = 0;
+
+       /* Get a reference to the system bus */
+       dbus_error_init(&error);
+       priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
+       if (!priv->con) {
+               wpa_printf(MSG_ERROR, "dbus: Could not acquire the system "
+                          "bus: %s - %s", error.name, error.message);
+               ret = -1;
+       }
+       dbus_error_free(&error);
+
+       return ret;
+}
+
+
+static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv)
+{
+       /* Tell dbus about our mainloop integration functions */
+       integrate_with_eloop(priv);
+
+       /*
+        * Dispatch initial DBus messages that may have come in since the bus
+        * name was claimed above. Happens when clients are quick to notice the
+        * service.
+        *
+        * FIXME: is there a better solution to this problem?
+        */
+       eloop_register_timeout(0, 50, dispatch_initial_dbus_messages,
+                              priv->con, NULL);
+
+       return 0;
+}
+
+
+static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv)
+{
+       if (priv->con) {
+               eloop_cancel_timeout(dispatch_initial_dbus_messages,
+                                    priv->con, NULL);
+               dbus_connection_set_watch_functions(priv->con, NULL, NULL,
+                                                   NULL, NULL, NULL);
+               dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
+                                                     NULL, NULL, NULL);
+               dbus_connection_unref(priv->con);
+       }
+
+       os_free(priv);
+}
+
+
+struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global)
+{
+       struct wpas_dbus_priv *priv;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       priv->global = global;
+
+       if (wpas_dbus_init_common(priv) < 0) {
+               wpas_dbus_deinit(priv);
+               return NULL;
+       }
+
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+       if (wpas_dbus_ctrl_iface_init(priv) < 0) {
+               wpas_dbus_deinit(priv);
+               return NULL;
+       }
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+#ifdef CONFIG_CTRL_IFACE_DBUS
+       if (wpa_supplicant_dbus_ctrl_iface_init(priv) < 0) {
+               wpas_dbus_deinit(priv);
+               return NULL;
+       }
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+
+       if (wpas_dbus_init_common_finish(priv) < 0) {
+               wpas_dbus_deinit(priv);
+               return NULL;
+       }
+
+       return priv;
+}
+
+
+void wpas_dbus_deinit(struct wpas_dbus_priv *priv)
+{
+       if (priv == NULL)
+               return;
+
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+       wpas_dbus_ctrl_iface_deinit(priv);
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+#ifdef CONFIG_CTRL_IFACE_DBUS
+       /* TODO: is any deinit needed? */
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+
+       wpas_dbus_deinit_common(priv);
+}
diff --git a/wpa_supplicant/dbus/dbus_common.h b/wpa_supplicant/dbus/dbus_common.h
new file mode 100644 (file)
index 0000000..50da09b
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * wpa_supplicant D-Bus control interface - common definitions
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 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.
+ */
+
+#ifndef DBUS_COMMON_H
+#define DBUS_COMMON_H
+
+struct wpas_dbus_priv;
+struct wpa_global;
+
+struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global);
+void wpas_dbus_deinit(struct wpas_dbus_priv *priv);
+
+#endif /* DBUS_COMMON_H */
diff --git a/wpa_supplicant/dbus/dbus_common_i.h b/wpa_supplicant/dbus/dbus_common_i.h
new file mode 100644 (file)
index 0000000..9dab1ee
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * wpa_supplicant D-Bus control interface - internal definitions
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 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.
+ */
+
+#ifndef DBUS_COMMON_I_H
+#define DBUS_COMMON_I_H
+
+#include <dbus/dbus.h>
+
+struct wpas_dbus_priv {
+       DBusConnection *con;
+       int should_dispatch;
+       struct wpa_global *global;
+       u32 next_objid;
+       int dbus_new_initialized;
+};
+
+#endif /* DBUS_COMMON_I_H */
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.c b/wpa_supplicant/dbus/dbus_dict_helpers.c
new file mode 100644 (file)
index 0000000..b3aff40
--- /dev/null
@@ -0,0 +1,923 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 <dbus/dbus.h>
+
+#include "common.h"
+#include "dbus_dict_helpers.h"
+
+
+/**
+ * Start a dict in a dbus message.  Should be paired with a call to
+ * wpa_dbus_dict_close_write().
+ *
+ * @param iter A valid dbus message iterator
+ * @param iter_dict (out) A dict iterator to pass to further dict functions
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter,
+                                    DBusMessageIter *iter_dict)
+{
+       dbus_bool_t result;
+
+       if (!iter || !iter_dict)
+               return FALSE;
+
+       result = dbus_message_iter_open_container(
+               iter,
+               DBUS_TYPE_ARRAY,
+               DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+               DBUS_TYPE_STRING_AS_STRING
+               DBUS_TYPE_VARIANT_AS_STRING
+               DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+               iter_dict);
+       return result;
+}
+
+
+/**
+ * End a dict element in a dbus message.  Should be paired with
+ * a call to wpa_dbus_dict_open_write().
+ *
+ * @param iter valid dbus message iterator, same as passed to
+ *    wpa_dbus_dict_open_write()
+ * @param iter_dict a dbus dict iterator returned from
+ *    wpa_dbus_dict_open_write()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter,
+                                     DBusMessageIter *iter_dict)
+{
+       if (!iter || !iter_dict)
+               return FALSE;
+
+       return dbus_message_iter_close_container(iter, iter_dict);
+}
+
+
+const char * wpa_dbus_type_as_string(const int type)
+{
+       switch(type) {
+       case DBUS_TYPE_BYTE:
+               return DBUS_TYPE_BYTE_AS_STRING;
+       case DBUS_TYPE_BOOLEAN:
+               return DBUS_TYPE_BOOLEAN_AS_STRING;
+       case DBUS_TYPE_INT16:
+               return DBUS_TYPE_INT16_AS_STRING;
+       case DBUS_TYPE_UINT16:
+               return DBUS_TYPE_UINT16_AS_STRING;
+       case DBUS_TYPE_INT32:
+               return DBUS_TYPE_INT32_AS_STRING;
+       case DBUS_TYPE_UINT32:
+               return DBUS_TYPE_UINT32_AS_STRING;
+       case DBUS_TYPE_INT64:
+               return DBUS_TYPE_INT64_AS_STRING;
+       case DBUS_TYPE_UINT64:
+               return DBUS_TYPE_UINT64_AS_STRING;
+       case DBUS_TYPE_DOUBLE:
+               return DBUS_TYPE_DOUBLE_AS_STRING;
+       case DBUS_TYPE_STRING:
+               return DBUS_TYPE_STRING_AS_STRING;
+       case DBUS_TYPE_OBJECT_PATH:
+               return DBUS_TYPE_OBJECT_PATH_AS_STRING;
+       case DBUS_TYPE_ARRAY:
+               return DBUS_TYPE_ARRAY_AS_STRING;
+       default:
+               return NULL;
+       }
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_start(
+       DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry,
+       const char *key, const int value_type)
+{
+       if (!dbus_message_iter_open_container(iter_dict,
+                                             DBUS_TYPE_DICT_ENTRY, NULL,
+                                             iter_dict_entry))
+               return FALSE;
+
+       if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING,
+                                           &key))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_end(
+       DBusMessageIter *iter_dict, DBusMessageIter *iter_dict_entry,
+       DBusMessageIter *iter_dict_val)
+{
+       if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val))
+               return FALSE;
+       if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict,
+                                                 const char *key,
+                                                 const int value_type,
+                                                 const void *value)
+{
+       DBusMessageIter iter_dict_entry, iter_dict_val;
+       const char *type_as_string = NULL;
+
+       if (key == NULL)
+               return FALSE;
+
+       type_as_string = wpa_dbus_type_as_string(value_type);
+       if (!type_as_string)
+               return FALSE;
+
+       if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
+                                           key, value_type))
+               return FALSE;
+
+       if (!dbus_message_iter_open_container(&iter_dict_entry,
+                                             DBUS_TYPE_VARIANT,
+                                             type_as_string, &iter_dict_val))
+               return FALSE;
+
+       if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value))
+               return FALSE;
+
+       if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+                                         &iter_dict_val))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array(
+       DBusMessageIter *iter_dict, const char *key,
+       const char *value, const dbus_uint32_t value_len)
+{
+       DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+       dbus_uint32_t i;
+
+       if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
+                                           key, DBUS_TYPE_ARRAY))
+               return FALSE;
+
+       if (!dbus_message_iter_open_container(&iter_dict_entry,
+                                             DBUS_TYPE_VARIANT,
+                                             DBUS_TYPE_ARRAY_AS_STRING
+                                             DBUS_TYPE_BYTE_AS_STRING,
+                                             &iter_dict_val))
+               return FALSE;
+
+       if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY,
+                                             DBUS_TYPE_BYTE_AS_STRING,
+                                             &iter_array))
+               return FALSE;
+
+       for (i = 0; i < value_len; i++) {
+               if (!dbus_message_iter_append_basic(&iter_array,
+                                                   DBUS_TYPE_BYTE,
+                                                   &(value[i])))
+                       return FALSE;
+       }
+
+       if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array))
+               return FALSE;
+
+       if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+                                         &iter_dict_val))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/**
+ * Add a string entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The string value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict,
+                                       const char *key, const char *value)
+{
+       if (!value)
+               return FALSE;
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_STRING,
+                                             &value);
+}
+
+
+/**
+ * Add a byte entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The byte value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
+                                     const char *key, const char value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_BYTE,
+                                             &value);
+}
+
+
+/**
+ * Add a boolean entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The boolean value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict,
+                                     const char *key, const dbus_bool_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key,
+                                             DBUS_TYPE_BOOLEAN, &value);
+}
+
+
+/**
+ * Add a 16-bit signed integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 16-bit signed integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict,
+                                      const char *key,
+                                      const dbus_int16_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT16,
+                                             &value);
+}
+
+
+/**
+ * Add a 16-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 16-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const dbus_uint16_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT16,
+                                             &value);
+}
+
+
+/**
+ * Add a 32-bit signed integer to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 32-bit signed integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict,
+                                      const char *key,
+                                      const dbus_int32_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT32,
+                                             &value);
+}
+
+
+/**
+ * Add a 32-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 32-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const dbus_uint32_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT32,
+                                             &value);
+}
+
+
+/**
+ * Add a 64-bit integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
+                                      const char *key,
+                                      const dbus_int64_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_INT64,
+                                             &value);
+}
+
+
+/**
+ * Add a 64-bit unsigned integer entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The 64-bit unsigned integer value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const dbus_uint64_t value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_UINT64,
+                                             &value);
+}
+
+
+/**
+ * Add a double-precision floating point entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The double-precision floating point value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
+                                       const char *key, const double value)
+{
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key, DBUS_TYPE_DOUBLE,
+                                             &value);
+}
+
+
+/**
+ * Add a DBus object path entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The DBus object path value
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
+                                            const char *key,
+                                            const char *value)
+{
+       if (!value)
+               return FALSE;
+       return _wpa_dbus_add_dict_entry_basic(iter_dict, key,
+                                             DBUS_TYPE_OBJECT_PATH, &value);
+}
+
+
+/**
+ * Add a byte array entry to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param value The byte array
+ * @param value_len The length of the byte array, in bytes
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
+                                           const char *key,
+                                           const char *value,
+                                           const dbus_uint32_t value_len)
+{
+       if (!key)
+               return FALSE;
+       if (!value && (value_len != 0))
+               return FALSE;
+       return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value,
+                                                  value_len);
+}
+
+
+/**
+ * Begin a string array entry in the dict
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param iter_dict_entry A private DBusMessageIter provided by the caller to
+ *                        be passed to wpa_dbus_dict_end_string_array()
+ * @param iter_dict_val A private DBusMessageIter provided by the caller to
+ *                      be passed to wpa_dbus_dict_end_string_array()
+ * @param iter_array On return, the DBusMessageIter to be passed to
+ *                   wpa_dbus_dict_string_array_add_element()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+                                            const char *key,
+                                            DBusMessageIter *iter_dict_entry,
+                                            DBusMessageIter *iter_dict_val,
+                                            DBusMessageIter *iter_array)
+{
+       if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
+               return FALSE;
+
+       if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry,
+                                           key, DBUS_TYPE_ARRAY))
+               return FALSE;
+
+       if (!dbus_message_iter_open_container(iter_dict_entry,
+                                             DBUS_TYPE_VARIANT,
+                                             DBUS_TYPE_ARRAY_AS_STRING
+                                             DBUS_TYPE_STRING_AS_STRING,
+                                             iter_dict_val))
+               return FALSE;
+
+       if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
+                                             DBUS_TYPE_BYTE_AS_STRING,
+                                             iter_array))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/**
+ * Add a single string element to a string array dict entry
+ *
+ * @param iter_array A valid DBusMessageIter returned from
+ *                   wpa_dbus_dict_begin_string_array()'s
+ *                   iter_array parameter
+ * @param elem The string element to be added to the dict entry's string array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
+                                                  const char *elem)
+{
+       if (!iter_array || !elem)
+               return FALSE;
+
+       return dbus_message_iter_append_basic(iter_array, DBUS_TYPE_STRING,
+                                             &elem);
+}
+
+
+/**
+ * End a string array dict entry
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param iter_dict_entry A private DBusMessageIter returned from
+ *                        wpa_dbus_dict_end_string_array()
+ * @param iter_dict_val A private DBusMessageIter returned from
+ *                      wpa_dbus_dict_end_string_array()
+ * @param iter_array A DBusMessageIter returned from
+ *                   wpa_dbus_dict_end_string_array()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
+                                          DBusMessageIter *iter_dict_entry,
+                                          DBusMessageIter *iter_dict_val,
+                                          DBusMessageIter *iter_array)
+{
+       if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
+               return FALSE;
+
+       if (!dbus_message_iter_close_container(iter_dict_val, iter_array))
+               return FALSE;
+
+       if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry,
+                                         iter_dict_val))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/**
+ * Convenience function to add an entire string array to the dict.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *                  wpa_dbus_dict_open_write()
+ * @param key The key of the dict item
+ * @param items The array of strings
+ * @param num_items The number of strings in the array
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
+                                             const char *key,
+                                             const char **items,
+                                             const dbus_uint32_t num_items)
+{
+       DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+       dbus_uint32_t i;
+
+       if (!key)
+               return FALSE;
+       if (!items && (num_items != 0))
+               return FALSE;
+
+       if (!wpa_dbus_dict_begin_string_array(iter_dict, key,
+                                             &iter_dict_entry, &iter_dict_val,
+                                             &iter_array))
+               return FALSE;
+
+       for (i = 0; i < num_items; i++) {
+               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           items[i]))
+                       return FALSE;
+       }
+
+       if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
+                                           &iter_dict_val, &iter_array))
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/*****************************************************/
+/* Stuff for reading dicts                           */
+/*****************************************************/
+
+/**
+ * Start reading from a dbus dict.
+ *
+ * @param iter A valid DBusMessageIter pointing to the start of the dict
+ * @param iter_dict (out) A DBusMessageIter to be passed to
+ *    wpa_dbus_dict_read_next_entry()
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
+                                   DBusMessageIter *iter_dict)
+{
+       if (!iter || !iter_dict)
+               return FALSE;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+           dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY)
+               return FALSE;
+
+       dbus_message_iter_recurse(iter, iter_dict);
+       return TRUE;
+}
+
+
+#define BYTE_ARRAY_CHUNK_SIZE 34
+#define BYTE_ARRAY_ITEM_SIZE (sizeof(char))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
+       DBusMessageIter *iter, int array_type,
+       struct wpa_dbus_dict_entry *entry)
+{
+       dbus_uint32_t count = 0;
+       dbus_bool_t success = FALSE;
+       char *buffer, *nbuffer;;
+
+       entry->bytearray_value = NULL;
+       entry->array_type = DBUS_TYPE_BYTE;
+
+       buffer = os_zalloc(BYTE_ARRAY_ITEM_SIZE * BYTE_ARRAY_CHUNK_SIZE);
+       if (!buffer)
+               return FALSE;
+
+       entry->bytearray_value = buffer;
+       entry->array_len = 0;
+       while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) {
+               char byte;
+
+               if ((count % BYTE_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+                       nbuffer = os_realloc(buffer, BYTE_ARRAY_ITEM_SIZE *
+                                            (count + BYTE_ARRAY_CHUNK_SIZE));
+                       if (nbuffer == NULL) {
+                               os_free(buffer);
+                               wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
+                                          "entry_get_byte_array out of "
+                                          "memory trying to retrieve the "
+                                          "string array");
+                               goto done;
+                       }
+                       buffer = nbuffer;
+               }
+               entry->bytearray_value = buffer;
+
+               dbus_message_iter_get_basic(iter, &byte);
+               entry->bytearray_value[count] = byte;
+               entry->array_len = ++count;
+               dbus_message_iter_next(iter);
+       }
+
+       /* Zero-length arrays are valid. */
+       if (entry->array_len == 0) {
+               os_free(entry->bytearray_value);
+               entry->bytearray_value = NULL;
+       }
+
+       success = TRUE;
+
+done:
+       return success;
+}
+
+
+#define STR_ARRAY_CHUNK_SIZE 8
+#define STR_ARRAY_ITEM_SIZE (sizeof(char *))
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
+       DBusMessageIter *iter, int array_type,
+       struct wpa_dbus_dict_entry *entry)
+{
+       dbus_uint32_t count = 0;
+       dbus_bool_t success = FALSE;
+       char **buffer, **nbuffer;
+
+       entry->strarray_value = NULL;
+       entry->array_type = DBUS_TYPE_STRING;
+
+       buffer = os_zalloc(STR_ARRAY_ITEM_SIZE * STR_ARRAY_CHUNK_SIZE);
+       if (buffer == NULL)
+               return FALSE;
+
+       entry->strarray_value = buffer;
+       entry->array_len = 0;
+       while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
+               const char *value;
+               char *str;
+
+               if ((count % STR_ARRAY_CHUNK_SIZE) == 0 && count != 0) {
+                       nbuffer = os_realloc(buffer, STR_ARRAY_ITEM_SIZE *
+                                            (count + STR_ARRAY_CHUNK_SIZE));
+                       if (nbuffer == NULL) {
+                               os_free(buffer);
+                               wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
+                                          "entry_get_string_array out of "
+                                          "memory trying to retrieve the "
+                                          "string array");
+                               goto done;
+                       }
+                       buffer = nbuffer;
+               }
+               entry->strarray_value = buffer;
+
+               dbus_message_iter_get_basic(iter, &value);
+               str = os_strdup(value);
+               if (str == NULL) {
+                       wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_entry_get_"
+                                  "string_array out of memory trying to "
+                                  "duplicate the string array");
+                       goto done;
+               }
+               entry->strarray_value[count] = str;
+               entry->array_len = ++count;
+               dbus_message_iter_next(iter);
+       }
+
+       /* Zero-length arrays are valid. */
+       if (entry->array_len == 0) {
+               os_free(entry->strarray_value);
+               entry->strarray_value = NULL;
+       }
+
+       success = TRUE;
+
+done:
+       return success;
+}
+
+
+static dbus_bool_t _wpa_dbus_dict_entry_get_array(
+       DBusMessageIter *iter_dict_val, struct wpa_dbus_dict_entry *entry)
+{
+       int array_type = dbus_message_iter_get_element_type(iter_dict_val);
+       dbus_bool_t success = FALSE;
+       DBusMessageIter iter_array;
+
+       if (!entry)
+               return FALSE;
+
+       dbus_message_iter_recurse(iter_dict_val, &iter_array);
+
+       switch (array_type) {
+       case DBUS_TYPE_BYTE:
+               success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
+                                                             array_type,
+                                                             entry);
+               break;
+       case DBUS_TYPE_STRING:
+               success = _wpa_dbus_dict_entry_get_string_array(&iter_array,
+                                                               array_type,
+                                                               entry);
+               break;
+       default:
+               break;
+       }
+
+       return success;
+}
+
+
+static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant(
+       struct wpa_dbus_dict_entry *entry, DBusMessageIter *iter)
+{
+       const char *v;
+
+       switch (entry->type) {
+       case DBUS_TYPE_OBJECT_PATH:
+       case DBUS_TYPE_STRING:
+               dbus_message_iter_get_basic(iter, &v);
+               entry->str_value = os_strdup(v);
+               if (entry->str_value == NULL)
+                       return FALSE;
+               break;
+       case DBUS_TYPE_BOOLEAN:
+               dbus_message_iter_get_basic(iter, &entry->bool_value);
+               break;
+       case DBUS_TYPE_BYTE:
+               dbus_message_iter_get_basic(iter, &entry->byte_value);
+               break;
+       case DBUS_TYPE_INT16:
+               dbus_message_iter_get_basic(iter, &entry->int16_value);
+               break;
+       case DBUS_TYPE_UINT16:
+               dbus_message_iter_get_basic(iter, &entry->uint16_value);
+               break;
+       case DBUS_TYPE_INT32:
+               dbus_message_iter_get_basic(iter, &entry->int32_value);
+               break;
+       case DBUS_TYPE_UINT32:
+               dbus_message_iter_get_basic(iter, &entry->uint32_value);
+               break;
+       case DBUS_TYPE_INT64:
+               dbus_message_iter_get_basic(iter, &entry->int64_value);
+               break;
+       case DBUS_TYPE_UINT64:
+               dbus_message_iter_get_basic(iter, &entry->uint64_value);
+               break;
+       case DBUS_TYPE_DOUBLE:
+               dbus_message_iter_get_basic(iter, &entry->double_value);
+               break;
+       case DBUS_TYPE_ARRAY:
+               return _wpa_dbus_dict_entry_get_array(iter, entry);
+       default:
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+
+/**
+ * Read the current key/value entry from the dict.  Entries are dynamically
+ * allocated when needed and must be freed after use with the
+ * wpa_dbus_dict_entry_clear() function.
+ *
+ * The returned entry object will be filled with the type and value of the next
+ * entry in the dict, or the type will be DBUS_TYPE_INVALID if an error
+ * occurred.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_read()
+ * @param entry A valid dict entry object into which the dict key and value
+ *    will be placed
+ * @return TRUE on success, FALSE on failure
+ *
+ */
+dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
+                                   struct wpa_dbus_dict_entry * entry)
+{
+       DBusMessageIter iter_dict_entry, iter_dict_val;
+       int type;
+       const char *key;
+
+       if (!iter_dict || !entry)
+               goto error;
+
+       if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY)
+               goto error;
+
+       dbus_message_iter_recurse(iter_dict, &iter_dict_entry);
+       dbus_message_iter_get_basic(&iter_dict_entry, &key);
+       entry->key = key;
+
+       if (!dbus_message_iter_next(&iter_dict_entry))
+               goto error;
+       type = dbus_message_iter_get_arg_type(&iter_dict_entry);
+       if (type != DBUS_TYPE_VARIANT)
+               goto error;
+
+       dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val);
+       entry->type = dbus_message_iter_get_arg_type(&iter_dict_val);
+       if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val))
+               goto error;
+
+       dbus_message_iter_next(iter_dict);
+       return TRUE;
+
+error:
+       if (entry) {
+               wpa_dbus_dict_entry_clear(entry);
+               entry->type = DBUS_TYPE_INVALID;
+               entry->array_type = DBUS_TYPE_INVALID;
+       }
+
+       return FALSE;
+}
+
+
+/**
+ * Return whether or not there are additional dictionary entries.
+ *
+ * @param iter_dict A valid DBusMessageIter returned from
+ *    wpa_dbus_dict_open_read()
+ * @return TRUE if more dict entries exists, FALSE if no more dict entries
+ * exist
+ */
+dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict)
+{
+       if (!iter_dict)
+               return FALSE;
+       return dbus_message_iter_get_arg_type(iter_dict) ==
+               DBUS_TYPE_DICT_ENTRY;
+}
+
+
+/**
+ * Free any memory used by the entry object.
+ *
+ * @param entry The entry object
+ */
+void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry)
+{
+       unsigned int i;
+
+       if (!entry)
+               return;
+       switch (entry->type) {
+       case DBUS_TYPE_OBJECT_PATH:
+       case DBUS_TYPE_STRING:
+               os_free(entry->str_value);
+               break;
+       case DBUS_TYPE_ARRAY:
+               switch (entry->array_type) {
+               case DBUS_TYPE_BYTE:
+                       os_free(entry->bytearray_value);
+                       break;
+               case DBUS_TYPE_STRING:
+                       for (i = 0; i < entry->array_len; i++)
+                               os_free(entry->strarray_value[i]);
+                       os_free(entry->strarray_value);
+                       break;
+               }
+               break;
+       }
+
+       memset(entry, 0, sizeof(struct wpa_dbus_dict_entry));
+}
diff --git a/wpa_supplicant/dbus/dbus_dict_helpers.h b/wpa_supplicant/dbus/dbus_dict_helpers.h
new file mode 100644 (file)
index 0000000..eb31575
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 DBUS_DICT_HELPERS_H
+#define DBUS_DICT_HELPERS_H
+
+/*
+ * Adding a dict to a DBusMessage
+ */
+
+dbus_bool_t wpa_dbus_dict_open_write(DBusMessageIter *iter,
+                                    DBusMessageIter *iter_dict);
+
+dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter,
+                                     DBusMessageIter *iter_dict);
+
+const char * wpa_dbus_type_as_string(const int type);
+
+dbus_bool_t wpa_dbus_dict_append_string(DBusMessageIter *iter_dict,
+                                       const char *key, const char *value);
+
+dbus_bool_t wpa_dbus_dict_append_byte(DBusMessageIter *iter_dict,
+                                     const char *key, const char value);
+
+dbus_bool_t wpa_dbus_dict_append_bool(DBusMessageIter *iter_dict,
+                                     const char *key,
+                                     const dbus_bool_t value);
+
+dbus_bool_t wpa_dbus_dict_append_int16(DBusMessageIter *iter_dict,
+                                      const char *key,
+                                      const dbus_int16_t value);
+
+dbus_bool_t wpa_dbus_dict_append_uint16(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const dbus_uint16_t value);
+
+dbus_bool_t wpa_dbus_dict_append_int32(DBusMessageIter *iter_dict,
+                                      const char *key,
+                                      const dbus_int32_t value);
+
+dbus_bool_t wpa_dbus_dict_append_uint32(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const dbus_uint32_t value);
+
+dbus_bool_t wpa_dbus_dict_append_int64(DBusMessageIter *iter_dict,
+                                      const char *key,
+                                      const dbus_int64_t value);
+
+dbus_bool_t wpa_dbus_dict_append_uint64(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const dbus_uint64_t value);
+
+dbus_bool_t wpa_dbus_dict_append_double(DBusMessageIter *iter_dict,
+                                       const char *key,
+                                       const double value);
+
+dbus_bool_t wpa_dbus_dict_append_object_path(DBusMessageIter *iter_dict,
+                                            const char *key,
+                                            const char *value);
+
+dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
+                                           const char *key,
+                                           const char *value,
+                                           const dbus_uint32_t value_len);
+
+/* Manual construction and addition of string array elements */
+dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
+                                             const char *key,
+                                             DBusMessageIter *iter_dict_entry,
+                                             DBusMessageIter *iter_dict_val,
+                                             DBusMessageIter *iter_array);
+
+dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
+                                             const char *elem);
+
+dbus_bool_t wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
+                                           DBusMessageIter *iter_dict_entry,
+                                           DBusMessageIter *iter_dict_val,
+                                           DBusMessageIter *iter_array);
+
+/* Convenience function to add a whole string list */
+dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
+                                             const char *key,
+                                             const char **items,
+                                             const dbus_uint32_t num_items);
+
+/*
+ * Reading a dict from a DBusMessage
+ */
+
+struct wpa_dbus_dict_entry {
+       int type;         /** the dbus type of the dict entry's value */
+       int array_type;   /** the dbus type of the array elements if the dict
+                             entry value contains an array */
+       const char *key;  /** key of the dict entry */
+
+       /** Possible values of the property */
+       union {
+               char *str_value;
+               char byte_value;
+               dbus_bool_t bool_value;
+               dbus_int16_t int16_value;
+               dbus_uint16_t uint16_value;
+               dbus_int32_t int32_value;
+               dbus_uint32_t uint32_value;
+               dbus_int64_t int64_value;
+               dbus_uint64_t uint64_value;
+               double double_value;
+               char *bytearray_value;
+               char **strarray_value;
+       };
+       dbus_uint32_t array_len; /** length of the array if the dict entry's
+                                    value contains an array */
+};
+
+dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
+                                   DBusMessageIter *iter_dict);
+
+dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
+                                   struct wpa_dbus_dict_entry *entry);
+
+dbus_bool_t wpa_dbus_dict_has_dict_entry(DBusMessageIter *iter_dict);
+
+void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry);
+
+#endif  /* DBUS_DICT_HELPERS_H */
diff --git a/wpa_supplicant/dbus/dbus_new.c b/wpa_supplicant/dbus/dbus_new.c
new file mode 100644 (file)
index 0000000..bdfbbac
--- /dev/null
@@ -0,0 +1,1562 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 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 "common.h"
+#include "wps/wps.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../bss.h"
+#include "dbus_new_helpers.h"
+#include "dbus_dict_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_common.h"
+#include "dbus_common_i.h"
+
+
+/**
+ * wpas_dbus_signal_interface - Send a interface related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sig_name: signal name - InterfaceAdded or InterfaceRemoved
+ * @properties: Whether to add second argument with object properties
+ *
+ * Notify listeners about event related with interface
+ */
+static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
+                                      const char *sig_name, int properties)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       DBusMessageIter iter, iter_dict;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(WPAS_DBUS_NEW_PATH,
+                                     WPAS_DBUS_NEW_INTERFACE, sig_name);
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                           &wpa_s->dbus_new_path))
+               goto err;
+
+       if (properties) {
+               if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
+                       goto err;
+
+               wpa_dbus_get_object_properties(iface, wpa_s->dbus_new_path,
+                                              WPAS_DBUS_NEW_IFACE_INTERFACE,
+                                              &iter_dict);
+
+               if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+                       goto err;
+       }
+
+       dbus_connection_send(iface->con, msg, NULL);
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_interface_added - Send a interface created signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about creating new interface
+ */
+static void wpas_dbus_signal_interface_added(struct wpa_supplicant *wpa_s)
+{
+       wpas_dbus_signal_interface(wpa_s, "InterfaceAdded", TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_interface_removed - Send a interface removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about removing interface
+ */
+static void wpas_dbus_signal_interface_removed(struct wpa_supplicant *wpa_s)
+{
+       wpas_dbus_signal_interface(wpa_s, "InterfaceRemoved", FALSE);
+
+}
+
+
+/**
+ * wpas_dbus_signal_scan_done - send scan done signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @success: indicates if scanning succeed or failed
+ *
+ * Notify listeners about finishing a scan
+ */
+void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       dbus_bool_t succ;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_INTERFACE,
+                                     "ScanDone");
+       if (msg == NULL)
+               return;
+
+       succ = success ? TRUE : FALSE;
+       if (dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &succ,
+                                    DBUS_TYPE_INVALID))
+               dbus_connection_send(iface->con, msg, NULL);
+       else
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_blob - Send a BSS related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @bss_obj_path: BSS object path
+ * @sig_name: signal name - BSSAdded or BSSRemoved
+ * @properties: Whether to add second argument with object properties
+ *
+ * Notify listeners about event related with BSS
+ */
+static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
+                                const char *bss_obj_path,
+                                const char *sig_name, int properties)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       DBusMessageIter iter, iter_dict;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_INTERFACE,
+                                     sig_name);
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                           &bss_obj_path))
+               goto err;
+
+       if (properties) {
+               if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
+                       goto err;
+
+               wpa_dbus_get_object_properties(iface, bss_obj_path,
+                                              WPAS_DBUS_NEW_IFACE_BSS,
+                                              &iter_dict);
+
+               if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+                       goto err;
+       }
+
+       dbus_connection_send(iface->con, msg, NULL);
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_bss_added - Send a BSS added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @bss_obj_path: new BSS object path
+ *
+ * Notify listeners about adding new BSS
+ */
+static void wpas_dbus_signal_bss_added(struct wpa_supplicant *wpa_s,
+                                      const char *bss_obj_path)
+{
+       wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSAdded", TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_bss_removed - Send a BSS removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @bss_obj_path: BSS object path
+ *
+ * Notify listeners about removing BSS
+ */
+static void wpas_dbus_signal_bss_removed(struct wpa_supplicant *wpa_s,
+                                        const char *bss_obj_path)
+{
+       wpas_dbus_signal_bss(wpa_s, bss_obj_path, "BSSRemoved", FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_blob - Send a blob related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @name: blob name
+ * @sig_name: signal name - BlobAdded or BlobRemoved
+ *
+ * Notify listeners about event related with blob
+ */
+static void wpas_dbus_signal_blob(struct wpa_supplicant *wpa_s,
+                                 const char *name, const char *sig_name)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_INTERFACE,
+                                     sig_name);
+       if (msg == NULL)
+               return;
+
+       if (dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
+                                    DBUS_TYPE_INVALID))
+               dbus_connection_send(iface->con, msg, NULL);
+       else
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_blob_added - Send a blob added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @name: blob name
+ *
+ * Notify listeners about adding a new blob
+ */
+void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+                                const char *name)
+{
+       wpas_dbus_signal_blob(wpa_s, name, "BlobAdded");
+}
+
+
+/**
+ * wpas_dbus_signal_blob_removed - Send a blob removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @name: blob name
+ *
+ * Notify listeners about removing blob
+ */
+void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+                                  const char *name)
+{
+       wpas_dbus_signal_blob(wpa_s, name, "BlobRemoved");
+}
+
+
+/**
+ * wpas_dbus_signal_network - Send a network related event signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new network id
+ * @sig_name: signal name - NetworkAdded, NetworkRemoved or NetworkSelected
+ * @properties: determines if add second argument with object properties
+ *
+ * Notify listeners about event related with configured network
+ */
+static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
+                                    int id, const char *sig_name,
+                                    int properties)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+       DBusMessageIter iter, iter_dict;
+       char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+                   wpa_s->dbus_new_path, id);
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_INTERFACE,
+                                     sig_name);
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       path = net_obj_path;
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
+                                           &path))
+               goto err;
+
+       if (properties) {
+               if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
+                       goto err;
+
+               wpa_dbus_get_object_properties(iface, net_obj_path,
+                                              WPAS_DBUS_NEW_IFACE_NETWORK,
+                                              &iter_dict);
+
+               if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+                       goto err;
+       }
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_network_added - Send a network added signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: new network id
+ *
+ * Notify listeners about adding new network
+ */
+static void wpas_dbus_signal_network_added(struct wpa_supplicant *wpa_s,
+                                          int id)
+{
+       wpas_dbus_signal_network(wpa_s, id, "NetworkAdded", TRUE);
+}
+
+
+/**
+ * wpas_dbus_signal_network_removed - Send a network removed signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: network id
+ *
+ * Notify listeners about removing a network
+ */
+static void wpas_dbus_signal_network_removed(struct wpa_supplicant *wpa_s,
+                                            int id)
+{
+       wpas_dbus_signal_network(wpa_s, id, "NetworkRemoved", FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_network_selected - Send a network selected signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @id: network id
+ *
+ * Notify listeners about selecting a network
+ */
+void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id)
+{
+       wpas_dbus_signal_network(wpa_s, id, "NetworkSelected", FALSE);
+}
+
+
+/**
+ * wpas_dbus_signal_network_enabled_changed - Signals Enabled property changes
+ * @wpa_s: %wpa_supplicant network interface data
+ * @ssid: configured network which Enabled property has changed
+ *
+ * Sends PropertyChanged signals containing new value of Enabled property
+ * for specified network
+ */
+void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
+                                             struct wpa_ssid *ssid)
+{
+
+       char path[WPAS_DBUS_OBJECT_PATH_MAX];
+       os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
+                   wpa_s->dbus_new_path, ssid->id);
+
+       wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
+                                      WPAS_DBUS_NEW_IFACE_NETWORK, "Enabled");
+}
+
+
+#ifdef CONFIG_WPS
+
+/**
+ * wpas_dbus_signal_wps_event_success - Signals Success WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "success" and empty dict as arguments
+ */
+void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+
+       DBusMessage *msg;
+       DBusMessageIter iter, dict_iter;
+       struct wpas_dbus_priv *iface;
+       char *key = "success";
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_WPS, "Event");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+           !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_event_fail - Signals Fail WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "fail" and dictionary containing
+ * "msg field with fail message number (int32) as arguments
+ */
+void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
+                                    struct wps_event_fail *fail)
+{
+
+       DBusMessage *msg;
+       DBusMessageIter iter, dict_iter;
+       struct wpas_dbus_priv *iface;
+       char *key = "fail";
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_WPS, "Event");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+           !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_event_m2d - Signals M2D WPS event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal with name "m2d" and dictionary containing
+ * fields of wps_event_m2d structure.
+ */
+void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+                                   struct wps_event_m2d *m2d)
+{
+
+       DBusMessage *msg;
+       DBusMessageIter iter, dict_iter;
+       struct wpas_dbus_priv *iface;
+       char *key = "m2d";
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_WPS, "Event");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+           !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_uint16(&dict_iter, "config_methods",
+                                        m2d->config_methods) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "manufacturer",
+                                            (const char *) m2d->manufacturer,
+                                            m2d->manufacturer_len) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "model_name",
+                                            (const char *) m2d->model_name,
+                                            m2d->model_name_len) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "model_number",
+                                            (const char *) m2d->model_number,
+                                            m2d->model_number_len) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "serial_number",
+                                            (const char *)
+                                            m2d->serial_number,
+                                            m2d->serial_number_len) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "dev_name",
+                                            (const char *) m2d->dev_name,
+                                            m2d->dev_name_len) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "primary_dev_type",
+                                            (const char *)
+                                            m2d->primary_dev_type, 8) ||
+           !wpa_dbus_dict_append_uint16(&dict_iter, "config_error",
+                                        m2d->config_error) ||
+           !wpa_dbus_dict_append_uint16(&dict_iter, "dev_password_id",
+                                        m2d->dev_password_id) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_wps_cred - Signals new credentials
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends signal with credentials in directory argument
+ */
+void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+                              const struct wps_credential *cred)
+{
+       DBusMessage *msg;
+       DBusMessageIter iter, dict_iter;
+       struct wpas_dbus_priv *iface;
+       char *auth_type[6]; /* we have six possible authorization types */
+       int at_num = 0;
+       char *encr_type[4]; /* we have four possible encryption types */
+       int et_num = 0;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_WPS,
+                                     "Credentials");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto nomem;
+
+       if (cred->auth_type & WPS_AUTH_OPEN)
+               auth_type[at_num++] = "open";
+       if (cred->auth_type & WPS_AUTH_WPAPSK)
+               auth_type[at_num++] = "wpa-psk";
+       if (cred->auth_type & WPS_AUTH_SHARED)
+               auth_type[at_num++] = "shared";
+       if (cred->auth_type & WPS_AUTH_WPA)
+               auth_type[at_num++] = "wpa-eap";
+       if (cred->auth_type & WPS_AUTH_WPA2)
+               auth_type[at_num++] = "wpa2-eap";
+       if (cred->auth_type & WPS_AUTH_WPA2PSK)
+               auth_type[at_num++] =
+               "wpa2-psk";
+
+       if (cred->encr_type & WPS_ENCR_NONE)
+               encr_type[et_num++] = "none";
+       if (cred->encr_type & WPS_ENCR_WEP)
+               encr_type[et_num++] = "wep";
+       if (cred->encr_type & WPS_ENCR_TKIP)
+               encr_type[et_num++] = "tkip";
+       if (cred->encr_type & WPS_ENCR_AES)
+               encr_type[et_num++] = "aes";
+
+       if (wpa_s->current_ssid) {
+               if (!wpa_dbus_dict_append_byte_array(
+                           &dict_iter, "BSSID",
+                           (const char *) wpa_s->current_ssid->bssid,
+                           ETH_ALEN))
+                       goto nomem;
+       }
+
+       if (!wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+                                            (const char *) cred->ssid,
+                                            cred->ssid_len) ||
+           !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType",
+                                              (const char **) auth_type,
+                                              at_num) ||
+           !wpa_dbus_dict_append_string_array(&dict_iter, "EncrType",
+                                              (const char **) encr_type,
+                                              et_num) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "Key",
+                                            (const char *) cred->key,
+                                            cred->key_len) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "KeyIndex",
+                                        cred->key_idx) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               goto nomem;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
+       dbus_message_unref(msg);
+}
+
+#endif /* CONFIG_WPS */
+
+
+/**
+ * wpas_dbus_signal_prop_changed - Signals change of property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ *
+ * Sends ProertyChanged signals with path, interface and arguments
+ * depending on which property has changed.
+ */
+void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+                                  enum wpas_dbus_prop property)
+{
+       WPADBusPropertyAccessor getter;
+       char *prop;
+
+       if (wpa_s->dbus_new_path == NULL)
+               return; /* Skip signal since D-Bus setup is not yet ready */
+
+       switch (property) {
+       case WPAS_DBUS_PROP_AP_SCAN:
+               getter = (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan;
+               prop = "ApScan";
+               break;
+       case WPAS_DBUS_PROP_SCANNING:
+               getter = (WPADBusPropertyAccessor) wpas_dbus_getter_scanning;
+               prop = "Scanning";
+               break;
+       case WPAS_DBUS_PROP_STATE:
+               getter = (WPADBusPropertyAccessor) wpas_dbus_getter_state;
+               prop = "State";
+               break;
+       case WPAS_DBUS_PROP_CURRENT_BSS:
+               getter = (WPADBusPropertyAccessor)
+                       wpas_dbus_getter_current_bss;
+               prop = "CurrentBSS";
+               break;
+       case WPAS_DBUS_PROP_CURRENT_NETWORK:
+               getter = (WPADBusPropertyAccessor)
+                       wpas_dbus_getter_current_network;
+               prop = "CurrentNetwork";
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+                          __func__, property);
+               return;
+       }
+
+       wpa_dbus_mark_property_changed(wpa_s->global->dbus,
+                                      wpa_s->dbus_new_path,
+                                      WPAS_DBUS_NEW_IFACE_INTERFACE, prop);
+}
+
+
+/**
+ * wpas_dbus_bss_signal_prop_changed - Signals change of BSS property
+ * @wpa_s: %wpa_supplicant network interface data
+ * @property: indicates which property has changed
+ * @id: unique BSS identifier
+ *
+ * Sends PropertyChanged signals with path, interface, and arguments depending
+ * on which property has changed.
+ */
+void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
+                                      enum wpas_dbus_bss_prop property,
+                                      unsigned int id)
+{
+       char path[WPAS_DBUS_OBJECT_PATH_MAX];
+       char *prop;
+
+       switch (property) {
+       case WPAS_DBUS_BSS_PROP_SIGNAL:
+               prop = "Signal";
+               break;
+       case WPAS_DBUS_BSS_PROP_FREQ:
+               prop = "Frequency";
+               break;
+       case WPAS_DBUS_BSS_PROP_MODE:
+               prop = "Mode";
+               break;
+       case WPAS_DBUS_BSS_PROP_PRIVACY:
+               prop = "Privacy";
+               break;
+       case WPAS_DBUS_BSS_PROP_RATES:
+               prop = "Rates";
+               break;
+       case WPAS_DBUS_BSS_PROP_WPA:
+               prop = "WPA";
+               break;
+       case WPAS_DBUS_BSS_PROP_RSN:
+               prop = "RSN";
+               break;
+       case WPAS_DBUS_BSS_PROP_IES:
+               prop = "IEs";
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
+                          __func__, property);
+               return;
+       }
+
+       os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+                   wpa_s->dbus_new_path, id);
+
+       wpa_dbus_mark_property_changed(wpa_s->global->dbus, path,
+                                      WPAS_DBUS_NEW_IFACE_BSS, prop);
+}
+
+
+/**
+ * wpas_dbus_signal_debug_level_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends ProertyChanged signals informing that debug level has changed.
+ */
+void wpas_dbus_signal_debug_level_changed(struct wpa_global *global)
+{
+       wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+                                      WPAS_DBUS_NEW_INTERFACE,
+                                      "DebugLevel");
+}
+
+
+/**
+ * wpas_dbus_signal_debug_timestamp_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends ProertyChanged signals informing that debug timestamp has changed.
+ */
+void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global)
+{
+       wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+                                      WPAS_DBUS_NEW_INTERFACE,
+                                      "DebugTimestamp");
+}
+
+
+/**
+ * wpas_dbus_signal_debug_show_keys_changed - Signals change of debug param
+ * @global: wpa_global structure
+ *
+ * Sends ProertyChanged signals informing that debug show_keys has changed.
+ */
+void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global)
+{
+       wpa_dbus_mark_property_changed(global->dbus, WPAS_DBUS_NEW_PATH,
+                                      WPAS_DBUS_NEW_INTERFACE,
+                                      "DebugShowKeys");
+}
+
+
+static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc,
+                              void *priv,
+                              WPADBusArgumentFreeFunction priv_free,
+                              const struct wpa_dbus_method_desc *methods,
+                              const struct wpa_dbus_property_desc *properties,
+                              const struct wpa_dbus_signal_desc *signals)
+{
+       int n;
+
+       obj_desc->user_data = priv;
+       obj_desc->user_data_free_func = priv_free;
+       obj_desc->methods = methods;
+       obj_desc->properties = properties;
+       obj_desc->signals = signals;
+
+       for (n = 0; properties && properties->dbus_property; properties++)
+               n++;
+
+       obj_desc->prop_changed_flags = os_zalloc(n);
+       if (!obj_desc->prop_changed_flags)
+               wpa_printf(MSG_DEBUG, "dbus: %s: can't register handlers",
+                          __func__);
+}
+
+
+static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
+       { "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_create_interface,
+         {
+                 { "args", "a{sv}", ARG_IN },
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface,
+         {
+                 { "path", "o", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "GetInterface", WPAS_DBUS_NEW_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_get_interface,
+         {
+                 { "ifname", "s", ARG_IN },
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { NULL, NULL, NULL, { END_ARGS } }
+};
+
+static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
+       { "DebugLevel", WPAS_DBUS_NEW_INTERFACE, "s",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_debug_level,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_debug_level,
+         RW
+       },
+       { "DebugTimestamp", WPAS_DBUS_NEW_INTERFACE, "b",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_debug_timestamp,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_debug_timestamp,
+         RW
+       },
+       { "DebugShowKeys", WPAS_DBUS_NEW_INTERFACE, "b",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_debug_show_keys,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_debug_show_keys,
+         RW
+       },
+       { "Interfaces", WPAS_DBUS_NEW_INTERFACE, "ao",
+         (WPADBusPropertyAccessor) &wpas_dbus_getter_interfaces,
+         NULL,
+         R
+       },
+       { "EapMethods", WPAS_DBUS_NEW_INTERFACE, "as",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_eap_methods,
+         NULL,
+         R
+       },
+       { NULL, NULL, NULL, NULL, NULL, 0 }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
+       { "InterfaceAdded", WPAS_DBUS_NEW_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "InterfaceRemoved", WPAS_DBUS_NEW_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
+         {
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success or -1 on failure
+ *
+ * Initialize the dbus control interface for wpa_supplicantand and start
+ * receiving commands from external programs over the bus.
+ */
+int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
+{
+       struct wpa_dbus_object_desc *obj_desc;
+       int ret;
+
+       obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create object description");
+               return -1;
+       }
+
+       wpas_dbus_register(obj_desc, priv->global, NULL,
+                          wpas_dbus_global_methods,
+                          wpas_dbus_global_properties,
+                          wpas_dbus_global_signals);
+
+       wpa_printf(MSG_DEBUG, "dbus: Register D-Bus object '%s'",
+                  WPAS_DBUS_NEW_PATH);
+       ret = wpa_dbus_ctrl_iface_init(priv, WPAS_DBUS_NEW_PATH,
+                                      WPAS_DBUS_NEW_SERVICE,
+                                      obj_desc);
+       if (ret < 0)
+               free_dbus_object_desc(obj_desc);
+       else
+               priv->dbus_new_initialized = 1;
+
+       return ret;
+}
+
+
+/**
+ * wpas_dbus_ctrl_iface_deinit - Deinitialize dbus ctrl interface for
+ * wpa_supplicant
+ * @iface: Pointer to dbus private data from wpas_dbus_init()
+ *
+ * Deinitialize the dbus control interface that was initialized with
+ * wpas_dbus_ctrl_iface_init().
+ */
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface)
+{
+       if (!iface->dbus_new_initialized)
+               return;
+       wpa_printf(MSG_DEBUG, "dbus: Unregister D-Bus object '%s'",
+                  WPAS_DBUS_NEW_PATH);
+       dbus_connection_unregister_object_path(iface->con,
+                                              WPAS_DBUS_NEW_PATH);
+}
+
+
+static void wpa_dbus_free(void *ptr)
+{
+       os_free(ptr);
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_network_properties[] = {
+       { "Properties", WPAS_DBUS_NEW_IFACE_NETWORK, "a{sv}",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_network_properties,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_network_properties,
+         RW
+       },
+       { "Enabled", WPAS_DBUS_NEW_IFACE_NETWORK, "b",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_enabled,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_enabled,
+         RW
+       },
+       { NULL, NULL, NULL, NULL, NULL, 0 }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_network_signals[] = {
+       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_NETWORK,
+         {
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_register_network - Register a configured network with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @ssid: network configuration data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers network representing object with dbus
+ */
+int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+                              struct wpa_ssid *ssid)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+       struct wpa_dbus_object_desc *obj_desc;
+       struct network_handler_args *arg;
+       char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
+
+       os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+                   wpa_s->dbus_new_path, ssid->id);
+
+       wpa_printf(MSG_DEBUG, "dbus: Register network object '%s'",
+                  net_obj_path);
+       obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create object description");
+               goto err;
+       }
+
+       /* allocate memory for handlers arguments */
+       arg = os_zalloc(sizeof(struct network_handler_args));
+       if (!arg) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create arguments for method");
+               goto err;
+       }
+
+       arg->wpa_s = wpa_s;
+       arg->ssid = ssid;
+
+       wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+                          wpas_dbus_network_properties,
+                          wpas_dbus_network_signals);
+
+       if (wpa_dbus_register_object_per_iface(ctrl_iface, net_obj_path,
+                                              wpa_s->ifname, obj_desc))
+               goto err;
+
+       wpas_dbus_signal_network_added(wpa_s, ssid->id);
+
+       return 0;
+
+err:
+       free_dbus_object_desc(obj_desc);
+       return -1;
+}
+
+
+/**
+ * wpas_dbus_unregister_network - Unregister a configured network from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @nid: network id
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters network representing object from dbus
+ */
+int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+       char net_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+       int ret;
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL ||
+           wpa_s->dbus_new_path == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
+
+       os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+                   wpa_s->dbus_new_path, nid);
+
+       wpa_printf(MSG_DEBUG, "dbus: Unregister network object '%s'",
+                  net_obj_path);
+       ret = wpa_dbus_unregister_object_per_iface(ctrl_iface, net_obj_path);
+
+       if (!ret)
+               wpas_dbus_signal_network_removed(wpa_s, nid);
+
+       return ret;
+}
+
+
+static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
+       { "SSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ssid,
+         NULL,
+         R
+       },
+       { "BSSID", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_bssid,
+         NULL,
+         R
+       },
+       { "Privacy", WPAS_DBUS_NEW_IFACE_BSS, "b",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_privacy,
+         NULL,
+         R
+       },
+       { "Mode", WPAS_DBUS_NEW_IFACE_BSS, "s",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_mode,
+         NULL,
+         R
+       },
+       { "Signal", WPAS_DBUS_NEW_IFACE_BSS, "n",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_signal,
+         NULL,
+         R
+       },
+       { "Frequency", WPAS_DBUS_NEW_IFACE_BSS, "q",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_frequency,
+         NULL,
+         R
+       },
+       { "Rates", WPAS_DBUS_NEW_IFACE_BSS, "au",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rates,
+         NULL,
+         R
+       },
+       { "WPA", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_wpa,
+         NULL,
+         R
+       },
+       { "RSN", WPAS_DBUS_NEW_IFACE_BSS, "a{sv}",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_rsn,
+         NULL,
+         R
+       },
+       { "IEs", WPAS_DBUS_NEW_IFACE_BSS, "ay",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bss_ies,
+         NULL,
+         R
+       },
+       { NULL, NULL, NULL, NULL, NULL, 0 }
+};
+
+
+static const struct wpa_dbus_signal_desc wpas_dbus_bss_signals[] = {
+       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_BSS,
+         {
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { NULL, NULL, { END_ARGS } }
+};
+
+
+/**
+ * wpas_dbus_unregister_bss - Unregister a scanned BSS from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: scanned network bssid
+ * @id: unique BSS identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters BSS representing object from dbus
+ */
+int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+                            u8 bssid[ETH_ALEN], unsigned int id)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+       char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
+
+       os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+                   wpa_s->dbus_new_path, id);
+
+       wpa_printf(MSG_DEBUG, "dbus: Unregister BSS object '%s'",
+                  bss_obj_path);
+       if (wpa_dbus_unregister_object_per_iface(ctrl_iface, bss_obj_path)) {
+               wpa_printf(MSG_ERROR, "dbus: Cannot unregister BSS object %s",
+                          bss_obj_path);
+               return -1;
+       }
+
+       wpas_dbus_signal_bss_removed(wpa_s, bss_obj_path);
+
+       return 0;
+}
+
+
+/**
+ * wpas_dbus_register_bss - Register a scanned BSS with dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * @bssid: scanned network bssid
+ * @id: unique BSS identifier
+ * Returns: 0 on success, -1 on failure
+ *
+ * Registers BSS representing object with dbus
+ */
+int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+                          u8 bssid[ETH_ALEN], unsigned int id)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+       struct wpa_dbus_object_desc *obj_desc;
+       char bss_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+       struct bss_handler_args *arg;
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
+
+       os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+                   wpa_s->dbus_new_path, id);
+
+       obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create object description");
+               goto err;
+       }
+
+       arg = os_zalloc(sizeof(struct bss_handler_args));
+       if (!arg) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create arguments for handler");
+               goto err;
+       }
+       arg->wpa_s = wpa_s;
+       arg->id = id;
+
+       wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
+                          wpas_dbus_bss_properties,
+                          wpas_dbus_bss_signals);
+
+       wpa_printf(MSG_DEBUG, "dbus: Register BSS object '%s'",
+                  bss_obj_path);
+       if (wpa_dbus_register_object_per_iface(ctrl_iface, bss_obj_path,
+                                              wpa_s->ifname, obj_desc)) {
+               wpa_printf(MSG_ERROR,
+                          "Cannot register BSSID dbus object %s.",
+                          bss_obj_path);
+               goto err;
+       }
+
+       wpas_dbus_signal_bss_added(wpa_s, bss_obj_path);
+
+       return 0;
+
+err:
+       free_dbus_object_desc(obj_desc);
+       return -1;
+}
+
+
+static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
+       { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_scan,
+         {
+                 { "args", "a{sv}", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_disconnect,
+         {
+                 END_ARGS
+         }
+       },
+       { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_add_network,
+         {
+                 { "args", "a{sv}", ARG_IN },
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
+         {
+                 { "path", "o", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_select_network,
+         {
+                 { "path", "o", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_add_blob,
+         {
+                 { "name", "s", ARG_IN },
+                 { "data", "ay", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_get_blob,
+         {
+                 { "name", "s", ARG_IN },
+                 { "data", "ay", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob,
+         {
+                 { "name", "s", ARG_IN },
+                 END_ARGS
+         }
+       },
+#ifdef CONFIG_WPS
+       { "Start", WPAS_DBUS_NEW_IFACE_WPS,
+         (WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
+         {
+                 { "args", "a{sv}", ARG_IN },
+                 { "output", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+#endif /* CONFIG_WPS */
+       { NULL, NULL, NULL, { END_ARGS } }
+};
+
+static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
+       { "Capabilities", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{sv}",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_capabilities,
+         NULL, R
+       },
+       { "State", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_state,
+         NULL, R
+       },
+       { "Scanning", WPAS_DBUS_NEW_IFACE_INTERFACE, "b",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_scanning,
+         NULL, R
+       },
+       { "ApScan", WPAS_DBUS_NEW_IFACE_INTERFACE, "u",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_ap_scan,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_ap_scan,
+         RW
+       },
+       { "Ifname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_ifname,
+         NULL, R
+       },
+       { "Driver", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_driver,
+         NULL, R
+       },
+       { "BridgeIfname", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bridge_ifname,
+         NULL, R
+       },
+       { "CurrentBSS", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_current_bss,
+         NULL, R
+       },
+       { "CurrentNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE, "o",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_current_network,
+         NULL, R
+       },
+       { "Blobs", WPAS_DBUS_NEW_IFACE_INTERFACE, "a{say}",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_blobs,
+         NULL, R
+       },
+       { "BSSs", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_bsss,
+         NULL, R
+       },
+       { "Networks", WPAS_DBUS_NEW_IFACE_INTERFACE, "ao",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_networks,
+         NULL, R
+       },
+#ifdef CONFIG_WPS
+       { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
+         (WPADBusPropertyAccessor) wpas_dbus_getter_process_credentials,
+         (WPADBusPropertyAccessor) wpas_dbus_setter_process_credentials,
+         RW
+       },
+#endif /* CONFIG_WPS */
+       { NULL, NULL, NULL, NULL, NULL, 0 }
+};
+
+static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
+       { "ScanDone", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "success", "b", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "BSSAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "BSSRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "BlobAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "name", "s", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "BlobRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "name", "s", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "NetworkAdded", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "NetworkRemoved", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "NetworkSelected", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+#ifdef CONFIG_WPS
+       { "Event", WPAS_DBUS_NEW_IFACE_WPS,
+         {
+                 { "name", "s", ARG_OUT },
+                 { "args", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "Credentials", WPAS_DBUS_NEW_IFACE_WPS,
+         {
+                 { "credentials", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_WPS,
+         {
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
+#endif /* CONFIG_WPS */
+       { NULL, NULL, { END_ARGS } }
+};
+
+
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+{
+
+       struct wpa_dbus_object_desc *obj_desc = NULL;
+       struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
+       int next;
+
+       /* Do nothing if the control interface is not turned on */
+       if (ctrl_iface == NULL)
+               return 0;
+
+       /* Create and set the interface's object path */
+       wpa_s->dbus_new_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+       if (wpa_s->dbus_new_path == NULL)
+               return -1;
+       next = ctrl_iface->next_objid++;
+       os_snprintf(wpa_s->dbus_new_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   WPAS_DBUS_NEW_PATH_INTERFACES "/%u",
+                   next);
+
+       obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "Not enough memory "
+                          "to create object description");
+               goto err;
+       }
+
+       wpas_dbus_register(obj_desc, wpa_s, NULL, wpas_dbus_interface_methods,
+                          wpas_dbus_interface_properties,
+                          wpas_dbus_interface_signals);
+
+       wpa_printf(MSG_DEBUG, "dbus: Register interface object '%s'",
+                  wpa_s->dbus_new_path);
+       if (wpa_dbus_register_object_per_iface(ctrl_iface,
+                                              wpa_s->dbus_new_path,
+                                              wpa_s->ifname, obj_desc))
+               goto err;
+
+       wpas_dbus_signal_interface_added(wpa_s);
+
+       return 0;
+
+err:
+       os_free(wpa_s->dbus_new_path);
+       wpa_s->dbus_new_path = NULL;
+       free_dbus_object_desc(obj_desc);
+       return -1;
+}
+
+
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
+                  wpa_s->dbus_new_path);
+       if (wpa_dbus_unregister_object_per_iface(ctrl_iface,
+                                                wpa_s->dbus_new_path))
+               return -1;
+
+       wpas_dbus_signal_interface_removed(wpa_s);
+
+       os_free(wpa_s->dbus_new_path);
+       wpa_s->dbus_new_path = NULL;
+
+       return 0;
+}
diff --git a/wpa_supplicant/dbus/dbus_new.h b/wpa_supplicant/dbus/dbus_new.h
new file mode 100644 (file)
index 0000000..80ea98c
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * 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_DBUS_NEW_H
+#define CTRL_IFACE_DBUS_NEW_H
+
+struct wpa_global;
+struct wpa_supplicant;
+struct wpa_ssid;
+struct wps_event_m2d;
+struct wps_event_fail;
+struct wps_credential;
+enum wpa_states;
+
+enum wpas_dbus_prop {
+       WPAS_DBUS_PROP_AP_SCAN,
+       WPAS_DBUS_PROP_SCANNING,
+       WPAS_DBUS_PROP_STATE,
+       WPAS_DBUS_PROP_CURRENT_BSS,
+       WPAS_DBUS_PROP_CURRENT_NETWORK,
+};
+
+enum wpas_dbus_bss_prop {
+       WPAS_DBUS_BSS_PROP_SIGNAL,
+       WPAS_DBUS_BSS_PROP_FREQ,
+       WPAS_DBUS_BSS_PROP_MODE,
+       WPAS_DBUS_BSS_PROP_PRIVACY,
+       WPAS_DBUS_BSS_PROP_RATES,
+       WPAS_DBUS_BSS_PROP_WPA,
+       WPAS_DBUS_BSS_PROP_RSN,
+       WPAS_DBUS_BSS_PROP_IES,
+};
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+
+#define WPAS_DBUS_NEW_SERVICE          "fi.w1.wpa_supplicant1"
+#define WPAS_DBUS_NEW_PATH             "/fi/w1/wpa_supplicant1"
+#define WPAS_DBUS_NEW_INTERFACE                "fi.w1.wpa_supplicant1"
+
+#define WPAS_DBUS_NEW_PATH_INTERFACES  WPAS_DBUS_NEW_PATH "/Interfaces"
+#define WPAS_DBUS_NEW_IFACE_INTERFACE  WPAS_DBUS_NEW_INTERFACE ".Interface"
+#define WPAS_DBUS_NEW_IFACE_WPS WPAS_DBUS_NEW_IFACE_INTERFACE ".WPS"
+
+#define WPAS_DBUS_NEW_NETWORKS_PART "Networks"
+#define WPAS_DBUS_NEW_IFACE_NETWORK WPAS_DBUS_NEW_INTERFACE ".Network"
+
+#define WPAS_DBUS_NEW_BSSIDS_PART "BSSs"
+#define WPAS_DBUS_NEW_IFACE_BSS        WPAS_DBUS_NEW_INTERFACE ".BSS"
+
+
+/* Errors */
+#define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
+       WPAS_DBUS_NEW_INTERFACE ".UnknownError"
+#define WPAS_DBUS_ERROR_INVALID_ARGS \
+       WPAS_DBUS_NEW_INTERFACE ".InvalidArgs"
+
+#define WPAS_DBUS_ERROR_IFACE_EXISTS \
+       WPAS_DBUS_NEW_INTERFACE ".InterfaceExists"
+#define WPAS_DBUS_ERROR_IFACE_UNKNOWN \
+       WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown"
+
+#define WPAS_DBUS_ERROR_NOT_CONNECTED \
+       WPAS_DBUS_NEW_INTERFACE ".NotConnected"
+#define WPAS_DBUS_ERROR_NETWORK_UNKNOWN \
+       WPAS_DBUS_NEW_INTERFACE ".NetworkUnknown"
+
+#define WPAS_DBUS_ERROR_BLOB_EXISTS \
+       WPAS_DBUS_NEW_INTERFACE ".BlobExists"
+#define WPAS_DBUS_ERROR_BLOB_UNKNOWN \
+       WPAS_DBUS_NEW_INTERFACE ".BlobUnknown"
+
+
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+
+int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv);
+void wpas_dbus_ctrl_iface_deinit(struct wpas_dbus_priv *iface);
+
+int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s);
+int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+                                  enum wpas_dbus_prop property);
+void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
+                                      enum wpas_dbus_bss_prop property,
+                                      unsigned int id);
+void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
+                                             struct wpa_ssid *ssid);
+void wpas_dbus_signal_network_selected(struct wpa_supplicant *wpa_s, int id);
+void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+                              const struct wps_credential *cred);
+void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+                                   struct wps_event_m2d *m2d);
+void wpas_dbus_signal_wps_event_fail(struct wpa_supplicant *wpa_s,
+                                    struct wps_event_fail *fail);
+void wpas_dbus_signal_wps_event_success(struct wpa_supplicant *wpa_s);
+int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+                              struct wpa_ssid *ssid);
+int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s, int nid);
+int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+                            u8 bssid[ETH_ALEN], unsigned int id);
+int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+                          u8 bssid[ETH_ALEN], unsigned int id);
+void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+                                const char *name);
+void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+                                  const char *name);
+void wpas_dbus_signal_debug_level_changed(struct wpa_global *global);
+void wpas_dbus_signal_debug_timestamp_changed(struct wpa_global *global);
+void wpas_dbus_signal_debug_show_keys_changed(struct wpa_global *global);
+
+#else /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+static inline int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+#define wpas_dbus_signal_state_changed(w, n, o) do { } while (0)
+
+static inline void wpas_dbus_signal_prop_changed(struct wpa_supplicant *wpa_s,
+                                                enum wpas_dbus_prop property)
+{
+}
+
+static inline void wpas_dbus_bss_signal_prop_changed(
+       struct wpa_supplicant *wpa_s, enum wpas_dbus_bss_prop property,
+       unsigned int id)
+{
+}
+
+static inline void wpas_dbus_signal_network_enabled_changed(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+}
+
+static inline void wpas_dbus_signal_network_selected(
+       struct wpa_supplicant *wpa_s, int id)
+{
+}
+
+static inline void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s,
+                                             int success)
+{
+}
+
+static inline void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
+                                            const struct wps_credential *cred)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
+                                                 struct wps_event_m2d *m2d)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_fail(
+       struct wpa_supplicant *wpa_s, struct wps_event_fail *fail)
+{
+}
+
+static inline void wpas_dbus_signal_wps_event_success(
+       struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
+                                            struct wpa_ssid *ssid)
+{
+       return 0;
+}
+
+static inline int wpas_dbus_unregister_network(struct wpa_supplicant *wpa_s,
+                                              int nid)
+{
+       return 0;
+}
+
+static inline int wpas_dbus_unregister_bss(struct wpa_supplicant *wpa_s,
+                                          u8 bssid[ETH_ALEN], unsigned int id)
+{
+       return 0;
+}
+
+static inline int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
+                                        u8 bssid[ETH_ALEN], unsigned int id)
+{
+       return 0;
+}
+
+static inline void wpas_dbus_signal_blob_added(struct wpa_supplicant *wpa_s,
+                                              const char *name)
+{
+}
+
+static inline void wpas_dbus_signal_blob_removed(struct wpa_supplicant *wpa_s,
+                                                const char *name)
+{
+}
+
+static inline void wpas_dbus_signal_debug_level_changed(
+       struct wpa_global *global)
+{
+}
+
+static inline void wpas_dbus_signal_debug_timestamp_changed(
+       struct wpa_global *global)
+{
+}
+
+static inline void wpas_dbus_signal_debug_show_keys_changed(
+       struct wpa_global *global)
+{
+}
+
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+
+#endif /* CTRL_IFACE_DBUS_H_NEW */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.c b/wpa_supplicant/dbus/dbus_new_handlers.c
new file mode 100644 (file)
index 0000000..e2b5e50
--- /dev/null
@@ -0,0 +1,2957 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 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 "common.h"
+#include "common/ieee802_11_defs.h"
+#include "eap_peer/eap_methods.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../driver_i.h"
+#include "../notify.h"
+#include "../wpas_glue.h"
+#include "../bss.h"
+#include "../scan.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_dict_helpers.h"
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
+static const char *debug_strings[] = {
+       "msgdump", "debug", "info", "warning", "error", NULL
+};
+
+
+/**
+ * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
+ * @path: The dbus object path
+ * @network: (out) the configured network this object path refers to, if any
+ * @bssid: (out) the scanned bssid this object path refers to, if any
+ * Returns: The object path of the network interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id, network,
+ * and BSSID parts, if those parts exist.
+ */
+static char * wpas_dbus_new_decompose_object_path(const char *path,
+                                                 char **network,
+                                                 char **bssid)
+{
+       const unsigned int dev_path_prefix_len =
+               strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
+       char *obj_path_only;
+       char *next_sep;
+
+       /* Be a bit paranoid about path */
+       if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
+                               dev_path_prefix_len))
+               return NULL;
+
+       /* Ensure there's something at the end of the path */
+       if ((path + dev_path_prefix_len)[0] == '\0')
+               return NULL;
+
+       obj_path_only = os_strdup(path);
+       if (obj_path_only == NULL)
+               return NULL;
+
+       next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
+       if (next_sep != NULL) {
+               const char *net_part = os_strstr(
+                       next_sep, WPAS_DBUS_NEW_NETWORKS_PART "/");
+               const char *bssid_part = os_strstr(
+                       next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
+
+               if (network && net_part) {
+                       /* Deal with a request for a configured network */
+                       const char *net_name = net_part +
+                               os_strlen(WPAS_DBUS_NEW_NETWORKS_PART "/");
+                       *network = NULL;
+                       if (os_strlen(net_name))
+                               *network = os_strdup(net_name);
+               } else if (bssid && bssid_part) {
+                       /* Deal with a request for a scanned BSSID */
+                       const char *bssid_name = bssid_part +
+                               os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
+                       if (strlen(bssid_name))
+                               *bssid = os_strdup(bssid_name);
+                       else
+                               *bssid = NULL;
+               }
+
+               /* Cut off interface object path before "/" */
+               *next_sep = '\0';
+       }
+
+       return obj_path_only;
+}
+
+
+/**
+ * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * @arg: Optional string appended to error message
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an UnknownError
+ */
+DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
+                                           const char *arg)
+{
+       return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+                                     arg);
+}
+
+
+/**
+ * wpas_dbus_error_iface_unknown - Return a new invalid interface error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: A dbus error message
+ *
+ * Convenience function to create and return an invalid interface error
+ */
+static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message)
+{
+       return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
+                                     "wpa_supplicant knows nothing about "
+                                     "this interface.");
+}
+
+
+/**
+ * wpas_dbus_error_network_unknown - Return a new NetworkUnknown error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid network error
+ */
+static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message)
+{
+       return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+                                     "There is no such a network in this "
+                                     "interface.");
+}
+
+
+/**
+ * wpas_dbus_error_invalid_args - Return a new InvalidArgs error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid options error
+ */
+DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
+                                         const char *arg)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_error(message, WPAS_DBUS_ERROR_INVALID_ARGS,
+                                      "Did not receive correct message "
+                                      "arguments.");
+       if (arg != NULL)
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
+                                        DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+
+static const char *dont_quote[] = {
+       "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
+       "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
+       "bssid", NULL
+};
+
+static dbus_bool_t should_quote_opt(const char *key)
+{
+       int i = 0;
+       while (dont_quote[i] != NULL) {
+               if (os_strcmp(key, dont_quote[i]) == 0)
+                       return FALSE;
+               i++;
+       }
+       return TRUE;
+}
+
+/**
+ * get_iface_by_dbus_path - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @path: Pointer to a dbus object path representing an interface
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+static struct wpa_supplicant * get_iface_by_dbus_path(
+       struct wpa_global *global, const char *path)
+{
+       struct wpa_supplicant *wpa_s;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (os_strcmp(wpa_s->dbus_new_path, path) == 0)
+                       return wpa_s;
+       }
+       return NULL;
+}
+
+
+/**
+ * set_network_properties - Set properties of a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * @iter: DBus message iterator containing dictionary of network
+ * properties to set.
+ * Returns: NULL when succeed or DBus error on failure
+ *
+ * Sets network configuration with parameters given id DBus dictionary
+ */
+static DBusMessage * set_network_properties(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s,
+                                           struct wpa_ssid *ssid,
+                                           DBusMessageIter *iter)
+{
+
+       struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter_dict;
+
+       if (!wpa_dbus_dict_open_read(iter, &iter_dict))
+               return wpas_dbus_error_invalid_args(message, NULL);
+
+       while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+               char *value = NULL;
+               size_t size = 50;
+               int ret;
+               if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+                       reply = wpas_dbus_error_invalid_args(message, NULL);
+                       break;
+               }
+               if (entry.type == DBUS_TYPE_ARRAY &&
+                   entry.array_type == DBUS_TYPE_BYTE) {
+                       if (entry.array_len <= 0)
+                               goto error;
+
+                       size = entry.array_len * 2 + 1;
+                       value = os_zalloc(size);
+                       if (value == NULL)
+                               goto error;
+
+                       ret = wpa_snprintf_hex(value, size,
+                                              (u8 *) entry.bytearray_value,
+                                              entry.array_len);
+                       if (ret <= 0)
+                               goto error;
+               } else if (entry.type == DBUS_TYPE_STRING) {
+                       if (should_quote_opt(entry.key)) {
+                               size = os_strlen(entry.str_value);
+                               if (size <= 0)
+                                       goto error;
+
+                               size += 3;
+                               value = os_zalloc(size);
+                               if (value == NULL)
+                                       goto error;
+
+                               ret = os_snprintf(value, size, "\"%s\"",
+                                                 entry.str_value);
+                               if (ret < 0 || (size_t) ret != (size - 1))
+                                       goto error;
+                       } else {
+                               value = os_strdup(entry.str_value);
+                               if (value == NULL)
+                                       goto error;
+                       }
+               } else if (entry.type == DBUS_TYPE_UINT32) {
+                       value = os_zalloc(size);
+                       if (value == NULL)
+                               goto error;
+
+                       ret = os_snprintf(value, size, "%u",
+                                         entry.uint32_value);
+                       if (ret <= 0)
+                               goto error;
+               } else if (entry.type == DBUS_TYPE_INT32) {
+                       value = os_zalloc(size);
+                       if (value == NULL)
+                               goto error;
+
+                       ret = os_snprintf(value, size, "%d",
+                                         entry.int32_value);
+                       if (ret <= 0)
+                               goto error;
+               } else
+                       goto error;
+
+               if (wpa_config_set(ssid, entry.key, value, 0) < 0)
+                       goto error;
+
+               if ((os_strcmp(entry.key, "psk") == 0 &&
+                    value[0] == '"' && ssid->ssid_len) ||
+                   (strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
+                       wpa_config_update_psk(ssid);
+               else if (os_strcmp(entry.key, "priority") == 0)
+                       wpa_config_update_prio_list(wpa_s->conf);
+
+               os_free(value);
+               wpa_dbus_dict_entry_clear(&entry);
+               continue;
+
+       error:
+               os_free(value);
+               reply = wpas_dbus_error_invalid_args(message, entry.key);
+               wpa_dbus_dict_entry_clear(&entry);
+               break;
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_simple_property_getter - Get basic type property
+ * @message: Pointer to incoming dbus message
+ * @type: DBus type of property (must be basic type)
+ * @val: pointer to place holding property value
+ * Returns: The DBus message containing response for Properties.Get call
+ * or DBus error message if error occurred.
+ *
+ * Generic getter for basic type properties. Type is required to be basic.
+ */
+DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
+                                              const int type, const void *val)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, variant_iter;
+
+       if (!dbus_type_is_basic(type)) {
+               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
+                          " given type is not basic");
+               return wpas_dbus_error_unknown_error(message, NULL);
+       }
+
+       if (message == NULL)
+               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       else
+               reply = dbus_message_new_method_return(message);
+
+       if (reply != NULL) {
+               dbus_message_iter_init_append(reply, &iter);
+               if (!dbus_message_iter_open_container(
+                           &iter, DBUS_TYPE_VARIANT,
+                           wpa_dbus_type_as_string(type), &variant_iter) ||
+                   !dbus_message_iter_append_basic(&variant_iter, type,
+                                                   val) ||
+                   !dbus_message_iter_close_container(&iter, &variant_iter)) {
+                       wpa_printf(MSG_ERROR, "dbus: "
+                                  "wpas_dbus_simple_property_getter: out of "
+                                  "memory to put property value into "
+                                  "message");
+                       dbus_message_unref(reply);
+                       reply = dbus_message_new_error(message,
+                                                      DBUS_ERROR_NO_MEMORY,
+                                                      NULL);
+               }
+       } else {
+               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_getter:"
+                          " out of memory to return property value");
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_simple_property_setter - Set basic type property
+ * @message: Pointer to incoming dbus message
+ * @type: DBus type of property (must be basic type)
+ * @val: pointer to place where value being set will be stored
+ * Returns: NULL or DBus error message if error occurred.
+ *
+ * Generic setter for basic type properties. Type is required to be basic.
+ */
+DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
+                                              const int type, void *val)
+{
+       DBusMessageIter iter, variant_iter;
+
+       if (!dbus_type_is_basic(type)) {
+               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
+                          " given type is not basic");
+               return wpas_dbus_error_unknown_error(message, NULL);
+       }
+
+       if (!dbus_message_iter_init(message, &iter)) {
+               wpa_printf(MSG_ERROR, "dbus: wpas_dbus_simple_property_setter:"
+                          " out of memory to return scanning state");
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       /* omit first and second argument and get value from third */
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &variant_iter);
+
+       if (dbus_message_iter_get_arg_type(&variant_iter) != type) {
+               wpa_printf(MSG_DEBUG, "dbus: wpas_dbus_simple_property_setter:"
+                          " wrong property type");
+               return wpas_dbus_error_invalid_args(message,
+                                                   "wrong property type");
+       }
+       dbus_message_iter_get_basic(&variant_iter, val);
+
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_simple_array_property_getter - Get array type property
+ * @message: Pointer to incoming dbus message
+ * @type: DBus type of property array elements (must be basic type)
+ * @array: pointer to array of elements to put into response message
+ * @array_len: length of above array
+ * Returns: The DBus message containing response for Properties.Get call
+ * or DBus error message if error occurred.
+ *
+ * Generic getter for array type properties. Array elements type is
+ * required to be basic.
+ */
+DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
+                                                    const int type,
+                                                    const void *array,
+                                                    size_t array_len)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, variant_iter, array_iter;
+       char type_str[] = "a?"; /* ? will be replaced with subtype letter; */
+       const char *sub_type_str;
+       size_t element_size, i;
+
+       if (!dbus_type_is_basic(type)) {
+               wpa_printf(MSG_ERROR, "dbus: "
+                          "wpas_dbus_simple_array_property_getter: given "
+                          "type is not basic");
+               return wpas_dbus_error_unknown_error(message, NULL);
+       }
+
+       sub_type_str = wpa_dbus_type_as_string(type);
+       type_str[1] = sub_type_str[0];
+
+       if (message == NULL)
+               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       else
+               reply = dbus_message_new_method_return(message);
+       if (reply == NULL) {
+               wpa_printf(MSG_ERROR, "dbus: "
+                          "wpas_dbus_simple_array_property_getter: out of "
+                          "memory to create return message");
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             type_str, &variant_iter) ||
+           !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             sub_type_str, &array_iter)) {
+               wpa_printf(MSG_ERROR, "dbus: "
+                          "wpas_dbus_simple_array_property_getter: out of "
+                          "memory to open container");
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       switch(type) {
+       case DBUS_TYPE_BYTE:
+       case DBUS_TYPE_BOOLEAN:
+               element_size = 1;
+               break;
+       case DBUS_TYPE_INT16:
+       case DBUS_TYPE_UINT16:
+               element_size = sizeof(uint16_t);
+               break;
+       case DBUS_TYPE_INT32:
+       case DBUS_TYPE_UINT32:
+               element_size = sizeof(uint32_t);
+               break;
+       case DBUS_TYPE_INT64:
+       case DBUS_TYPE_UINT64:
+               element_size = sizeof(uint64_t);
+               break;
+       case DBUS_TYPE_DOUBLE:
+               element_size = sizeof(double);
+               break;
+       case DBUS_TYPE_STRING:
+       case DBUS_TYPE_OBJECT_PATH:
+               element_size = sizeof(char *);
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "dbus: "
+                          "wpas_dbus_simple_array_property_getter: "
+                          "fatal: unknown element type");
+               element_size = 1;
+               break;
+       }
+
+       for (i = 0; i < array_len; i++) {
+               dbus_message_iter_append_basic(&array_iter, type,
+                                              array + i * element_size);
+       }
+
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+           !dbus_message_iter_close_container(&iter, &variant_iter)) {
+               wpa_printf(MSG_ERROR, "dbus: "
+                          "wpas_dbus_simple_array_property_getter: out of "
+                          "memory to close container");
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_create_interface - Request registration of a network iface
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the new interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "CreateInterface" method call. Handles requests
+ * by dbus clients to register a network interface that wpa_supplicant
+ * will manage.
+ */
+DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
+                                                struct wpa_global *global)
+{
+       DBusMessageIter iter_dict;
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter;
+       struct wpa_dbus_dict_entry entry;
+       char *driver = NULL;
+       char *ifname = NULL;
+       char *bridge_ifname = NULL;
+
+       dbus_message_iter_init(message, &iter);
+
+       if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+               goto error;
+       while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+               if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+                       goto error;
+               if (!strcmp(entry.key, "Driver") &&
+                   (entry.type == DBUS_TYPE_STRING)) {
+                       driver = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
+                       if (driver == NULL)
+                               goto error;
+               } else if (!strcmp(entry.key, "Ifname") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       ifname = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
+                       if (ifname == NULL)
+                               goto error;
+               } else if (!strcmp(entry.key, "BridgeIfname") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       bridge_ifname = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
+                       if (bridge_ifname == NULL)
+                               goto error;
+               } else {
+                       wpa_dbus_dict_entry_clear(&entry);
+                       goto error;
+               }
+       }
+
+       if (ifname == NULL)
+               goto error; /* Required Ifname argument missing */
+
+       /*
+        * Try to get the wpa_supplicant record for this iface, return
+        * an error if we already control it.
+        */
+       if (wpa_supplicant_get_iface(global, ifname) != NULL) {
+               reply = dbus_message_new_error(message,
+                                              WPAS_DBUS_ERROR_IFACE_EXISTS,
+                                              "wpa_supplicant already "
+                                              "controls this interface.");
+       } else {
+               struct wpa_supplicant *wpa_s;
+               struct wpa_interface iface;
+               os_memset(&iface, 0, sizeof(iface));
+               iface.driver = driver;
+               iface.ifname = ifname;
+               iface.bridge_ifname = bridge_ifname;
+               /* Otherwise, have wpa_supplicant attach to it. */
+               if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
+                       const char *path = wpa_s->dbus_new_path;
+                       reply = dbus_message_new_method_return(message);
+                       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
+                                                &path, DBUS_TYPE_INVALID);
+               } else {
+                       reply = wpas_dbus_error_unknown_error(
+                               message, "wpa_supplicant couldn't grab this "
+                               "interface.");
+               }
+       }
+
+out:
+       os_free(driver);
+       os_free(ifname);
+       os_free(bridge_ifname);
+       return reply;
+
+error:
+       reply = wpas_dbus_error_invalid_args(message, NULL);
+       goto out;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_interface - Request deregistration of an interface
+ * @message: Pointer to incoming dbus message
+ * @global: wpa_supplicant global data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "removeInterface" method call.  Handles requests
+ * by dbus clients to deregister a network interface that wpa_supplicant
+ * currently manages.
+ */
+DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
+                                                struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s;
+       char *path;
+       DBusMessage *reply = NULL;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+                             DBUS_TYPE_INVALID);
+
+       wpa_s = get_iface_by_dbus_path(global, path);
+       if (wpa_s == NULL)
+               reply = wpas_dbus_error_iface_unknown(message);
+       else if (wpa_supplicant_remove_iface(global, wpa_s)) {
+               reply = wpas_dbus_error_unknown_error(
+                       message, "wpa_supplicant couldn't remove this "
+                       "interface.");
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_get_interface - Get the object path for an interface name
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "getInterface" method call.
+ */
+DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
+                                             struct wpa_global *global)
+{
+       DBusMessage *reply = NULL;
+       const char *ifname;
+       const char *path;
+       struct wpa_supplicant *wpa_s;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &ifname,
+                             DBUS_TYPE_INVALID);
+
+       wpa_s = wpa_supplicant_get_iface(global, ifname);
+       if (wpa_s == NULL)
+               return wpas_dbus_error_iface_unknown(message);
+
+       path = wpa_s->dbus_new_path;
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                                     DBUS_TYPE_INVALID)) {
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_debug_level - Get debug level
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: DBus message with value of debug level
+ *
+ * Getter for "DebugLevel" property.
+ */
+DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
+                                          struct wpa_global *global)
+{
+       const char *str;
+       int idx = wpa_debug_level;
+       if (idx < 0)
+               idx = 0;
+       if (idx > 4)
+               idx = 4;
+       str = debug_strings[idx];
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
+                                               &str);
+}
+
+
+/**
+ * wpas_dbus_getter_debug_timestamp - Get debug timestamp
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: DBus message with value of debug timestamp
+ *
+ * Getter for "DebugTimestamp" property.
+ */
+DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
+                                              struct wpa_global *global)
+{
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
+                                               &wpa_debug_timestamp);
+
+}
+
+
+/**
+ * wpas_dbus_getter_debug_show_keys - Get debug show keys
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: DBus message with value of debug show_keys
+ *
+ * Getter for "DebugShowKeys" property.
+ */
+DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
+                                              struct wpa_global *global)
+{
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
+                                               &wpa_debug_show_keys);
+
+}
+
+/**
+ * wpas_dbus_setter_debug_level - Set debug level
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: %NULL or DBus error message
+ *
+ * Setter for "DebugLevel" property.
+ */
+DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
+                                          struct wpa_global *global)
+{
+       DBusMessage *reply;
+       const char *str = NULL;
+       int i, val = -1;
+
+       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_STRING,
+                                                &str);
+       if (reply)
+               return reply;
+
+       for (i = 0; debug_strings[i]; i++)
+               if (os_strcmp(debug_strings[i], str) == 0) {
+                       val = i;
+                       break;
+               }
+
+       if (val < 0 ||
+           wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
+                                           wpa_debug_show_keys)) {
+               dbus_message_unref(reply);
+               return wpas_dbus_error_invalid_args(
+                       message, "Wrong debug level value");
+       }
+
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_setter_debug_timestamp - Set debug timestamp
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: %NULL or DBus error message
+ *
+ * Setter for "DebugTimestamp" property.
+ */
+DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
+                                              struct wpa_global *global)
+{
+       DBusMessage *reply;
+       dbus_bool_t val;
+
+       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
+                                                &val);
+       if (reply)
+               return reply;
+
+       wpa_supplicant_set_debug_params(global, wpa_debug_level, val ? 1 : 0,
+                                       wpa_debug_show_keys);
+
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_setter_debug_show_keys - Set debug show keys
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: %NULL or DBus error message
+ *
+ * Setter for "DebugShowKeys" property.
+ */
+DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
+                                              struct wpa_global *global)
+{
+       DBusMessage *reply;
+       dbus_bool_t val;
+
+       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
+                                                &val);
+       if (reply)
+               return reply;
+
+       wpa_supplicant_set_debug_params(global, wpa_debug_level,
+                                       wpa_debug_timestamp,
+                                       val ? 1 : 0);
+
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_getter_interfaces - Request registered interfaces list
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object paths array containing registered interfaces
+ * objects paths or DBus error on failure
+ *
+ * Getter for "Interfaces" property. Handles requests
+ * by dbus clients to return list of registered interfaces objects
+ * paths
+ */
+DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
+                                         struct wpa_global *global)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_supplicant *wpa_s;
+       const char **paths;
+       unsigned int i = 0, num = 0;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+               num++;
+
+       paths = os_zalloc(num * sizeof(char*));
+       if (!paths) {
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+               paths[i] = wpa_s->dbus_new_path;
+
+       reply = wpas_dbus_simple_array_property_getter(message,
+                                                      DBUS_TYPE_OBJECT_PATH,
+                                                      paths, num);
+
+       os_free(paths);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_eap_methods - Request supported EAP methods list
+ * @message: Pointer to incoming dbus message
+ * @nothing: not used argument. may be NULL or anything else
+ * Returns: The object paths array containing supported EAP methods
+ * represented by strings or DBus error on failure
+ *
+ * Getter for "EapMethods" property. Handles requests
+ * by dbus clients to return list of strings with supported EAP methods
+ */
+DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message, void *nothing)
+{
+       DBusMessage *reply = NULL;
+       char **eap_methods;
+       size_t num_items = 0;
+
+       eap_methods = eap_get_names_as_string_array(&num_items);
+       if (!eap_methods) {
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       reply = wpas_dbus_simple_array_property_getter(message,
+                                                      DBUS_TYPE_STRING,
+                                                      eap_methods, num_items);
+
+       while (num_items)
+               os_free(eap_methods[--num_items]);
+       os_free(eap_methods);
+       return reply;
+}
+
+
+static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var,
+                                  char **type, DBusMessage **reply)
+{
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                          "Type must be a string");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong Type value type. String required");
+               return -1;
+       }
+       dbus_message_iter_get_basic(var, type);
+       return 0;
+}
+
+
+static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
+                                   struct wpa_driver_scan_params *params,
+                                   DBusMessage **reply)
+{
+       struct wpa_driver_scan_ssid *ssids = params->ssids;
+       size_t ssids_num = 0;
+       u8 *ssid;
+       DBusMessageIter array_iter, sub_array_iter;
+       char *val;
+       int len;
+
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids "
+                          "must be an array of arrays of bytes");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong SSIDs value type. Array of arrays of "
+                       "bytes required");
+               return -1;
+       }
+
+       dbus_message_iter_recurse(var, &array_iter);
+
+       if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
+           dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE)
+       {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids "
+                          "must be an array of arrays of bytes");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong SSIDs value type. Array of arrays of "
+                       "bytes required");
+               return -1;
+       }
+
+       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY)
+       {
+               if (ssids_num >= WPAS_MAX_SCAN_SSIDS) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "Too many ssids specified on scan dbus "
+                                  "call");
+                       *reply = wpas_dbus_error_invalid_args(
+                               message, "Too many ssids specified. Specify "
+                               "at most four");
+                       return -1;
+               }
+
+               dbus_message_iter_recurse(&array_iter, &sub_array_iter);
+
+               dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
+               if (len == 0) {
+                       dbus_message_iter_next(&array_iter);
+                       continue;
+               }
+
+               ssid = os_malloc(len);
+               if (ssid == NULL) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "out of memory. Cannot allocate memory for "
+                                  "SSID");
+                       *reply = dbus_message_new_error(
+                               message, DBUS_ERROR_NO_MEMORY, NULL);
+                       return -1;
+               }
+               os_memcpy(ssid, val, len);
+               ssids[ssids_num].ssid = ssid;
+               ssids[ssids_num].ssid_len = len;
+
+               dbus_message_iter_next(&array_iter);
+               ssids_num++;
+       }
+
+       params->num_ssids = ssids_num;
+       return 0;
+}
+
+
+static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var,
+                                 struct wpa_driver_scan_params *params,
+                                 DBusMessage **reply)
+{
+       u8 *ies = NULL, *nies;
+       int ies_len = 0;
+       DBusMessageIter array_iter, sub_array_iter;
+       char *val;
+       int len;
+
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must "
+                          "be an array of arrays of bytes");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong IEs value type. Array of arrays of "
+                       "bytes required");
+               return -1;
+       }
+
+       dbus_message_iter_recurse(var, &array_iter);
+
+       if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
+           dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE)
+       {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must "
+                          "be an array of arrays of bytes");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong IEs value type. Array required");
+               return -1;
+       }
+
+       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY)
+       {
+               dbus_message_iter_recurse(&array_iter, &sub_array_iter);
+
+               dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
+               if (len == 0) {
+                       dbus_message_iter_next(&array_iter);
+                       continue;
+               }
+
+               nies = os_realloc(ies, ies_len + len);
+               if (nies == NULL) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "out of memory. Cannot allocate memory for "
+                                  "IE");
+                       os_free(ies);
+                       *reply = dbus_message_new_error(
+                               message, DBUS_ERROR_NO_MEMORY, NULL);
+                       return -1;
+               }
+               ies = nies;
+               os_memcpy(ies + ies_len, val, len);
+               ies_len += len;
+
+               dbus_message_iter_next(&array_iter);
+       }
+
+       params->extra_ies = ies;
+       params->extra_ies_len = ies_len;
+       return 0;
+}
+
+
+static int wpas_dbus_get_scan_channels(DBusMessage *message,
+                                      DBusMessageIter *var,
+                                      struct wpa_driver_scan_params *params,
+                                      DBusMessage **reply)
+{
+       DBusMessageIter array_iter, sub_array_iter;
+       int *freqs = NULL, *nfreqs;
+       int freqs_num = 0;
+
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                          "Channels must be an array of structs");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong Channels value type. Array of structs "
+                       "required");
+               return -1;
+       }
+
+       dbus_message_iter_recurse(var, &array_iter);
+
+       if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) {
+               wpa_printf(MSG_DEBUG,
+                          "wpas_dbus_handler_scan[dbus]: Channels must be an "
+                          "array of structs");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong Channels value type. Array of structs "
+                       "required");
+               return -1;
+       }
+
+       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_STRUCT)
+       {
+               int freq, width;
+
+               dbus_message_iter_recurse(&array_iter, &sub_array_iter);
+
+               if (dbus_message_iter_get_arg_type(&sub_array_iter) !=
+                   DBUS_TYPE_UINT32) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "Channel must by specified by struct of "
+                                  "two UINT32s %c",
+                                  dbus_message_iter_get_arg_type(
+                                          &sub_array_iter));
+                       *reply = wpas_dbus_error_invalid_args(
+                               message, "Wrong Channel struct. Two UINT32s "
+                               "required");
+                       os_free(freqs);
+                       return -1;
+               }
+               dbus_message_iter_get_basic(&sub_array_iter, &freq);
+
+               if (!dbus_message_iter_next(&sub_array_iter) ||
+                   dbus_message_iter_get_arg_type(&sub_array_iter) !=
+                   DBUS_TYPE_UINT32) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "Channel must by specified by struct of "
+                                  "two UINT32s");
+                       *reply = wpas_dbus_error_invalid_args(
+                               message,
+                               "Wrong Channel struct. Two UINT32s required");
+                       os_free(freqs);
+                       return -1;
+               }
+
+               dbus_message_iter_get_basic(&sub_array_iter, &width);
+
+#define FREQS_ALLOC_CHUNK 32
+               if (freqs_num % FREQS_ALLOC_CHUNK == 0) {
+                       nfreqs = os_realloc(freqs, sizeof(int) *
+                                           (freqs_num + FREQS_ALLOC_CHUNK));
+                       if (nfreqs == NULL)
+                               os_free(freqs);
+                       freqs = nfreqs;
+               }
+               if (freqs == NULL) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "out of memory. can't allocate memory for "
+                                  "freqs");
+                       *reply = dbus_message_new_error(
+                               message, DBUS_ERROR_NO_MEMORY, NULL);
+                       return -1;
+               }
+
+               freqs[freqs_num] = freq;
+
+               freqs_num++;
+               dbus_message_iter_next(&array_iter);
+       }
+
+       nfreqs = os_realloc(freqs,
+                           sizeof(int) * (freqs_num + 1));
+       if (nfreqs == NULL)
+               os_free(freqs);
+       freqs = nfreqs;
+       if (freqs == NULL) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                          "out of memory. Can't allocate memory for freqs");
+               *reply = dbus_message_new_error(
+                       message, DBUS_ERROR_NO_MEMORY, NULL);
+               return -1;
+       }
+       freqs[freqs_num] = 0;
+
+       params->freqs = freqs;
+       return 0;
+}
+
+
+/**
+ * wpas_dbus_handler_scan - Request a wireless scan on an interface
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "Scan" method call of a network device. Requests
+ * that wpa_supplicant perform a wireless scan as soon as possible
+ * on a particular wireless interface.
+ */
+DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
+                                    struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, dict_iter, entry_iter, variant_iter;
+       char *key = NULL, *type = NULL;
+       struct wpa_driver_scan_params params;
+       size_t i;
+
+       os_memset(&params, 0, sizeof(params));
+
+       dbus_message_iter_init(message, &iter);
+
+       dbus_message_iter_recurse(&iter, &dict_iter);
+
+       while (dbus_message_iter_get_arg_type(&dict_iter) ==
+                       DBUS_TYPE_DICT_ENTRY) {
+               dbus_message_iter_recurse(&dict_iter, &entry_iter);
+               dbus_message_iter_get_basic(&entry_iter, &key);
+               dbus_message_iter_next(&entry_iter);
+               dbus_message_iter_recurse(&entry_iter, &variant_iter);
+
+               if (os_strcmp(key, "Type") == 0) {
+                       if (wpas_dbus_get_scan_type(message, &variant_iter,
+                                                   &type, &reply) < 0)
+                               goto out;
+               } else if (os_strcmp(key, "SSIDs") == 0) {
+                       if (wpas_dbus_get_scan_ssids(message, &variant_iter,
+                                                    &params, &reply) < 0)
+                               goto out;
+               } else if (os_strcmp(key, "IEs") == 0) {
+                       if (wpas_dbus_get_scan_ies(message, &variant_iter,
+                                                  &params, &reply) < 0)
+                               goto out;
+               } else if (os_strcmp(key, "Channels") == 0) {
+                       if (wpas_dbus_get_scan_channels(message, &variant_iter,
+                                                       &params, &reply) < 0)
+                               goto out;
+               } else {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "Unknown argument %s", key);
+                       reply = wpas_dbus_error_invalid_args(message, key);
+                       goto out;
+               }
+
+               dbus_message_iter_next(&dict_iter);
+       }
+
+       if (!type) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                          "Scan type not specified");
+               reply = wpas_dbus_error_invalid_args(message, key);
+               goto out;
+       }
+
+       if (!os_strcmp(type, "passive")) {
+               if (params.num_ssids || params.extra_ies_len) {
+                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                                  "SSIDs or IEs specified for passive scan.");
+                       reply = wpas_dbus_error_invalid_args(
+                               message, "You can specify only Channels in "
+                               "passive scan");
+                       goto out;
+               } else if (params.freqs && params.freqs[0]) {
+                       /* wildcard ssid */
+                       params.num_ssids++;
+                       wpa_supplicant_trigger_scan(wpa_s, &params);
+               } else {
+                       wpa_s->scan_req = 2;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
+       } else if (!os_strcmp(type, "active")) {
+               wpa_supplicant_trigger_scan(wpa_s, &params);
+       } else {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
+                          "Unknown scan type: %s", type);
+               reply = wpas_dbus_error_invalid_args(message,
+                                                    "Wrong scan type");
+               goto out;
+       }
+
+out:
+       for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++)
+               os_free((u8 *) params.ssids[i].ssid);
+       os_free((u8 *) params.extra_ies);
+       os_free(params.freqs);
+       return reply;
+}
+
+
+/*
+ * wpas_dbus_handler_disconnect - Terminate the current connection
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if already not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Disconnect" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->current_ssid != NULL) {
+               wpa_s->disconnected = 1;
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+
+               return NULL;
+       }
+
+       return dbus_message_new_error(message, WPAS_DBUS_ERROR_NOT_CONNECTED,
+                                     "This interface is not connected");
+}
+
+
+/**
+ * wpas_dbus_new_iface_add_network - Add a new configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new network
+ *
+ * Handler function for "AddNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter;
+       struct wpa_ssid *ssid = NULL;
+       char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+
+       dbus_message_iter_init(message, &iter);
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_handler_add_network[dbus]: "
+                          "can't add new interface.");
+               reply = wpas_dbus_error_unknown_error(
+                       message,
+                       "wpa_supplicant could not add "
+                       "a network on this interface.");
+               goto err;
+       }
+       wpas_notify_network_added(wpa_s, ssid);
+       ssid->disabled = 1;
+       wpa_config_set_network_defaults(ssid);
+
+       reply = set_network_properties(message, wpa_s, ssid, &iter);
+       if (reply) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:"
+                          "control interface couldn't set network "
+                          "properties");
+               goto err;
+       }
+
+       /* Construct the object path for this network. */
+       os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
+                   wpa_s->dbus_new_path, ssid->id);
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto err;
+       }
+       if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                                     DBUS_TYPE_INVALID)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto err;
+       }
+
+       return reply;
+
+err:
+       if (ssid) {
+               wpas_notify_network_removed(wpa_s, ssid);
+               wpa_config_remove_network(wpa_s->conf, ssid->id);
+       }
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_remove_network - Remove a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "RemoveNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       const char *op;
+       char *iface = NULL, *net_id = NULL;
+       int id;
+       struct wpa_ssid *ssid;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+                             DBUS_TYPE_INVALID);
+
+       /* Extract the network ID and ensure the network */
+       /* is actually a child of this interface */
+       iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
+       if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+               reply = wpas_dbus_error_invalid_args(message, op);
+               goto out;
+       }
+
+       id = strtoul(net_id, NULL, 10);
+       if (errno == EINVAL) {
+               reply = wpas_dbus_error_invalid_args(message, op);
+               goto out;
+       }
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               reply = wpas_dbus_error_network_unknown(message);
+               goto out;
+       }
+
+       wpas_notify_network_removed(wpa_s, ssid);
+
+       if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "wpas_dbus_handler_remove_network[dbus]: "
+                          "error occurred when removing network %d", id);
+               reply = wpas_dbus_error_unknown_error(
+                       message, "error removing the specified network on "
+                       "this interface.");
+               goto out;
+       }
+
+       if (ssid == wpa_s->current_ssid)
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+
+out:
+       os_free(iface);
+       os_free(net_id);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_select_network - Attempt association with a network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL on success or dbus error on failure
+ *
+ * Handler function for "SelectNetwork" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       const char *op;
+       char *iface = NULL, *net_id = NULL;
+       int id;
+       struct wpa_ssid *ssid;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &op,
+                             DBUS_TYPE_INVALID);
+
+       /* Extract the network ID and ensure the network */
+       /* is actually a child of this interface */
+       iface = wpas_dbus_new_decompose_object_path(op, &net_id, NULL);
+       if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+               reply = wpas_dbus_error_invalid_args(message, op);
+               goto out;
+       }
+
+       id = strtoul(net_id, NULL, 10);
+       if (errno == EINVAL) {
+               reply = wpas_dbus_error_invalid_args(message, op);
+               goto out;
+       }
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               reply = wpas_dbus_error_network_unknown(message);
+               goto out;
+       }
+
+       /* Finally, associate with the network */
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+out:
+       os_free(iface);
+       os_free(net_id);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_add_blob - Store named binary blob (ie, for certificates)
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing an error on failure or NULL on success
+ *
+ * Asks wpa_supplicant to internally store a binary blobs.
+ */
+DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, array_iter;
+
+       char *blob_name;
+       u8 *blob_data;
+       int blob_len;
+       struct wpa_config_blob *blob = NULL;
+
+       dbus_message_iter_init(message, &iter);
+       dbus_message_iter_get_basic(&iter, &blob_name);
+
+       if (wpa_config_get_blob(wpa_s->conf, blob_name)) {
+               return dbus_message_new_error(message,
+                                             WPAS_DBUS_ERROR_BLOB_EXISTS,
+                                             NULL);
+       }
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_recurse(&iter, &array_iter);
+
+       dbus_message_iter_get_fixed_array(&array_iter, &blob_data, &blob_len);
+
+       blob = os_zalloc(sizeof(*blob));
+       if (!blob) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto err;
+       }
+
+       blob->data = os_malloc(blob_len);
+       if (!blob->data) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto err;
+       }
+       os_memcpy(blob->data, blob_data, blob_len);
+
+       blob->len = blob_len;
+       blob->name = os_strdup(blob_name);
+       if (!blob->name) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto err;
+       }
+
+       wpa_config_set_blob(wpa_s->conf, blob);
+       wpas_notify_blob_added(wpa_s, blob->name);
+
+       return reply;
+
+err:
+       if (blob) {
+               os_free(blob->name);
+               os_free(blob->data);
+               os_free(blob);
+       }
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_handler_get_blob - Get named binary blob (ie, for certificates)
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing array of bytes (blob)
+ *
+ * Gets one wpa_supplicant's binary blobs.
+ */
+DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, array_iter;
+
+       char *blob_name;
+       const struct wpa_config_blob *blob;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name,
+                             DBUS_TYPE_INVALID);
+
+       blob = wpa_config_get_blob(wpa_s->conf, blob_name);
+       if (!blob) {
+               return dbus_message_new_error(message,
+                                             WPAS_DBUS_ERROR_BLOB_UNKNOWN,
+                                             "Blob id not set");
+       }
+
+       reply = dbus_message_new_method_return(message);
+       if (!reply) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                             DBUS_TYPE_BYTE_AS_STRING,
+                                             &array_iter)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+       if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+                                                 &(blob->data), blob->len)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+       if (!dbus_message_iter_close_container(&iter, &array_iter)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+out:
+       return reply;
+}
+
+
+/**
+ * wpas_remove_handler_remove_blob - Remove named binary blob
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or dbus error
+ *
+ * Asks wpa_supplicant to internally remove a binary blobs.
+ */
+DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       char *blob_name;
+
+       dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &blob_name,
+                             DBUS_TYPE_INVALID);
+
+       if (wpa_config_remove_blob(wpa_s->conf, blob_name)) {
+               return dbus_message_new_error(message,
+                                             WPAS_DBUS_ERROR_BLOB_UNKNOWN,
+                                             "Blob id not set");
+       }
+       wpas_notify_blob_removed(wpa_s, blob_name);
+
+       return reply;
+
+}
+
+
+/**
+ * wpas_dbus_getter_capabilities - Return interface capabilities
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a dict of strings
+ *
+ * Getter for "Capabilities" property of an interface.
+ */
+DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_driver_capa capa;
+       int res;
+       DBusMessageIter iter, iter_dict;
+       DBusMessageIter iter_dict_entry, iter_dict_val, iter_array,
+               variant_iter;
+       const char *scans[] = { "active", "passive", "ssid" };
+       const char *modes[] = { "infrastructure", "ad-hoc", "ap" };
+       int n = sizeof(modes) / sizeof(char *);
+
+       if (message == NULL)
+               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       else
+               reply = dbus_message_new_method_return(message);
+       if (!reply)
+               goto nomem;
+
+       dbus_message_iter_init_append(reply, &iter);
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             "a{sv}", &variant_iter))
+               goto nomem;
+
+       if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+               goto nomem;
+
+       res = wpa_drv_get_capa(wpa_s, &capa);
+
+       /***** pairwise cipher */
+       if (res < 0) {
+               const char *args[] = {"ccmp", "tkip", "none"};
+               if (!wpa_dbus_dict_append_string_array(
+                           &iter_dict, "Pairwise", args,
+                           sizeof(args) / sizeof(char*)))
+                       goto nomem;
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto nomem;
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "ccmp"))
+                               goto nomem;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "tkip"))
+                               goto nomem;
+               }
+
+               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "none"))
+                               goto nomem;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto nomem;
+       }
+
+       /***** group cipher */
+       if (res < 0) {
+               const char *args[] = {
+                       "ccmp", "tkip", "wep104", "wep40"
+               };
+               if (!wpa_dbus_dict_append_string_array(
+                           &iter_dict, "Group", args,
+                           sizeof(args) / sizeof(char*)))
+                       goto nomem;
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto nomem;
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "ccmp"))
+                               goto nomem;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "tkip"))
+                               goto nomem;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wep104"))
+                               goto nomem;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wep40"))
+                               goto nomem;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto nomem;
+       }
+
+       /***** key management */
+       if (res < 0) {
+               const char *args[] = {
+                       "wpa-psk", "wpa-eap", "ieee8021x", "wpa-none",
+#ifdef CONFIG_WPS
+                       "wps",
+#endif /* CONFIG_WPS */
+                       "none"
+               };
+               if (!wpa_dbus_dict_append_string_array(
+                           &iter_dict, "KeyMgmt", args,
+                           sizeof(args) / sizeof(char*)))
+                       goto nomem;
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto nomem;
+
+               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "none"))
+                       goto nomem;
+
+               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "ieee8021x"))
+                       goto nomem;
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wpa-eap"))
+                               goto nomem;
+
+                       if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT)
+                               if (!wpa_dbus_dict_string_array_add_element(
+                                           &iter_array, "wpa-ft-eap"))
+                                       goto nomem;
+
+/* TODO: Ensure that driver actually supports sha256 encryption. */
+#ifdef CONFIG_IEEE80211W
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wpa-eap-sha256"))
+                               goto nomem;
+#endif /* CONFIG_IEEE80211W */
+               }
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wpa-psk"))
+                               goto nomem;
+
+                       if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK)
+                               if (!wpa_dbus_dict_string_array_add_element(
+                                           &iter_array, "wpa-ft-psk"))
+                                       goto nomem;
+
+/* TODO: Ensure that driver actually supports sha256 encryption. */
+#ifdef CONFIG_IEEE80211W
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wpa-psk-sha256"))
+                               goto nomem;
+#endif /* CONFIG_IEEE80211W */
+               }
+
+               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wpa-none"))
+                               goto nomem;
+               }
+
+
+#ifdef CONFIG_WPS
+               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "wps"))
+                       goto nomem;
+#endif /* CONFIG_WPS */
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto nomem;
+       }
+
+       /***** WPA protocol */
+       if (res < 0) {
+               const char *args[] = { "rsn", "wpa" };
+               if (!wpa_dbus_dict_append_string_array(
+                           &iter_dict, "Protocol", args,
+                           sizeof(args) / sizeof(char*)))
+                       goto nomem;
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto nomem;
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "rsn"))
+                               goto nomem;
+               }
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "wpa"))
+                               goto nomem;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto nomem;
+       }
+
+       /***** auth alg */
+       if (res < 0) {
+               const char *args[] = { "open", "shared", "leap" };
+               if (!wpa_dbus_dict_append_string_array(
+                           &iter_dict, "AuthAlg", args,
+                           sizeof(args) / sizeof(char*)))
+                       goto nomem;
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto nomem;
+
+               if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "open"))
+                               goto nomem;
+               }
+
+               if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "shared"))
+                               goto nomem;
+               }
+
+               if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "leap"))
+                               goto nomem;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto nomem;
+       }
+
+       /***** Scan */
+       if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans,
+                                              sizeof(scans) / sizeof(char *)))
+               goto nomem;
+
+       /***** Modes */
+       if (res < 0 || !(capa.flags & WPA_DRIVER_FLAGS_AP))
+               n--; /* exclude ap mode if it is not supported by the driver */
+       if (!wpa_dbus_dict_append_string_array(&iter_dict, "Modes", modes, n))
+               goto nomem;
+
+       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
+               goto nomem;
+       if (!dbus_message_iter_close_container(&iter, &variant_iter))
+               goto nomem;
+
+       return reply;
+
+nomem:
+       if (reply)
+               dbus_message_unref(reply);
+
+       return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+}
+
+
+/**
+ * wpas_dbus_getter_state - Get interface state
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a STRING representing the current
+ *          interface state
+ *
+ * Getter for "State" property.
+ */
+DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
+                                    struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       const char *str_state;
+       char *state_ls, *tmp;
+
+       str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
+
+       /* make state string lowercase to fit new DBus API convention
+        */
+       state_ls = tmp = os_strdup(str_state);
+       if (!tmp) {
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+       while (*tmp) {
+               *tmp = tolower(*tmp);
+               tmp++;
+       }
+
+       reply = wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
+                                                &state_ls);
+
+       os_free(state_ls);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_new_iface_get_scanning - Get interface scanning state
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing whether the interface is scanning
+ *
+ * Getter for "scanning" property.
+ */
+DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s)
+{
+       dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
+                                               &scanning);
+}
+
+
+/**
+ * wpas_dbus_getter_ap_scan - Control roaming mode
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A message containong value of ap_scan variable
+ *
+ * Getter function for "ApScan" property.
+ */
+DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
+                                      struct wpa_supplicant *wpa_s)
+{
+       dbus_uint32_t ap_scan = wpa_s->conf->ap_scan;
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT32,
+                                               &ap_scan);
+}
+
+
+/**
+ * wpas_dbus_setter_ap_scan - Control roaming mode
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL
+ *
+ * Setter function for "ApScan" property.
+ */
+DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
+                                      struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       dbus_uint32_t ap_scan;
+
+       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_UINT32,
+                                                &ap_scan);
+       if (reply)
+               return reply;
+
+       if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
+               return wpas_dbus_error_invalid_args(
+                       message, "ap_scan must equal 0, 1 or 2");
+       }
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_getter_ifname - Get interface name
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a name of network interface
+ * associated with with wpa_s
+ *
+ * Getter for "Ifname" property.
+ */
+DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s)
+{
+       const char *ifname = wpa_s->ifname;
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
+                                               &ifname);
+}
+
+
+/**
+ * wpas_dbus_getter_driver - Get interface name
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a name of network interface
+ * driver associated with with wpa_s
+ *
+ * Getter for "Driver" property.
+ */
+DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s)
+{
+       const char *driver;
+
+       if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
+               wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: "
+                          "wpa_s has no driver set");
+               return wpas_dbus_error_unknown_error(message, NULL);
+       }
+
+       driver = wpa_s->driver->name;
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
+                                               &driver);
+}
+
+
+/**
+ * wpas_dbus_getter_current_bss - Get current bss object path
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a DBus object path to
+ * current BSS
+ *
+ * Getter for "CurrentBSS" property.
+ */
+DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply;
+       char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *bss_obj_path = path_buf;
+
+       if (wpa_s->current_bss)
+               os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+                           wpa_s->dbus_new_path, wpa_s->current_bss->id);
+       else
+               os_snprintf(bss_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+
+       reply = wpas_dbus_simple_property_getter(message,
+                                                DBUS_TYPE_OBJECT_PATH,
+                                                &bss_obj_path);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_current_network - Get current network object path
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a DBus object path to
+ * current network
+ *
+ * Getter for "CurrentNetwork" property.
+ */
+DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply;
+       char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *net_obj_path = path_buf;
+
+       if (wpa_s->current_ssid)
+               os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%u",
+                           wpa_s->dbus_new_path, wpa_s->current_ssid->id);
+       else
+               os_snprintf(net_obj_path, WPAS_DBUS_OBJECT_PATH_MAX, "/");
+
+       reply = wpas_dbus_simple_property_getter(message,
+                                                DBUS_TYPE_OBJECT_PATH,
+                                                &net_obj_path);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_bridge_ifname - Get interface name
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a name of bridge network
+ * interface associated with with wpa_s
+ *
+ * Getter for "BridgeIfname" property.
+ */
+DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s)
+{
+       const char *bridge_ifname = NULL;
+
+       bridge_ifname = wpa_s->bridge_ifname;
+       if (bridge_ifname == NULL) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bridge_ifname[dbus]: "
+                          "wpa_s has no bridge interface name set");
+               return wpas_dbus_error_unknown_error(message, NULL);
+       }
+
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
+                                               &bridge_ifname);
+}
+
+
+/**
+ * wpas_dbus_getter_bsss - Get array of BSSs objects
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing an array of all known BSS objects
+ * dbus paths
+ *
+ * Getter for "BSSs" property.
+ */
+DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
+                                   struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_bss *bss;
+       char **paths;
+       unsigned int i = 0;
+
+       paths = os_zalloc(wpa_s->num_bss * sizeof(char *));
+       if (!paths) {
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       /* Loop through scan results and append each result's object path */
+       dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+               paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+               if (paths[i] == NULL) {
+                       reply = dbus_message_new_error(message,
+                                                      DBUS_ERROR_NO_MEMORY,
+                                                      NULL);
+                       goto out;
+               }
+               /* Construct the object path for this BSS. */
+               os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s/" WPAS_DBUS_NEW_BSSIDS_PART "/%u",
+                           wpa_s->dbus_new_path, bss->id);
+       }
+
+       reply = wpas_dbus_simple_array_property_getter(message,
+                                                      DBUS_TYPE_OBJECT_PATH,
+                                                      paths, wpa_s->num_bss);
+
+out:
+       while (i)
+               os_free(paths[--i]);
+       os_free(paths);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_networks - Get array of networks objects
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing an array of all configured
+ * networks dbus object paths.
+ *
+ * Getter for "Networks" property.
+ */
+DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_ssid *ssid;
+       char **paths;
+       unsigned int i = 0, num = 0;
+
+       if (wpa_s->conf == NULL) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_networks[dbus]: "
+                          "An error occurred getting networks list.");
+               return wpas_dbus_error_unknown_error(message, NULL);
+       }
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+               num++;
+
+       paths = os_zalloc(num * sizeof(char *));
+       if (!paths) {
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       /* Loop through configured networks and append object path of each */
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+               if (paths[i] == NULL) {
+                       reply = dbus_message_new_error(message,
+                                                      DBUS_ERROR_NO_MEMORY,
+                                                      NULL);
+                       goto out;
+               }
+
+               /* Construct the object path for this network. */
+               os_snprintf(paths[i++], WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
+                           wpa_s->dbus_new_path, ssid->id);
+       }
+
+       reply = wpas_dbus_simple_array_property_getter(message,
+                                                      DBUS_TYPE_OBJECT_PATH,
+                                                      paths, num);
+
+out:
+       while (i)
+               os_free(paths[--i]);
+       os_free(paths);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_blobs - Get all blobs defined for this interface
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing a dictionary of pairs (blob_name, blob)
+ *
+ * Getter for "Blobs" property.
+ */
+DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
+                                    struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, variant_iter, dict_iter, entry_iter, array_iter;
+       struct wpa_config_blob *blob;
+
+       if (message == NULL)
+               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       else
+               reply = dbus_message_new_method_return(message);
+       if (!reply)
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             "a{say}", &variant_iter) ||
+           !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             "{say}", &dict_iter)) {
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       blob = wpa_s->conf->blobs;
+       while (blob) {
+               if (!dbus_message_iter_open_container(&dict_iter,
+                                                     DBUS_TYPE_DICT_ENTRY,
+                                                     NULL, &entry_iter) ||
+                   !dbus_message_iter_append_basic(&entry_iter,
+                                                   DBUS_TYPE_STRING,
+                                                   &(blob->name)) ||
+                   !dbus_message_iter_open_container(&entry_iter,
+                                                     DBUS_TYPE_ARRAY,
+                                                     DBUS_TYPE_BYTE_AS_STRING,
+                                                     &array_iter) ||
+                   !dbus_message_iter_append_fixed_array(&array_iter,
+                                                         DBUS_TYPE_BYTE,
+                                                         &(blob->data),
+                                                         blob->len) ||
+                   !dbus_message_iter_close_container(&entry_iter,
+                                                      &array_iter) ||
+                   !dbus_message_iter_close_container(&dict_iter,
+                                                      &entry_iter)) {
+                       dbus_message_unref(reply);
+                       return dbus_message_new_error(message,
+                                                     DBUS_ERROR_NO_MEMORY,
+                                                     NULL);
+               }
+
+               blob = blob->next;
+       }
+
+       if (!dbus_message_iter_close_container(&variant_iter, &dict_iter) ||
+           !dbus_message_iter_close_container(&iter, &variant_iter)) {
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_bss_bssid - Return the BSSID of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the bssid for the requested bss
+ *
+ * Getter for "BSSID" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
+                                        struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_bssid[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
+                                                     res->bssid, ETH_ALEN);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_ssid - Return the SSID of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the ssid for the requested bss
+ *
+ * Getter for "SSID" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
+                                             struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ssid[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
+                                                     res->ssid,
+                                                     res->ssid_len);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_privacy - Return the privacy flag of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the privacy flag value of requested bss
+ *
+ * Getter for "Privacy" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
+                                          struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       dbus_bool_t privacy;
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_privacy[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       privacy = (res->caps & IEEE80211_CAP_PRIVACY) ? TRUE : FALSE;
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
+                                               &privacy);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_mode - Return the mode of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the mode of requested bss
+ *
+ * Getter for "Mode" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
+                                       struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       const char *mode;
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_mode[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       if (res->caps & IEEE80211_CAP_IBSS)
+               mode = "ad-hoc";
+       else
+               mode = "infrastructure";
+
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_STRING,
+                                               &mode);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_level - Return the signal strength of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the signal strength of requested bss
+ *
+ * Getter for "Level" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
+                                         struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_signal[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_INT16,
+                                               &res->level);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_frequency - Return the frequency of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the frequency of requested bss
+ *
+ * Getter for "Frequency" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
+                                            struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_frequency[dbus]: "
+                          "no bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_UINT16,
+                                               &res->freq);
+}
+
+
+static int cmp_u8s_desc(const void *a, const void *b)
+{
+       return (*(u8 *) b - *(u8 *) a);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_rates - Return available bit rates of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing sorted array of bit rates
+ *
+ * Getter for "Rates" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
+                                           struct bss_handler_args *bss)
+{
+       DBusMessage *reply;
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       u8 *ie_rates = NULL;
+       u32 *real_rates;
+       int rates_num, i;
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rates[dbus]: "
+                          "no bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       rates_num = wpa_bss_get_bit_rates(res, &ie_rates);
+       if (rates_num < 0)
+               return NULL;
+
+       qsort(ie_rates, rates_num, 1, cmp_u8s_desc);
+
+       real_rates = os_malloc(sizeof(u32) * rates_num);
+       if (!real_rates) {
+               os_free(ie_rates);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       for (i = 0; i < rates_num; i++)
+               real_rates[i] = ie_rates[i] * 500000;
+
+       reply = wpas_dbus_simple_array_property_getter(message,
+                                                      DBUS_TYPE_UINT32,
+                                                      real_rates, rates_num);
+
+       os_free(ie_rates);
+       os_free(real_rates);
+       return reply;
+}
+
+
+static DBusMessage * wpas_dbus_get_bss_security_prop(
+       DBusMessage *message, struct wpa_ie_data *ie_data)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, iter_dict, variant_iter;
+       const char *group;
+       const char *pairwise[2]; /* max 2 pairwise ciphers is supported */
+       const char *key_mgmt[7]; /* max 7 key managements may be supported */
+       int n;
+
+       if (message == NULL)
+               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       else
+               reply = dbus_message_new_method_return(message);
+       if (!reply)
+               goto nomem;
+
+       dbus_message_iter_init_append(reply, &iter);
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             "a{sv}", &variant_iter))
+               goto nomem;
+
+       if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+               goto nomem;
+
+       /* KeyMgmt */
+       n = 0;
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK)
+               key_mgmt[n++] = "wpa-psk";
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_PSK)
+               key_mgmt[n++] = "wpa-ft-psk";
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+               key_mgmt[n++] = "wpa-psk-sha256";
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+               key_mgmt[n++] = "wpa-eap";
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+               key_mgmt[n++] = "wpa-ft-eap";
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+               key_mgmt[n++] = "wpa-eap-sha256";
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
+               key_mgmt[n++] = "wpa-none";
+
+       if (!wpa_dbus_dict_append_string_array(&iter_dict, "KeyMgmt",
+                                              key_mgmt, n))
+               goto nomem;
+
+       /* Group */
+       switch (ie_data->group_cipher) {
+       case WPA_CIPHER_WEP40:
+               group = "wep40";
+               break;
+       case WPA_CIPHER_TKIP:
+               group = "tkip";
+               break;
+       case WPA_CIPHER_CCMP:
+               group = "ccmp";
+               break;
+       case WPA_CIPHER_WEP104:
+               group = "wep104";
+               break;
+       default:
+               group = "";
+               break;
+       }
+
+       if (!wpa_dbus_dict_append_string(&iter_dict, "Group", group))
+               goto nomem;
+
+       /* Pairwise */
+       n = 0;
+       if (ie_data->pairwise_cipher & WPA_CIPHER_TKIP)
+               pairwise[n++] = "tkip";
+       if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP)
+               pairwise[n++] = "ccmp";
+
+       if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise",
+                                              pairwise, n))
+               goto nomem;
+
+       /* Management group (RSN only) */
+       if (ie_data->proto == WPA_PROTO_RSN) {
+               switch (ie_data->mgmt_group_cipher) {
+#ifdef CONFIG_IEEE80211W
+               case WPA_CIPHER_AES_128_CMAC:
+                       group = "aes128cmac";
+                       break;
+#endif /* CONFIG_IEEE80211W */
+               default:
+                       group = "";
+                       break;
+               }
+
+               if (!wpa_dbus_dict_append_string(&iter_dict, "MgmtGroup",
+                                                group))
+                       goto nomem;
+       }
+
+       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
+               goto nomem;
+       if (!dbus_message_iter_close_container(&iter, &variant_iter))
+               goto nomem;
+
+       return reply;
+
+nomem:
+       if (reply)
+               dbus_message_unref(reply);
+
+       return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_wpa - Return the WPA options of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the WPA options of requested bss
+ *
+ * Getter for "WPA" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
+                                      struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct wpa_ie_data wpa_data;
+       const u8 *ie;
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_wpa[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       os_memset(&wpa_data, 0, sizeof(wpa_data));
+       ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
+       if (ie) {
+               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
+                       return wpas_dbus_error_unknown_error(message,
+                                                            "invalid WPA IE");
+       }
+
+       return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_rsn - Return the RSN options of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing the RSN options of requested bss
+ *
+ * Getter for "RSN" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
+                                      struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+       struct wpa_ie_data wpa_data;
+       const u8 *ie;
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_rsn[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       os_memset(&wpa_data, 0, sizeof(wpa_data));
+       ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
+       if (ie) {
+               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0)
+                       return wpas_dbus_error_unknown_error(message,
+                                                            "invalid RSN IE");
+       }
+
+       return wpas_dbus_get_bss_security_prop(message, &wpa_data);
+}
+
+
+/**
+ * wpas_dbus_getter_bss_ies - Return all IEs of a BSS
+ * @message: Pointer to incoming dbus message
+ * @bss: a pair of interface describing structure and bss's id
+ * Returns: a dbus message containing IEs byte array
+ *
+ * Getter for "IEs" property.
+ */
+DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
+                                      struct bss_handler_args *bss)
+{
+       struct wpa_bss *res = wpa_bss_get_id(bss->wpa_s, bss->id);
+
+       if (!res) {
+               wpa_printf(MSG_ERROR, "wpas_dbus_getter_bss_ies[dbus]: no "
+                          "bss with id %d found", bss->id);
+               return NULL;
+       }
+
+       return wpas_dbus_simple_array_property_getter(message, DBUS_TYPE_BYTE,
+                                                     res + 1, res->ie_len);
+}
+
+
+/**
+ * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
+ * @message: Pointer to incoming dbus message
+ * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
+ * and wpa_ssid structure for a configured network
+ * Returns: DBus message with boolean indicating state of configured network
+ * or DBus error on failure
+ *
+ * Getter for "enabled" property of a configured network.
+ */
+DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
+                                      struct network_handler_args *net)
+{
+       dbus_bool_t enabled = net->ssid->disabled ? FALSE : TRUE;
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
+                                               &enabled);
+}
+
+
+/**
+ * wpas_dbus_setter_enabled - Mark a configured network as enabled or disabled
+ * @message: Pointer to incoming dbus message
+ * @wpas_dbus_setter_enabled: wpa_supplicant structure for a network interface
+ * and wpa_ssid structure for a configured network
+ * Returns: NULL indicating success or DBus error on failure
+ *
+ * Setter for "Enabled" property of a configured network.
+ */
+DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
+                                      struct network_handler_args *net)
+{
+       DBusMessage *reply = NULL;
+
+       struct wpa_supplicant *wpa_s;
+       struct wpa_ssid *ssid;
+
+       dbus_bool_t enable;
+
+       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
+                                                &enable);
+
+       if (reply)
+               return reply;
+
+       wpa_s = net->wpa_s;
+       ssid = net->ssid;
+
+       if (enable)
+               wpa_supplicant_enable_network(wpa_s, ssid);
+       else
+               wpa_supplicant_disable_network(wpa_s, ssid);
+
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_getter_network_properties - Get options for a configured network
+ * @message: Pointer to incoming dbus message
+ * @net: wpa_supplicant structure for a network interface and
+ * wpa_ssid structure for a configured network
+ * Returns: DBus message with network properties or DBus error on failure
+ *
+ * Getter for "Properties" property of a configured network.
+ */
+DBusMessage * wpas_dbus_getter_network_properties(
+       DBusMessage *message, struct network_handler_args *net)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, variant_iter, dict_iter;
+       char **iterator;
+       char **props = wpa_config_get_all(net->ssid, 0);
+       if (!props)
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+
+       if (message == NULL)
+               reply = dbus_message_new(DBUS_MESSAGE_TYPE_SIGNAL);
+       else
+               reply = dbus_message_new_method_return(message);
+       if (!reply) {
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                       "a{sv}", &variant_iter) ||
+           !wpa_dbus_dict_open_write(&variant_iter, &dict_iter)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+       iterator = props;
+       while (*iterator) {
+               if (!wpa_dbus_dict_append_string(&dict_iter, *iterator,
+                                                *(iterator + 1))) {
+                       dbus_message_unref(reply);
+                       reply = dbus_message_new_error(message,
+                                                      DBUS_ERROR_NO_MEMORY,
+                                                      NULL);
+                       goto out;
+               }
+               iterator += 2;
+       }
+
+
+       if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+           !dbus_message_iter_close_container(&iter, &variant_iter)) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                              NULL);
+               goto out;
+       }
+
+out:
+       iterator = props;
+       while (*iterator) {
+               os_free(*iterator);
+               iterator++;
+       }
+       os_free(props);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_setter_network_properties - Set options for a configured network
+ * @message: Pointer to incoming dbus message
+ * @net: wpa_supplicant structure for a network interface and
+ * wpa_ssid structure for a configured network
+ * Returns: NULL indicating success or DBus error on failure
+ *
+ * Setter for "Properties" property of a configured network.
+ */
+DBusMessage * wpas_dbus_setter_network_properties(
+       DBusMessage *message, struct network_handler_args *net)
+{
+       struct wpa_ssid *ssid = net->ssid;
+
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, variant_iter;
+
+       dbus_message_iter_init(message, &iter);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_next(&iter);
+
+       dbus_message_iter_recurse(&iter, &variant_iter);
+
+       reply = set_network_properties(message, net->wpa_s, ssid,
+                                      &variant_iter);
+       if (reply)
+               wpa_printf(MSG_DEBUG, "dbus control interface couldn't set "
+                          "network properties");
+
+       return reply;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_handlers.h b/wpa_supplicant/dbus/dbus_new_handlers.h
new file mode 100644 (file)
index 0000000..3cdf9cb
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * 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_DBUS_NEW_HANDLERS_H
+#define CTRL_IFACE_DBUS_NEW_HANDLERS_H
+
+struct network_handler_args {
+       struct wpa_supplicant *wpa_s;
+       struct wpa_ssid *ssid;
+};
+
+struct bss_handler_args {
+       struct wpa_supplicant *wpa_s;
+       unsigned int id;
+};
+
+DBusMessage * wpas_dbus_simple_property_getter(DBusMessage *message,
+                                              const int type,
+                                              const void *val);
+
+DBusMessage * wpas_dbus_simple_property_setter(DBusMessage *message,
+                                              const int type, void *val);
+
+DBusMessage * wpas_dbus_simple_array_property_getter(DBusMessage *message,
+                                                    const int type,
+                                                    const void *array,
+                                                    size_t array_len);
+
+DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
+                                                struct wpa_global *global);
+
+DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
+                                                struct wpa_global *global);
+
+DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
+                                             struct wpa_global *global);
+
+DBusMessage * wpas_dbus_getter_debug_level(DBusMessage *message,
+                                          struct wpa_global *global);
+
+DBusMessage * wpas_dbus_getter_debug_timestamp(DBusMessage *message,
+                                              struct wpa_global *global);
+
+DBusMessage * wpas_dbus_getter_debug_show_keys(DBusMessage *message,
+                                              struct wpa_global *global);
+
+DBusMessage * wpas_dbus_setter_debug_level(DBusMessage *message,
+                                          struct wpa_global *global);
+
+DBusMessage * wpas_dbus_setter_debug_timestamp(DBusMessage *message,
+                                              struct wpa_global *global);
+
+DBusMessage * wpas_dbus_setter_debug_show_keys(DBusMessage *message,
+                                              struct wpa_global *global);
+
+DBusMessage * wpas_dbus_getter_interfaces(DBusMessage *message,
+                                         struct wpa_global *global);
+
+DBusMessage * wpas_dbus_getter_eap_methods(DBusMessage *message,
+                                          void *nothing);
+
+DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
+                                    struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_capabilities(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_state(DBusMessage *message,
+                                    struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_scanning(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_ap_scan(DBusMessage *message,
+                                      struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_setter_ap_scan(DBusMessage *message,
+                                      struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_ifname(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_driver(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_bridge_ifname(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_current_bss(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_current_network(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_bsss(DBusMessage *message,
+                                   struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_networks(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_blobs(DBusMessage *message,
+                                    struct wpa_supplicant *bss);
+
+DBusMessage * wpas_dbus_getter_bss_bssid(DBusMessage *message,
+                                        struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_ssid(DBusMessage *message,
+                                       struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_privacy(DBusMessage *message,
+                                          struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_mode(DBusMessage *message,
+                                       struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_signal(DBusMessage *message,
+                                         struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_frequency(DBusMessage *message,
+                                            struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_rates(DBusMessage *message,
+                                        struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_wpa(DBusMessage *message,
+                                      struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_rsn(DBusMessage *message,
+                                      struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_bss_ies(DBusMessage *message,
+                                      struct bss_handler_args *bss);
+
+DBusMessage * wpas_dbus_getter_enabled(DBusMessage *message,
+                                      struct network_handler_args *net);
+
+DBusMessage * wpas_dbus_setter_enabled(DBusMessage *message,
+                                      struct network_handler_args *net);
+
+DBusMessage * wpas_dbus_getter_network_properties(
+       DBusMessage *message, struct network_handler_args *net);
+
+DBusMessage * wpas_dbus_setter_network_properties(
+       DBusMessage *message, struct network_handler_args *net);
+
+DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_process_credentials(
+       DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_setter_process_credentials(
+       DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_getter_credentials(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
+                                          const char *arg);
+DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
+                                           const char *arg);
+
+#endif /* CTRL_IFACE_DBUS_HANDLERS_NEW_H */
diff --git a/wpa_supplicant/dbus/dbus_new_handlers_wps.c b/wpa_supplicant/dbus/dbus_new_handlers_wps.c
new file mode 100644 (file)
index 0000000..dc44a59
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * WPA Supplicant / dbus-based control interface (WPS)
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * 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_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "dbus_new_helpers.h"
+#include "dbus_new.h"
+#include "dbus_new_handlers.h"
+#include "dbus_dict_helpers.h"
+
+
+struct wps_start_params {
+       int role; /* 0 - not set, 1 - enrollee, 2 - registrar */
+       int type; /* 0 - not set, 1 - pin,      2 - pbc       */
+       u8 *bssid;
+       char *pin;
+};
+
+
+static int wpas_dbus_handler_wps_role(DBusMessage *message,
+                                     DBusMessageIter *entry_iter,
+                                     struct wps_start_params *params,
+                                     DBusMessage **reply)
+{
+       DBusMessageIter variant_iter;
+       char *val;
+
+       dbus_message_iter_recurse(entry_iter, &variant_iter);
+       if (dbus_message_iter_get_arg_type(&variant_iter) !=
+           DBUS_TYPE_STRING) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Role type, "
+                          "string required");
+               *reply = wpas_dbus_error_invalid_args(message,
+                                                     "Role must be a string");
+               return -1;
+       }
+       dbus_message_iter_get_basic(&variant_iter, &val);
+       if (os_strcmp(val, "enrollee") == 0)
+               params->role = 1;
+       else if (os_strcmp(val, "registrar") == 0)
+               params->role = 2;
+       else {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Uknown role %s", val);
+               *reply = wpas_dbus_error_invalid_args(message, val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wpas_dbus_handler_wps_type(DBusMessage *message,
+                                     DBusMessageIter *entry_iter,
+                                     struct wps_start_params *params,
+                                     DBusMessage **reply)
+{
+       DBusMessageIter variant_iter;
+       char *val;
+
+       dbus_message_iter_recurse(entry_iter, &variant_iter);
+       if (dbus_message_iter_get_arg_type(&variant_iter) !=
+           DBUS_TYPE_STRING) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Type type, "
+                          "string required");
+               *reply = wpas_dbus_error_invalid_args(message,
+                                                     "Type must be a string");
+               return -1;
+       }
+       dbus_message_iter_get_basic(&variant_iter, &val);
+       if (os_strcmp(val, "pin") == 0)
+               params->type = 1;
+       else if (os_strcmp(val, "pbc") == 0)
+               params->type = 2;
+       else {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Unknown type %s",
+                          val);
+               *reply = wpas_dbus_error_invalid_args(message, val);
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
+                                      DBusMessageIter *entry_iter,
+                                      struct wps_start_params *params,
+                                      DBusMessage **reply)
+{
+       DBusMessageIter variant_iter, array_iter;
+       int len;
+
+       dbus_message_iter_recurse(entry_iter, &variant_iter);
+       if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
+           dbus_message_iter_get_element_type(&variant_iter) !=
+           DBUS_TYPE_ARRAY) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, "
+                          "byte array required");
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Bssid must be a byte array");
+               return -1;
+       }
+       dbus_message_iter_recurse(&variant_iter, &array_iter);
+       dbus_message_iter_get_fixed_array(&array_iter, &params->bssid, &len);
+       if (len != ETH_ALEN) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length "
+                          "%d", len);
+               *reply = wpas_dbus_error_invalid_args(message,
+                                                     "Bssid is wrong length");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int wpas_dbus_handler_wps_pin(DBusMessage *message,
+                                    DBusMessageIter *entry_iter,
+                                    struct wps_start_params *params,
+                                    DBusMessage **reply)
+{
+       DBusMessageIter variant_iter;
+
+       dbus_message_iter_recurse(entry_iter, &variant_iter);
+       if (dbus_message_iter_get_arg_type(&variant_iter) !=
+           DBUS_TYPE_STRING) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Pin type, "
+                          "string required");
+               *reply = wpas_dbus_error_invalid_args(message,
+                                                     "Pin must be a string");
+               return -1;
+       }
+       dbus_message_iter_get_basic(&variant_iter, &params->pin);
+       return 0;
+}
+
+
+static int wpas_dbus_handler_wps_start_entry(DBusMessage *message, char *key,
+                                            DBusMessageIter *entry_iter,
+                                            struct wps_start_params *params,
+                                            DBusMessage **reply)
+{
+       if (os_strcmp(key, "Role") == 0)
+               return wpas_dbus_handler_wps_role(message, entry_iter,
+                                                 params, reply);
+       else if (os_strcmp(key, "Type") == 0)
+               return wpas_dbus_handler_wps_type(message, entry_iter,
+                                                 params, reply);
+       else if (os_strcmp(key, "Bssid") == 0)
+               return wpas_dbus_handler_wps_bssid(message, entry_iter,
+                                                  params, reply);
+       else if (os_strcmp(key, "Pin") == 0)
+               return wpas_dbus_handler_wps_pin(message, entry_iter,
+                                                params, reply);
+
+       wpa_printf(MSG_DEBUG, "dbus: WPS.Start - unknown key %s", key);
+       *reply = wpas_dbus_error_invalid_args(message, key);
+       return -1;
+}
+
+
+/**
+ * wpas_dbus_handler_wps_start - Start WPS configuration
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: DBus message dictionary on success or DBus error on failure
+ *
+ * Handler for "Start" method call. DBus dictionary argument contains
+ * information about role (enrollee or registrar), authorization method
+ * (pin or push button) and optionally pin and bssid. Returned message
+ * has a dictionary argument which may contain newly generated pin (optional).
+ */
+DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, dict_iter, entry_iter;
+       struct wps_start_params params;
+       char *key;
+       char npin[9] = { '\0' };
+       int ret;
+
+       os_memset(&params, 0, sizeof(params));
+       dbus_message_iter_init(message, &iter);
+
+       dbus_message_iter_recurse(&iter, &dict_iter);
+       while (dbus_message_iter_get_arg_type(&dict_iter) ==
+              DBUS_TYPE_DICT_ENTRY) {
+               dbus_message_iter_recurse(&dict_iter, &entry_iter);
+
+               dbus_message_iter_get_basic(&entry_iter, &key);
+               dbus_message_iter_next(&entry_iter);
+
+               if (wpas_dbus_handler_wps_start_entry(message, key,
+                                                     &entry_iter,
+                                                     &params, &reply))
+                       return reply;
+
+               dbus_message_iter_next(&dict_iter);
+       }
+
+       if (params.role == 0) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified");
+               return wpas_dbus_error_invalid_args(message,
+                                                   "Role not specified");
+       } else if (params.role == 1 && params.type == 0) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified");
+               return wpas_dbus_error_invalid_args(message,
+                                                   "Type not specified");
+       } else if (params.role == 2 && params.pin == NULL) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Pin required for "
+                          "registrar role");
+               return wpas_dbus_error_invalid_args(
+                       message, "Pin required for registrar role.");
+       }
+
+       if (params.role == 2)
+               ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
+                                        NULL);
+       else if (params.type == 1) {
+               ret = wpas_wps_start_pin(wpa_s, params.bssid, params.pin);
+               if (ret > 0)
+                       os_snprintf(npin, sizeof(npin), "%08d", ret);
+       } else
+               ret = wpas_wps_start_pbc(wpa_s, params.bssid);
+
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in "
+                          "role %s and key %s",
+                          (params.role == 1 ? "enrollee" : "registrar"),
+                          (params.type == 0 ? "" :
+                           (params.type == 1 ? "pin" : "pbc")));
+               return wpas_dbus_error_unknown_error(message,
+                                                    "WPS start failed");
+       }
+
+       reply = dbus_message_new_method_return(message);
+       if (!reply) {
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       dbus_message_iter_init_append(reply, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       if (os_strlen(npin) > 0) {
+               if (!wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) {
+                       dbus_message_unref(reply);
+                       return dbus_message_new_error(message,
+                                                     DBUS_ERROR_NO_MEMORY,
+                                                     NULL);
+               }
+       }
+
+       if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+               dbus_message_unref(reply);
+               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
+                                             NULL);
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_getter_process_credentials - Check if credentials are processed
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: DBus message with a boolean on success or DBus error on failure
+ *
+ * Getter for "ProcessCredentials" property. Returns returned boolean will be
+ * true if wps_cred_processing configuration field is not equal to 1 or false
+ * if otherwise.
+ */
+DBusMessage * wpas_dbus_getter_process_credentials(
+       DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+       dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1);
+       return wpas_dbus_simple_property_getter(message, DBUS_TYPE_BOOLEAN,
+                                               &process);
+}
+
+
+/**
+ * wpas_dbus_setter_process_credentials - Set credentials_processed conf param
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: NULL on success or DBus error on failure
+ *
+ * Setter for "ProcessCredentials" property. Sets credentials_processed on 2
+ * if boolean argument is true or on 1 if otherwise.
+ */
+DBusMessage * wpas_dbus_setter_process_credentials(
+       DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       dbus_bool_t process_credentials, old_pc;
+
+       reply = wpas_dbus_simple_property_setter(message, DBUS_TYPE_BOOLEAN,
+                                                &process_credentials);
+       if (reply)
+               return reply;
+
+       old_pc = (wpa_s->conf->wps_cred_processing != 1);
+       wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1);
+
+       if ((wpa_s->conf->wps_cred_processing != 1) != old_pc)
+               wpa_dbus_mark_property_changed(wpa_s->global->dbus,
+                                              wpa_s->dbus_new_path,
+                                              WPAS_DBUS_NEW_IFACE_WPS,
+                                              "ProcessCredentials");
+
+       return NULL;
+}
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.c b/wpa_supplicant/dbus/dbus_new_helpers.c
new file mode 100644 (file)
index 0000000..06749db
--- /dev/null
@@ -0,0 +1,875 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "dbus_common.h"
+#include "dbus_common_i.h"
+#include "dbus_new.h"
+#include "dbus_new_helpers.h"
+
+
+/**
+ * recursive_iter_copy - Reads arguments from one iterator and
+ * writes to another recursively
+ * @from: iterator to read from
+ * @to: iterator to write to
+ *
+ * Copies one iterator's elements to another. If any element in
+ * iterator is of container type, its content is copied recursively
+ */
+static void recursive_iter_copy(DBusMessageIter *from, DBusMessageIter *to)
+{
+
+       char *subtype = NULL;
+       int type;
+
+       /* iterate over iterator to copy */
+       while ((type = dbus_message_iter_get_arg_type(from)) !=
+              DBUS_TYPE_INVALID) {
+
+               /* simply copy basic type entries */
+               if (dbus_type_is_basic(type)) {
+                       if (dbus_type_is_fixed(type)) {
+                               /*
+                                * According to DBus documentation all
+                                * fixed-length types are guaranteed to fit
+                                * 8 bytes
+                                */
+                               dbus_uint64_t v;
+                               dbus_message_iter_get_basic(from, &v);
+                               dbus_message_iter_append_basic(to, type, &v);
+                       } else {
+                               char *v;
+                               dbus_message_iter_get_basic(from, &v);
+                               dbus_message_iter_append_basic(to, type, &v);
+                       }
+               } else {
+                       /* recursively copy container type entries */
+                       DBusMessageIter write_subiter, read_subiter;
+
+                       dbus_message_iter_recurse(from, &read_subiter);
+
+                       if (type == DBUS_TYPE_VARIANT ||
+                           type == DBUS_TYPE_ARRAY) {
+                               subtype = dbus_message_iter_get_signature(
+                                       &read_subiter);
+                       }
+
+                       dbus_message_iter_open_container(to, type, subtype,
+                                                        &write_subiter);
+
+                       recursive_iter_copy(&read_subiter, &write_subiter);
+
+                       dbus_message_iter_close_container(to, &write_subiter);
+                       if (subtype)
+                               dbus_free(subtype);
+               }
+
+               dbus_message_iter_next(from);
+       }
+}
+
+
+static unsigned int fill_dict_with_properties(
+       DBusMessageIter *dict_iter, const struct wpa_dbus_property_desc *props,
+       const char *interface, const void *user_data)
+{
+       DBusMessage *reply;
+       DBusMessageIter entry_iter, ret_iter;
+       unsigned int counter = 0;
+       const struct wpa_dbus_property_desc *dsc;
+
+       for (dsc = props; dsc && dsc->dbus_property; dsc++) {
+               if (!os_strncmp(dsc->dbus_interface, interface,
+                               WPAS_DBUS_INTERFACE_MAX) &&
+                   dsc->access != W && dsc->getter) {
+                       reply = dsc->getter(NULL, user_data);
+                       if (!reply)
+                               continue;
+
+                       if (dbus_message_get_type(reply) ==
+                           DBUS_MESSAGE_TYPE_ERROR) {
+                               dbus_message_unref(reply);
+                               continue;
+                       }
+
+                       dbus_message_iter_init(reply, &ret_iter);
+
+                       dbus_message_iter_open_container(dict_iter,
+                                                        DBUS_TYPE_DICT_ENTRY,
+                                                        NULL, &entry_iter);
+                       dbus_message_iter_append_basic(
+                               &entry_iter, DBUS_TYPE_STRING,
+                               &dsc->dbus_property);
+
+                       recursive_iter_copy(&ret_iter, &entry_iter);
+
+                       dbus_message_iter_close_container(dict_iter,
+                                                         &entry_iter);
+                       dbus_message_unref(reply);
+                       counter++;
+               }
+       }
+
+       return counter;
+}
+
+
+/**
+ * get_all_properties - Responds for GetAll properties calls on object
+ * @message: Message with GetAll call
+ * @interface: interface name which properties will be returned
+ * @property_dsc: list of object's properties
+ * Returns: Message with dict of variants as argument with properties values
+ *
+ * Iterates over all properties registered with object and execute getters
+ * of those, which are readable and which interface matches interface
+ * specified as argument. Returned message contains one dict argument
+ * with properties names as keys and theirs values as values.
+ */
+static DBusMessage * get_all_properties(
+       DBusMessage *message, char *interface,
+       struct wpa_dbus_object_desc *obj_dsc)
+{
+       /* Create and initialize the return message */
+       DBusMessage *reply = dbus_message_new_method_return(message);
+       DBusMessageIter iter, dict_iter;
+       int props_num;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                        DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+                                        DBUS_TYPE_STRING_AS_STRING
+                                        DBUS_TYPE_VARIANT_AS_STRING
+                                        DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+                                        &dict_iter);
+
+       props_num = fill_dict_with_properties(&dict_iter, obj_dsc->properties,
+                                             interface, obj_dsc->user_data);
+
+       dbus_message_iter_close_container(&iter, &dict_iter);
+
+       if (props_num == 0) {
+               dbus_message_unref(reply);
+               reply = dbus_message_new_error(message,
+                                              DBUS_ERROR_INVALID_ARGS,
+                                              "No readable properties in "
+                                              "this interface");
+       }
+
+       return reply;
+}
+
+
+static int is_signature_correct(DBusMessage *message,
+                               const struct wpa_dbus_method_desc *method_dsc)
+{
+       /* According to DBus documentation max length of signature is 255 */
+#define MAX_SIG_LEN 256
+       char registered_sig[MAX_SIG_LEN], *pos;
+       const char *sig = dbus_message_get_signature(message);
+       int ret;
+       const struct wpa_dbus_argument *arg;
+
+       pos = registered_sig;
+       *pos = '\0';
+
+       for (arg = method_dsc->args; arg && arg->name; arg++) {
+               if (arg->dir == ARG_IN) {
+                       size_t blen = registered_sig + MAX_SIG_LEN - pos;
+                       ret = os_snprintf(pos, blen, "%s", arg->type);
+                       if (ret < 0 || (size_t) ret >= blen)
+                               return 0;
+                       pos += ret;
+               }
+       }
+
+       return !os_strncmp(registered_sig, sig, MAX_SIG_LEN);
+}
+
+
+static DBusMessage * properties_get_all(DBusMessage *message, char *interface,
+                                       struct wpa_dbus_object_desc *obj_dsc)
+{
+       if (os_strcmp(dbus_message_get_signature(message), "s") != 0)
+               return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                             NULL);
+
+       return get_all_properties(message, interface, obj_dsc);
+}
+
+
+static DBusMessage * properties_get(DBusMessage *message,
+                                   const struct wpa_dbus_property_desc *dsc,
+                                   void *user_data)
+{
+       if (os_strcmp(dbus_message_get_signature(message), "ss"))
+               return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                             NULL);
+
+       if (dsc->access != W && dsc->getter)
+               return dsc->getter(message, user_data);
+
+       return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                     "Property is write-only");
+}
+
+
+static DBusMessage * properties_set(DBusMessage *message,
+                                   const struct wpa_dbus_property_desc *dsc,
+                                   void *user_data)
+{
+       if (os_strcmp(dbus_message_get_signature(message), "ssv"))
+               return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                             NULL);
+
+       if (dsc->access != R && dsc->setter)
+               return dsc->setter(message, user_data);
+
+       return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                     "Property is read-only");
+}
+
+
+static DBusMessage *
+properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
+                     char *interface,
+                     struct wpa_dbus_object_desc *obj_dsc)
+{
+       const struct wpa_dbus_property_desc *property_dsc;
+       char *property;
+       const char *method;
+
+       method = dbus_message_get_member(message);
+       property_dsc = obj_dsc->properties;
+
+       /* Second argument: property name (DBUS_TYPE_STRING) */
+       if (!dbus_message_iter_next(iter) ||
+           dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+               return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                             NULL);
+       }
+       dbus_message_iter_get_basic(iter, &property);
+
+       while (property_dsc && property_dsc->dbus_property) {
+               /* compare property names and
+                * interfaces */
+               if (!os_strncmp(property_dsc->dbus_property, property,
+                               WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
+                   !os_strncmp(property_dsc->dbus_interface, interface,
+                               WPAS_DBUS_INTERFACE_MAX))
+                       break;
+
+               property_dsc++;
+       }
+       if (property_dsc == NULL || property_dsc->dbus_property == NULL) {
+               wpa_printf(MSG_DEBUG, "no property handler for %s.%s on %s",
+                          interface, property,
+                          dbus_message_get_path(message));
+               return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                             "No such property");
+       }
+
+       if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
+                      WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0)
+               return properties_get(message, property_dsc,
+                                     obj_dsc->user_data);
+
+       return properties_set(message, property_dsc, obj_dsc->user_data);
+}
+
+
+static DBusMessage * properties_handler(DBusMessage *message,
+                                       struct wpa_dbus_object_desc *obj_dsc)
+{
+       DBusMessageIter iter;
+       char *interface;
+       const char *method;
+
+       method = dbus_message_get_member(message);
+       dbus_message_iter_init(message, &iter);
+
+       if (!os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
+                       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
+           !os_strncmp(WPA_DBUS_PROPERTIES_SET, method,
+                       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) ||
+           !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
+                       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
+               /* First argument: interface name (DBUS_TYPE_STRING) */
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               {
+                       return dbus_message_new_error(message,
+                                                     DBUS_ERROR_INVALID_ARGS,
+                                                     NULL);
+               }
+
+               dbus_message_iter_get_basic(&iter, &interface);
+
+               if (!os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
+                               WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
+                       /* GetAll */
+                       return properties_get_all(message, interface, obj_dsc);
+               }
+               /* Get or Set */
+               return properties_get_or_set(message, &iter, interface,
+                                            obj_dsc);
+       }
+       return dbus_message_new_error(message, DBUS_ERROR_UNKNOWN_METHOD,
+                                     NULL);
+}
+
+
+static DBusMessage * msg_method_handler(DBusMessage *message,
+                                       struct wpa_dbus_object_desc *obj_dsc)
+{
+       const struct wpa_dbus_method_desc *method_dsc = obj_dsc->methods;
+       const char *method;
+       const char *msg_interface;
+
+       method = dbus_message_get_member(message);
+       msg_interface = dbus_message_get_interface(message);
+
+       /* try match call to any registered method */
+       while (method_dsc && method_dsc->dbus_method) {
+               /* compare method names and interfaces */
+               if (!os_strncmp(method_dsc->dbus_method, method,
+                               WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
+                   !os_strncmp(method_dsc->dbus_interface, msg_interface,
+                               WPAS_DBUS_INTERFACE_MAX))
+                       break;
+
+               method_dsc++;
+       }
+       if (method_dsc == NULL || method_dsc->dbus_method == NULL) {
+               wpa_printf(MSG_DEBUG, "no method handler for %s.%s on %s",
+                          msg_interface, method,
+                          dbus_message_get_path(message));
+               return dbus_message_new_error(message,
+                                             DBUS_ERROR_UNKNOWN_METHOD, NULL);
+       }
+
+       if (!is_signature_correct(message, method_dsc)) {
+               return dbus_message_new_error(message, DBUS_ERROR_INVALID_ARGS,
+                                             NULL);
+       }
+
+       return method_dsc->method_handler(message,
+                                         obj_dsc->user_data);
+}
+
+
+/**
+ * message_handler - Handles incoming DBus messages
+ * @connection: DBus connection on which message was received
+ * @message: Received message
+ * @user_data: pointer to description of object to which message was sent
+ * Returns: Returns information whether message was handled or not
+ *
+ * Reads message interface and method name, then checks if they matches one
+ * of the special cases i.e. introspection call or properties get/getall/set
+ * methods and handles it. Else it iterates over registered methods list
+ * and tries to match method's name and interface to those read from message
+ * If appropriate method was found its handler function is called and
+ * response is sent. Otherwise, the DBUS_ERROR_UNKNOWN_METHOD error message
+ * will be sent.
+ */
+static DBusHandlerResult message_handler(DBusConnection *connection,
+                                        DBusMessage *message, void *user_data)
+{
+       struct wpa_dbus_object_desc *obj_dsc = user_data;
+       const char *method;
+       const char *path;
+       const char *msg_interface;
+       DBusMessage *reply;
+
+       /* get method, interface and path the message is addressed to */
+       method = dbus_message_get_member(message);
+       path = dbus_message_get_path(message);
+       msg_interface = dbus_message_get_interface(message);
+       if (!method || !path || !msg_interface)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)",
+                  msg_interface, method, path);
+
+       /* if message is introspection method call */
+       if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
+                       WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) &&
+           !os_strncmp(WPA_DBUS_INTROSPECTION_INTERFACE, msg_interface,
+                       WPAS_DBUS_INTERFACE_MAX)) {
+#ifdef CONFIG_CTRL_IFACE_DBUS_INTRO
+               reply = wpa_dbus_introspect(message, obj_dsc);
+#else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
+               reply = dbus_message_new_error(
+                       message, DBUS_ERROR_UNKNOWN_METHOD,
+                       "wpa_supplicant was compiled without "
+                       "introspection support.");
+#endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
+       } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
+                            WPAS_DBUS_INTERFACE_MAX)) {
+               /* if message is properties method call */
+               reply = properties_handler(message, obj_dsc);
+       } else {
+               reply = msg_method_handler(message, obj_dsc);
+       }
+
+       /* If handler succeed returning NULL, reply empty message */
+       if (!reply)
+               reply = dbus_message_new_method_return(message);
+       if (reply) {
+               if (!dbus_message_get_no_reply(message))
+                       dbus_connection_send(connection, reply, NULL);
+               dbus_message_unref(reply);
+       }
+
+       wpa_dbus_flush_all_changed_properties(connection);
+
+       return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+
+/**
+ * free_dbus_object_desc - Frees object description data structure
+ * @connection: DBus connection
+ * @obj_dsc: Object description to free
+ *
+ * Frees each of properties, methods and signals description lists and
+ * the object description structure itself.
+ */
+void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc)
+{
+       if (!obj_dsc)
+               return;
+
+       /* free handler's argument */
+       if (obj_dsc->user_data_free_func)
+               obj_dsc->user_data_free_func(obj_dsc->user_data);
+
+       os_free(obj_dsc->path);
+       os_free(obj_dsc->prop_changed_flags);
+       os_free(obj_dsc);
+}
+
+
+static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
+{
+       free_dbus_object_desc(obj_dsc);
+}
+
+/**
+ * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @application_data: Pointer to application specific data structure
+ * @dbus_path: DBus path to interface object
+ * @dbus_service: DBus service name to register with
+ * @messageHandler: a pointer to function which will handle dbus messages
+ * coming on interface
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initialize the dbus control interface and start receiving commands from
+ * external programs over the bus.
+ */
+int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
+                            char *dbus_path, char *dbus_service,
+                            struct wpa_dbus_object_desc *obj_desc)
+{
+       DBusError error;
+       int ret = -1;
+       DBusObjectPathVTable wpa_vtable = {
+               &free_dbus_object_desc_cb, &message_handler,
+               NULL, NULL, NULL, NULL
+       };
+
+       obj_desc->connection = iface->con;
+       obj_desc->path = os_strdup(dbus_path);
+
+       /* Register the message handler for the global dbus interface */
+       if (!dbus_connection_register_object_path(iface->con,
+                                                 dbus_path, &wpa_vtable,
+                                                 obj_desc)) {
+               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+                          "handler");
+               return -1;
+       }
+
+       /* Register our service with the message bus */
+       dbus_error_init(&error);
+       switch (dbus_bus_request_name(iface->con, dbus_service,
+                                     0, &error)) {
+       case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+               ret = 0;
+               break;
+       case DBUS_REQUEST_NAME_REPLY_EXISTS:
+       case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+       case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
+                          "already registered");
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
+                          "%s %s", error.name, error.message);
+               break;
+       }
+       dbus_error_free(&error);
+
+       if (ret != 0)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "Providing DBus service '%s'.", dbus_service);
+
+       return 0;
+}
+
+
+/**
+ * wpa_dbus_register_object_per_iface - Register a new object with dbus
+ * @ctrl_iface: pointer to dbus private data
+ * @path: DBus path to object
+ * @ifname: interface name
+ * @obj_desc: description of object's methods, signals and properties
+ * Returns: 0 on success, -1 on error
+ *
+ * Registers a new interface with dbus and assigns it a dbus object path.
+ */
+int wpa_dbus_register_object_per_iface(
+       struct wpas_dbus_priv *ctrl_iface,
+       const char *path, const char *ifname,
+       struct wpa_dbus_object_desc *obj_desc)
+{
+       DBusConnection *con;
+
+       DBusObjectPathVTable vtable = {
+               &free_dbus_object_desc_cb, &message_handler,
+               NULL, NULL, NULL, NULL
+       };
+
+       /* Do nothing if the control interface is not turned on */
+       if (ctrl_iface == NULL)
+               return 0;
+
+       con = ctrl_iface->con;
+       obj_desc->connection = con;
+       obj_desc->path = os_strdup(path);
+
+       /* Register the message handler for the interface functions */
+       if (!dbus_connection_register_object_path(con, path, &vtable,
+                                                 obj_desc)) {
+               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+                          "handler for interface %s object %s", ifname, path);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx);
+
+
+/**
+ * wpa_dbus_unregister_object_per_iface - Unregisters DBus object
+ * @ctrl_iface: Pointer to dbus private data
+ * @path: DBus path to object which will be unregistered
+ * Returns: Zero on success and -1 on failure
+ *
+ * Unregisters DBus object given by its path
+ */
+int wpa_dbus_unregister_object_per_iface(
+       struct wpas_dbus_priv *ctrl_iface, const char *path)
+{
+       DBusConnection *con = ctrl_iface->con;
+       struct wpa_dbus_object_desc *obj_desc = NULL;
+
+       dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
+                          "private data: %s", __func__, path);
+       } else {
+               eloop_cancel_timeout(flush_object_timeout_handler, con,
+                                    obj_desc);
+       }
+
+       if (!dbus_connection_unregister_object_path(con, path))
+               return -1;
+
+       return 0;
+}
+
+
+static void put_changed_properties(const struct wpa_dbus_object_desc *obj_dsc,
+                                  const char *interface,
+                                  DBusMessageIter *dict_iter)
+{
+       DBusMessage *getter_reply;
+       DBusMessageIter prop_iter, entry_iter;
+       const struct wpa_dbus_property_desc *dsc;
+       int i;
+
+       for (dsc = obj_dsc->properties, i = 0; dsc && dsc->dbus_property;
+            dsc++, i++) {
+               if (obj_dsc->prop_changed_flags == NULL ||
+                   !obj_dsc->prop_changed_flags[i])
+                       continue;
+               if (os_strcmp(dsc->dbus_interface, interface) != 0)
+                       continue;
+               obj_dsc->prop_changed_flags[i] = 0;
+
+               getter_reply = dsc->getter(NULL, obj_dsc->user_data);
+               if (!getter_reply ||
+                   dbus_message_get_type(getter_reply) ==
+                   DBUS_MESSAGE_TYPE_ERROR) {
+                       wpa_printf(MSG_ERROR, "dbus: %s: Cannot get new value "
+                                  "of property %s", __func__,
+                                  dsc->dbus_property);
+                       continue;
+               }
+
+               if (!dbus_message_iter_init(getter_reply, &prop_iter) ||
+                   !dbus_message_iter_open_container(dict_iter,
+                                                     DBUS_TYPE_DICT_ENTRY,
+                                                     NULL, &entry_iter) ||
+                   !dbus_message_iter_append_basic(&entry_iter,
+                                                   DBUS_TYPE_STRING,
+                                                   &dsc->dbus_property))
+                       goto err;
+
+               recursive_iter_copy(&prop_iter, &entry_iter);
+
+               if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
+                       goto err;
+
+               dbus_message_unref(getter_reply);
+       }
+
+       return;
+
+err:
+       wpa_printf(MSG_ERROR, "dbus: %s: Cannot construct signal", __func__);
+}
+
+
+static void send_prop_changed_signal(
+       DBusConnection *con, const char *path, const char *interface,
+       const struct wpa_dbus_object_desc *obj_dsc)
+{
+       DBusMessage *msg;
+       DBusMessageIter signal_iter, dict_iter;
+
+       msg = dbus_message_new_signal(path, interface, "PropertiesChanged");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &signal_iter);
+
+       if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+                                             "{sv}", &dict_iter))
+               goto err;
+
+       put_changed_properties(obj_dsc, interface, &dict_iter);
+
+       if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
+               goto err;
+
+       dbus_connection_send(con, msg, NULL);
+
+out:
+       dbus_message_unref(msg);
+       return;
+
+err:
+       wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+                  __func__);
+       goto out;
+}
+
+
+static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
+{
+       DBusConnection *con = eloop_ctx;
+       struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
+
+       wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
+                  "of object %s", __func__, obj_desc->path);
+       wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
+}
+
+
+static void recursive_flush_changed_properties(DBusConnection *con,
+                                              const char *path)
+{
+       char **objects = NULL;
+       char subobj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+       int i;
+
+       wpa_dbus_flush_object_changed_properties(con, path);
+
+       if (!dbus_connection_list_registered(con, path, &objects))
+               goto out;
+
+       for (i = 0; objects[i]; i++) {
+               os_snprintf(subobj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s/%s", path, objects[i]);
+               recursive_flush_changed_properties(con, subobj_path);
+       }
+
+out:
+       dbus_free_string_array(objects);
+}
+
+
+/**
+ * wpa_dbus_flush_all_changed_properties - Send all PropertiesChanged signals
+ * @con: DBus connection
+ *
+ * Traverses through all registered objects and sends PropertiesChanged for
+ * each properties.
+ */
+void wpa_dbus_flush_all_changed_properties(DBusConnection *con)
+{
+       recursive_flush_changed_properties(con, WPAS_DBUS_NEW_PATH);
+}
+
+
+/**
+ * wpa_dbus_flush_object_changed_properties - Send PropertiesChanged for object
+ * @con: DBus connection
+ * @path: path to a DBus object for which PropertiesChanged will be sent.
+ *
+ * Iterates over all properties registered with object and for each interface
+ * containing properties marked as changed, sends a PropertiesChanged signal
+ * containing names and new values of properties that have changed.
+ *
+ * You need to call this function after wpa_dbus_mark_property_changed()
+ * if you want to send PropertiesChanged signal immediately (i.e., without
+ * waiting timeout to expire). PropertiesChanged signal for an object is sent
+ * automatically short time after first marking property as changed. All
+ * PropertiesChanged signals are sent automatically after responding on DBus
+ * message, so if you marked a property changed as a result of DBus call
+ * (e.g., param setter), you usually do not need to call this function.
+ */
+void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
+                                             const char *path)
+{
+       struct wpa_dbus_object_desc *obj_desc = NULL;
+       const struct wpa_dbus_property_desc *dsc;
+       int i;
+
+       dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
+       if (!obj_desc)
+               return;
+       eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
+
+       dsc = obj_desc->properties;
+       for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
+            dsc++, i++) {
+               if (obj_desc->prop_changed_flags == NULL ||
+                   !obj_desc->prop_changed_flags[i])
+                       continue;
+               send_prop_changed_signal(con, path, dsc->dbus_interface,
+                                        obj_desc);
+       }
+}
+
+
+#define WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT 5000
+
+
+/**
+ * wpa_dbus_mark_property_changed - Mark a property as changed and
+ * @iface: dbus priv struct
+ * @path: path to DBus object which property has changed
+ * @interface: interface containing changed property
+ * @property: property name which has changed
+ *
+ * Iterates over all properties registered with an object and marks the one
+ * given in parameters as changed. All parameters registered for an object
+ * within a single interface will be aggregated together and sent in one
+ * PropertiesChanged signal when function
+ * wpa_dbus_flush_object_changed_properties() is called.
+ */
+void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
+                                   const char *path, const char *interface,
+                                   const char *property)
+{
+       struct wpa_dbus_object_desc *obj_desc = NULL;
+       const struct wpa_dbus_property_desc *dsc;
+       int i = 0;
+
+       if (iface == NULL)
+               return;
+
+       dbus_connection_get_object_path_data(iface->con, path,
+                                            (void **) &obj_desc);
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
+                          "could not obtain object's private data: %s", path);
+               return;
+       }
+
+       for (dsc = obj_desc->properties; dsc && dsc->dbus_property; dsc++, i++)
+               if (os_strcmp(property, dsc->dbus_property) == 0 &&
+                   os_strcmp(interface, dsc->dbus_interface) == 0) {
+                       if (obj_desc->prop_changed_flags)
+                               obj_desc->prop_changed_flags[i] = 1;
+                       break;
+               }
+
+       if (!dsc || !dsc->dbus_property) {
+               wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
+                          "no property %s in object %s", property, path);
+               return;
+       }
+
+       if (!eloop_is_timeout_registered(flush_object_timeout_handler,
+                                        iface->con, obj_desc->path)) {
+               eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
+                                      flush_object_timeout_handler,
+                                      iface->con, obj_desc);
+       }
+}
+
+
+/**
+ * wpa_dbus_get_object_properties - Put object's properties into dictionary
+ * @iface: dbus priv struct
+ * @path: path to DBus object which properties will be obtained
+ * @interface: interface name which properties will be obtained
+ * @dict_iter: correct, open DBus dictionary iterator.
+ *
+ * Iterates over all properties registered with object and execute getters
+ * of those, which are readable and which interface matches interface
+ * specified as argument. Obtained properties values are stored in
+ * dict_iter dictionary.
+ */
+void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+                                   const char *path, const char *interface,
+                                   DBusMessageIter *dict_iter)
+{
+       struct wpa_dbus_object_desc *obj_desc = NULL;
+
+       dbus_connection_get_object_path_data(iface->con, path,
+                                            (void **) &obj_desc);
+       if (!obj_desc) {
+               wpa_printf(MSG_ERROR, "dbus: wpa_dbus_get_object_properties: "
+                          "could not obtain object's private data: %s", path);
+               return;
+       }
+
+       fill_dict_with_properties(dict_iter, obj_desc->properties,
+                                 interface, obj_desc->user_data);
+}
diff --git a/wpa_supplicant/dbus/dbus_new_helpers.h b/wpa_supplicant/dbus/dbus_new_helpers.h
new file mode 100644 (file)
index 0000000..8db7a37
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ *
+ * 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_DBUS_CTRL_H
+#define WPA_DBUS_CTRL_H
+
+#include <dbus/dbus.h>
+
+typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message,
+                                              void *user_data);
+typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg);
+
+typedef DBusMessage * (* WPADBusPropertyAccessor)(DBusMessage *message,
+                                                 const void *user_data);
+
+struct wpa_dbus_object_desc {
+       DBusConnection *connection;
+       char *path;
+
+       /* list of methods, properties and signals registered with object */
+       const struct wpa_dbus_method_desc *methods;
+       const struct wpa_dbus_signal_desc *signals;
+       const struct wpa_dbus_property_desc *properties;
+
+       /* property changed flags */
+       u8 *prop_changed_flags;
+
+       /* argument for method handlers and properties
+        * getter and setter functions */
+       void *user_data;
+       /* function used to free above argument */
+       WPADBusArgumentFreeFunction user_data_free_func;
+};
+
+enum dbus_prop_access { R, W, RW };
+
+enum dbus_arg_direction { ARG_IN, ARG_OUT };
+
+struct wpa_dbus_argument {
+       char *name;
+       char *type;
+       enum dbus_arg_direction dir;
+};
+
+#define END_ARGS { NULL, NULL, ARG_IN }
+
+/**
+ * struct wpa_dbus_method_desc - DBus method description
+ */
+struct wpa_dbus_method_desc {
+       /* method name */
+       const char *dbus_method;
+       /* method interface */
+       const char *dbus_interface;
+       /* method handling function */
+       WPADBusMethodHandler method_handler;
+       /* array of arguments */
+       struct wpa_dbus_argument args[3];
+};
+
+/**
+ * struct wpa_dbus_signal_desc - DBus signal description
+ */
+struct wpa_dbus_signal_desc {
+       /* signal name */
+       const char *dbus_signal;
+       /* signal interface */
+       const char *dbus_interface;
+       /* array of arguments */
+       struct wpa_dbus_argument args[3];
+};
+
+/**
+ * struct wpa_dbus_property_desc - DBus property description
+ */
+struct wpa_dbus_property_desc {
+       /* property name */
+       const char *dbus_property;
+       /* property interface */
+       const char *dbus_interface;
+       /* property type signature in DBus type notation */
+       const char *type;
+       /* property getter function */
+       WPADBusPropertyAccessor getter;
+       /* property setter function */
+       WPADBusPropertyAccessor setter;
+       /* property access permissions */
+       enum dbus_prop_access access;
+};
+
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+#define WPAS_DBUS_INTERFACE_MAX 150
+#define WPAS_DBUS_METHOD_SIGNAL_PROP_MAX 50
+
+#define WPA_DBUS_INTROSPECTION_INTERFACE "org.freedesktop.DBus.Introspectable"
+#define WPA_DBUS_INTROSPECTION_METHOD "Introspect"
+#define WPA_DBUS_PROPERTIES_INTERFACE "org.freedesktop.DBus.Properties"
+#define WPA_DBUS_PROPERTIES_GET "Get"
+#define WPA_DBUS_PROPERTIES_SET "Set"
+#define WPA_DBUS_PROPERTIES_GETALL "GetAll"
+
+void free_dbus_object_desc(struct wpa_dbus_object_desc *obj_dsc);
+
+int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface, char *dbus_path,
+                            char *dbus_service,
+                            struct wpa_dbus_object_desc *obj_desc);
+
+int wpa_dbus_register_object_per_iface(
+       struct wpas_dbus_priv *ctrl_iface,
+       const char *path, const char *ifname,
+       struct wpa_dbus_object_desc *obj_desc);
+
+int wpa_dbus_unregister_object_per_iface(
+       struct wpas_dbus_priv *ctrl_iface,
+       const char *path);
+
+void wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
+                                   const char *path, const char *interface,
+                                   DBusMessageIter *dict_iter);
+
+
+void wpa_dbus_flush_all_changed_properties(DBusConnection *con);
+
+void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
+                                             const char *path);
+
+void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
+                                   const char *path, const char *interface,
+                                   const char *property);
+
+DBusMessage * wpa_dbus_introspect(DBusMessage *message,
+                                 struct wpa_dbus_object_desc *obj_dsc);
+
+#endif /* WPA_DBUS_CTRL_H */
diff --git a/wpa_supplicant/dbus/dbus_new_introspect.c b/wpa_supplicant/dbus/dbus_new_introspect.c
new file mode 100644 (file)
index 0000000..c660c04
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * wpa_supplicant - D-Bus introspection
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
+ * Copyright (c) 2009, Witold Sowa <witold.sowa@gmail.com>
+ * Copyright (c) 2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/wpabuf.h"
+#include "dbus_common_i.h"
+#include "dbus_new_helpers.h"
+
+
+struct interfaces {
+       struct dl_list list;
+       char *dbus_interface;
+       struct wpabuf *xml;
+};
+
+
+static struct interfaces * add_interface(struct dl_list *list,
+                                        const char *dbus_interface)
+{
+       struct interfaces *iface;
+
+       dl_list_for_each(iface, list, struct interfaces, list) {
+               if (os_strcmp(iface->dbus_interface, dbus_interface) == 0)
+                       return iface; /* already in the list */
+       }
+
+       iface = os_zalloc(sizeof(struct interfaces));
+       if (!iface)
+               return NULL;
+       iface->xml = wpabuf_alloc(3000);
+       if (iface->xml == NULL) {
+               os_free(iface);
+               return NULL;
+       }
+       wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface);
+       dl_list_add_tail(list, &iface->list);
+       iface->dbus_interface = os_strdup(dbus_interface);
+       return iface;
+}
+
+
+static void add_arg(struct wpabuf *xml, const char *name, const char *type,
+                   const char *direction)
+{
+       wpabuf_printf(xml, "<arg name=\"%s\"", name);
+       if (type)
+               wpabuf_printf(xml, " type=\"%s\"", type);
+       if (direction)
+               wpabuf_printf(xml, " direction=\"%s\"", direction);
+       wpabuf_put_str(xml, "/>");
+}
+
+
+static void add_entry(struct wpabuf *xml, const char *type, const char *name,
+                     const struct wpa_dbus_argument *args, int include_dir)
+{
+       const struct wpa_dbus_argument *arg;
+
+       if (args == NULL || args->name == NULL) {
+               wpabuf_printf(xml, "<%s name=\"%s\"/>", type, name);
+               return;
+       }
+       wpabuf_printf(xml, "<%s name=\"%s\">", type, name);
+       for (arg = args; arg && arg->name; arg++) {
+               add_arg(xml, arg->name, arg->type,
+                       include_dir ? (arg->dir == ARG_IN ? "in" : "out") :
+                       NULL);
+       }
+       wpabuf_printf(xml, "</%s>", type);
+}
+
+
+static void add_property(struct wpabuf *xml,
+                        const struct wpa_dbus_property_desc *dsc)
+{
+       wpabuf_printf(xml, "<property name=\"%s\" type=\"%s\" access=\"%s\"/>",
+                     dsc->dbus_property, dsc->type,
+                     (dsc->access == R ? "read" :
+                      (dsc->access == W ? "write" : "readwrite")));
+}
+
+
+static void extract_interfaces_methods(
+       struct dl_list *list, const struct wpa_dbus_method_desc *methods)
+{
+       const struct wpa_dbus_method_desc *dsc;
+       struct interfaces *iface;
+       for (dsc = methods; dsc && dsc->dbus_method; dsc++) {
+               iface = add_interface(list, dsc->dbus_interface);
+               if (iface)
+                       add_entry(iface->xml, "method", dsc->dbus_method,
+                                 dsc->args, 1);
+       }
+}
+
+
+static void extract_interfaces_signals(
+       struct dl_list *list, const struct wpa_dbus_signal_desc *signals)
+{
+       const struct wpa_dbus_signal_desc *dsc;
+       struct interfaces *iface;
+       for (dsc = signals; dsc && dsc->dbus_signal; dsc++) {
+               iface = add_interface(list, dsc->dbus_interface);
+               if (iface)
+                       add_entry(iface->xml, "signal", dsc->dbus_signal,
+                                 dsc->args, 0);
+       }
+}
+
+
+static void extract_interfaces_properties(
+       struct dl_list *list, const struct wpa_dbus_property_desc *properties)
+{
+       const struct wpa_dbus_property_desc *dsc;
+       struct interfaces *iface;
+       for (dsc = properties; dsc && dsc->dbus_property; dsc++) {
+               iface = add_interface(list, dsc->dbus_interface);
+               if (iface)
+                       add_property(iface->xml, dsc);
+       }
+}
+
+
+/**
+ * extract_interfaces - Extract interfaces from methods, signals and props
+ * @list: Interface list to be filled
+ * @obj_dsc: Description of object from which interfaces will be extracted
+ *
+ * Iterates over all methods, signals, and properties registered with an
+ * object and collects all declared DBus interfaces and create interfaces'
+ * node in XML root node for each. Returned list elements contain interface
+ * name and XML node of corresponding interface.
+ */
+static void extract_interfaces(struct dl_list *list,
+                              struct wpa_dbus_object_desc *obj_dsc)
+{
+       extract_interfaces_methods(list, obj_dsc->methods);
+       extract_interfaces_signals(list, obj_dsc->signals);
+       extract_interfaces_properties(list, obj_dsc->properties);
+}
+
+
+static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
+{
+       struct interfaces *iface, *n;
+       dl_list_for_each_safe(iface, n, list, struct interfaces, list) {
+               if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
+                       wpabuf_put_buf(xml, iface->xml);
+                       wpabuf_put_str(xml, "</interface>");
+               }
+               dl_list_del(&iface->list);
+               wpabuf_free(iface->xml);
+               os_free(iface->dbus_interface);
+               os_free(iface);
+       }
+}
+
+
+static void add_child_nodes(struct wpabuf *xml, DBusConnection *con,
+                           const char *path)
+{
+       char **children;
+       int i;
+
+       /* add child nodes to introspection tree */
+       dbus_connection_list_registered(con, path, &children);
+       for (i = 0; children[i]; i++)
+               wpabuf_printf(xml, "<node name=\"%s\"/>", children[i]);
+       dbus_free_string_array(children);
+}
+
+
+static void add_introspectable_interface(struct wpabuf *xml)
+{
+       wpabuf_printf(xml, "<interface name=\"%s\">"
+                     "<method name=\"%s\">"
+                     "<arg name=\"data\" type=\"s\" direction=\"out\"/>"
+                     "</method>"
+                     "</interface>",
+                     WPA_DBUS_INTROSPECTION_INTERFACE,
+                     WPA_DBUS_INTROSPECTION_METHOD);
+}
+
+
+static void add_properties_interface(struct wpabuf *xml)
+{
+       wpabuf_printf(xml, "<interface name=\"%s\">",
+                     WPA_DBUS_PROPERTIES_INTERFACE);
+
+       wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GET);
+       add_arg(xml, "interface", "s", "in");
+       add_arg(xml, "propname", "s", "in");
+       add_arg(xml, "value", "v", "out");
+       wpabuf_put_str(xml, "</method>");
+
+       wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_GETALL);
+       add_arg(xml, "interface", "s", "in");
+       add_arg(xml, "props", "a{sv}", "out");
+       wpabuf_put_str(xml, "</method>");
+
+       wpabuf_printf(xml, "<method name=\"%s\">", WPA_DBUS_PROPERTIES_SET);
+       add_arg(xml, "interface", "s", "in");
+       add_arg(xml, "propname", "s", "in");
+       add_arg(xml, "value", "v", "in");
+       wpabuf_put_str(xml, "</method>");
+
+       wpabuf_put_str(xml, "</interface>");
+}
+
+
+static void add_wpas_interfaces(struct wpabuf *xml,
+                               struct wpa_dbus_object_desc *obj_dsc)
+{
+       struct dl_list ifaces;
+       dl_list_init(&ifaces);
+       extract_interfaces(&ifaces, obj_dsc);
+       add_interfaces(&ifaces, xml);
+}
+
+
+/**
+ * wpa_dbus_introspect - Responds for Introspect calls on object
+ * @message: Message with Introspect call
+ * @obj_dsc: Object description on which Introspect was called
+ * Returns: Message with introspection result XML string as only argument
+ *
+ * Iterates over all methods, signals and properties registered with
+ * object and generates introspection data for the object as XML string.
+ */
+DBusMessage * wpa_dbus_introspect(DBusMessage *message,
+                                 struct wpa_dbus_object_desc *obj_dsc)
+{
+
+       DBusMessage *reply;
+       struct wpabuf *xml;
+
+       xml = wpabuf_alloc(4000);
+       if (xml == NULL)
+               return NULL;
+
+       wpabuf_put_str(xml, "<?xml version=\"1.0\"?>\n");
+       wpabuf_put_str(xml, DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE);
+       wpabuf_put_str(xml, "<node>");
+
+       add_introspectable_interface(xml);
+       add_properties_interface(xml);
+       add_wpas_interfaces(xml, obj_dsc);
+       add_child_nodes(xml, obj_dsc->connection,
+                       dbus_message_get_path(message));
+
+       wpabuf_put_str(xml, "</node>\n");
+
+       reply = dbus_message_new_method_return(message);
+       if (reply) {
+               const char *intro_str = wpabuf_head(xml);
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
+                                        DBUS_TYPE_INVALID);
+       }
+       wpabuf_free(xml);
+
+       return reply;
+}
diff --git a/wpa_supplicant/dbus/dbus_old.c b/wpa_supplicant/dbus/dbus_old.c
new file mode 100644 (file)
index 0000000..7f25bf0
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 <dbus/dbus.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "wps/wps.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../bss.h"
+#include "dbus_old.h"
+#include "dbus_old_handlers.h"
+#include "dbus_common.h"
+#include "dbus_common_i.h"
+
+
+/**
+ * wpas_dbus_decompose_object_path - Decompose an interface object path into parts
+ * @path: The dbus object path
+ * @network: (out) the configured network this object path refers to, if any
+ * @bssid: (out) the scanned bssid this object path refers to, if any
+ * Returns: The object path of the network interface this path refers to
+ *
+ * For a given object path, decomposes the object path into object id, network,
+ * and BSSID parts, if those parts exist.
+ */
+char * wpas_dbus_decompose_object_path(const char *path, char **network,
+                                      char **bssid)
+{
+       const unsigned int dev_path_prefix_len =
+               strlen(WPAS_DBUS_PATH_INTERFACES "/");
+       char *obj_path_only;
+       char *next_sep;
+
+       /* Be a bit paranoid about path */
+       if (!path || strncmp(path, WPAS_DBUS_PATH_INTERFACES "/",
+                            dev_path_prefix_len))
+               return NULL;
+
+       /* Ensure there's something at the end of the path */
+       if ((path + dev_path_prefix_len)[0] == '\0')
+               return NULL;
+
+       obj_path_only = os_strdup(path);
+       if (obj_path_only == NULL)
+               return NULL;
+
+       next_sep = strchr(obj_path_only + dev_path_prefix_len, '/');
+       if (next_sep != NULL) {
+               const char *net_part = strstr(next_sep,
+                                             WPAS_DBUS_NETWORKS_PART "/");
+               const char *bssid_part = strstr(next_sep,
+                                               WPAS_DBUS_BSSIDS_PART "/");
+
+               if (network && net_part) {
+                       /* Deal with a request for a configured network */
+                       const char *net_name = net_part +
+                               strlen(WPAS_DBUS_NETWORKS_PART "/");
+                       *network = NULL;
+                       if (strlen(net_name))
+                               *network = os_strdup(net_name);
+               } else if (bssid && bssid_part) {
+                       /* Deal with a request for a scanned BSSID */
+                       const char *bssid_name = bssid_part +
+                               strlen(WPAS_DBUS_BSSIDS_PART "/");
+                       if (strlen(bssid_name))
+                               *bssid = os_strdup(bssid_name);
+                       else
+                               *bssid = NULL;
+               }
+
+               /* Cut off interface object path before "/" */
+               *next_sep = '\0';
+       }
+
+       return obj_path_only;
+}
+
+
+/**
+ * wpas_dbus_new_invalid_iface_error - Return a new invalid interface error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: A dbus error message
+ *
+ * Convenience function to create and return an invalid interface error
+ */
+DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message)
+{
+       return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE,
+                                     "wpa_supplicant knows nothing about "
+                                     "this interface.");
+}
+
+
+/**
+ * wpas_dbus_new_invalid_network_error - Return a new invalid network error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid network error
+ */
+DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message)
+{
+       return dbus_message_new_error(message, WPAS_ERROR_INVALID_NETWORK,
+                                     "The requested network does not exist.");
+}
+
+
+/**
+ * wpas_dbus_new_invalid_bssid_error - Return a new invalid bssid error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid bssid error
+ */
+static DBusMessage * wpas_dbus_new_invalid_bssid_error(DBusMessage *message)
+{
+       return dbus_message_new_error(message, WPAS_ERROR_INVALID_BSSID,
+                                     "The BSSID requested was invalid.");
+}
+
+
+/**
+ * wpas_dispatch_network_method - dispatch messages for configured networks
+ * @message: the incoming dbus message
+ * @wpa_s: a network interface's data
+ * @network_id: id of the configured network we're interested in
+ * Returns: a reply dbus message, or a dbus error message
+ *
+ * This function dispatches all incoming dbus messages for configured networks.
+ */
+static DBusMessage * wpas_dispatch_network_method(DBusMessage *message,
+                                                 struct wpa_supplicant *wpa_s,
+                                                 int network_id)
+{
+       DBusMessage *reply = NULL;
+       const char *method = dbus_message_get_member(message);
+       struct wpa_ssid *ssid;
+
+       ssid = wpa_config_get_network(wpa_s->conf, network_id);
+       if (ssid == NULL)
+               return wpas_dbus_new_invalid_network_error(message);
+
+       if (!strcmp(method, "set"))
+               reply = wpas_dbus_iface_set_network(message, wpa_s, ssid);
+       else if (!strcmp(method, "enable"))
+               reply = wpas_dbus_iface_enable_network(message, wpa_s, ssid);
+       else if (!strcmp(method, "disable"))
+               reply = wpas_dbus_iface_disable_network(message, wpa_s, ssid);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dispatch_bssid_method - dispatch messages for scanned networks
+ * @message: the incoming dbus message
+ * @wpa_s: a network interface's data
+ * @bssid: bssid of the scanned network we're interested in
+ * Returns: a reply dbus message, or a dbus error message
+ *
+ * This function dispatches all incoming dbus messages for scanned networks.
+ */
+static DBusMessage * wpas_dispatch_bssid_method(DBusMessage *message,
+                                               struct wpa_supplicant *wpa_s,
+                                               const char *bssid_txt)
+{
+       u8 bssid[ETH_ALEN];
+       struct wpa_bss *bss;
+
+       if (hexstr2bin(bssid_txt, bssid, ETH_ALEN) < 0)
+               return wpas_dbus_new_invalid_bssid_error(message);
+
+       bss = wpa_bss_get_bssid(wpa_s, bssid);
+       if (bss == NULL)
+               return wpas_dbus_new_invalid_bssid_error(message);
+
+       /* Dispatch the method call against the scanned bssid */
+       if (os_strcmp(dbus_message_get_member(message), "properties") == 0)
+               return wpas_dbus_bssid_properties(message, wpa_s, bss);
+
+       return NULL;
+}
+
+
+/**
+ * wpas_iface_message_handler - Dispatch messages for interfaces or networks
+ * @connection: Connection to the system message bus
+ * @message: An incoming dbus message
+ * @user_data: A pointer to a dbus control interface data structure
+ * Returns: Whether or not the message was handled
+ *
+ * This function dispatches all incoming dbus messages for network interfaces,
+ * or objects owned by them, such as scanned BSSIDs and configured networks.
+ */
+static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
+                                                   DBusMessage *message,
+                                                   void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       const char *method = dbus_message_get_member(message);
+       const char *path = dbus_message_get_path(message);
+       const char *msg_interface = dbus_message_get_interface(message);
+       char *iface_obj_path = NULL;
+       char *network = NULL;
+       char *bssid = NULL;
+       DBusMessage *reply = NULL;
+
+       /* Caller must specify a message interface */
+       if (!msg_interface)
+               goto out;
+
+       iface_obj_path = wpas_dbus_decompose_object_path(path, &network,
+                                                        &bssid);
+       if (iface_obj_path == NULL) {
+               reply = wpas_dbus_new_invalid_iface_error(message);
+               goto out;
+       }
+
+       /* Make sure the message's object path actually refers to the
+        * wpa_supplicant structure it's supposed to (which is wpa_s)
+        */
+       if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global,
+                                                 iface_obj_path) != wpa_s) {
+               reply = wpas_dbus_new_invalid_iface_error(message);
+               goto out;
+       }
+
+       if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) {
+               /* A method for one of this interface's configured networks */
+               int nid = strtoul(network, NULL, 10);
+               if (errno != EINVAL)
+                       reply = wpas_dispatch_network_method(message, wpa_s,
+                                                            nid);
+               else
+                       reply = wpas_dbus_new_invalid_network_error(message);
+       } else if (bssid && !strcmp(msg_interface, WPAS_DBUS_IFACE_BSSID)) {
+               /* A method for one of this interface's scanned BSSIDs */
+               reply = wpas_dispatch_bssid_method(message, wpa_s, bssid);
+       } else if (!strcmp(msg_interface, WPAS_DBUS_IFACE_INTERFACE)) {
+               /* A method for an interface only. */
+               if (!strcmp(method, "scan"))
+                       reply = wpas_dbus_iface_scan(message, wpa_s);
+               else if (!strcmp(method, "scanResults"))
+                       reply = wpas_dbus_iface_scan_results(message, wpa_s);
+               else if (!strcmp(method, "addNetwork"))
+                       reply = wpas_dbus_iface_add_network(message, wpa_s);
+               else if (!strcmp(method, "removeNetwork"))
+                       reply = wpas_dbus_iface_remove_network(message, wpa_s);
+               else if (!strcmp(method, "selectNetwork"))
+                       reply = wpas_dbus_iface_select_network(message, wpa_s);
+               else if (!strcmp(method, "capabilities"))
+                       reply = wpas_dbus_iface_capabilities(message, wpa_s);
+               else if (!strcmp(method, "disconnect"))
+                       reply = wpas_dbus_iface_disconnect(message, wpa_s);
+               else if (!strcmp(method, "setAPScan"))
+                       reply = wpas_dbus_iface_set_ap_scan(message, wpa_s);
+               else if (!strcmp(method, "setSmartcardModules"))
+                       reply = wpas_dbus_iface_set_smartcard_modules(message,
+                                                                     wpa_s);
+               else if (!strcmp(method, "state"))
+                       reply = wpas_dbus_iface_get_state(message, wpa_s);
+               else if (!strcmp(method, "scanning"))
+                       reply = wpas_dbus_iface_get_scanning(message, wpa_s);
+               else if (!strcmp(method, "setBlobs"))
+                       reply = wpas_dbus_iface_set_blobs(message, wpa_s);
+               else if (!strcmp(method, "removeBlobs"))
+                       reply = wpas_dbus_iface_remove_blobs(message, wpa_s);
+#ifdef CONFIG_WPS
+               else if (!os_strcmp(method, "wpsPbc"))
+                       reply = wpas_dbus_iface_wps_pbc(message, wpa_s);
+               else if (!os_strcmp(method, "wpsPin"))
+                       reply = wpas_dbus_iface_wps_pin(message, wpa_s);
+               else if (!os_strcmp(method, "wpsReg"))
+                       reply = wpas_dbus_iface_wps_reg(message, wpa_s);
+#endif /* CONFIG_WPS */
+       }
+
+       /* If the message was handled, send back the reply */
+       if (reply) {
+               if (!dbus_message_get_no_reply(message))
+                       dbus_connection_send(connection, reply, NULL);
+               dbus_message_unref(reply);
+       }
+
+out:
+       os_free(iface_obj_path);
+       os_free(network);
+       os_free(bssid);
+       return reply ? DBUS_HANDLER_RESULT_HANDLED :
+               DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+/**
+ * wpas_message_handler - dispatch incoming dbus messages
+ * @connection: connection to the system message bus
+ * @message: an incoming dbus message
+ * @user_data: a pointer to a dbus control interface data structure
+ * Returns: whether or not the message was handled
+ *
+ * This function dispatches all incoming dbus messages to the correct
+ * handlers, depending on what the message's target object path is,
+ * and what the method call is.
+ */
+static DBusHandlerResult wpas_message_handler(DBusConnection *connection,
+       DBusMessage *message, void *user_data)
+{
+       struct wpas_dbus_priv *ctrl_iface = user_data;
+       const char *method;
+       const char *path;
+       const char *msg_interface;
+       DBusMessage *reply = NULL;
+
+       method = dbus_message_get_member(message);
+       path = dbus_message_get_path(message);
+       msg_interface = dbus_message_get_interface(message);
+       if (!method || !path || !ctrl_iface || !msg_interface)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       /* Validate the method interface */
+       if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0)
+               return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+       if (!strcmp(path, WPAS_DBUS_PATH)) {
+               /* dispatch methods against our global dbus interface here */
+               if (!strcmp(method, "addInterface")) {
+                       reply = wpas_dbus_global_add_interface(
+                               message, ctrl_iface->global);
+               } else if (!strcmp(method, "removeInterface")) {
+                       reply = wpas_dbus_global_remove_interface(
+                               message, ctrl_iface->global);
+               } else if (!strcmp(method, "getInterface")) {
+                       reply = wpas_dbus_global_get_interface(
+                               message, ctrl_iface->global);
+               } else if (!strcmp(method, "setDebugParams")) {
+                       reply = wpas_dbus_global_set_debugparams(
+                               message, ctrl_iface->global);
+               }
+       }
+
+       /* If the message was handled, send back the reply */
+       if (reply) {
+               if (!dbus_message_get_no_reply(message))
+                       dbus_connection_send(connection, reply, NULL);
+               dbus_message_unref(reply);
+       }
+
+       return reply ? DBUS_HANDLER_RESULT_HANDLED :
+               DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+
+/**
+ * wpa_supplicant_dbus_notify_scan_results - Send a scan results signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify listeners that this interface has updated scan results.
+ */
+void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+       DBusMessage *_signal;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       _signal = dbus_message_new_signal(wpa_s->dbus_path,
+                                         WPAS_DBUS_IFACE_INTERFACE,
+                                         "ScanResultsAvailable");
+       if (_signal == NULL) {
+               wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan "
+                          "results signal");
+               return;
+       }
+       dbus_connection_send(iface->con, _signal, NULL);
+       dbus_message_unref(_signal);
+}
+
+
+/**
+ * wpa_supplicant_dbus_notify_state_change - Send a state change signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @new_state: new state wpa_supplicant is entering
+ * @old_state: old state wpa_supplicant is leaving
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify listeners that wpa_supplicant has changed state
+ */
+void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+                                            enum wpa_states new_state,
+                                            enum wpa_states old_state)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *_signal = NULL;
+       const char *new_state_str, *old_state_str;
+
+       if (wpa_s->dbus_path == NULL)
+               return; /* Skip signal since D-Bus setup is not yet ready */
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s->global == NULL)
+               return;
+       iface = wpa_s->global->dbus;
+       if (iface == NULL)
+               return;
+
+       /* Only send signal if state really changed */
+       if (new_state == old_state)
+               return;
+
+       _signal = dbus_message_new_signal(wpa_s->dbus_path,
+                                         WPAS_DBUS_IFACE_INTERFACE,
+                                         "StateChange");
+       if (_signal == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_supplicant_dbus_notify_state_change: "
+                          "could not create dbus signal; likely out of "
+                          "memory");
+               return;
+       }
+
+       new_state_str = wpa_supplicant_state_txt(new_state);
+       old_state_str = wpa_supplicant_state_txt(old_state);
+       if (new_state_str == NULL || old_state_str == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_supplicant_dbus_notify_state_change: "
+                          "Could not convert state strings");
+               goto out;
+       }
+
+       if (!dbus_message_append_args(_signal,
+                                     DBUS_TYPE_STRING, &new_state_str,
+                                     DBUS_TYPE_STRING, &old_state_str,
+                                     DBUS_TYPE_INVALID)) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_supplicant_dbus_notify_state_change: "
+                          "Not enough memory to construct state change "
+                          "signal");
+               goto out;
+       }
+
+       dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+       dbus_message_unref(_signal);
+}
+
+
+/**
+ * wpa_supplicant_dbus_notify_scanning - send scanning status
+ * @wpa_s: %wpa_supplicant network interface data
+ * Returns: 0 on success, -1 on failure
+ *
+ * Notify listeners of interface scanning state changes
+ */
+void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_dbus_priv *iface = wpa_s->global->dbus;
+       DBusMessage *_signal;
+       dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       _signal = dbus_message_new_signal(wpa_s->dbus_path,
+                                         WPAS_DBUS_IFACE_INTERFACE,
+                                         "Scanning");
+       if (_signal == NULL) {
+               wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan "
+                          "results signal");
+               return;
+       }
+
+       if (dbus_message_append_args(_signal,
+                                    DBUS_TYPE_BOOLEAN, &scanning,
+                                    DBUS_TYPE_INVALID)) {
+               dbus_connection_send(iface->con, _signal, NULL);
+       } else {
+               wpa_printf(MSG_ERROR, "dbus: Not enough memory to construct "
+                          "signal");
+       }
+       dbus_message_unref(_signal);
+}
+
+
+#ifdef CONFIG_WPS
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+                                        const struct wps_credential *cred)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *_signal = NULL;
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s->global == NULL)
+               return;
+       iface = wpa_s->global->dbus;
+       if (iface == NULL)
+               return;
+
+       _signal = dbus_message_new_signal(wpa_s->dbus_path,
+                                         WPAS_DBUS_IFACE_INTERFACE,
+                                         "WpsCred");
+       if (_signal == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_supplicant_dbus_notify_wps_cred: "
+                          "Could not create dbus signal; likely out of "
+                          "memory");
+               return;
+       }
+
+       if (!dbus_message_append_args(_signal,
+                                     DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                     &cred->cred_attr, cred->cred_attr_len,
+                                     DBUS_TYPE_INVALID)) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_supplicant_dbus_notify_wps_cred: "
+                          "Not enough memory to construct signal");
+               goto out;
+       }
+
+       dbus_connection_send(iface->con, _signal, NULL);
+
+out:
+       dbus_message_unref(_signal);
+}
+#else /* CONFIG_WPS */
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+                                        const struct wps_credential *cred)
+{
+}
+#endif /* CONFIG_WPS */
+
+
+/**
+ * wpa_supplicant_dbus_ctrl_iface_init - Initialize dbus control interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 on success, -1 on failure
+ *
+ * Initialize the dbus control interface and start receiving commands from
+ * external programs over the bus.
+ */
+int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface)
+{
+       DBusError error;
+       int ret = -1;
+       DBusObjectPathVTable wpas_vtable = {
+               NULL, &wpas_message_handler, NULL, NULL, NULL, NULL
+       };
+
+       /* Register the message handler for the global dbus interface */
+       if (!dbus_connection_register_object_path(iface->con,
+                                                 WPAS_DBUS_PATH, &wpas_vtable,
+                                                 iface)) {
+               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+                          "handler");
+               return -1;
+       }
+
+       /* Register our service with the message bus */
+       dbus_error_init(&error);
+       switch (dbus_bus_request_name(iface->con, WPAS_DBUS_SERVICE,
+                                     0, &error)) {
+       case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
+               ret = 0;
+               break;
+       case DBUS_REQUEST_NAME_REPLY_EXISTS:
+       case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
+       case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
+               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
+                          "already registered");
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
+                          "%s %s", error.name, error.message);
+               break;
+       }
+       dbus_error_free(&error);
+
+       if (ret != 0)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "Providing DBus service '" WPAS_DBUS_SERVICE
+                  "'.");
+
+       return 0;
+}
+
+
+/**
+ * wpas_dbus_register_new_iface - Register a new interface with dbus
+ * @wpa_s: %wpa_supplicant interface description structure to register
+ * Returns: 0 on success, -1 on error
+ *
+ * Registers a new interface with dbus and assigns it a dbus object path.
+ */
+int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_dbus_priv *ctrl_iface = wpa_s->global->dbus;
+       DBusConnection * con;
+       u32 next;
+       DBusObjectPathVTable vtable = {
+               NULL, &wpas_iface_message_handler, NULL, NULL, NULL, NULL
+       };
+
+       /* Do nothing if the control interface is not turned on */
+       if (ctrl_iface == NULL)
+               return 0;
+
+       con = ctrl_iface->con;
+       next = ctrl_iface->next_objid++;
+
+       /* Create and set the interface's object path */
+       wpa_s->dbus_path = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
+       if (wpa_s->dbus_path == NULL)
+               return -1;
+       os_snprintf(wpa_s->dbus_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   WPAS_DBUS_PATH_INTERFACES "/%u",
+                   next);
+
+       /* Register the message handler for the interface functions */
+       if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable,
+                                              wpa_s)) {
+               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
+                          "handler for interface %s", wpa_s->ifname);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * wpas_dbus_unregister_iface - Unregister an interface from dbus
+ * @wpa_s: wpa_supplicant interface structure
+ * Returns: 0 on success, -1 on failure
+ *
+ * Unregisters the interface with dbus
+ */
+int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_dbus_priv *ctrl_iface;
+       DBusConnection *con;
+
+       /* Do nothing if the control interface is not turned on */
+       if (wpa_s == NULL || wpa_s->global == NULL)
+               return 0;
+       ctrl_iface = wpa_s->global->dbus;
+       if (ctrl_iface == NULL)
+               return 0;
+
+       con = ctrl_iface->con;
+       if (!dbus_connection_unregister_object_path(con, wpa_s->dbus_path))
+               return -1;
+
+       os_free(wpa_s->dbus_path);
+       wpa_s->dbus_path = NULL;
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_iface_by_dbus_path - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @path: Pointer to a dbus object path representing an interface
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path(
+       struct wpa_global *global, const char *path)
+{
+       struct wpa_supplicant *wpa_s;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (strcmp(wpa_s->dbus_path, path) == 0)
+                       return wpa_s;
+       }
+       return NULL;
+}
diff --git a/wpa_supplicant/dbus/dbus_old.h b/wpa_supplicant/dbus/dbus_old.h
new file mode 100644 (file)
index 0000000..a9840c2
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 CTRL_IFACE_DBUS_H
+#define CTRL_IFACE_DBUS_H
+
+struct wps_credential;
+
+#ifdef CONFIG_CTRL_IFACE_DBUS
+
+#define WPAS_DBUS_OBJECT_PATH_MAX 150
+
+#define WPAS_DBUS_SERVICE      "fi.epitest.hostap.WPASupplicant"
+#define WPAS_DBUS_PATH         "/fi/epitest/hostap/WPASupplicant"
+#define WPAS_DBUS_INTERFACE    "fi.epitest.hostap.WPASupplicant"
+
+#define WPAS_DBUS_PATH_INTERFACES      WPAS_DBUS_PATH "/Interfaces"
+#define WPAS_DBUS_IFACE_INTERFACE      WPAS_DBUS_INTERFACE ".Interface"
+
+#define WPAS_DBUS_NETWORKS_PART "Networks"
+#define WPAS_DBUS_IFACE_NETWORK        WPAS_DBUS_INTERFACE ".Network"
+
+#define WPAS_DBUS_BSSIDS_PART  "BSSIDs"
+#define WPAS_DBUS_IFACE_BSSID  WPAS_DBUS_INTERFACE ".BSSID"
+
+
+/* Errors */
+#define WPAS_ERROR_INVALID_NETWORK \
+       WPAS_DBUS_IFACE_INTERFACE ".InvalidNetwork"
+#define WPAS_ERROR_INVALID_BSSID \
+       WPAS_DBUS_IFACE_INTERFACE ".InvalidBSSID"
+
+#define WPAS_ERROR_INVALID_OPTS \
+       WPAS_DBUS_INTERFACE ".InvalidOptions"
+#define WPAS_ERROR_INVALID_IFACE \
+       WPAS_DBUS_INTERFACE ".InvalidInterface"
+
+#define WPAS_ERROR_ADD_ERROR \
+       WPAS_DBUS_INTERFACE ".AddError"
+#define WPAS_ERROR_EXISTS_ERROR \
+       WPAS_DBUS_INTERFACE ".ExistsError"
+#define WPAS_ERROR_REMOVE_ERROR \
+       WPAS_DBUS_INTERFACE ".RemoveError"
+
+#define WPAS_ERROR_SCAN_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".ScanError"
+#define WPAS_ERROR_ADD_NETWORK_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".AddNetworkError"
+#define WPAS_ERROR_INTERNAL_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".InternalError"
+#define WPAS_ERROR_REMOVE_NETWORK_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".RemoveNetworkError"
+
+#define WPAS_ERROR_WPS_PBC_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".WpsPbcError"
+#define WPAS_ERROR_WPS_PIN_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".WpsPinError"
+#define WPAS_ERROR_WPS_REG_ERROR \
+       WPAS_DBUS_IFACE_INTERFACE ".WpsRegError"
+
+#define WPAS_DBUS_BSSID_FORMAT "%02x%02x%02x%02x%02x%02x"
+
+struct wpa_global;
+struct wpa_supplicant;
+
+int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface);
+void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+                                            enum wpa_states new_state,
+                                            enum wpa_states old_state);
+void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+                                        const struct wps_credential *cred);
+
+char * wpas_dbus_decompose_object_path(const char *path, char **network,
+                                       char **bssid);
+
+int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s);
+int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s);
+
+
+/* Methods internal to the dbus control interface */
+struct wpa_supplicant * wpa_supplicant_get_iface_by_dbus_path(
+       struct wpa_global *global, const char *path);
+
+#else /* CONFIG_CTRL_IFACE_DBUS */
+
+static inline void
+wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void
+wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+}
+
+#define wpa_supplicant_dbus_notify_state_change(w,n,o) do { } while (0)
+
+static inline void
+wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
+                                   const struct wps_credential *cred)
+{
+}
+
+static inline int
+wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline int
+wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+
+#endif /* CTRL_IFACE_DBUS_H */
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.c b/wpa_supplicant/dbus/dbus_old_handlers.c
new file mode 100644 (file)
index 0000000..d914697
--- /dev/null
@@ -0,0 +1,1436 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 <dbus/dbus.h>
+
+#include "common.h"
+#include "eap_peer/eap_methods.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../driver_i.h"
+#include "../notify.h"
+#include "../wpas_glue.h"
+#include "../bss.h"
+#include "../scan.h"
+#include "dbus_old.h"
+#include "dbus_old_handlers.h"
+#include "dbus_dict_helpers.h"
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
+/**
+ * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return an invalid options error
+ */
+DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
+                                              const char *arg)
+{
+       DBusMessage *reply;
+
+       reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS,
+                                      "Did not receive correct message "
+                                      "arguments.");
+       if (arg != NULL)
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
+                                        DBUS_TYPE_INVALID);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_new_success_reply - Return a new success reply message
+ * @message: Pointer to incoming dbus message this reply refers to
+ * Returns: a dbus message containing a single UINT32 that indicates
+ *          success (ie, a value of 1)
+ *
+ * Convenience function to create and return a success reply message
+ */
+DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message)
+{
+       DBusMessage *reply;
+       unsigned int success = 1;
+
+       reply = dbus_message_new_method_return(message);
+       dbus_message_append_args(reply, DBUS_TYPE_UINT32, &success,
+                                DBUS_TYPE_INVALID);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_global_add_interface - Request registration of a network interface
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the new interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "addInterface" method call. Handles requests
+ * by dbus clients to register a network interface that wpa_supplicant
+ * will manage.
+ */
+DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
+                                            struct wpa_global *global)
+{
+       char *ifname = NULL;
+       char *driver = NULL;
+       char *driver_param = NULL;
+       char *confname = NULL;
+       char *bridge_ifname = NULL;
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter;
+
+       dbus_message_iter_init(message, &iter);
+
+       /* First argument: interface name (DBUS_TYPE_STRING)
+        *    Required; must be non-zero length
+        */
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               goto error;
+       dbus_message_iter_get_basic(&iter, &ifname);
+       if (!os_strlen(ifname))
+               goto error;
+
+       /* Second argument: dict of options */
+       if (dbus_message_iter_next(&iter)) {
+               DBusMessageIter iter_dict;
+               struct wpa_dbus_dict_entry entry;
+
+               if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+                       goto error;
+               while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+                       if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+                               goto error;
+                       if (!strcmp(entry.key, "driver") &&
+                           (entry.type == DBUS_TYPE_STRING)) {
+                               driver = os_strdup(entry.str_value);
+                               wpa_dbus_dict_entry_clear(&entry);
+                               if (driver == NULL)
+                                       goto error;
+                       } else if (!strcmp(entry.key, "driver-params") &&
+                                  (entry.type == DBUS_TYPE_STRING)) {
+                               driver_param = os_strdup(entry.str_value);
+                               wpa_dbus_dict_entry_clear(&entry);
+                               if (driver_param == NULL)
+                                       goto error;
+                       } else if (!strcmp(entry.key, "config-file") &&
+                                  (entry.type == DBUS_TYPE_STRING)) {
+                               confname = os_strdup(entry.str_value);
+                               wpa_dbus_dict_entry_clear(&entry);
+                               if (confname == NULL)
+                                       goto error;
+                       } else if (!strcmp(entry.key, "bridge-ifname") &&
+                                  (entry.type == DBUS_TYPE_STRING)) {
+                               bridge_ifname = os_strdup(entry.str_value);
+                               wpa_dbus_dict_entry_clear(&entry);
+                               if (bridge_ifname == NULL)
+                                       goto error;
+                       } else {
+                               wpa_dbus_dict_entry_clear(&entry);
+                               goto error;
+                       }
+               }
+       }
+
+       /*
+        * Try to get the wpa_supplicant record for this iface, return
+        * an error if we already control it.
+        */
+       if (wpa_supplicant_get_iface(global, ifname) != NULL) {
+               reply = dbus_message_new_error(message,
+                                              WPAS_ERROR_EXISTS_ERROR,
+                                              "wpa_supplicant already "
+                                              "controls this interface.");
+       } else {
+               struct wpa_supplicant *wpa_s;
+               struct wpa_interface iface;
+               os_memset(&iface, 0, sizeof(iface));
+               iface.ifname = ifname;
+               iface.driver = driver;
+               iface.driver_param = driver_param;
+               iface.confname = confname;
+               iface.bridge_ifname = bridge_ifname;
+               /* Otherwise, have wpa_supplicant attach to it. */
+               if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
+                       const char *path = wpa_s->dbus_path;
+                       reply = dbus_message_new_method_return(message);
+                       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
+                                                &path, DBUS_TYPE_INVALID);
+               } else {
+                       reply = dbus_message_new_error(message,
+                                                      WPAS_ERROR_ADD_ERROR,
+                                                      "wpa_supplicant "
+                                                      "couldn't grab this "
+                                                      "interface.");
+               }
+       }
+
+out:
+       os_free(driver);
+       os_free(driver_param);
+       os_free(confname);
+       os_free(bridge_ifname);
+       return reply;
+
+error:
+       reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+       goto out;
+}
+
+
+/**
+ * wpas_dbus_global_remove_interface - Request deregistration of an interface
+ * @message: Pointer to incoming dbus message
+ * @global: wpa_supplicant global data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "removeInterface" method call.  Handles requests
+ * by dbus clients to deregister a network interface that wpa_supplicant
+ * currently manages.
+ */
+DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
+                                               struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s;
+       char *path;
+       DBusMessage *reply = NULL;
+
+       if (!dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_OBJECT_PATH, &path,
+                                  DBUS_TYPE_INVALID)) {
+               reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+               goto out;
+       }
+
+       wpa_s = wpa_supplicant_get_iface_by_dbus_path(global, path);
+       if (wpa_s == NULL) {
+               reply = wpas_dbus_new_invalid_iface_error(message);
+               goto out;
+       }
+
+       if (!wpa_supplicant_remove_iface(global, wpa_s)) {
+               reply = wpas_dbus_new_success_reply(message);
+       } else {
+               reply = dbus_message_new_error(message,
+                                              WPAS_ERROR_REMOVE_ERROR,
+                                              "wpa_supplicant couldn't "
+                                              "remove this interface.");
+       }
+
+out:
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_global_get_interface - Get the object path for an interface name
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: The object path of the interface object,
+ *          or a dbus error message with more information
+ *
+ * Handler function for "getInterface" method call. Handles requests
+ * by dbus clients for the object path of an specific network interface.
+ */
+DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
+                                            struct wpa_global *global)
+{
+       DBusMessage *reply = NULL;
+       const char *ifname;
+       const char *path;
+       struct wpa_supplicant *wpa_s;
+
+       if (!dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_STRING, &ifname,
+                                  DBUS_TYPE_INVALID)) {
+               reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+               goto out;
+       }
+
+       wpa_s = wpa_supplicant_get_iface(global, ifname);
+       if (wpa_s == NULL) {
+               reply = wpas_dbus_new_invalid_iface_error(message);
+               goto out;
+       }
+
+       path = wpa_s->dbus_path;
+       reply = dbus_message_new_method_return(message);
+       dbus_message_append_args(reply,
+                                DBUS_TYPE_OBJECT_PATH, &path,
+                                DBUS_TYPE_INVALID);
+
+out:
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_global_set_debugparams- Set the debug params
+ * @message: Pointer to incoming dbus message
+ * @global: %wpa_supplicant global data structure
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0), or returns a dbus error message with more information
+ *
+ * Handler function for "setDebugParams" method call. Handles requests
+ * by dbus clients for the object path of an specific network interface.
+ */
+DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
+                                              struct wpa_global *global)
+{
+       DBusMessage *reply = NULL;
+       int debug_level;
+       dbus_bool_t debug_timestamp;
+       dbus_bool_t debug_show_keys;
+
+       if (!dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_INT32, &debug_level,
+                                  DBUS_TYPE_BOOLEAN, &debug_timestamp,
+                                  DBUS_TYPE_BOOLEAN, &debug_show_keys,
+                                  DBUS_TYPE_INVALID)) {
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+       }
+
+       if (wpa_supplicant_set_debug_params(global, debug_level,
+                                           debug_timestamp ? 1 : 0,
+                                           debug_show_keys ? 1 : 0)) {
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+       }
+
+       reply = wpas_dbus_new_success_reply(message);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_scan - Request a wireless scan on an interface
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "scan" method call of a network device. Requests
+ * that wpa_supplicant perform a wireless scan as soon as possible
+ * on a particular wireless interface.
+ */
+DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
+                                  struct wpa_supplicant *wpa_s)
+{
+       wpa_s->scan_req = 2;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+       return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_scan_results - Get the results of a recent scan request
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: a dbus message containing a dbus array of objects paths, or returns
+ *          a dbus error message if not scan results could be found
+ *
+ * Handler function for "scanResults" method call of a network device. Returns
+ * a dbus message containing the object paths of wireless networks found.
+ */
+DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter;
+       DBusMessageIter sub_iter;
+       struct wpa_bss *bss;
+
+       /* Create and initialize the return message */
+       reply = dbus_message_new_method_return(message);
+       dbus_message_iter_init_append(reply, &iter);
+       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                        DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                        &sub_iter);
+
+       /* Loop through scan results and append each result's object path */
+       dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
+               char path_buf[WPAS_DBUS_OBJECT_PATH_MAX];
+               char *path = path_buf;
+
+               /* Construct the object path for this network.  Note that ':'
+                * is not a valid character in dbus object paths.
+                */
+               os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s/" WPAS_DBUS_BSSIDS_PART "/"
+                           WPAS_DBUS_BSSID_FORMAT,
+                           wpa_s->dbus_path, MAC2STR(bss->bssid));
+               dbus_message_iter_append_basic(&sub_iter,
+                                              DBUS_TYPE_OBJECT_PATH, &path);
+       }
+
+       dbus_message_iter_close_container(&iter, &sub_iter);
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_bssid_properties - Return the properties of a scanned network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @res: wpa_supplicant scan result for which to get properties
+ * Returns: a dbus message containing the properties for the requested network
+ *
+ * Handler function for "properties" method call of a scanned network.
+ * Returns a dbus message containing the the properties.
+ */
+DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s,
+                                        struct wpa_bss *bss)
+{
+       DBusMessage *reply;
+       DBusMessageIter iter, iter_dict;
+       const u8 *ie;
+
+       /* Dump the properties into a dbus message */
+       reply = dbus_message_new_method_return(message);
+
+       dbus_message_iter_init_append(reply, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
+               goto error;
+
+       if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid",
+                                            (const char *) bss->bssid,
+                                            ETH_ALEN))
+               goto error;
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
+       if (ie) {
+               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
+                                                    (const char *) (ie + 2),
+                                                    ie[1]))
+               goto error;
+       }
+
+       ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       if (ie) {
+               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie",
+                                                    (const char *) ie,
+                                                    ie[1] + 2))
+                       goto error;
+       }
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+       if (ie) {
+               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie",
+                                                    (const char *) ie,
+                                                    ie[1] + 2))
+                       goto error;
+       }
+
+       ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
+       if (ie) {
+               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
+                                                    (const char *) ie,
+                                                    ie[1] + 2))
+                       goto error;
+       }
+
+       if (bss->freq) {
+               if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency",
+                                               bss->freq))
+                       goto error;
+       }
+       if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities",
+                                        bss->caps))
+               goto error;
+       if (!(bss->flags & WPA_BSS_QUAL_INVALID) &&
+           !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual))
+               goto error;
+       if (!(bss->flags & WPA_BSS_NOISE_INVALID) &&
+           !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise))
+               goto error;
+       if (!(bss->flags & WPA_BSS_LEVEL_INVALID) &&
+           !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level))
+               goto error;
+       if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate",
+                                       wpa_bss_get_max_rate(bss) * 500000))
+               goto error;
+
+       if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+               goto error;
+
+       return reply;
+
+error:
+       if (reply)
+               dbus_message_unref(reply);
+       return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
+                                     "an internal error occurred returning "
+                                     "BSSID properties.");
+}
+
+
+/**
+ * wpas_dbus_iface_capabilities - Return interface capabilities
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a dict of strings
+ *
+ * Handler function for "capabilities" method call of an interface.
+ */
+DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_driver_capa capa;
+       int res;
+       DBusMessageIter iter, iter_dict;
+       char **eap_methods;
+       size_t num_items;
+       dbus_bool_t strict = FALSE;
+       DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
+
+       if (!dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_BOOLEAN, &strict,
+                                  DBUS_TYPE_INVALID))
+               strict = FALSE;
+
+       reply = dbus_message_new_method_return(message);
+
+       dbus_message_iter_init_append(reply, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
+               goto error;
+
+       /* EAP methods */
+       eap_methods = eap_get_names_as_string_array(&num_items);
+       if (eap_methods) {
+               dbus_bool_t success = FALSE;
+               size_t i = 0;
+
+               success = wpa_dbus_dict_append_string_array(
+                       &iter_dict, "eap", (const char **) eap_methods,
+                       num_items);
+
+               /* free returned method array */
+               while (eap_methods[i])
+                       os_free(eap_methods[i++]);
+               os_free(eap_methods);
+
+               if (!success)
+                       goto error;
+       }
+
+       res = wpa_drv_get_capa(wpa_s, &capa);
+
+       /***** pairwise cipher */
+       if (res < 0) {
+               if (!strict) {
+                       const char *args[] = {"CCMP", "TKIP", "NONE"};
+                       if (!wpa_dbus_dict_append_string_array(
+                                   &iter_dict, "pairwise", args,
+                                   sizeof(args) / sizeof(char*)))
+                               goto error;
+               }
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto error;
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "CCMP"))
+                               goto error;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "TKIP"))
+                               goto error;
+               }
+
+               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "NONE"))
+                               goto error;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto error;
+       }
+
+       /***** group cipher */
+       if (res < 0) {
+               if (!strict) {
+                       const char *args[] = {
+                               "CCMP", "TKIP", "WEP104", "WEP40"
+                       };
+                       if (!wpa_dbus_dict_append_string_array(
+                                   &iter_dict, "group", args,
+                                   sizeof(args) / sizeof(char*)))
+                               goto error;
+               }
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "group",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto error;
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "CCMP"))
+                               goto error;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "TKIP"))
+                               goto error;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "WEP104"))
+                               goto error;
+               }
+
+               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "WEP40"))
+                               goto error;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto error;
+       }
+
+       /***** key management */
+       if (res < 0) {
+               if (!strict) {
+                       const char *args[] = {
+                               "WPA-PSK", "WPA-EAP", "IEEE8021X", "WPA-NONE",
+                               "NONE"
+                       };
+                       if (!wpa_dbus_dict_append_string_array(
+                                   &iter_dict, "key_mgmt", args,
+                                   sizeof(args) / sizeof(char*)))
+                               goto error;
+               }
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto error;
+
+               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "NONE"))
+                       goto error;
+
+               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "IEEE8021X"))
+                       goto error;
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "WPA-EAP"))
+                               goto error;
+               }
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "WPA-PSK"))
+                               goto error;
+               }
+
+               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "WPA-NONE"))
+                               goto error;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto error;
+       }
+
+       /***** WPA protocol */
+       if (res < 0) {
+               if (!strict) {
+                       const char *args[] = { "RSN", "WPA" };
+                       if (!wpa_dbus_dict_append_string_array(
+                                   &iter_dict, "proto", args,
+                                   sizeof(args) / sizeof(char*)))
+                               goto error;
+               }
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto error;
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "RSN"))
+                               goto error;
+               }
+
+               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "WPA"))
+                               goto error;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto error;
+       }
+
+       /***** auth alg */
+       if (res < 0) {
+               if (!strict) {
+                       const char *args[] = { "OPEN", "SHARED", "LEAP" };
+                       if (!wpa_dbus_dict_append_string_array(
+                                   &iter_dict, "auth_alg", args,
+                                   sizeof(args) / sizeof(char*)))
+                               goto error;
+               }
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array))
+                       goto error;
+
+               if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "OPEN"))
+                               goto error;
+               }
+
+               if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "SHARED"))
+                               goto error;
+               }
+
+               if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
+                       if (!wpa_dbus_dict_string_array_add_element(
+                                   &iter_array, "LEAP"))
+                               goto error;
+               }
+
+               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                   &iter_dict_entry,
+                                                   &iter_dict_val,
+                                                   &iter_array))
+                       goto error;
+       }
+
+       if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
+               goto error;
+
+       return reply;
+
+error:
+       if (reply)
+               dbus_message_unref(reply);
+       return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
+                                     "an internal error occurred returning "
+                                     "interface capabilities.");
+}
+
+
+/**
+ * wpas_dbus_iface_add_network - Add a new configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing the object path of the new network
+ *
+ * Handler function for "addNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_ssid *ssid;
+       char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL) {
+               reply = dbus_message_new_error(message,
+                                              WPAS_ERROR_ADD_NETWORK_ERROR,
+                                              "wpa_supplicant could not add "
+                                              "a network on this interface.");
+               goto out;
+       }
+       wpas_notify_network_added(wpa_s, ssid);
+       ssid->disabled = 1;
+       wpa_config_set_network_defaults(ssid);
+
+       /* Construct the object path for this network. */
+       os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NETWORKS_PART "/%d",
+                   wpa_s->dbus_path, ssid->id);
+
+       reply = dbus_message_new_method_return(message);
+       dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
+                                &path, DBUS_TYPE_INVALID);
+
+out:
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_remove_network - Remove a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "removeNetwork" method call of a network interface.
+ */
+DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       const char *op;
+       char *iface = NULL, *net_id = NULL;
+       int id;
+       struct wpa_ssid *ssid;
+
+       if (!dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_OBJECT_PATH, &op,
+                                  DBUS_TYPE_INVALID)) {
+               reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+               goto out;
+       }
+
+       /* Extract the network ID */
+       iface = wpas_dbus_decompose_object_path(op, &net_id, NULL);
+       if (iface == NULL) {
+               reply = wpas_dbus_new_invalid_network_error(message);
+               goto out;
+       }
+
+       /* Ensure the network is actually a child of this interface */
+       if (os_strcmp(iface, wpa_s->dbus_path) != 0) {
+               reply = wpas_dbus_new_invalid_network_error(message);
+               goto out;
+       }
+
+       id = strtoul(net_id, NULL, 10);
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               reply = wpas_dbus_new_invalid_network_error(message);
+               goto out;
+       }
+
+       wpas_notify_network_removed(wpa_s, ssid);
+
+       if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+               reply = dbus_message_new_error(message,
+                                              WPAS_ERROR_REMOVE_NETWORK_ERROR,
+                                              "error removing the specified "
+                                              "on this interface.");
+               goto out;
+       }
+
+       if (ssid == wpa_s->current_ssid)
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+       reply = wpas_dbus_new_success_reply(message);
+
+out:
+       os_free(iface);
+       os_free(net_id);
+       return reply;
+}
+
+
+static const char *dont_quote[] = {
+       "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
+       "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
+       "bssid", NULL
+};
+
+
+static dbus_bool_t should_quote_opt(const char *key)
+{
+       int i = 0;
+       while (dont_quote[i] != NULL) {
+               if (strcmp(key, dont_quote[i]) == 0)
+                       return FALSE;
+               i++;
+       }
+       return TRUE;
+}
+
+
+/**
+ * wpas_dbus_iface_set_network - Set options for a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * Returns: a dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "set" method call of a configured network.
+ */
+DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s,
+                                         struct wpa_ssid *ssid)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+       DBusMessageIter iter, iter_dict;
+
+       dbus_message_iter_init(message, &iter);
+
+       if (!wpa_dbus_dict_open_read(&iter, &iter_dict)) {
+               reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+               goto out;
+       }
+
+       while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+               char *value = NULL;
+               size_t size = 50;
+               int ret;
+
+               if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+                       reply = wpas_dbus_new_invalid_opts_error(message,
+                                                                NULL);
+                       goto out;
+               }
+
+               /* Type conversions, since wpa_supplicant wants strings */
+               if (entry.type == DBUS_TYPE_ARRAY &&
+                   entry.array_type == DBUS_TYPE_BYTE) {
+                       if (entry.array_len <= 0)
+                               goto error;
+
+                       size = entry.array_len * 2 + 1;
+                       value = os_zalloc(size);
+                       if (value == NULL)
+                               goto error;
+                       ret = wpa_snprintf_hex(value, size,
+                                              (u8 *) entry.bytearray_value,
+                                              entry.array_len);
+                       if (ret <= 0)
+                               goto error;
+               } else if (entry.type == DBUS_TYPE_STRING) {
+                       if (should_quote_opt(entry.key)) {
+                               size = os_strlen(entry.str_value);
+                               /* Zero-length option check */
+                               if (size <= 0)
+                                       goto error;
+                               size += 3;  /* For quotes and terminator */
+                               value = os_zalloc(size);
+                               if (value == NULL)
+                                       goto error;
+                               ret = os_snprintf(value, size, "\"%s\"",
+                                                 entry.str_value);
+                               if (ret < 0 || (size_t) ret != (size - 1))
+                                       goto error;
+                       } else {
+                               value = os_strdup(entry.str_value);
+                               if (value == NULL)
+                                       goto error;
+                       }
+               } else if (entry.type == DBUS_TYPE_UINT32) {
+                       value = os_zalloc(size);
+                       if (value == NULL)
+                               goto error;
+                       ret = os_snprintf(value, size, "%u",
+                                         entry.uint32_value);
+                       if (ret <= 0)
+                               goto error;
+               } else if (entry.type == DBUS_TYPE_INT32) {
+                       value = os_zalloc(size);
+                       if (value == NULL)
+                               goto error;
+                       ret = os_snprintf(value, size, "%d",
+                                         entry.int32_value);
+                       if (ret <= 0)
+                               goto error;
+               } else
+                       goto error;
+
+               if (wpa_config_set(ssid, entry.key, value, 0) < 0)
+                       goto error;
+
+               if ((os_strcmp(entry.key, "psk") == 0 &&
+                    value[0] == '"' && ssid->ssid_len) ||
+                   (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
+                       wpa_config_update_psk(ssid);
+               else if (os_strcmp(entry.key, "priority") == 0)
+                       wpa_config_update_prio_list(wpa_s->conf);
+
+               os_free(value);
+               wpa_dbus_dict_entry_clear(&entry);
+               continue;
+
+       error:
+               os_free(value);
+               reply = wpas_dbus_new_invalid_opts_error(message, entry.key);
+               wpa_dbus_dict_entry_clear(&entry);
+               break;
+       }
+
+       if (!reply)
+               reply = wpas_dbus_new_success_reply(message);
+
+out:
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_enable_network - Mark a configured network as enabled
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "enable" method call of a configured network.
+ */
+DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s,
+                                            struct wpa_ssid *ssid)
+{
+       wpa_supplicant_enable_network(wpa_s, ssid);
+       return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_disable_network - Mark a configured network as disabled
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "disable" method call of a configured network.
+ */
+DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s,
+                                             struct wpa_ssid *ssid)
+{
+       wpa_supplicant_disable_network(wpa_s, ssid);
+       return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_select_network - Attempt association with a configured network
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "selectNetwork" method call of network interface.
+ */
+DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       const char *op;
+       struct wpa_ssid *ssid;
+       char *iface_obj_path = NULL;
+       char *network = NULL;
+
+       if (os_strlen(dbus_message_get_signature(message)) == 0) {
+               /* Any network */
+               ssid = NULL;
+       } else {
+               int nid;
+
+               if (!dbus_message_get_args(message, NULL,
+                                          DBUS_TYPE_OBJECT_PATH, &op,
+                                          DBUS_TYPE_INVALID)) {
+                       reply = wpas_dbus_new_invalid_opts_error(message,
+                                                                NULL);
+                       goto out;
+               }
+
+               /* Extract the network number */
+               iface_obj_path = wpas_dbus_decompose_object_path(op,
+                                                                &network,
+                                                                NULL);
+               if (iface_obj_path == NULL) {
+                       reply = wpas_dbus_new_invalid_iface_error(message);
+                       goto out;
+               }
+               /* Ensure the object path really points to this interface */
+               if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
+                       reply = wpas_dbus_new_invalid_network_error(message);
+                       goto out;
+               }
+
+               nid = strtoul(network, NULL, 10);
+               if (errno == EINVAL) {
+                       reply = wpas_dbus_new_invalid_network_error(message);
+                       goto out;
+               }
+
+               ssid = wpa_config_get_network(wpa_s->conf, nid);
+               if (ssid == NULL) {
+                       reply = wpas_dbus_new_invalid_network_error(message);
+                       goto out;
+               }
+       }
+
+       /* Finally, associate with the network */
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       reply = wpas_dbus_new_success_reply(message);
+
+out:
+       os_free(iface_obj_path);
+       os_free(network);
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_disconnect - Terminate the current connection
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "disconnect" method call of network interface.
+ */
+DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s)
+{
+       wpa_s->disconnected = 1;
+       wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+       return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_set_ap_scan - Control roaming mode
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "setAPScan" method call.
+ */
+DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       dbus_uint32_t ap_scan = 1;
+
+       if (!dbus_message_get_args(message, NULL, DBUS_TYPE_UINT32, &ap_scan,
+                                  DBUS_TYPE_INVALID)) {
+               reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+               goto out;
+       }
+
+       if (wpa_supplicant_set_ap_scan(wpa_s, ap_scan)) {
+               reply = wpas_dbus_new_invalid_opts_error(message, NULL);
+               goto out;
+       }
+
+       reply = wpas_dbus_new_success_reply(message);
+
+out:
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_set_smartcard_modules - Set smartcard related module paths
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "setSmartcardModules" method call.
+ */
+DBusMessage * wpas_dbus_iface_set_smartcard_modules(
+       DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+       DBusMessageIter iter, iter_dict;
+       char *opensc_engine_path = NULL;
+       char *pkcs11_engine_path = NULL;
+       char *pkcs11_module_path = NULL;
+       struct wpa_dbus_dict_entry entry;
+
+       if (!dbus_message_iter_init(message, &iter))
+               goto error;
+
+       if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+               goto error;
+
+       while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+               if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
+                       goto error;
+               if (!strcmp(entry.key, "opensc_engine_path") &&
+                   (entry.type == DBUS_TYPE_STRING)) {
+                       opensc_engine_path = os_strdup(entry.str_value);
+                       if (opensc_engine_path == NULL)
+                               goto error;
+               } else if (!strcmp(entry.key, "pkcs11_engine_path") &&
+                          (entry.type == DBUS_TYPE_STRING)) {
+                       pkcs11_engine_path = os_strdup(entry.str_value);
+                       if (pkcs11_engine_path == NULL)
+                               goto error;
+               } else if (!strcmp(entry.key, "pkcs11_module_path") &&
+                                (entry.type == DBUS_TYPE_STRING)) {
+                       pkcs11_module_path = os_strdup(entry.str_value);
+                       if (pkcs11_module_path == NULL)
+                               goto error;
+               } else {
+                       wpa_dbus_dict_entry_clear(&entry);
+                       goto error;
+               }
+               wpa_dbus_dict_entry_clear(&entry);
+       }
+
+       os_free(wpa_s->conf->opensc_engine_path);
+       wpa_s->conf->opensc_engine_path = opensc_engine_path;
+       os_free(wpa_s->conf->pkcs11_engine_path);
+       wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path;
+       os_free(wpa_s->conf->pkcs11_module_path);
+       wpa_s->conf->pkcs11_module_path = pkcs11_module_path;
+
+       wpa_sm_set_eapol(wpa_s->wpa, NULL);
+       eapol_sm_deinit(wpa_s->eapol);
+       wpa_s->eapol = NULL;
+       wpa_supplicant_init_eapol(wpa_s);
+       wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+       return wpas_dbus_new_success_reply(message);
+
+error:
+       os_free(opensc_engine_path);
+       os_free(pkcs11_engine_path);
+       os_free(pkcs11_module_path);
+       return wpas_dbus_new_invalid_opts_error(message, NULL);
+}
+
+
+/**
+ * wpas_dbus_iface_get_state - Get interface state
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing a STRING representing the current
+ *          interface state
+ *
+ * Handler function for "state" method call.
+ */
+DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       const char *str_state;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply != NULL) {
+               str_state = wpa_supplicant_state_txt(wpa_s->wpa_state);
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &str_state,
+                                        DBUS_TYPE_INVALID);
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_get_scanning - Get interface scanning state
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A dbus message containing whether the interface is scanning
+ *
+ * Handler function for "scanning" method call.
+ */
+DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply != NULL) {
+               dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning,
+                                        DBUS_TYPE_INVALID);
+       } else {
+               wpa_printf(MSG_ERROR, "dbus: Not enough memory to return "
+                          "scanning state");
+       }
+
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_set_blobs - Store named binary blobs (ie, for certificates)
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Asks wpa_supplicant to internally store a one or more binary blobs.
+ */
+DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
+       DBusMessageIter iter, iter_dict;
+
+       dbus_message_iter_init(message, &iter);
+
+       if (!wpa_dbus_dict_open_read(&iter, &iter_dict))
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+       while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
+               struct wpa_config_blob *blob;
+
+               if (!wpa_dbus_dict_get_entry(&iter_dict, &entry)) {
+                       reply = wpas_dbus_new_invalid_opts_error(message,
+                                                                NULL);
+                       break;
+               }
+
+               if (entry.type != DBUS_TYPE_ARRAY ||
+                   entry.array_type != DBUS_TYPE_BYTE) {
+                       reply = wpas_dbus_new_invalid_opts_error(
+                               message, "Byte array expected.");
+                       break;
+               }
+
+               if ((entry.array_len <= 0) || (entry.array_len > 65536) ||
+                   !strlen(entry.key)) {
+                       reply = wpas_dbus_new_invalid_opts_error(
+                               message, "Invalid array size.");
+                       break;
+               }
+
+               blob = os_zalloc(sizeof(*blob));
+               if (blob == NULL) {
+                       reply = dbus_message_new_error(
+                               message, WPAS_ERROR_ADD_ERROR,
+                               "Not enough memory to add blob.");
+                       break;
+               }
+               blob->data = os_zalloc(entry.array_len);
+               if (blob->data == NULL) {
+                       reply = dbus_message_new_error(
+                               message, WPAS_ERROR_ADD_ERROR,
+                               "Not enough memory to add blob data.");
+                       os_free(blob);
+                       break;
+               }
+
+               blob->name = os_strdup(entry.key);
+               blob->len = entry.array_len;
+               os_memcpy(blob->data, (u8 *) entry.bytearray_value,
+                               entry.array_len);
+               if (blob->name == NULL || blob->data == NULL) {
+                       wpa_config_free_blob(blob);
+                       reply = dbus_message_new_error(
+                               message, WPAS_ERROR_ADD_ERROR,
+                               "Error adding blob.");
+                       break;
+               }
+
+               /* Success */
+               if (!wpa_config_remove_blob(wpa_s->conf, blob->name))
+                       wpas_notify_blob_removed(wpa_s, blob->name);
+               wpa_config_set_blob(wpa_s->conf, blob);
+               wpas_notify_blob_added(wpa_s, blob->name);
+
+               wpa_dbus_dict_entry_clear(&entry);
+       }
+       wpa_dbus_dict_entry_clear(&entry);
+
+       return reply ? reply : wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_remove_blob - Remove named binary blobs
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Asks wpa_supplicant to remove one or more previously stored binary blobs.
+ */
+DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       DBusMessageIter iter, array;
+       char *err_msg = NULL;
+
+       dbus_message_iter_init(message, &iter);
+
+       if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) ||
+           (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING))
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+       dbus_message_iter_recurse(&iter, &array);
+       while (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_STRING) {
+               const char *name;
+
+               dbus_message_iter_get_basic(&array, &name);
+               if (!os_strlen(name))
+                       err_msg = "Invalid blob name.";
+
+               if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
+                       err_msg = "Error removing blob.";
+               else
+                       wpas_notify_blob_removed(wpa_s, name);
+               dbus_message_iter_next(&array);
+       }
+
+       if (err_msg)
+               return dbus_message_new_error(message, WPAS_ERROR_REMOVE_ERROR,
+                                             err_msg);
+
+       return wpas_dbus_new_success_reply(message);
+}
diff --git a/wpa_supplicant/dbus/dbus_old_handlers.h b/wpa_supplicant/dbus/dbus_old_handlers.h
new file mode 100644 (file)
index 0000000..65e876f
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * WPA Supplicant / dbus-based control interface
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 CTRL_IFACE_DBUS_HANDLERS_H
+#define CTRL_IFACE_DBUS_HANDLERS_H
+
+struct wpa_bss;
+
+DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message);
+DBusMessage * wpas_dbus_new_invalid_network_error(DBusMessage *message);
+
+DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
+                                            struct wpa_global *global);
+
+DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
+                                               struct wpa_global *global);
+
+DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
+                                            struct wpa_global *global);
+
+DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
+                                              struct wpa_global *global);
+
+DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
+                                  struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s,
+                                        struct wpa_bss *bss);
+
+DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s,
+                                         struct wpa_ssid *ssid);
+
+DBusMessage * wpas_dbus_iface_enable_network(DBusMessage *message,
+                                            struct wpa_supplicant *wpa_s,
+                                            struct wpa_ssid *ssid);
+
+DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s,
+                                             struct wpa_ssid *ssid);
+
+DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_smartcard_modules(
+       DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_get_state(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
+                                       struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_new_success_reply(DBusMessage *message);
+DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
+                                              const char *arg);
+
+#endif /* CTRL_IFACE_DBUS_HANDLERS_H */
+
diff --git a/wpa_supplicant/dbus/dbus_old_handlers_wps.c b/wpa_supplicant/dbus/dbus_old_handlers_wps.c
new file mode 100644 (file)
index 0000000..b5879f3
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * WPA Supplicant / dbus-based control interface (WPS)
+ * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, 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 <dbus/dbus.h>
+
+#include "common.h"
+#include "../config.h"
+#include "../wpa_supplicant_i.h"
+#include "../wps_supplicant.h"
+#include "dbus_old.h"
+#include "dbus_old_handlers.h"
+
+/**
+ * wpas_dbus_iface_wps_pbc - Request credentials using WPS PBC method
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "wpsPbc" method call
+ */
+DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s)
+{
+       char *arg_bssid = NULL;
+       u8 bssid[ETH_ALEN];
+       int ret = 0;
+
+       if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
+                                  DBUS_TYPE_INVALID))
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+       if (!os_strcmp(arg_bssid, "any"))
+               ret = wpas_wps_start_pbc(wpa_s, NULL);
+       else if (!hwaddr_aton(arg_bssid, bssid))
+               ret = wpas_wps_start_pbc(wpa_s, bssid);
+       else {
+               return wpas_dbus_new_invalid_opts_error(message,
+                                                       "Invalid BSSID");
+       }
+
+       if (ret < 0) {
+               return dbus_message_new_error(message,
+                                             WPAS_ERROR_WPS_PBC_ERROR,
+                                             "Could not start PBC "
+                                             "negotiation");
+       }
+
+       return wpas_dbus_new_success_reply(message);
+}
+
+
+/**
+ * wpas_dbus_iface_wps_pin - Establish the PIN number of the enrollee
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "wpsPin" method call
+ */
+DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       char *arg_bssid;
+       char *pin = NULL;
+       u8 bssid[ETH_ALEN], *_bssid = NULL;
+       int ret = 0;
+
+       if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
+                                  DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+       if (!os_strcmp(arg_bssid, "any"))
+               _bssid = NULL;
+       else if (!hwaddr_aton(arg_bssid, bssid))
+               _bssid = bssid;
+       else {
+               return wpas_dbus_new_invalid_opts_error(message,
+                                                       "Invalid BSSID");
+       }
+
+       if (os_strlen(pin) > 0)
+               ret = wpas_wps_start_pin(wpa_s, _bssid, pin);
+       else
+               ret = wpas_wps_start_pin(wpa_s, _bssid, NULL);
+
+       if (ret < 0) {
+               return dbus_message_new_error(message,
+                                             WPAS_ERROR_WPS_PIN_ERROR,
+                                             "Could not init PIN");
+       }
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               return NULL;
+
+       if (ret == 0) {
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin,
+                                        DBUS_TYPE_INVALID);
+       } else {
+               char npin[9];
+               os_snprintf(npin, sizeof(npin), "%08d", ret);
+               dbus_message_append_args(reply, DBUS_TYPE_STRING, &npin,
+                                        DBUS_TYPE_INVALID);
+       }
+       return reply;
+}
+
+
+/**
+ * wpas_dbus_iface_wps_reg - Request credentials using the PIN of the AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing a UINT32 indicating success (1) or
+ *          failure (0)
+ *
+ * Handler function for "wpsReg" method call
+ */
+DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
+                                     struct wpa_supplicant *wpa_s)
+{
+       char *arg_bssid;
+       char *pin = NULL;
+       u8 bssid[ETH_ALEN];
+       int ret = 0;
+
+       if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
+                                  DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
+               return wpas_dbus_new_invalid_opts_error(message, NULL);
+
+       if (!os_strcmp(arg_bssid, "any"))
+               ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL);
+       else if (!hwaddr_aton(arg_bssid, bssid))
+               ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
+       else {
+               return wpas_dbus_new_invalid_opts_error(message,
+                                                       "Invalid BSSID");
+       }
+
+       if (ret < 0) {
+               return dbus_message_new_error(message,
+                                             WPAS_ERROR_WPS_PBC_ERROR,
+                                             "Could not request credentials");
+       }
+
+       return wpas_dbus_new_success_reply(message);
+}
diff --git a/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service b/wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service
new file mode 100644 (file)
index 0000000..a9ce1ec
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=fi.epitest.hostap.WPASupplicant
+Exec=/sbin/wpa_supplicant -u
+User=root
diff --git a/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service b/wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service
new file mode 100644 (file)
index 0000000..df78471
--- /dev/null
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=fi.w1.wpa_supplicant1
+Exec=/sbin/wpa_supplicant -u
+User=root
diff --git a/wpa_supplicant/defconfig b/wpa_supplicant/defconfig
new file mode 100644 (file)
index 0000000..8c32cb3
--- /dev/null
@@ -0,0 +1,404 @@
+# Example wpa_supplicant build time configuration
+#
+# This file lists the configuration options that are used when building the
+# hostapd binary. All lines starting with # are ignored. Configuration option
+# lines must be commented out complete, if they are not to be included, i.e.,
+# just setting VARIABLE=n is not disabling that variable.
+#
+# This file is included in Makefile, so variables like CFLAGS and LIBS can also
+# be modified from here. In most cases, these lines should use += in order not
+# to override previous values of the variables.
+
+
+# Uncomment following two lines and fix the paths if you have installed OpenSSL
+# or GnuTLS in non-default location
+#CFLAGS += -I/usr/local/openssl/include
+#LIBS += -L/usr/local/openssl/lib
+
+# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
+# the kerberos files are not in the default include path. Following line can be
+# used to fix build issues on such systems (krb5.h not found).
+#CFLAGS += -I/usr/include/kerberos
+
+# Example configuration for various cross-compilation platforms
+
+#### sveasoft (e.g., for Linksys WRT54G) ######################################
+#CC=mipsel-uclibc-gcc
+#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
+#CFLAGS += -Os
+#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
+#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
+###############################################################################
+
+#### openwrt (e.g., for Linksys WRT54G) #######################################
+#CC=mipsel-uclibc-gcc
+#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
+#CFLAGS += -Os
+#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
+#      -I../WRT54GS/release/src/include
+#LIBS = -lssl
+###############################################################################
+
+
+# Driver interface for Host AP driver
+CONFIG_DRIVER_HOSTAP=y
+
+# Driver interface for Agere driver
+#CONFIG_DRIVER_HERMES=y
+# Change include directories to match with the local setup
+#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
+#CFLAGS += -I../../include/wireless
+
+# Driver interface for madwifi driver
+# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
+#CONFIG_DRIVER_MADWIFI=y
+# Set include directory to the madwifi source tree
+#CFLAGS += -I../../madwifi
+
+# Driver interface for ndiswrapper
+# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
+#CONFIG_DRIVER_NDISWRAPPER=y
+
+# Driver interface for Atmel driver
+CONFIG_DRIVER_ATMEL=y
+
+# Driver interface for old Broadcom driver
+# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
+# Linux wireless extensions and does not need (or even work) with the old
+# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
+#CONFIG_DRIVER_BROADCOM=y
+# Example path for wlioctl.h; change to match your configuration
+#CFLAGS += -I/opt/WRT54GS/release/src/include
+
+# Driver interface for Intel ipw2100/2200 driver
+# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
+#CONFIG_DRIVER_IPW=y
+
+# Driver interface for Ralink driver
+#CONFIG_DRIVER_RALINK=y
+
+# Driver interface for generic Linux wireless extensions
+CONFIG_DRIVER_WEXT=y
+
+# Driver interface for Linux drivers using the nl80211 kernel interface
+#CONFIG_DRIVER_NL80211=y
+
+# Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
+#CONFIG_DRIVER_BSD=y
+#CFLAGS += -I/usr/local/include
+#LIBS += -L/usr/local/lib
+#LIBS_p += -L/usr/local/lib
+#LIBS_c += -L/usr/local/lib
+
+# Driver interface for Windows NDIS
+#CONFIG_DRIVER_NDIS=y
+#CFLAGS += -I/usr/include/w32api/ddk
+#LIBS += -L/usr/local/lib
+# For native build using mingw
+#CONFIG_NATIVE_WINDOWS=y
+# Additional directories for cross-compilation on Linux host for mingw target
+#CFLAGS += -I/opt/mingw/mingw32/include/ddk
+#LIBS += -L/opt/mingw/mingw32/lib
+#CC=mingw32-gcc
+# By default, driver_ndis uses WinPcap for low-level operations. This can be
+# replaced with the following option which replaces WinPcap calls with NDISUIO.
+# However, this requires that WZC is disabled (net stop wzcsvc) before starting
+# wpa_supplicant.
+# CONFIG_USE_NDISUIO=y
+
+# Driver interface for development testing
+#CONFIG_DRIVER_TEST=y
+
+# Include client MLME (management frame processing) for test driver
+# This can be used to test MLME operations in hostapd with the test interface.
+# space.
+#CONFIG_CLIENT_MLME=y
+
+# Driver interface for wired Ethernet drivers
+CONFIG_DRIVER_WIRED=y
+
+# Driver interface for the Broadcom RoboSwitch family
+#CONFIG_DRIVER_ROBOSWITCH=y
+
+# Driver interface for no driver (e.g., WPS ER only)
+#CONFIG_DRIVER_NONE=y
+
+# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
+# included)
+CONFIG_IEEE8021X_EAPOL=y
+
+# EAP-MD5
+CONFIG_EAP_MD5=y
+
+# EAP-MSCHAPv2
+CONFIG_EAP_MSCHAPV2=y
+
+# EAP-TLS
+CONFIG_EAP_TLS=y
+
+# EAL-PEAP
+CONFIG_EAP_PEAP=y
+
+# EAP-TTLS
+CONFIG_EAP_TTLS=y
+
+# EAP-FAST
+# 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.8d-tls-extensions.patch)
+# to add the needed functions.
+#CONFIG_EAP_FAST=y
+
+# EAP-GTC
+CONFIG_EAP_GTC=y
+
+# EAP-OTP
+CONFIG_EAP_OTP=y
+
+# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
+#CONFIG_EAP_SIM=y
+
+# EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
+#CONFIG_EAP_PSK=y
+
+# EAP-PAX
+#CONFIG_EAP_PAX=y
+
+# LEAP
+CONFIG_EAP_LEAP=y
+
+# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
+#CONFIG_EAP_AKA=y
+
+# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
+# This requires CONFIG_EAP_AKA to be enabled, too.
+#CONFIG_EAP_AKA_PRIME=y
+
+# Enable USIM simulator (Milenage) for EAP-AKA
+#CONFIG_USIM_SIMULATOR=y
+
+# EAP-SAKE
+#CONFIG_EAP_SAKE=y
+
+# EAP-GPSK
+#CONFIG_EAP_GPSK=y
+# Include support for optional SHA256 cipher suite in EAP-GPSK
+#CONFIG_EAP_GPSK_SHA256=y
+
+# EAP-TNC and related Trusted Network Connect support (experimental)
+#CONFIG_EAP_TNC=y
+
+# Wi-Fi Protected Setup (WPS)
+#CONFIG_WPS=y
+
+# EAP-IKEv2
+#CONFIG_EAP_IKEV2=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
+
+# Smartcard support (i.e., private key on a smartcard), e.g., with openssl
+# engine.
+CONFIG_SMARTCARD=y
+
+# PC/SC interface for smartcards (USIM, GSM SIM)
+# Enable this if EAP-SIM or EAP-AKA is included
+#CONFIG_PCSC=y
+
+# Development testing
+#CONFIG_EAPOL_TEST=y
+
+# Select control interface backend for external programs, e.g, wpa_cli:
+# unix = UNIX domain sockets (default for Linux/*BSD)
+# udp = UDP sockets using localhost (127.0.0.1)
+# named_pipe = Windows Named Pipe (default for Windows)
+# y = use default (backwards compatibility)
+# If this option is commented out, control interface is not included in the
+# build.
+CONFIG_CTRL_IFACE=y
+
+# Include support for GNU Readline and History Libraries in wpa_cli.
+# When building a wpa_cli binary for distribution, please note that these
+# libraries are licensed under GPL and as such, BSD license may not apply for
+# the resulting binary.
+#CONFIG_READLINE=y
+
+# Remove debugging code that is printing out debug message to stdout.
+# This can be used to reduce the size of the wpa_supplicant considerably
+# if debugging code is not needed. The size reduction can be around 35%
+# (e.g., 90 kB).
+#CONFIG_NO_STDOUT_DEBUG=y
+
+# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save
+# 35-50 kB in code size.
+#CONFIG_NO_WPA=y
+
+# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to
+# save about 1 kB in code size when building only WPA-Personal (no EAP support)
+# or 6 kB if building for WPA-Enterprise.
+#CONFIG_NO_WPA2=y
+
+# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support
+# This option can be used to reduce code size by removing support for
+# converting ASCII passphrases into PSK. If this functionality is removed, the
+# PSK can only be configured as the 64-octet hexstring (e.g., from
+# wpa_passphrase). This saves about 0.5 kB in code size.
+#CONFIG_NO_WPA_PASSPHRASE=y
+
+# Disable scan result processing (ap_mode=1) to save code size by about 1 kB.
+# This can be used if ap_scan=1 mode is never enabled.
+#CONFIG_NO_SCAN_PROCESSING=y
+
+# Select configuration backend:
+# file = text file (e.g., wpa_supplicant.conf; note: the configuration file
+#      path is given on command line, not here; this option is just used to
+#      select the backend that allows configuration files to be used)
+# winreg = Windows registry (see win_example.reg for an example)
+CONFIG_BACKEND=file
+
+# Remove configuration write functionality (i.e., to allow the configuration
+# file to be updated based on runtime configuration changes). The runtime
+# configuration can still be changed, the changes are just not going to be
+# persistent over restarts. This option can be used to reduce code size by
+# about 3.5 kB.
+#CONFIG_NO_CONFIG_WRITE=y
+
+# Remove support for configuration blobs to reduce code size by about 1.5 kB.
+#CONFIG_NO_CONFIG_BLOBS=y
+
+# Select program entry point implementation:
+# main = UNIX/POSIX like main() function (default)
+# main_winsvc = Windows service (read parameters from registry)
+# main_none = Very basic example (development use only)
+#CONFIG_MAIN=main
+
+# Select wrapper for operatins system and C library specific functions
+# unix = UNIX/POSIX like systems (default)
+# win32 = Windows systems
+# none = Empty template
+#CONFIG_OS=unix
+
+# Select event loop implementation
+# eloop = select() loop (default)
+# eloop_win = Windows events and WaitForMultipleObject() loop
+# eloop_none = Empty template
+#CONFIG_ELOOP=eloop
+
+# Select layer 2 packet implementation
+# linux = Linux packet socket (default)
+# pcap = libpcap/libdnet/WinPcap
+# freebsd = FreeBSD libpcap
+# winpcap = WinPcap with receive thread
+# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y)
+# none = Empty template
+#CONFIG_L2_PACKET=linux
+
+# 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
+
+# Select TLS implementation
+# openssl = OpenSSL (default)
+# gnutls = GnuTLS (needed for TLS/IA, see also CONFIG_GNUTLS_EXTRA)
+# internal = Internal TLSv1 implementation (experimental)
+# none = Empty template
+#CONFIG_TLS=openssl
+
+# Whether to enable TLS/IA support, which is required for EAP-TTLSv1.
+# You need CONFIG_TLS=gnutls for this to have any effect. Please note that
+# even though the core GnuTLS library is released under LGPL, this extra
+# library uses GPL and as such, the terms of GPL apply to the combination
+# of wpa_supplicant and GnuTLS if this option is enabled. BSD license may not
+# apply for distribution of the resulting binary.
+#CONFIG_GNUTLS_EXTRA=y
+
+# If CONFIG_TLS=internal is used, additional library and include paths are
+# needed for LibTomMath. Alternatively, an integrated, minimal version of
+# LibTomMath can be used. See beginning of libtommath.c for details on benefits
+# and drawbacks of this option.
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#ifndef CONFIG_INTERNAL_LIBTOMMATH
+#LTM_PATH=/usr/src/libtommath-0.39
+#CFLAGS += -I$(LTM_PATH)
+#LIBS += -L$(LTM_PATH)
+#LIBS_p += -L$(LTM_PATH)
+#endif
+# At the cost of about 4 kB of additional binary size, the internal LibTomMath
+# can be configured to include faster routines for exptmod, sqr, and div to
+# speed up DH and RSA calculation considerably
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+# Include NDIS event processing through WMI into wpa_supplicant/wpasvc.
+# This is only for Windows builds and requires WMI-related header files and
+# WbemUuid.Lib from Platform SDK even when building with MinGW.
+#CONFIG_NDIS_EVENTS_INTEGRATED=y
+#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib"
+
+# Add support for old DBus control interface
+# (fi.epitest.hostap.WPASupplicant)
+#CONFIG_CTRL_IFACE_DBUS=y
+
+# Add support for new DBus control interface
+# (fi.w1.hostap.wpa_supplicant1)
+#CONFIG_CTRL_IFACE_DBUS_NEW=y
+
+# Add introspection support for new DBus control interface
+#CONFIG_CTRL_IFACE_DBUS_INTRO=y
+
+# Add support for loading EAP methods dynamically as shared libraries.
+# When this option is enabled, each EAP method can be either included
+# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn).
+# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to
+# be loaded in the beginning of the wpa_supplicant configuration file
+# (see load_dynamic_eap parameter in the example file) before being used in
+# the network blocks.
+#
+# Note that some shared parts of EAP methods are included in the main program
+# and in order to be able to use dynamic EAP methods using these parts, the
+# main program must have been build with the EAP method enabled (=y or =dyn).
+# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries
+# unless at least one of them was included in the main build to force inclusion
+# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included
+# in the main build to be able to load these methods dynamically.
+#
+# Please also note that using dynamic libraries will increase the total binary
+# size. Thus, it may not be the best option for targets that have limited
+# amount of memory/flash.
+#CONFIG_DYNAMIC_EAP_METHODS=y
+
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+#CONFIG_IEEE80211R=y
+
+# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
+#CONFIG_DEBUG_FILE=y
+
+# Enable privilege separation (see README 'Privilege separation' for details)
+#CONFIG_PRIVSEP=y
+
+# Enable mitigation against certain attacks against TKIP by delaying Michael
+# MIC error reports by a random amount of time between 0 and 60 seconds
+#CONFIG_DELAYED_MIC_ERROR_REPORT=y
+
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, comment out these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
+
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, comment out these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
diff --git a/wpa_supplicant/doc/docbook/.gitignore b/wpa_supplicant/doc/docbook/.gitignore
new file mode 100644 (file)
index 0000000..8c3945c
--- /dev/null
@@ -0,0 +1,6 @@
+manpage.links
+manpage.refs
+*.8
+*.5
+*.html
+*.pdf
diff --git a/wpa_supplicant/doc/docbook/Makefile b/wpa_supplicant/doc/docbook/Makefile
new file mode 100644 (file)
index 0000000..aaeee2e
--- /dev/null
@@ -0,0 +1,27 @@
+all: man html pdf
+
+FILES += wpa_background
+FILES += wpa_cli
+FILES += wpa_gui
+FILES += wpa_passphrase
+FILES += wpa_priv
+FILES += wpa_supplicant.conf
+FILES += wpa_supplicant
+
+man:
+       for i in $(FILES); do docbook2man $$i.sgml; done
+
+html:
+       for i in $(FILES); do docbook2html $$i.sgml && \
+               mv index.html $$i.html; done
+
+pdf:
+       for i in $(FILES); do docbook2pdf $$i.sgml; done
+
+
+clean:
+       rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8
+       rm -f wpa_supplicant.conf.5
+       rm -f manpage.links manpage.refs
+       rm -f $(FILES:%=%.pdf)
+       rm -f $(FILES:%=%.html)
diff --git a/wpa_supplicant/doc/docbook/wpa_background.sgml b/wpa_supplicant/doc/docbook/wpa_background.sgml
new file mode 100644 (file)
index 0000000..f47235b
--- /dev/null
@@ -0,0 +1,101 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_background</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_background</refname>
+    <refpurpose>Background information on Wi-Fi Protected Access and IEEE 802.11i</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>WPA</title>
+
+    <para>The original security mechanism of IEEE 802.11 standard was
+    not designed to be strong and has proven to be insufficient for
+    most networks that require some kind of security. Task group I
+    (Security) of IEEE 802.11 working group
+    (http://www.ieee802.org/11/) has worked to address the flaws of
+    the base standard and has in practice completed its work in May
+    2004. The IEEE 802.11i amendment to the IEEE 802.11 standard was
+    approved in June 2004 and published in July 2004.</para>
+
+    <para>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&lt;TM&gt; (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).</para>
+
+    <para>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.</para>
+
+    <para>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).</para>
+
+    <para>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).</para>
+
+    <para>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).</para>
+  </refsect1>
+
+  <refsect1>
+    <title>IEEE 802.11i / WPA2</title>
+
+    <para>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).</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2007,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is dual-licensed under both the GPL version 2
+    and BSD license. Either license may be used at your option.</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_cli.sgml b/wpa_supplicant/doc/docbook/wpa_cli.sgml
new file mode 100644 (file)
index 0000000..1fe98f4
--- /dev/null
@@ -0,0 +1,339 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_cli</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_cli</refname>
+
+    <refpurpose>WPA command line client</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_cli</command>
+      <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
+      <arg>-i <replaceable>ifname</replaceable></arg>
+      <arg>-hvB</arg>
+      <arg>-a <replaceable>action file</replaceable></arg>
+      <arg>-P <replaceable>pid file</replaceable></arg>
+      <arg><replaceable>command ...</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para>wpa_cli is a text-based frontend program for interacting
+    with wpa_supplicant. It is used to query current status, change
+    configuration, trigger events, and request interactive user
+    input.</para>
+
+    <para>wpa_cli can show the current authentication status, selected
+    security mode, dot11 and dot1x MIBs, etc. In addition, it can
+    configure some variables like EAPOL state machine parameters and
+    trigger events like reassociation and IEEE 802.1X
+    logoff/logon. wpa_cli provides a user interface to request
+    authentication information, like username and password, if these
+    are not included in the configuration. This can be used to
+    implement, e.g., one-time-passwords or generic token card
+    authentication where the authentication is based on a
+    challenge-response that uses an external device for generating the
+    response.</para>
+
+    <para>The control interface of wpa_supplicant can be configured to
+    allow non-root user access (ctrl_interface GROUP= parameter in the
+    configuration file). This makes it possible to run wpa_cli with a
+    normal user account.</para>
+
+    <para>wpa_cli supports two modes: interactive and command
+    line. Both modes share the same command set and the main
+    difference is in interactive mode providing access to unsolicited
+    messages (event messages, username/password requests).</para>
+
+    <para>Interactive mode is started when wpa_cli is executed without
+    including the command as a command line parameter. Commands are
+    then entered on the wpa_cli prompt. In command line mode, the same
+    commands are entered as command line arguments for wpa_cli.</para>
+ </refsect1>
+ <refsect1>
+   <title>Interactive authentication parameters request</title>
+
+   <para>When wpa_supplicant need authentication parameters, like
+   username and password, which are not present in the configuration
+   file, it sends a request message to all attached frontend programs,
+   e.g., wpa_cli in interactive mode. wpa_cli shows these requests
+   with "CTRL-REQ-&lt;type&gt;-&lt;id&gt;:&lt;text&gt;"
+   prefix. &lt;type&gt; is IDENTITY, PASSWORD, or OTP
+   (one-time-password). &lt;id&gt; is a unique identifier for the
+   current network. &lt;text&gt; is description of the request. In
+   case of OTP request, it includes the challenge from the
+   authentication server.</para>
+
+    <para>The reply to these requests can be given with
+    <emphasis>identity</emphasis>, <emphasis>password</emphasis>, and
+    <emphasis>otp</emphasis> commands. &lt;id&gt; needs to be copied from
+    the matching request. <emphasis>password</emphasis> and
+    <emphasis>otp</emphasis> commands can be used regardless of whether
+    the request was for PASSWORD or OTP. The main difference between these
+    two commands is that values given with <emphasis>password</emphasis> are
+    remembered as long as wpa_supplicant is running whereas values given
+    with <emphasis>otp</emphasis> are used only once and then forgotten,
+    i.e., wpa_supplicant will ask frontend for a new value for every use.
+    This can be used to implement one-time-password lists and generic token
+    card -based authentication.</para>
+
+    <para>Example request for password and a matching reply:</para>
+
+<blockquote><programlisting>
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+</programlisting></blockquote>
+
+    <para>Example request for generic token card challenge-response:</para>
+
+<blockquote><programlisting>
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+</programlisting></blockquote>
+
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+       <term>-p path</term>
+
+       <listitem><para>Change the path where control sockets should
+       be found.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-i ifname</term>
+
+        <listitem><para>Specify the interface that is being
+       configured.  By default, choose the first interface found with
+       a control socket in the socket path.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-h</term>
+       <listitem><para>Help.  Show a usage message.</para></listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+       <term>-v</term>
+       <listitem><para>Show version information.</para></listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+       <term>-B</term>
+       <listitem><para>Run as a daemon in the background.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-a file</term>
+
+       <listitem><para>Run in daemon mode executing the action file
+        based on events from wpa_supplicant.  The specified file will
+       be executed with the first argument set to interface name and
+       second to "CONNECTED" or "DISCONNECTED" depending on the event.
+       This can be used to execute networking tools required to configure
+       the interface.</para>
+
+       <para>Additionally, three environmental variables are available to
+       the file: WPA_CTRL_DIR, WPA_ID, and WPA_ID_STR. WPA_CTRL_DIR
+       contains the absolute path to the ctrl_interface socket. WPA_ID
+       contains the unique network_id identifier assigned to the active
+       network, and WPA_ID_STR contains the content of the id_str option.
+       </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-P file</term>
+
+       <listitem><para>Set the location of the PID
+       file.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>command</term>
+
+       <listitem><para>Run a command.  The available commands are
+       listed in the next section.</para></listitem>
+
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>Commands</title>
+    <para>The following commands are available:</para>
+
+    <variablelist>
+      <varlistentry>
+       <term>status</term>
+       <listitem>
+         <para>get current WPA/EAPOL/EAP status</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>mib</term>
+       <listitem>
+         <para>get MIB variables (dot1x, dot11)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>help</term>
+       <listitem>
+         <para>show this usage help</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>interface [ifname]</term>
+       <listitem>
+         <para>show interfaces/select interface</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>level &lt;debug level&gt;</term>
+       <listitem>
+         <para>change debug level</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>license</term>
+       <listitem>
+         <para>show full wpa_cli license</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>logoff</term>
+       <listitem>
+         <para>IEEE 802.1X EAPOL state machine logoff</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>logon</term>
+       <listitem>
+         <para>IEEE 802.1X EAPOL state machine logon</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>set</term>
+       <listitem>
+         <para>set variables (shows list of variables when run without arguments)</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term>pmksa</term>
+       <listitem>
+         <para>show PMKSA cache</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term>reassociate</term>
+       <listitem>
+         <para>force reassociation</para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term>reconfigure</term>
+       <listitem>
+         <para>force wpa_supplicant to re-read its configuration file</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>preauthenticate &lt;BSSID&gt;</term>
+       <listitem>
+         <para>force preauthentication</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>identity &lt;network id&gt; &lt;identity&gt;</term>
+       <listitem>
+         <para>configure identity for an SSID</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>password &lt;network id&gt; &lt;password&gt;</term>
+       <listitem>
+         <para>configure password for an SSID</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>pin &lt;network id&gt; &lt;pin&gt;</term>
+       <listitem>
+         <para>configure pin for an SSID</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>otp &lt;network id&gt; &lt;password&gt;</term>
+       <listitem>
+         <para>configure one-time-password for an SSID</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>bssid &lt;network id&gt; &lt;BSSID&gt;</term>
+       <listitem>
+         <para>set preferred BSSID for an SSID</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>list_networks</term>
+       <listitem>
+         <para>list configured networks</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>terminate</term>
+       <listitem>
+         <para>terminate <command>wpa_supplicant</command></para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>quit</term>
+       <listitem><para>exit wpa_cli</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2007,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is dual-licensed under both the GPL version 2
+    and BSD license. Either license may be used at your option.</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_gui.sgml b/wpa_supplicant/doc/docbook/wpa_gui.sgml
new file mode 100644 (file)
index 0000000..41b5849
--- /dev/null
@@ -0,0 +1,85 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_gui</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_gui</refname>
+
+    <refpurpose>WPA Graphical User Interface</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_gui</command>
+      <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
+      <arg>-i <replaceable>ifname</replaceable></arg>
+      <arg>-t</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para>wpa_gui is a QT graphical frontend program for interacting
+    with wpa_supplicant. It is used to query current status, change
+    configuration and request interactive user input.</para>
+
+    <para>wpa_gui supports (almost) all of the interactive status and
+    configuration features of the command line client, wpa_cli. Refer
+    to the wpa_cli manpage for a comprehensive list of the
+    interactive mode features.</para>
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+       <term>-p path</term>
+
+       <listitem><para>Change the path where control sockets should
+       be found.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-i ifname</term>
+
+        <listitem><para>Specify the interface that is being
+       configured. By default, choose the first interface found with
+       a control socket in the socket path.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-t</term>
+
+        <listitem><para>Start program in the system tray only (if the window
+       manager supports it). By default the main status window is
+       shown.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_cli</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2007,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is dual-licensed under both the GPL version 2
+    and BSD license. Either license may be used at your option.</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_passphrase.sgml b/wpa_supplicant/doc/docbook/wpa_passphrase.sgml
new file mode 100644 (file)
index 0000000..402ea09
--- /dev/null
@@ -0,0 +1,73 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_passphrase</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_passphrase</refname>
+    <refpurpose>Generate a WPA PSK from an ASCII passphrase for a SSID</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_passphrase</command>
+      <arg><replaceable>ssid</replaceable></arg>
+      <arg><replaceable>passphrase</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title> 
+
+    <para><command>wpa_passphrase</command> pre-computes PSK entries for
+    network configuration blocks of a
+    <filename>wpa_supplicant.conf</filename> file. An ASCII passphrase
+    and SSID are used to generate a 256-bit PSK.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Options</title>
+    <variablelist>
+      <varlistentry>
+       <term>ssid</term>
+       <listitem>
+         <para>The SSID whose passphrase should be derived.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>passphrase</term>
+       <listitem>
+         <para>The passphrase to use. If not included on the command line,
+         passphrase will be read from standard input.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant.conf</refentrytitle>
+       <manvolnum>5</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2007,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is dual-licensed under both the GPL version 2
+    and BSD license. Either license may be used at your option.</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_priv.sgml b/wpa_supplicant/doc/docbook/wpa_priv.sgml
new file mode 100644 (file)
index 0000000..89b8a92
--- /dev/null
@@ -0,0 +1,148 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_priv</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_priv</refname>
+
+    <refpurpose>wpa_supplicant privilege separation helper</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_priv</command>
+      <arg>-c <replaceable>ctrl path</replaceable></arg>
+      <arg>-Bdd</arg>
+      <arg>-P <replaceable>pid file</replaceable></arg>
+      <arg>driver:ifname <replaceable>[driver:ifname ...]</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para><command>wpa_priv</command> is a privilege separation helper that
+    minimizes the size of <command>wpa_supplicant</command> code that needs
+    to be run with root privileges.</para>
+
+    <para>If enabled, privileged operations are done in the wpa_priv process
+    while leaving rest of the code (e.g., EAP authentication and WPA
+    handshakes) to operate in an unprivileged process (wpa_supplicant) that
+    can be run as non-root user. Privilege separation restricts the effects
+    of potential software errors by containing the majority of the code in an
+    unprivileged process to avoid the possibility of a full system
+    compromise.</para>
+
+    <para><command>wpa_priv</command> needs to be run with network admin
+    privileges (usually, root user). It opens a UNIX domain socket for each
+    interface that is included on the command line; any other interface will
+    be off limits for <command>wpa_supplicant</command> in this kind of
+    configuration. After this, <command>wpa_supplicant</command> can be run as
+    a non-root user (e.g., all standard users on a laptop or as a special
+    non-privileged user account created just for this purpose to limit access
+    to user files even further).</para>
+  </refsect1>
+  <refsect1>
+    <title>Example configuration</title>
+
+    <para>The following steps are an example of how to configure
+    <command>wpa_priv</command> to allow users in the
+    <emphasis>wpapriv</emphasis> group to communicate with
+    <command>wpa_supplicant</command> with privilege separation:</para>
+
+    <para>Create user group (e.g., wpapriv) and assign users that
+    should be able to use wpa_supplicant into that group.</para>
+
+    <para>Create /var/run/wpa_priv directory for UNIX domain sockets and
+    control user access by setting it accessible only for the wpapriv
+    group:</para>
+
+<blockquote><programlisting>
+mkdir /var/run/wpa_priv
+chown root:wpapriv /var/run/wpa_priv
+chmod 0750 /var/run/wpa_priv
+</programlisting></blockquote>
+
+    <para>Start <command>wpa_priv</command> as root (e.g., from system
+    startup scripts) with the enabled interfaces configured on the
+    command line:</para>
+
+<blockquote><programlisting>
+wpa_priv -B -c /var/run/wpa_priv -P /var/run/wpa_priv.pid wext:wlan0
+</programlisting></blockquote>
+
+    <para>Run <command>wpa_supplicant</command> as non-root with a user
+    that is in the wpapriv group:</para>
+
+<blockquote><programlisting>
+wpa_supplicant -i ath0 -c wpa_supplicant.conf
+</programlisting></blockquote>
+
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+       <term>-c ctrl path</term>
+
+       <listitem><para>Specify the path to wpa_priv control directory
+       (Default: /var/run/wpa_priv/).</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-B</term>
+       <listitem><para>Run as a daemon in the background.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-P file</term>
+
+       <listitem><para>Set the location of the PID
+       file.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>driver:ifname [driver:ifname ...]</term>
+
+       <listitem><para>The &lt;driver&gt; string dictates which of the
+       supported <command>wpa_supplicant</command> driver backends is to be
+       used. To get a list of supported driver types see wpa_supplicant help
+       (e.g, wpa_supplicant -h). The driver backend supported by most good
+       drivers is <emphasis>wext</emphasis>.</para>
+
+       <para>The &lt;ifname&gt; string specifies which network
+       interface is to be managed by <command>wpa_supplicant</command>
+       (e.g., wlan0 or ath0).</para>
+
+       <para><command>wpa_priv</command> does not use the network interface
+       before <command>wpa_supplicant</command> is started, so it is fine to
+       include network interfaces that are not available at the time wpa_priv
+       is started. wpa_priv can control multiple interfaces with one process,
+       but it is also possible to run multiple <command>wpa_priv</command>
+       processes at the same time, if desired.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2007,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is dual-licensed under both the GPL version 2
+    and BSD license. Either license may be used at your option.</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.conf.sgml
new file mode 100644 (file)
index 0000000..462039d
--- /dev/null
@@ -0,0 +1,239 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_supplicant.conf</refentrytitle>
+    <manvolnum>5</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_supplicant.conf</refname>
+    <refpurpose>configuration file for wpa_supplicant</refpurpose>
+  </refnamediv>
+  <refsect1>
+    <title>Overview</title>
+
+    <para><command>wpa_supplicant</command> is configured using a text
+    file that lists all accepted networks and security policies,
+    including pre-shared keys. See the example configuration file,
+    probably in <command>/usr/share/doc/wpa_supplicant/</command>, for
+    detailed information about the configuration format and supported
+    fields.</para>
+
+    <para>All file paths in this configuration file should use full
+    (absolute, not relative to working directory) path in order to allow
+    working directory to be changed. This can happen if wpa_supplicant is
+    run in the background.</para>
+
+    <para>Changes to configuration file can be reloaded be sending
+    SIGHUP signal to <command>wpa_supplicant</command> ('killall -HUP
+    wpa_supplicant'). Similarly, reloading can be triggered with
+    the <emphasis>wpa_cli reconfigure</emphasis> command.</para>
+
+    <para>Configuration file can include one or more network blocks,
+    e.g., one for each used SSID. wpa_supplicant will automatically
+    select the best network based on the order of network blocks in
+    the configuration file, network security level (WPA/WPA2 is
+    preferred), and signal strength.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Quick Examples</title>
+
+    <orderedlist>
+      <listitem>
+
+      <para>WPA-Personal (PSK) as home network and WPA-Enterprise with
+      EAP-TLS as work network.</para>
+
+<blockquote><programlisting>
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+#
+# home network; allow all valid ciphers
+network={
+       ssid="home"
+       scan_ssid=1
+       key_mgmt=WPA-PSK
+       psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+       ssid="work"
+       scan_ssid=1
+       key_mgmt=WPA-EAP
+       pairwise=CCMP TKIP
+       group=CCMP TKIP
+       eap=TLS
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+}
+</programlisting></blockquote>   
+      </listitem>
+
+      <listitem>
+       <para>WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that
+        use old peaplabel (e.g., Funk Odyssey and SBR, Meetinghouse
+        Aegis, Interlink RAD-Series)</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP
+       eap=PEAP
+       identity="user@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       phase1="peaplabel=0"
+       phase2="auth=MSCHAPV2"
+}
+</programlisting></blockquote>
+      </listitem>
+
+      <listitem>
+       <para>EAP-TTLS/EAP-MD5-Challenge configuration with anonymous
+        identity for the unencrypted use. Real identity is sent only
+        within an encrypted TLS tunnel.</para>
+
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="user@example.com"
+       anonymous_identity="anonymous@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       phase2="auth=MD5"
+}
+</programlisting></blockquote>
+
+      </listitem>
+
+      <listitem>
+       <para>IEEE 802.1X (i.e., no WPA) with dynamic WEP keys
+        (require both unicast and broadcast); use EAP-TLS for
+        authentication</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+       ssid="1x-test"
+       scan_ssid=1
+       key_mgmt=IEEE8021X
+       eap=TLS
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       eapol_flags=3
+}
+</programlisting></blockquote>
+      </listitem>
+
+
+      <listitem>
+       <para>Catch all example that allows more or less all
+        configuration modes. The configuration options are used based
+        on what security policy is used in the selected SSID. This is
+        mostly for testing and is not recommended for normal
+        use.</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+       pairwise=CCMP TKIP
+       group=CCMP TKIP WEP104 WEP40
+       psk="very secret passphrase"
+       eap=TTLS PEAP TLS
+       identity="user@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       phase1="peaplabel=0"
+       ca_cert2="/etc/cert/ca2.pem"
+       client_cert2="/etc/cer/user.pem"
+       private_key2="/etc/cer/user.prv"
+       private_key2_passwd="password"
+}
+</programlisting></blockquote>
+      </listitem>
+
+      <listitem>
+       <para>Authentication for wired Ethernet. This can be used with
+        <emphasis>wired</emphasis> or <emphasis>roboswitch</emphasis> interface
+        (-Dwired or -Droboswitch on command line).</para>
+
+<blockquote><programlisting>
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=wheel
+ap_scan=0
+network={
+       key_mgmt=IEEE8021X
+       eap=MD5
+       identity="user"
+       password="password"
+       eapol_flags=0
+}
+</programlisting></blockquote>
+      </listitem>
+    </orderedlist>
+
+
+
+
+
+  </refsect1>
+  <refsect1>
+    <title>Certificates</title>
+
+    <para>Some EAP authentication methods require use of
+    certificates. EAP-TLS uses both server side and client
+    certificates whereas EAP-PEAP and EAP-TTLS only require the server
+    side certificate. When client certificate is used, a matching
+    private key file has to also be included in configuration. If the
+    private key uses a passphrase, this has to be configured in
+    wpa_supplicant.conf ("private_key_passwd").</para>
+
+    <para>wpa_supplicant supports X.509 certificates in PEM and DER
+    formats. User certificate and private key can be included in the
+    same file.</para>
+
+    <para>If the user certificate and private key is received in
+    PKCS#12/PFX format, they need to be converted to suitable PEM/DER
+    format for wpa_supplicant. This can be done, e.g., with following
+    commands:</para>
+<blockquote><programlisting>
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+</programlisting></blockquote>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>openssl</refentrytitle>
+       <manvolnum>1</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/doc/docbook/wpa_supplicant.sgml b/wpa_supplicant/doc/docbook/wpa_supplicant.sgml
new file mode 100644 (file)
index 0000000..3aae51b
--- /dev/null
@@ -0,0 +1,827 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>wpa_supplicant</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>wpa_supplicant</refname>
+    <refpurpose>Wi-Fi Protected Access client and IEEE 802.1X supplicant</refpurpose>
+  </refnamediv>
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>wpa_supplicant</command>
+      <arg>-BddfhKLqqtuvW</arg>
+      <arg>-i<replaceable>ifname</replaceable></arg>
+      <arg>-c<replaceable>config file</replaceable></arg>
+      <arg>-D<replaceable>driver</replaceable></arg>
+      <arg>-P<replaceable>PID_file</replaceable></arg>
+      <arg>-f<replaceable>output file</replaceable></arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+  <refsect1>
+    <title>Overview</title>
+
+    <para>
+    Wireless networks do not require physical access to the network equipment
+    in the same way as wired networks. This makes it easier for unauthorized
+    users to passively monitor a network and capture all transmitted frames.
+    In addition, unauthorized use of the network is much easier. In many cases,
+    this can happen even without user's explicit knowledge since the wireless
+    LAN adapter may have been configured to automatically join any available
+    network.
+    </para>
+
+    <para>
+    Link-layer encryption can be used to provide a layer of security for
+    wireless networks. The original wireless LAN standard, IEEE 802.11,
+    included a simple encryption mechanism, WEP. However, that proved to
+    be flawed in many areas and network protected with WEP cannot be consider
+    secure. IEEE 802.1X authentication and frequently changed dynamic WEP keys
+    can be used to improve the network security, but even that has inherited
+    security issues due to the use of WEP for encryption. Wi-Fi Protected
+    Access and IEEE 802.11i amendment to the wireless LAN standard introduce
+    a much improvement mechanism for securing wireless networks. IEEE 802.11i
+    enabled networks that are using CCMP (encryption mechanism based on strong
+    cryptographic algorithm AES) can finally be called secure used for
+    applications which require efficient protection against unauthorized
+    access.
+    </para>
+
+    <para><command>wpa_supplicant</command> is an implementation of
+    the WPA Supplicant component, i.e., the part that runs in the
+    client stations. It implements WPA key negotiation with a WPA
+    Authenticator and EAP authentication with Authentication
+    Server. In addition, it controls the roaming and IEEE 802.11
+    authentication/association of the wireless LAN driver.</para>
+
+    <para><command>wpa_supplicant</command> is designed to be a
+    "daemon" program that runs in the background and acts as the
+    backend component controlling the wireless
+    connection. <command>wpa_supplicant</command> supports separate
+    frontend programs and an example text-based frontend,
+    <command>wpa_cli</command>, is included with
+    wpa_supplicant.</para>
+
+    <para>Before wpa_supplicant can do its work, the network interface
+    must be available.  That means that the physical device must be
+    present and enabled, and the driver for the device must be
+    loaded. The daemon will exit immediately if the device is not already
+    available.</para>
+
+    <para>After <command>wpa_supplicant</command> has configured the
+    network device, higher level configuration such as DHCP may
+    proceed.  There are a variety of ways to integrate wpa_supplicant
+    into a machine's networking scripts, a few of which are described
+    in sections below.</para>
+
+    <para>The following steps are used when associating with an AP
+    using WPA:</para>
+
+    <itemizedlist>
+      <listitem>
+       <para><command>wpa_supplicant</command> requests the kernel
+       driver to scan neighboring BSSes</para>
+      </listitem>
+
+      <listitem>
+       <para><command>wpa_supplicant</command> selects a BSS based on
+       its configuration</para>
+      </listitem>
+
+      <listitem>
+       <para><command>wpa_supplicant</command> requests the kernel
+        driver to associate with the chosen BSS</para>
+      </listitem>
+
+      <listitem>
+       <para>If WPA-EAP: integrated IEEE 802.1X Supplicant
+        completes EAP authentication with the
+        authentication server (proxied by the Authenticator in the
+        AP)</para>
+      </listitem>
+
+      <listitem>
+       <para>If WPA-EAP: master key is received from the IEEE 802.1X
+       Supplicant</para>
+      </listitem>
+
+      <listitem>
+       <para>If WPA-PSK: <command>wpa_supplicant</command> uses PSK
+       as the master session key</para>
+      </listitem>
+
+      <listitem>
+       <para><command>wpa_supplicant</command> completes WPA 4-Way
+        Handshake and Group Key Handshake with the Authenticator
+        (AP)</para>
+      </listitem>
+
+      <listitem>
+       <para><command>wpa_supplicant</command> configures encryption
+       keys for unicast and broadcast</para>
+      </listitem>
+
+      <listitem>
+       <para>normal data packets can be transmitted and received</para>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Supported Features</title>
+    <para>Supported WPA/IEEE 802.11i features:</para>
+    <itemizedlist>
+      <listitem>
+       <para>WPA-PSK ("WPA-Personal")</para>
+      </listitem>
+
+      <listitem>
+       <para>WPA with EAP (e.g., with RADIUS authentication server)
+       ("WPA-Enterprise") Following authentication methods are
+       supported with an integrate IEEE 802.1X Supplicant:</para>
+
+       <itemizedlist>
+         <listitem>
+           <para>EAP-TLS</para>
+         </listitem>
+       </itemizedlist>
+
+       <itemizedlist>
+         <listitem>
+           <para>EAP-PEAP/MSCHAPv2 (both PEAPv0 and PEAPv1)</para>
+         </listitem>
+
+
+         <listitem>
+           <para>EAP-PEAP/TLS (both PEAPv0 and PEAPv1)</para>
+         </listitem>
+
+         <listitem>
+           <para>EAP-PEAP/GTC (both PEAPv0 and PEAPv1)</para>
+         </listitem>
+
+         <listitem>
+           <para>EAP-PEAP/OTP (both PEAPv0 and PEAPv1)</para>
+         </listitem>
+
+         <listitem>
+           <para>EAP-PEAP/MD5-Challenge (both PEAPv0 and PEAPv1)</para>
+         </listitem>
+
+         <listitem>
+           <para>EAP-TTLS/EAP-MD5-Challenge</para>
+         </listitem>
+
+         <listitem>
+           <para>EAP-TTLS/EAP-GTC</para>
+         </listitem>
+
+          <listitem><para>EAP-TTLS/EAP-OTP</para></listitem>
+
+          <listitem><para>EAP-TTLS/EAP-MSCHAPv2</para></listitem>
+
+          <listitem><para>EAP-TTLS/EAP-TLS</para></listitem>
+
+          <listitem><para>EAP-TTLS/MSCHAPv2</para></listitem>
+
+          <listitem><para>EAP-TTLS/MSCHAP</para></listitem>
+
+          <listitem><para>EAP-TTLS/PAP</para></listitem>
+
+          <listitem><para>EAP-TTLS/CHAP</para></listitem>
+
+          <listitem><para>EAP-SIM</para></listitem>
+
+          <listitem><para>EAP-AKA</para></listitem>
+
+          <listitem><para>EAP-PSK</para></listitem>
+
+          <listitem><para>EAP-PAX</para></listitem>
+
+          <listitem><para>LEAP (note: requires special support from
+          the driver for IEEE 802.11 authentication)</para></listitem>
+
+          <listitem><para>(following methods are supported, but since
+          they do not generate keying material, they cannot be used
+          with WPA or IEEE 802.1X WEP keying)</para></listitem>
+
+          <listitem><para>EAP-MD5-Challenge </para></listitem>
+
+          <listitem><para>EAP-MSCHAPv2</para></listitem>
+
+          <listitem><para>EAP-GTC</para></listitem>
+
+          <listitem><para>EAP-OTP</para></listitem>
+       </itemizedlist>
+      </listitem>
+
+      <listitem>
+       <para>key management for CCMP, TKIP, WEP104, WEP40</para>
+      </listitem>
+
+      <listitem>
+       <para>RSN/WPA2 (IEEE 802.11i)</para>
+       <itemizedlist>
+         <listitem>
+           <para>pre-authentication</para>
+         </listitem>
+
+         <listitem>
+           <para>PMKSA caching</para>
+         </listitem>
+       </itemizedlist>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Available Drivers</title>
+    <para>A summary of available driver backends is below. Support for each
+    of the driver backends is chosen at wpa_supplicant compile time. For a
+    list of supported driver backends that may be used with the -D option on
+    your system, refer to the help output of wpa_supplicant
+    (<emphasis>wpa_supplicant -h</emphasis>).</para>
+
+    <variablelist>
+      <varlistentry>
+       <term>hostap</term>
+       <listitem>
+         <para>(default) Host AP driver (Intersil Prism2/2.5/3).
+         (this can also be used with Linuxant DriverLoader).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>hermes</term>
+       <listitem>
+         <para>Agere Systems Inc. driver (Hermes-I/Hermes-II).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>madwifi</term>
+       <listitem>
+         <para>MADWIFI 802.11 support (Atheros, etc.).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>atmel</term>
+       <listitem>
+         <para>ATMEL AT76C5XXx (USB, PCMCIA).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>wext</term>
+       <listitem>
+         <para>Linux wireless extensions (generic).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>ndiswrapper</term>
+       <listitem>
+         <para>Linux ndiswrapper.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>broadcom</term>
+       <listitem>
+         <para>Broadcom wl.o driver.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>ipw</term>
+       <listitem>
+         <para>Intel ipw2100/2200 driver.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>wired</term>
+       <listitem>
+         <para>wpa_supplicant wired Ethernet driver</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>roboswitch</term>
+       <listitem>
+         <para>wpa_supplicant Broadcom switch driver</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>bsd</term>
+       <listitem>
+         <para>BSD 802.11 support (Atheros, etc.).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>ndis</term>
+       <listitem>
+         <para>Windows NDIS driver.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Command Line Options</title>
+    <para>Most command line options have global scope. Some are given per
+    interface, and are only valid if at least one <option>-i</option> option
+    is specified, otherwise they're ignored. Option groups for different
+    interfaces must be separated by <option>-N</option> option.</para>
+    <variablelist>
+      <varlistentry>
+       <term>-b br_ifname</term>
+       <listitem>
+         <para>Optional bridge interface name. (Per interface)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-B</term>
+       <listitem>
+         <para>Run daemon in the background.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-c filename</term>
+       <listitem>
+         <para>Path to configuration file. (Per interface)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-C ctrl_interface</term>
+       <listitem>
+         <para>Path to ctrl_interface socket (Per interface. Only used if
+                 <option>-c</option> is not).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-i ifname</term>
+       <listitem>
+         <para>Interface to listen on. Multiple instances of this option can
+         be present, one per interface, separated by <option>-N</option>
+         option (see below).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-d</term>
+       <listitem>
+         <para>Increase debugging verbosity (<option>-dd</option> even
+                 more).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-D driver</term>
+       <listitem>
+         <para>Driver to use (can be multiple drivers: nl80211,wext).
+                 (Per interface, see the available options below.)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-f output file</term>
+       <listitem>
+         <para>Log output to specified file instead of stdout.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-g global ctrl_interface</term>
+       <listitem>
+         <para>Path to global ctrl_interface socket. If specified, interface
+         definitions may be omitted.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-K</term>
+       <listitem>
+         <para>Include keys (passwords, etc.) in debug output.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-t</term>
+       <listitem>
+         <para>Include timestamp in debug messages.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-h</term>
+       <listitem>
+         <para>Help.  Show a usage message.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-L</term>
+       <listitem>
+         <para>Show license (GPL and BSD).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-p</term>
+       <listitem>
+         <para>Driver parameters. (Per interface)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-P PID_file</term>
+       <listitem>
+         <para>Path to PID file.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-q</term>
+       <listitem>
+         <para>Decrease debugging verbosity (<option>-qq</option> even
+                 less).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-u</term>
+       <listitem>
+         <para>Enabled DBus control interface. If enabled, interface
+         definitions may be omitted.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-v</term>
+       <listitem>
+         <para>Show version.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-W</term>
+       <listitem>
+         <para>Wait for a control interface monitor before starting.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-N</term>
+       <listitem>
+         <para>Start describing new interface.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Examples</title>
+
+    <para>In most common cases, <command>wpa_supplicant</command> is
+    started with:</para>
+
+<blockquote><programlisting>
+wpa_supplicant -B -c/etc/wpa_supplicant.conf -iwlan0
+</programlisting></blockquote>
+
+    <para>This makes the process fork into background.</para>
+
+    <para>The easiest way to debug problems, and to get debug log for
+    bug reports, is to start <command>wpa_supplicant</command> on
+    foreground with debugging enabled:</para>
+
+<blockquote><programlisting>
+wpa_supplicant -c/etc/wpa_supplicant.conf -iwlan0 -d
+</programlisting></blockquote>
+
+    <para>If the specific driver wrapper is not known beforehand, it is
+    possible to specify multiple comma separated driver wrappers on the command
+    line. <command>wpa_supplicant</command> will use the first driver
+    wrapper that is able to initialize the interface.</para>
+
+<blockquote><programlisting>
+wpa_supplicant -Dnl80211,wext -c/etc/wpa_supplicant.conf -iwlan0
+</programlisting></blockquote>
+
+    <para><command>wpa_supplicant</command> can control multiple
+    interfaces (radios) either by running one process for each
+    interface separately or by running just one process and list of
+    options at command line. Each interface is separated with -N
+    argument. As an example, following command would start
+    wpa_supplicant for two interfaces:</para>
+
+<blockquote><programlisting>
+wpa_supplicant \
+       -c wpa1.conf -i wlan0 -D hostap -N \
+       -c wpa2.conf -i ath0 -D madwifi
+</programlisting></blockquote>
+  </refsect1>
+
+  <refsect1>
+    <title>OS Requirements</title>
+    <para>Current hardware/software requirements:</para>
+
+    <itemizedlist>
+      <listitem>
+       <para>Linux kernel 2.4.x or 2.6.x with Linux Wireless
+       Extensions v15 or newer</para>
+      </listitem>
+
+
+      <listitem>
+       <para>FreeBSD 6-CURRENT</para>
+      </listitem>
+
+      <listitem>
+       <para>Microsoft Windows with WinPcap (at least WinXP, may work
+       with other versions)</para>
+      </listitem>
+    </itemizedlist>
+  </refsect1>
+
+  <refsect1>
+    <title>Supported Drivers</title>
+    <variablelist>
+      <varlistentry>
+       <term>Host AP driver for Prism2/2.5/3 (development
+       snapshot/v0.2.x)</term>
+       <listitem>
+         <para> (http://hostap.epitest.fi/) Driver needs to be set in
+         Managed mode (<emphasis>iwconfig wlan0 mode managed</emphasis>).
+         Please note that station firmware version needs to be 1.7.0 or
+         newer to work in WPA mode.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Linuxant DriverLoader</term>
+       <listitem>
+         <para>(http://www.linuxant.com/driverloader/)
+       with Windows NDIS driver for your wlan card supporting WPA.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Agere Systems Inc. Linux Driver</term>
+       <listitem>
+         <para> (http://www.agere.com/support/drivers/) Please note
+       that the driver interface file (driver_hermes.c) and hardware
+       specific include files are not included in the wpa_supplicant
+       distribution. You will need to copy these from the source
+       package of the Agere driver.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>madwifi driver for cards based on Atheros chip set (ar521x)</term>
+       <listitem>
+         <para> (http://sourceforge.net/projects/madwifi/) Please
+       note that you will need to modify the wpa_supplicant .config
+       file to use the correct path for the madwifi driver root
+       directory (CFLAGS += -I../madwifi/wpa line in example
+       defconfig).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>ATMEL AT76C5XXx driver for USB and PCMCIA cards</term>
+       <listitem>
+         <para> (http://atmelwlandriver.sourceforge.net/).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Linux ndiswrapper</term>
+       <listitem>
+         <para> (http://ndiswrapper.sourceforge.net/) with Windows
+       NDIS driver.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Broadcom wl.o driver</term>
+       <listitem>
+         <para> This is a generic Linux driver for Broadcom IEEE
+       802.11a/g cards.  However, it is proprietary driver that is
+       not publicly available except for couple of exceptions, mainly
+       Broadcom-based APs/wireless routers that use Linux. The driver
+       binary can be downloaded, e.g., from Linksys support site
+       (http://www.linksys.com/support/gpl.asp) for Linksys
+       WRT54G. The GPL tarball includes cross-compiler and the needed
+       header file, wlioctl.h, for compiling wpa_supplicant.  This
+       driver support in wpa_supplicant is expected to work also with
+       other devices based on Broadcom driver (assuming the driver
+       includes client mode support).</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term> Intel ipw2100 driver</term>
+       <listitem>
+         <para> (http://sourceforge.net/projects/ipw2100/)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Intel ipw2200 driver</term>
+       <listitem>
+         <para> (http://sourceforge.net/projects/ipw2200/)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Linux wireless extensions</term>
+       <listitem>
+         <para>In theory, any driver that supports Linux wireless
+       extensions can be used with IEEE 802.1X (i.e., not WPA) when
+       using ap_scan=0 option in configuration file.</para>
+       </listitem>
+      </varlistentry>
+      
+      <varlistentry>
+       <term>Wired Ethernet drivers</term>
+       <listitem>
+         <para>Use ap_scan=0.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>BSD net80211 layer (e.g., Atheros driver)</term>
+       <listitem>
+         <para>At the moment, this is for FreeBSD 6-CURRENT branch.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>Windows NDIS</term>
+       <listitem>
+         <para>The current Windows port requires WinPcap
+       (http://winpcap.polito.it/).  See README-Windows.txt for more
+       information.</para>
+       </listitem>
+      </varlistentry>
+    </variablelist>
+
+       
+    <para>wpa_supplicant was designed to be portable for different
+    drivers and operating systems. Hopefully, support for more wlan
+    cards and OSes will be added in the future. See developer.txt for
+    more information about the design of wpa_supplicant and porting to
+    other drivers. One main goal is to add full WPA/WPA2 support to
+    Linux wireless extensions to allow new drivers to be supported
+    without having to implement new driver-specific interface code in
+    wpa_supplicant.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>Architecture</title> <para>The
+    <command>wpa_supplicant</command> system consists of the following
+    components:</para>
+
+    <variablelist>
+      <varlistentry>
+       <term><filename>wpa_supplicant.conf</filename> </term>
+       <listitem>
+        <para>the configuration file describing all networks that the
+        user wants the computer to connect to.  </para>
+       </listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><command>wpa_supplicant</command></term>
+        <listitem><para>the program that directly interacts with the
+        network interface.  </para></listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><command>wpa_cli</command></term> <listitem><para> the
+       client program that provides a high-level interface to the
+       functionality of the daemon.  </para></listitem>
+      </varlistentry>
+      <varlistentry>
+       <term><command>wpa_passphrase</command></term>
+        <listitem><para>a utility needed to construct
+        <filename>wpa_supplicant.conf</filename> files that include
+        encrypted passwords.</para></listitem>
+      </varlistentry>
+    </variablelist>
+  </refsect1>
+
+  <refsect1>
+    <title>Quick Start</title>
+
+    <para>First, make a configuration file, e.g.
+    <filename>/etc/wpa_supplicant.conf</filename>, that describes the networks
+    you are interested in.  See <citerefentry>
+       <refentrytitle>wpa_supplicant.conf</refentrytitle>
+       <manvolnum>5</manvolnum>
+      </citerefentry>
+    for details.</para>
+
+    <para>Once the configuration is ready, you can test whether the
+    configuration works by running <command>wpa_supplicant</command>
+    with following command to start it on foreground with debugging
+    enabled:</para>
+
+    <blockquote><programlisting>
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -d
+    </programlisting></blockquote>
+
+    <para>Assuming everything goes fine, you can start using following
+    command to start <command>wpa_supplicant</command> on background
+    without debugging:</para>
+
+    <blockquote><programlisting>
+wpa_supplicant -iwlan0 -c/etc/wpa_supplicant.conf -B
+    </programlisting></blockquote>
+
+    <para>Please note that if you included more than one driver
+    interface in the build time configuration (.config), you may need
+    to specify which interface to use by including -D&lt;driver
+    name&gt; option on the command line.</para>
+
+    <!-- XXX at this point, the page could include a little script
+         based on wpa_cli to wait for a connection and then run
+         dhclient -->
+
+  </refsect1>
+
+  <refsect1>
+    <title>Interface to pcmcia-cs/cardmrg</title>
+
+    <para>For example, following small changes to pcmcia-cs scripts
+    can be used to enable WPA support:</para>
+
+    <para>Add MODE="Managed" and WPA="y" to the network scheme in
+    <filename>/etc/pcmcia/wireless.opts</filename>.</para>
+
+    <para>Add the following block to the end of <emphasis>start</emphasis>
+    action handler in <filename>/etc/pcmcia/wireless</filename>:</para>
+
+    <blockquote><programlisting>
+if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+    /usr/local/bin/wpa_supplicant -B -c/etc/wpa_supplicant.conf -i$DEVICE
+fi
+    </programlisting></blockquote>
+
+
+    <para>Add the following block to the end of <emphasis>stop</emphasis>
+    action handler (may need to be separated from other actions) in
+    <filename>/etc/pcmcia/wireless</filename>:</para>
+
+    <blockquote><programlisting>
+if [ "$WPA" = "y" -a -x /usr/local/bin/wpa_supplicant ]; then
+    killall wpa_supplicant
+fi
+    </programlisting></blockquote>
+
+    <para>This will make <command>cardmgr</command> start
+    <command>wpa_supplicant</command> when the card is plugged
+    in.</para>
+  </refsect1>
+
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_background</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant.conf</refentrytitle>
+       <manvolnum>5</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>wpa_cli</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+      <citerefentry>
+       <refentrytitle>wpa_passphrase</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2007,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is dual-licensed under both the GPL version 2
+    and BSD license. Either license may be used at your option.</para>
+  </refsect1>
+</refentry>
diff --git a/wpa_supplicant/driver_i.h b/wpa_supplicant/driver_i.h
new file mode 100644 (file)
index 0000000..a70aa6a
--- /dev/null
@@ -0,0 +1,494 @@
+/*
+ * wpa_supplicant - Internal driver interface wrappers
+ * Copyright (c) 2003-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.
+ */
+
+#ifndef DRIVER_I_H
+#define DRIVER_I_H
+
+#include "drivers/driver.h"
+
+/* driver_ops */
+static inline void * wpa_drv_init(struct wpa_supplicant *wpa_s,
+                                 const char *ifname)
+{
+       if (wpa_s->driver->init2)
+               return wpa_s->driver->init2(wpa_s, ifname,
+                                           wpa_s->global_drv_priv);
+       if (wpa_s->driver->init) {
+               return wpa_s->driver->init(wpa_s, ifname);
+       }
+       return NULL;
+}
+
+static inline void wpa_drv_deinit(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->deinit)
+               wpa_s->driver->deinit(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_set_param(struct wpa_supplicant *wpa_s,
+                                   const char *param)
+{
+       if (wpa_s->driver->set_param)
+               return wpa_s->driver->set_param(wpa_s->drv_priv, param);
+       return 0;
+}
+
+static inline int wpa_drv_set_countermeasures(struct wpa_supplicant *wpa_s,
+                                             int enabled)
+{
+       if (wpa_s->driver->set_countermeasures) {
+               return wpa_s->driver->set_countermeasures(wpa_s->drv_priv,
+                                                         enabled);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_authenticate(struct wpa_supplicant *wpa_s,
+                                      struct wpa_driver_auth_params *params)
+{
+       if (wpa_s->driver->authenticate)
+               return wpa_s->driver->authenticate(wpa_s->drv_priv, params);
+       return -1;
+}
+
+static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
+                                   struct wpa_driver_associate_params *params)
+{
+       if (wpa_s->driver->associate) {
+               return wpa_s->driver->associate(wpa_s->drv_priv, params);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
+                              struct wpa_driver_scan_params *params)
+{
+       if (wpa_s->driver->scan2)
+               return wpa_s->driver->scan2(wpa_s->drv_priv, params);
+       return -1;
+}
+
+static inline struct wpa_scan_results * wpa_drv_get_scan_results2(
+       struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->get_scan_results2)
+               return wpa_s->driver->get_scan_results2(wpa_s->drv_priv);
+       return NULL;
+}
+
+static inline int wpa_drv_get_bssid(struct wpa_supplicant *wpa_s, u8 *bssid)
+{
+       if (wpa_s->driver->get_bssid) {
+               return wpa_s->driver->get_bssid(wpa_s->drv_priv, bssid);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid)
+{
+       if (wpa_s->driver->get_ssid) {
+               return wpa_s->driver->get_ssid(wpa_s->drv_priv, ssid);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
+                                 enum wpa_alg alg, const u8 *addr,
+                                 int key_idx, int set_tx,
+                                  const u8 *seq, size_t seq_len,
+                                  const u8 *key, size_t key_len)
+{
+       if (wpa_s->driver->set_key) {
+               wpa_s->keys_cleared = 0;
+               return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
+                                             alg, addr, key_idx, set_tx,
+                                             seq, seq_len, key, key_len);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
+                                        const u8 *addr, int reason_code)
+{
+       if (wpa_s->driver->deauthenticate) {
+               return wpa_s->driver->deauthenticate(wpa_s->drv_priv, addr,
+                                                    reason_code);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s,
+                                      const u8 *addr, int reason_code)
+{
+       if (wpa_s->driver->disassociate) {
+               return wpa_s->driver->disassociate(wpa_s->drv_priv, addr,
+                                                  reason_code);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
+                                   const u8 *bssid, const u8 *pmkid)
+{
+       if (wpa_s->driver->add_pmkid) {
+               return wpa_s->driver->add_pmkid(wpa_s->drv_priv, bssid, pmkid);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_remove_pmkid(struct wpa_supplicant *wpa_s,
+                                      const u8 *bssid, const u8 *pmkid)
+{
+       if (wpa_s->driver->remove_pmkid) {
+               return wpa_s->driver->remove_pmkid(wpa_s->drv_priv, bssid,
+                                                  pmkid);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_flush_pmkid(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->flush_pmkid) {
+               return wpa_s->driver->flush_pmkid(wpa_s->drv_priv);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_get_capa(struct wpa_supplicant *wpa_s,
+                                  struct wpa_driver_capa *capa)
+{
+       if (wpa_s->driver->get_capa) {
+               return wpa_s->driver->get_capa(wpa_s->drv_priv, capa);
+       }
+       return -1;
+}
+
+static inline void wpa_drv_poll(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->poll) {
+               wpa_s->driver->poll(wpa_s->drv_priv);
+       }
+}
+
+static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->get_ifname) {
+               return wpa_s->driver->get_ifname(wpa_s->drv_priv);
+       }
+       return NULL;
+}
+
+static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->get_mac_addr) {
+               return wpa_s->driver->get_mac_addr(wpa_s->drv_priv);
+       }
+       return NULL;
+}
+
+static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s,
+                                    const u8 *dst, u16 proto,
+                                    const u8 *data, size_t data_len)
+{
+       if (wpa_s->driver->send_eapol)
+               return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto,
+                                                data, data_len);
+       return -1;
+}
+
+static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s,
+                                       int state)
+{
+       if (wpa_s->driver->set_operstate)
+               return wpa_s->driver->set_operstate(wpa_s->drv_priv, state);
+       return 0;
+}
+
+static inline int wpa_drv_mlme_setprotection(struct wpa_supplicant *wpa_s,
+                                            const u8 *addr, int protect_type,
+                                            int key_type)
+{
+       if (wpa_s->driver->mlme_setprotection)
+               return wpa_s->driver->mlme_setprotection(wpa_s->drv_priv, addr,
+                                                        protect_type,
+                                                        key_type);
+       return 0;
+}
+
+static inline struct hostapd_hw_modes *
+wpa_drv_get_hw_feature_data(struct wpa_supplicant *wpa_s, u16 *num_modes,
+                           u16 *flags)
+{
+       if (wpa_s->driver->get_hw_feature_data)
+               return wpa_s->driver->get_hw_feature_data(wpa_s->drv_priv,
+                                                         num_modes, flags);
+       return NULL;
+}
+
+static inline int wpa_drv_set_channel(struct wpa_supplicant *wpa_s,
+                                     enum hostapd_hw_mode phymode, int chan,
+                                     int freq)
+{
+       if (wpa_s->driver->set_channel)
+               return wpa_s->driver->set_channel(wpa_s->drv_priv, phymode,
+                                                 chan, freq);
+       return -1;
+}
+
+static inline int wpa_drv_set_ssid(struct wpa_supplicant *wpa_s,
+                                  const u8 *ssid, size_t ssid_len)
+{
+       if (wpa_s->driver->set_ssid) {
+               return wpa_s->driver->set_ssid(wpa_s->drv_priv, ssid,
+                                              ssid_len);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_set_bssid(struct wpa_supplicant *wpa_s,
+                                   const u8 *bssid)
+{
+       if (wpa_s->driver->set_bssid) {
+               return wpa_s->driver->set_bssid(wpa_s->drv_priv, bssid);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_set_country(struct wpa_supplicant *wpa_s,
+                                     const char *alpha2)
+{
+       if (wpa_s->driver->set_country)
+               return wpa_s->driver->set_country(wpa_s->drv_priv, alpha2);
+       return 0;
+}
+
+static inline int wpa_drv_send_mlme(struct wpa_supplicant *wpa_s,
+                                   const u8 *data, size_t data_len)
+{
+       if (wpa_s->driver->send_mlme)
+               return wpa_s->driver->send_mlme(wpa_s->drv_priv,
+                                               data, data_len);
+       return -1;
+}
+
+static inline int wpa_drv_mlme_add_sta(struct wpa_supplicant *wpa_s,
+                                      const u8 *addr, const u8 *supp_rates,
+                                      size_t supp_rates_len)
+{
+       if (wpa_s->driver->mlme_add_sta)
+               return wpa_s->driver->mlme_add_sta(wpa_s->drv_priv, addr,
+                                                  supp_rates, supp_rates_len);
+       return -1;
+}
+
+static inline int wpa_drv_mlme_remove_sta(struct wpa_supplicant *wpa_s,
+                                         const u8 *addr)
+{
+       if (wpa_s->driver->mlme_remove_sta)
+               return wpa_s->driver->mlme_remove_sta(wpa_s->drv_priv, addr);
+       return -1;
+}
+
+static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s,
+                                       const u8 *md,
+                                       const u8 *ies, size_t ies_len)
+{
+       if (wpa_s->driver->update_ft_ies)
+               return wpa_s->driver->update_ft_ies(wpa_s->drv_priv, md,
+                                                   ies, ies_len);
+       return -1;
+}
+
+static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s,
+                                        u8 action, const u8 *target_ap,
+                                        const u8 *ies, size_t ies_len)
+{
+       if (wpa_s->driver->send_ft_action)
+               return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action,
+                                                    target_ap, ies, ies_len);
+       return -1;
+}
+
+static inline int wpa_drv_set_beacon(struct wpa_supplicant *wpa_s,
+                                    const u8 *head, size_t head_len,
+                                    const u8 *tail, size_t tail_len,
+                                    int dtim_period, int beacon_int)
+{
+       if (wpa_s->driver->set_beacon)
+               return wpa_s->driver->set_beacon(wpa_s->drv_priv, head,
+                                                head_len, tail, tail_len,
+                                                dtim_period, beacon_int);
+       return -1;
+}
+
+static inline int wpa_drv_sta_add(struct wpa_supplicant *wpa_s,
+                                 struct hostapd_sta_add_params *params)
+{
+       if (wpa_s->driver->sta_add)
+               return wpa_s->driver->sta_add(wpa_s->drv_priv, params);
+       return -1;
+}
+
+static inline int wpa_drv_sta_remove(struct wpa_supplicant *wpa_s,
+                                    const u8 *addr)
+{
+       if (wpa_s->driver->sta_remove)
+               return wpa_s->driver->sta_remove(wpa_s->drv_priv, addr);
+       return -1;
+}
+
+static inline int wpa_drv_hapd_send_eapol(struct wpa_supplicant *wpa_s,
+                                         const u8 *addr, const u8 *data,
+                                         size_t data_len, int encrypt,
+                                         const u8 *own_addr)
+{
+       if (wpa_s->driver->hapd_send_eapol)
+               return wpa_s->driver->hapd_send_eapol(wpa_s->drv_priv, addr,
+                                                     data, data_len, encrypt,
+                                                     own_addr);
+       return -1;
+}
+
+static inline int wpa_drv_sta_set_flags(struct wpa_supplicant *wpa_s,
+                                       const u8 *addr, int total_flags,
+                                       int flags_or, int flags_and)
+{
+       if (wpa_s->driver->sta_set_flags)
+               return wpa_s->driver->sta_set_flags(wpa_s->drv_priv, addr,
+                                                   total_flags, flags_or,
+                                                   flags_and);
+       return -1;
+}
+
+static inline int wpa_drv_set_supp_port(struct wpa_supplicant *wpa_s,
+                                       int authorized)
+{
+       if (wpa_s->driver->set_supp_port) {
+               return wpa_s->driver->set_supp_port(wpa_s->drv_priv,
+                                                   authorized);
+       }
+       return 0;
+}
+
+static inline int wpa_drv_send_action(struct wpa_supplicant *wpa_s,
+                                     unsigned int freq,
+                                     const u8 *dst, const u8 *src,
+                                     const u8 *bssid,
+                                     const u8 *data, size_t data_len)
+{
+       if (wpa_s->driver->send_action)
+               return wpa_s->driver->send_action(wpa_s->drv_priv, freq,
+                                                 dst, src, bssid, data,
+                                                 data_len);
+       return -1;
+}
+
+static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
+                                enum wpa_driver_if_type type,
+                                const char *ifname, const u8 *addr,
+                                void *bss_ctx, char *force_ifname,
+                                u8 *if_addr)
+{
+       if (wpa_s->driver->if_add)
+               return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
+                                            addr, bss_ctx, NULL, force_ifname,
+                                            if_addr);
+       return -1;
+}
+
+static inline int wpa_drv_if_remove(struct wpa_supplicant *wpa_s,
+                                   enum wpa_driver_if_type type,
+                                   const char *ifname)
+{
+       if (wpa_s->driver->if_remove)
+               return wpa_s->driver->if_remove(wpa_s->drv_priv, type, ifname);
+       return -1;
+}
+
+static inline int wpa_drv_remain_on_channel(struct wpa_supplicant *wpa_s,
+                                           unsigned int freq,
+                                           unsigned int duration)
+{
+       if (wpa_s->driver->remain_on_channel)
+               return wpa_s->driver->remain_on_channel(wpa_s->drv_priv, freq,
+                                                       duration);
+       return -1;
+}
+
+static inline int wpa_drv_cancel_remain_on_channel(
+       struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->cancel_remain_on_channel)
+               return wpa_s->driver->cancel_remain_on_channel(
+                       wpa_s->drv_priv);
+       return -1;
+}
+
+static inline int wpa_drv_probe_req_report(struct wpa_supplicant *wpa_s,
+                                          int report)
+{
+       if (wpa_s->driver->probe_req_report)
+               return wpa_s->driver->probe_req_report(wpa_s->drv_priv,
+                                                      report);
+       return -1;
+}
+
+static inline int wpa_drv_disable_11b_rates(struct wpa_supplicant *wpa_s,
+                                           int disabled)
+{
+       if (wpa_s->driver->disable_11b_rates)
+               return wpa_s->driver->disable_11b_rates(wpa_s->drv_priv,
+                                                       disabled);
+       return -1;
+}
+
+static inline int wpa_drv_deinit_ap(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->deinit_ap)
+               return wpa_s->driver->deinit_ap(wpa_s->drv_priv);
+       return 0;
+}
+
+static inline void wpa_drv_suspend(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->suspend)
+               wpa_s->driver->suspend(wpa_s->drv_priv);
+}
+
+static inline void wpa_drv_resume(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->resume)
+               wpa_s->driver->resume(wpa_s->drv_priv);
+}
+
+static inline int wpa_drv_signal_monitor(struct wpa_supplicant *wpa_s,
+                                        int threshold, int hysteresis)
+{
+       if (wpa_s->driver->signal_monitor)
+               return wpa_s->driver->signal_monitor(wpa_s->drv_priv,
+                                                    threshold, hysteresis);
+       return -1;
+}
+
+static inline int wpa_drv_set_ap_wps_ie(struct wpa_supplicant *wpa_s,
+                                       const struct wpabuf *beacon,
+                                       const struct wpabuf *proberesp)
+{
+       if (!wpa_s->driver->set_ap_wps_ie)
+               return -1;
+       return wpa_s->driver->set_ap_wps_ie(wpa_s->drv_priv, beacon,
+                                           proberesp);
+}
+
+#endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eap_register.c b/wpa_supplicant/eap_register.c
new file mode 100644 (file)
index 0000000..f668874
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * EAP method registration
+ * 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 "common.h"
+#include "eap_peer/eap_methods.h"
+#include "eap_server/eap_methods.h"
+
+
+/**
+ * eap_register_methods - Register statically linked EAP methods
+ * Returns: 0 on success, -1 or -2 on failure
+ *
+ * This function is called at program initialization to register all EAP
+ * methods that were linked in statically.
+ */
+int eap_register_methods(void)
+{
+       int ret = 0;
+
+#ifdef EAP_MD5
+       if (ret == 0)
+               ret = eap_peer_md5_register();
+#endif /* EAP_MD5 */
+
+#ifdef EAP_TLS
+       if (ret == 0)
+               ret = eap_peer_tls_register();
+#endif /* EAP_TLS */
+
+#ifdef EAP_MSCHAPv2
+       if (ret == 0)
+               ret = eap_peer_mschapv2_register();
+#endif /* EAP_MSCHAPv2 */
+
+#ifdef EAP_PEAP
+       if (ret == 0)
+               ret = eap_peer_peap_register();
+#endif /* EAP_PEAP */
+
+#ifdef EAP_TTLS
+       if (ret == 0)
+               ret = eap_peer_ttls_register();
+#endif /* EAP_TTLS */
+
+#ifdef EAP_GTC
+       if (ret == 0)
+               ret = eap_peer_gtc_register();
+#endif /* EAP_GTC */
+
+#ifdef EAP_OTP
+       if (ret == 0)
+               ret = eap_peer_otp_register();
+#endif /* EAP_OTP */
+
+#ifdef EAP_SIM
+       if (ret == 0)
+               ret = eap_peer_sim_register();
+#endif /* EAP_SIM */
+
+#ifdef EAP_LEAP
+       if (ret == 0)
+               ret = eap_peer_leap_register();
+#endif /* EAP_LEAP */
+
+#ifdef EAP_PSK
+       if (ret == 0)
+               ret = eap_peer_psk_register();
+#endif /* EAP_PSK */
+
+#ifdef EAP_AKA
+       if (ret == 0)
+               ret = eap_peer_aka_register();
+#endif /* EAP_AKA */
+
+#ifdef EAP_AKA_PRIME
+       if (ret == 0)
+               ret = eap_peer_aka_prime_register();
+#endif /* EAP_AKA_PRIME */
+
+#ifdef EAP_FAST
+       if (ret == 0)
+               ret = eap_peer_fast_register();
+#endif /* EAP_FAST */
+
+#ifdef EAP_PAX
+       if (ret == 0)
+               ret = eap_peer_pax_register();
+#endif /* EAP_PAX */
+
+#ifdef EAP_SAKE
+       if (ret == 0)
+               ret = eap_peer_sake_register();
+#endif /* EAP_SAKE */
+
+#ifdef EAP_GPSK
+       if (ret == 0)
+               ret = eap_peer_gpsk_register();
+#endif /* EAP_GPSK */
+
+#ifdef EAP_WSC
+       if (ret == 0)
+               ret = eap_peer_wsc_register();
+#endif /* EAP_WSC */
+
+#ifdef EAP_IKEV2
+       if (ret == 0)
+               ret = eap_peer_ikev2_register();
+#endif /* EAP_IKEV2 */
+
+#ifdef EAP_VENDOR_TEST
+       if (ret == 0)
+               ret = eap_peer_vendor_test_register();
+#endif /* EAP_VENDOR_TEST */
+
+#ifdef EAP_TNC
+       if (ret == 0)
+               ret = eap_peer_tnc_register();
+#endif /* EAP_TNC */
+
+
+#ifdef EAP_SERVER_IDENTITY
+       if (ret == 0)
+               ret = eap_server_identity_register();
+#endif /* EAP_SERVER_IDENTITY */
+
+#ifdef EAP_SERVER_MD5
+       if (ret == 0)
+               ret = eap_server_md5_register();
+#endif /* EAP_SERVER_MD5 */
+
+#ifdef EAP_SERVER_TLS
+       if (ret == 0)
+               ret = eap_server_tls_register();
+#endif /* EAP_SERVER_TLS */
+
+#ifdef EAP_SERVER_MSCHAPV2
+       if (ret == 0)
+               ret = eap_server_mschapv2_register();
+#endif /* EAP_SERVER_MSCHAPV2 */
+
+#ifdef EAP_SERVER_PEAP
+       if (ret == 0)
+               ret = eap_server_peap_register();
+#endif /* EAP_SERVER_PEAP */
+
+#ifdef EAP_SERVER_TLV
+       if (ret == 0)
+               ret = eap_server_tlv_register();
+#endif /* EAP_SERVER_TLV */
+
+#ifdef EAP_SERVER_GTC
+       if (ret == 0)
+               ret = eap_server_gtc_register();
+#endif /* EAP_SERVER_GTC */
+
+#ifdef EAP_SERVER_TTLS
+       if (ret == 0)
+               ret = eap_server_ttls_register();
+#endif /* EAP_SERVER_TTLS */
+
+#ifdef EAP_SERVER_SIM
+       if (ret == 0)
+               ret = eap_server_sim_register();
+#endif /* EAP_SERVER_SIM */
+
+#ifdef EAP_SERVER_AKA
+       if (ret == 0)
+               ret = eap_server_aka_register();
+#endif /* EAP_SERVER_AKA */
+
+#ifdef EAP_SERVER_AKA_PRIME
+       if (ret == 0)
+               ret = eap_server_aka_prime_register();
+#endif /* EAP_SERVER_AKA_PRIME */
+
+#ifdef EAP_SERVER_PAX
+       if (ret == 0)
+               ret = eap_server_pax_register();
+#endif /* EAP_SERVER_PAX */
+
+#ifdef EAP_SERVER_PSK
+       if (ret == 0)
+               ret = eap_server_psk_register();
+#endif /* EAP_SERVER_PSK */
+
+#ifdef EAP_SERVER_SAKE
+       if (ret == 0)
+               ret = eap_server_sake_register();
+#endif /* EAP_SERVER_SAKE */
+
+#ifdef EAP_SERVER_GPSK
+       if (ret == 0)
+               ret = eap_server_gpsk_register();
+#endif /* EAP_SERVER_GPSK */
+
+#ifdef EAP_SERVER_VENDOR_TEST
+       if (ret == 0)
+               ret = eap_server_vendor_test_register();
+#endif /* EAP_SERVER_VENDOR_TEST */
+
+#ifdef EAP_SERVER_FAST
+       if (ret == 0)
+               ret = eap_server_fast_register();
+#endif /* EAP_SERVER_FAST */
+
+#ifdef EAP_SERVER_WSC
+       if (ret == 0)
+               ret = eap_server_wsc_register();
+#endif /* EAP_SERVER_WSC */
+
+#ifdef EAP_SERVER_IKEV2
+       if (ret == 0)
+               ret = eap_server_ikev2_register();
+#endif /* EAP_SERVER_IKEV2 */
+
+#ifdef EAP_SERVER_TNC
+       if (ret == 0)
+               ret = eap_server_tnc_register();
+#endif /* EAP_SERVER_TNC */
+
+       return ret;
+}
diff --git a/wpa_supplicant/eap_testing.txt b/wpa_supplicant/eap_testing.txt
new file mode 100644 (file)
index 0000000..8d13222
--- /dev/null
@@ -0,0 +1,392 @@
+Automatic regression and interoperability testing of wpa_supplicant's
+IEEE 802.1X/EAPOL authentication
+
+Test program:
+- Linked some parts of IEEE 802.1X Authenticator implementation from
+  hostapd (RADIUS client and RADIUS processing, EAP<->RADIUS
+  encapsulation/decapsulation) into wpa_supplicant.
+- Replaced wpa_supplicant.c and wpa.c with test code that trigger
+  IEEE 802.1X authentication automatically without need for wireless
+  client card or AP.
+- For EAP methods that generate keying material, the key derived by the
+  Supplicant is verified to match with the one received by the (now
+  integrated) Authenticator.
+
+The full automated test suite can now be run in couple of seconds, but
+I'm more than willing to add new RADIUS authentication servers to make
+this take a bit more time.. ;-) As an extra bonus, this can also be
+seen as automatic regression/interoperability testing for the RADIUS
+server, too.
+
+In order for me to be able to use a new authentication server, the
+server need to be available from Internet (at least from one static IP
+address) and I will need to get suitable user name/password pairs,
+certificates, and private keys for testing use. Other alternative
+would be to get an evaluation version of the server so that I can
+install it on my own test setup. If you are interested in providing
+either server access or evaluation version, please contact me
+(j@w1.fi).
+
+
+Test matrix
+
++) tested successfully
+F) failed
+-) server did not support
+?) not tested
+
+Cisco ACS ----------------------------------------------------------.
+hostapd --------------------------------------------------------.   |
+Cisco Aironet 1200 AP (local RADIUS server) ----------------.   |   |
+Periodik Labs Elektron ---------------------------------.   |   |   |
+Lucent NavisRadius ---------------------------------.   |   |   |   |
+Interlink RAD-Series ---------------------------.   |   |   |   |   |
+Radiator -----------------------------------.   |   |   |   |   |   |
+Meetinghouse Aegis ---------------------.   |   |   |   |   |   |   |
+Funk Steel-Belted ------------------.   |   |   |   |   |   |   |   |
+Funk Odyssey -------------------.   |   |   |   |   |   |   |   |   |
+Microsoft IAS --------------.   |   |   |   |   |   |   |   |   |   |
+FreeRADIUS -------------.   |   |   |   |   |   |   |   |   |   |   |
+                       |   |   |   |   |   |   |   |   |   |   |   |
+
+EAP-MD5                        +   -   -   +   +   +   +   +   -   -   +   +
+EAP-GTC                        +   -   -   ?   +   +   +   +   -   -   +   -
+EAP-OTP                        -   -   -   -   -   +   -   -   -   -   -   -
+EAP-MSCHAPv2           +   -   -   +   +   +   +   +   -   -   +   -
+EAP-TLS                        +   +   +   +   +   +   +   +   -   -   +   +
+EAP-PEAPv0/MSCHAPv2    +   +   +   +   +   +   +   +   +   -   +   +
+EAP-PEAPv0/GTC         +   -   +   -   +   +   +   +   -   -   +   +
+EAP-PEAPv0/OTP         -   -   -   -   -   +   -   -   -   -   -   -
+EAP-PEAPv0/MD5         +   -   -   +   +   +   +   +   -   -   +   -
+EAP-PEAPv0/TLS         +   +   -   +   +   +   F   +   -   -   +   +
+EAP-PEAPv0/SIM         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/AKA         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/PSK         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/PAX         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/SAKE                -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv0/GPSK                -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/MSCHAPv2    -   -   +   +   +   +1  +   +5  +8  -   +   +
+EAP-PEAPv1/GTC         -   -   +   +   +   +1  +   +5  +8  -   +   +
+EAP-PEAPv1/OTP         -   -   -   -   -   +1  -   -   -   -   -   -
+EAP-PEAPv1/MD5         -   -   -   +   +   +1  +   +5  -   -   +   -
+EAP-PEAPv1/TLS         -   -   -   +   +   +1  F   +5  -   -   +   +
+EAP-PEAPv1/SIM         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/AKA         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/PSK         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/PAX         -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/SAKE                -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PEAPv1/GPSK                -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/CHAP          +   -   +2  +   +   +   +   +   +   -   +   -
+EAP-TTLS/MSCHAP                +   -   +   +   +   +   +   +   +   -   +   -
+EAP-TTLS/MSCHAPv2      +   -   +   +   +   +   +   +   +   -   +   -
+EAP-TTLS/PAP           +   -   +   +   +   +   +   +   +   -   +   -
+EAP-TTLS/EAP-MD5       +   -   +2  +   +   +   +   +   +   -   +   -
+EAP-TTLS/EAP-GTC       +   -   +2  ?   +   +   +   +   -   -   +   -
+EAP-TTLS/EAP-OTP       -   -   -   -   -   +   -   -   -   -   -   -
+EAP-TTLS/EAP-MSCHAPv2  +   -   +2  +   +   +   +   +   +   -   +   -
+EAP-TTLS/EAP-TLS       +   -   +2  +   F   +   +   +   -   -   +   -
+EAP-TTLS/EAP-SIM       -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-AKA       -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-PSK       -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-PAX       -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-SAKE      -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS/EAP-GPSK      -   -   -   -   -   -   -   -   -   -   +   -
+EAP-TTLS + TNC         -   -   -   -   -   +   -   -   -   -   +   -
+EAP-SIM                        +   -   -   ?   -   +   -   ?   -   -   +   -
+EAP-AKA                        -   -   -   -   -   +   -   -   -   -   +   -
+EAP-AKA'               -   -   -   -   -   -   -   -   -   -   +   -
+EAP-PSK                        +7  -   -   -   -   +   -   -   -   -   +   -
+EAP-PAX                        -   -   -   -   -   +   -   -   -   -   +   -
+EAP-SAKE               -   -   -   -   -   -   -   -   -   -   +   -
+EAP-GPSK               -   -   -   -   -   -   -   -   -   -   +   -
+EAP-FAST/MSCHAPv2(prov)        -   -   -   +   -   +   -   -   -   +   +   +
+EAP-FAST/GTC(auth)     -   -   -   +   -   +   -   -   -   +   +   +
+EAP-FAST/MSCHAPv2(aprov)-   -   -   -   -   +   -   -   -   -   +   +
+EAP-FAST/GTC(aprov)    -   -   -   -   -   +   -   -   -   -   +   +
+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         -   -   -   -   -   -   -   -   -   -   +   -
+LEAP                   +   -   +   +   +   +   F   +6  -   +   -   +
+EAP-TNC                        +9  -   -   -   -   +   -   -   -   -   +   -
+EAP-IKEv2              +10 -   -   -   -   -   -   -   -   -   +   -
+
+1) PEAPv1 required new label, "client PEAP encryption" instead of "client EAP
+   encryption", during key derivation (requires phase1="peaplabel=1" in the
+   network configuration in wpa_supplicant.conf)
+2) used FreeRADIUS as inner auth server
+5) PEAPv1 required termination of negotiation on tunneled EAP-Success and new
+   label in key deriviation
+   (phase1="peap_outer_success=0 peaplabel=1") (in "IETF Draft 5" mode)
+6) Authenticator simulator required patching for handling Access-Accept within
+   negotiation (for the first EAP-Success of LEAP)
+7) tested only with an older (incompatible) draft of EAP-PSK; FreeRADIUS does
+   not support the current EAP-PSK (RFC) specification
+8) PEAPv1 used non-standard version negotiation (client had to force v1 even
+   though server reported v0 as the highest supported version)
+9) only EAP-TTLS/EAP-TNC tested, i.e., test did not include proper sequence of
+   client authentication followed by TNC inside the tunnel
+10) worked only with special compatibility code to match the IKEv2 server
+    implementation
+
+
+Automated tests:
+
+FreeRADIUS (2.0-beta/CVS snapshot)
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / TLS
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+- EAP-TTLS / CHAP
+- EAP-TTLS / PAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / EAP-TNC (partial support; no authentication sequence)
+- EAP-SIM
+- LEAP
+
+Microsoft Windows Server 2003 / IAS
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / TLS
+- EAP-MD5
+* IAS does not seem to support other EAP methods
+
+Funk Odyssey 2.01.00.653
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-GTC (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-MSCHAPv2 (using FreeRADIUS as inner auth srv)
+- EAP-TTLS / EAP-TLS (using FreeRADIUS as inner auth srv)
+* not supported in Odyssey:
+  - EAP-MD5-Challenge
+  - EAP-GTC
+  - EAP-MSCHAPv2
+  - EAP-PEAP / MD5-Challenge
+  - EAP-PEAP / TLS
+
+Funk Steel-Belted Radius Enterprise Edition v4.71.739
+- EAP-MD5-Challenge
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / MD5
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / MD5
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / TLS
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+
+Meetinghouse Aegis 1.1.4
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / TLS
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+* did not work
+  - EAP-TTLS / EAP-TLS
+    (Server rejects authentication without any reason in debug log. It
+     looks like the inner TLS negotiation starts properly and the last
+     packet from Supplicant looks like the one sent in the Phase 1. The
+     server generates a valid looking reply in the same way as in Phase
+     1, but then ends up sending Access-Reject. Maybe an issue with TTLS
+     fragmentation in the Aegis server(?) The packet seems to include
+     1328 bytes of EAP-Message and this may go beyond the fragmentation
+     limit with AVP encapsulation and TLS tunneling. Note: EAP-PEAP/TLS
+     did work, so this issue seems to be with something TTLS specific.)
+
+Radiator 3.17.1 (eval, with all patches up to and including 2007-05-25)
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-OTP
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / OTP
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / TLS
+  Note: Needed to use unknown identity in outer auth and some times the server
+       seems to get confused and fails to send proper Phase 2 data.
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / OTP
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-PEAPv1 / TLS
+  Note: This has some additional requirements for EAPTLS_MaxFragmentSize.
+        Using 1300 for outer auth and 500 for inner auth seemed to work.
+  Note: Needed to use unknown identity in outer auth and some times the server
+       seems to get confused and fails to send proper Phase 2 data.
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-OTP
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+  Note: This has some additional requirements for EAPTLS_MaxFragmentSize.
+        Using 1300 for outer auth and 500 for inner auth seemed to work.
+- EAP-SIM
+- EAP-AKA
+- EAP-PSK
+- EAP-PAX
+- EAP-TNC
+
+Interlink Networks RAD-Series 6.1.2.7
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+  Note: PEAPv1 requires TLS key derivation to use label "client EAP encryption"
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-TLS
+* did not work
+  - EAP-PEAPv0 / TLS
+  - EAP-PEAPv1 / TLS
+    (Failed to decrypt Phase 2 data)
+
+Lucent NavisRadius 4.4.0
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / TLS
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / TLS
+  "IETF Draft 5" mode requires phase1="peap_outer_success=0 peaplabel=1"
+  'Cisco ACU 5.05' mode works without phase1 configuration
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-TLS
+
+Note: user certificate from NavisRadius had private key in a format
+that wpa_supplicant could not use. Converting this to PKCS#12 and then
+back to PEM allowed wpa_supplicant to use the key.
+
+
+hostapd v0.3.3
+- EAP-MD5-Challenge
+- EAP-GTC
+- EAP-MSCHAPv2
+- EAP-TLS
+- EAP-PEAPv0 / MSCHAPv2
+- EAP-PEAPv0 / GTC
+- EAP-PEAPv0 / MD5-Challenge
+- EAP-PEAPv1 / MSCHAPv2
+- EAP-PEAPv1 / GTC
+- EAP-PEAPv1 / MD5-Challenge
+- EAP-TTLS / CHAP
+- EAP-TTLS / MSCHAP
+- EAP-TTLS / MSCHAPv2
+- EAP-TTLS / PAP
+- EAP-TTLS / EAP-MD5-Challenge
+- EAP-TTLS / EAP-GTC
+- EAP-TTLS / EAP-MSCHAPv2
+- EAP-SIM
+- EAP-PAX
+
+PEAPv1:
+
+Funk Odyssey 2.01.00.653:
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+  keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- (peap_outer_success 1 and 2 work)
+
+Funk Steel-Belted Radius Enterprise Edition v4.71.739
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+  keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- (peap_outer_success 1 and 2 work)
+
+Radiator 3.9:
+- uses TLV Success and Reply, sends MPPE keys with outer EAP-Success message
+  after this
+- uses label "client PEAP encryption"
+
+Lucent NavisRadius 4.4.0 (in "IETF Draft 5" mode):
+- sends tunneled EAP-Success with MPPE keys and expects the authentication to
+  terminate at this point (gets somewhat confused with reply to this)
+- uses label "client PEAP encryption"
+- phase1="peap_outer_success=0 peaplabel=1"
+
+Lucent NavisRadius 4.4.0 (in "Cisco ACU 5.05" mode):
+- sends tunneled EAP-Success with MPPE keys and expects to receive TLS ACK
+  as a reply
+- uses label "client EAP encryption"
+
+Meetinghouse Aegis 1.1.4
+- uses tunneled EAP-Success, expects reply in tunnel or TLS ACK, sends MPPE
+  keys with outer EAP-Success message after this
+- uses label "client EAP encryption"
+- peap_outer_success 1 and 2 work
diff --git a/wpa_supplicant/eapol_test.c b/wpa_supplicant/eapol_test.c
new file mode 100644 (file)
index 0000000..4eed854
--- /dev/null
@@ -0,0 +1,1209 @@
+/*
+ * WPA Supplicant - test code
+ * 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.
+ *
+ * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
+ * Not used in production version.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
+#include "eloop.h"
+#include "rsn_supp/wpa.h"
+#include "eap_peer/eap_i.h"
+#include "wpa_supplicant_i.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+
+struct wpa_driver_ops *wpa_drivers[] = { NULL };
+
+
+struct extra_radius_attr {
+       u8 type;
+       char syntax;
+       char *data;
+       struct extra_radius_attr *next;
+};
+
+struct eapol_test_data {
+       struct wpa_supplicant *wpa_s;
+
+       int eapol_test_num_reauths;
+       int no_mppe_keys;
+       int num_mppe_ok, num_mppe_mismatch;
+
+       u8 radius_identifier;
+       struct radius_msg *last_recv_radius;
+       struct in_addr own_ip_addr;
+       struct radius_client_data *radius;
+       struct hostapd_radius_servers *radius_conf;
+
+       u8 *last_eap_radius; /* last received EAP Response from Authentication
+                             * Server */
+       size_t last_eap_radius_len;
+
+       u8 authenticator_pmk[PMK_LEN];
+       size_t authenticator_pmk_len;
+       int radius_access_accept_received;
+       int radius_access_reject_received;
+       int auth_timed_out;
+
+       u8 *eap_identity;
+       size_t eap_identity_len;
+
+       char *connect_info;
+       u8 own_addr[ETH_ALEN];
+       struct extra_radius_attr *extra_attrs;
+};
+
+static struct eapol_test_data eapol_test;
+
+
+static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx);
+
+
+static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+                             int level, const char *txt, size_t len)
+{
+       if (addr)
+               wpa_printf(MSG_DEBUG, "STA " MACSTR ": %s\n",
+                          MAC2STR(addr), txt);
+       else
+               wpa_printf(MSG_DEBUG, "%s", txt);
+}
+
+
+static int add_extra_attr(struct radius_msg *msg,
+                         struct extra_radius_attr *attr)
+{
+       size_t len;
+       char *pos;
+       u32 val;
+       char buf[128];
+
+       switch (attr->syntax) {
+       case 's':
+               os_snprintf(buf, sizeof(buf), "%s", attr->data);
+               len = os_strlen(buf);
+               break;
+       case 'n':
+               buf[0] = '\0';
+               len = 1;
+               break;
+       case 'x':
+               pos = attr->data;
+               if (pos[0] == '0' && pos[1] == 'x')
+                       pos += 2;
+               len = os_strlen(pos);
+               if ((len & 1) || (len / 2) > sizeof(buf)) {
+                       printf("Invalid extra attribute hexstring\n");
+                       return -1;
+               }
+               len /= 2;
+               if (hexstr2bin(pos, (u8 *) buf, len) < 0) {
+                       printf("Invalid extra attribute hexstring\n");
+                       return -1;
+               }
+               break;
+       case 'd':
+               val = htonl(atoi(attr->data));
+               os_memcpy(buf, &val, 4);
+               len = 4;
+               break;
+       default:
+               printf("Incorrect extra attribute syntax specification\n");
+               return -1;
+       }
+
+       if (!radius_msg_add_attr(msg, attr->type, (u8 *) buf, len)) {
+               printf("Could not add attribute %d\n", attr->type);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int add_extra_attrs(struct radius_msg *msg,
+                          struct extra_radius_attr *attrs)
+{
+       struct extra_radius_attr *p;
+       for (p = attrs; p; p = p->next) {
+               if (add_extra_attr(msg, p) < 0)
+                       return -1;
+       }
+       return 0;
+}
+
+
+static struct extra_radius_attr *
+find_extra_attr(struct extra_radius_attr *attrs, u8 type)
+{
+       struct extra_radius_attr *p;
+       for (p = attrs; p; p = p->next) {
+               if (p->type == type)
+                       return p;
+       }
+       return NULL;
+}
+
+
+static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
+                                         const u8 *eap, size_t len)
+{
+       struct radius_msg *msg;
+       char buf[128];
+       const struct eap_hdr *hdr;
+       const u8 *pos;
+
+       wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS "
+                  "packet");
+
+       e->radius_identifier = radius_client_get_id(e->radius);
+       msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+                            e->radius_identifier);
+       if (msg == NULL) {
+               printf("Could not create net RADIUS packet\n");
+               return;
+       }
+
+       radius_msg_make_authenticator(msg, (u8 *) e, sizeof(*e));
+
+       hdr = (const struct eap_hdr *) eap;
+       pos = (const u8 *) (hdr + 1);
+       if (len > sizeof(*hdr) && hdr->code == EAP_CODE_RESPONSE &&
+           pos[0] == EAP_TYPE_IDENTITY) {
+               pos++;
+               os_free(e->eap_identity);
+               e->eap_identity_len = len - sizeof(*hdr) - 1;
+               e->eap_identity = os_malloc(e->eap_identity_len);
+               if (e->eap_identity) {
+                       os_memcpy(e->eap_identity, pos, e->eap_identity_len);
+                       wpa_hexdump(MSG_DEBUG, "Learned identity from "
+                                   "EAP-Response-Identity",
+                                   e->eap_identity, e->eap_identity_len);
+               }
+       }
+
+       if (e->eap_identity &&
+           !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+                                e->eap_identity, e->eap_identity_len)) {
+               printf("Could not add User-Name\n");
+               goto fail;
+       }
+
+       if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
+           !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+                                (u8 *) &e->own_ip_addr, 4)) {
+               printf("Could not add NAS-IP-Address\n");
+               goto fail;
+       }
+
+       os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+                   MAC2STR(e->wpa_s->own_addr));
+       if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CALLING_STATION_ID)
+           &&
+           !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 (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_FRAMED_MTU) &&
+           !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
+               printf("Could not add Framed-MTU\n");
+               goto fail;
+       }
+
+       if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_PORT_TYPE) &&
+           !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), "%s", e->connect_info);
+       if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_CONNECT_INFO) &&
+           !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
+                                (u8 *) buf, os_strlen(buf))) {
+               printf("Could not add Connect-Info\n");
+               goto fail;
+       }
+
+       if (add_extra_attrs(msg, e->extra_attrs) < 0)
+               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 (e->last_recv_radius &&
+           radius_msg_get_hdr(e->last_recv_radius)->code ==
+           RADIUS_CODE_ACCESS_CHALLENGE) {
+               int res = radius_msg_copy_attr(msg, e->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(e->radius, msg, RADIUS_AUTH, e->wpa_s->own_addr);
+       return;
+
+ fail:
+       radius_msg_free(msg);
+}
+
+
+static int eapol_test_eapol_send(void *ctx, int type, const u8 *buf,
+                                size_t len)
+{
+       /* struct wpa_supplicant *wpa_s = ctx; */
+       printf("WPA: eapol_test_eapol_send(type=%d len=%lu)\n",
+              type, (unsigned long) len);
+       if (type == IEEE802_1X_TYPE_EAP_PACKET) {
+               wpa_hexdump(MSG_DEBUG, "TX EAP -> RADIUS", buf, len);
+               ieee802_1x_encapsulate_radius(&eapol_test, buf, len);
+       }
+       return 0;
+}
+
+
+static void eapol_test_set_config_blob(void *ctx,
+                                      struct wpa_config_blob *blob)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_config_set_blob(wpa_s->conf, blob);
+}
+
+
+static const struct wpa_config_blob *
+eapol_test_get_config_blob(void *ctx, const char *name)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpa_config_get_blob(wpa_s->conf, name);
+}
+
+
+static void eapol_test_eapol_done_cb(void *ctx)
+{
+       printf("WPA: EAPOL processing complete\n");
+}
+
+
+static void eapol_sm_reauth(void *eloop_ctx, void *timeout_ctx)
+{
+       struct eapol_test_data *e = eloop_ctx;
+       printf("\n\n\n\n\neapol_test: Triggering EAP reauthentication\n\n");
+       e->radius_access_accept_received = 0;
+       send_eap_request_identity(e->wpa_s, NULL);
+}
+
+
+static int eapol_test_compare_pmk(struct eapol_test_data *e)
+{
+       u8 pmk[PMK_LEN];
+       int ret = 1;
+
+       if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) {
+               wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
+               if (os_memcmp(pmk, e->authenticator_pmk, PMK_LEN) != 0) {
+                       printf("WARNING: PMK mismatch\n");
+                       wpa_hexdump(MSG_DEBUG, "PMK from AS",
+                                   e->authenticator_pmk, PMK_LEN);
+               } else if (e->radius_access_accept_received)
+                       ret = 0;
+       } else if (e->authenticator_pmk_len == 16 &&
+                  eapol_sm_get_key(e->wpa_s->eapol, pmk, 16) == 0) {
+               wpa_hexdump(MSG_DEBUG, "LEAP PMK from EAPOL", pmk, 16);
+               if (os_memcmp(pmk, e->authenticator_pmk, 16) != 0) {
+                       printf("WARNING: PMK mismatch\n");
+                       wpa_hexdump(MSG_DEBUG, "PMK from AS",
+                                   e->authenticator_pmk, 16);
+               } else if (e->radius_access_accept_received)
+                       ret = 0;
+       } else if (e->radius_access_accept_received && e->no_mppe_keys) {
+               /* No keying material expected */
+               ret = 0;
+       }
+
+       if (ret && !e->no_mppe_keys)
+               e->num_mppe_mismatch++;
+       else if (!e->no_mppe_keys)
+               e->num_mppe_ok++;
+
+       return ret;
+}
+
+
+static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
+{
+       struct eapol_test_data *e = ctx;
+       printf("eapol_sm_cb: success=%d\n", success);
+       e->eapol_test_num_reauths--;
+       if (e->eapol_test_num_reauths < 0)
+               eloop_terminate();
+       else {
+               eapol_test_compare_pmk(e);
+               eloop_register_timeout(0, 100000, eapol_sm_reauth, e, NULL);
+       }
+}
+
+
+static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
+                     struct wpa_ssid *ssid)
+{
+       struct eapol_config eapol_conf;
+       struct eapol_ctx *ctx;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL) {
+               printf("Failed to allocate EAPOL context.\n");
+               return -1;
+       }
+       ctx->ctx = wpa_s;
+       ctx->msg_ctx = wpa_s;
+       ctx->scard_ctx = wpa_s->scard;
+       ctx->cb = eapol_sm_cb;
+       ctx->cb_ctx = e;
+       ctx->eapol_send_ctx = wpa_s;
+       ctx->preauth = 0;
+       ctx->eapol_done_cb = eapol_test_eapol_done_cb;
+       ctx->eapol_send = eapol_test_eapol_send;
+       ctx->set_config_blob = eapol_test_set_config_blob;
+       ctx->get_config_blob = eapol_test_get_config_blob;
+       ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
+       ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+       ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+
+       wpa_s->eapol = eapol_sm_init(ctx);
+       if (wpa_s->eapol == NULL) {
+               os_free(ctx);
+               printf("Failed to initialize EAPOL state machines.\n");
+               return -1;
+       }
+
+       wpa_s->current_ssid = ssid;
+       os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+       eapol_conf.accept_802_1x_keys = 1;
+       eapol_conf.required_keys = 0;
+       eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+       eapol_conf.workaround = ssid->eap_workaround;
+       eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+       eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+
+
+       eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+       /* 802.1X::portControl = Auto */
+       eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+
+       return 0;
+}
+
+
+static void test_eapol_clean(struct eapol_test_data *e,
+                            struct wpa_supplicant *wpa_s)
+{
+       struct extra_radius_attr *p, *prev;
+
+       radius_client_deinit(e->radius);
+       os_free(e->last_eap_radius);
+       radius_msg_free(e->last_recv_radius);
+       e->last_recv_radius = NULL;
+       os_free(e->eap_identity);
+       e->eap_identity = NULL;
+       eapol_sm_deinit(wpa_s->eapol);
+       wpa_s->eapol = NULL;
+       if (e->radius_conf && e->radius_conf->auth_server) {
+               os_free(e->radius_conf->auth_server->shared_secret);
+               os_free(e->radius_conf->auth_server);
+       }
+       os_free(e->radius_conf);
+       e->radius_conf = NULL;
+       scard_deinit(wpa_s->scard);
+       if (wpa_s->ctrl_iface) {
+               wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+               wpa_s->ctrl_iface = NULL;
+       }
+       wpa_config_free(wpa_s->conf);
+
+       p = e->extra_attrs;
+       while (p) {
+               prev = p;
+               p = p->next;
+               os_free(prev);
+       }
+}
+
+
+static void send_eap_request_identity(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       u8 buf[100], *pos;
+       struct ieee802_1x_hdr *hdr;
+       struct eap_hdr *eap;
+
+       hdr = (struct ieee802_1x_hdr *) buf;
+       hdr->version = EAPOL_VERSION;
+       hdr->type = IEEE802_1X_TYPE_EAP_PACKET;
+       hdr->length = htons(5);
+
+       eap = (struct eap_hdr *) (hdr + 1);
+       eap->code = EAP_CODE_REQUEST;
+       eap->identifier = 0;
+       eap->length = htons(5);
+       pos = (u8 *) (eap + 1);
+       *pos = EAP_TYPE_IDENTITY;
+
+       printf("Sending fake EAP-Request-Identity\n");
+       eapol_sm_rx_eapol(wpa_s->eapol, wpa_s->bssid, buf,
+                         sizeof(*hdr) + 5);
+}
+
+
+static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct eapol_test_data *e = eloop_ctx;
+       printf("EAPOL test timed out\n");
+       e->auth_timed_out = 1;
+       eloop_terminate();
+}
+
+
+static char *eap_type_text(u8 type)
+{
+       switch (type) {
+       case EAP_TYPE_IDENTITY: return "Identity";
+       case EAP_TYPE_NOTIFICATION: return "Notification";
+       case EAP_TYPE_NAK: return "Nak";
+       case EAP_TYPE_TLS: return "TLS";
+       case EAP_TYPE_TTLS: return "TTLS";
+       case EAP_TYPE_PEAP: return "PEAP";
+       case EAP_TYPE_SIM: return "SIM";
+       case EAP_TYPE_GTC: return "GTC";
+       case EAP_TYPE_MD5: return "MD5";
+       case EAP_TYPE_OTP: return "OTP";
+       case EAP_TYPE_FAST: return "FAST";
+       case EAP_TYPE_SAKE: return "SAKE";
+       case EAP_TYPE_PSK: return "PSK";
+       default: return "Unknown";
+       }
+}
+
+
+static void ieee802_1x_decapsulate_radius(struct eapol_test_data *e)
+{
+       u8 *eap;
+       size_t len;
+       struct eap_hdr *hdr;
+       int eap_type = -1;
+       char buf[64];
+       struct radius_msg *msg;
+
+       if (e->last_recv_radius == NULL)
+               return;
+
+       msg = e->last_recv_radius;
+
+       eap = radius_msg_get_eap(msg, &len);
+       if (eap == NULL) {
+               /* draft-aboba-radius-rfc2869bis-20.txt, Chap. 2.6.3:
+                * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message
+                * attribute */
+               wpa_printf(MSG_DEBUG, "could not extract "
+                              "EAP-Message from RADIUS message");
+               os_free(e->last_eap_radius);
+               e->last_eap_radius = NULL;
+               e->last_eap_radius_len = 0;
+               return;
+       }
+
+       if (len < sizeof(*hdr)) {
+               wpa_printf(MSG_DEBUG, "too short EAP packet "
+                              "received from authentication server");
+               os_free(eap);
+               return;
+       }
+
+       if (len > sizeof(*hdr))
+               eap_type = eap[sizeof(*hdr)];
+
+       hdr = (struct eap_hdr *) eap;
+       switch (hdr->code) {
+       case EAP_CODE_REQUEST:
+               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));
+               /* LEAP uses EAP Success within an authentication, so must not
+                * stop here with eloop_terminate(); */
+               break;
+       case EAP_CODE_FAILURE:
+               os_strlcpy(buf, "EAP Failure", sizeof(buf));
+               eloop_terminate();
+               break;
+       default:
+               os_strlcpy(buf, "unknown EAP code", sizeof(buf));
+               wpa_hexdump(MSG_DEBUG, "Decapsulated EAP packet", eap, len);
+               break;
+       }
+       wpa_printf(MSG_DEBUG, "decapsulated EAP packet (code=%d "
+                      "id=%d len=%d) from RADIUS server: %s",
+                     hdr->code, hdr->identifier, ntohs(hdr->length), buf);
+
+       /* sta->eapol_sm->be_auth.idFromServer = hdr->identifier; */
+
+       os_free(e->last_eap_radius);
+       e->last_eap_radius = eap;
+       e->last_eap_radius_len = len;
+
+       {
+               struct ieee802_1x_hdr *dot1x;
+               dot1x = os_malloc(sizeof(*dot1x) + len);
+               assert(dot1x != NULL);
+               dot1x->version = EAPOL_VERSION;
+               dot1x->type = IEEE802_1X_TYPE_EAP_PACKET;
+               dot1x->length = htons(len);
+               os_memcpy((u8 *) (dot1x + 1), eap, len);
+               eapol_sm_rx_eapol(e->wpa_s->eapol, e->wpa_s->bssid,
+                                 (u8 *) dot1x, sizeof(*dot1x) + len);
+               os_free(dot1x);
+       }
+}
+
+
+static void ieee802_1x_get_keys(struct eapol_test_data *e,
+                               struct radius_msg *msg, struct radius_msg *req,
+                               const u8 *shared_secret,
+                               size_t shared_secret_len)
+{
+       struct radius_ms_mppe_keys *keys;
+
+       keys = radius_msg_get_ms_keys(msg, req, shared_secret,
+                                     shared_secret_len);
+       if (keys && keys->send == NULL && keys->recv == NULL) {
+               os_free(keys);
+               keys = radius_msg_get_cisco_keys(msg, req, shared_secret,
+                                                shared_secret_len);
+       }
+
+       if (keys) {
+               if (keys->send) {
+                       wpa_hexdump(MSG_DEBUG, "MS-MPPE-Send-Key (sign)",
+                                   keys->send, keys->send_len);
+               }
+               if (keys->recv) {
+                       wpa_hexdump(MSG_DEBUG, "MS-MPPE-Recv-Key (crypt)",
+                                   keys->recv, keys->recv_len);
+                       e->authenticator_pmk_len =
+                               keys->recv_len > PMK_LEN ? PMK_LEN :
+                               keys->recv_len;
+                       os_memcpy(e->authenticator_pmk, keys->recv,
+                                 e->authenticator_pmk_len);
+                       if (e->authenticator_pmk_len == 16 && keys->send &&
+                           keys->send_len == 16) {
+                               /* MS-CHAP-v2 derives 16 octet keys */
+                               wpa_printf(MSG_DEBUG, "Use MS-MPPE-Send-Key "
+                                          "to extend PMK to 32 octets");
+                               os_memcpy(e->authenticator_pmk +
+                                         e->authenticator_pmk_len,
+                                         keys->send, keys->send_len);
+                               e->authenticator_pmk_len += keys->send_len;
+                       }
+               }
+
+               os_free(keys->send);
+               os_free(keys->recv);
+               os_free(keys);
+       }
+}
+
+
+/* Process the RADIUS frames from Authentication Server */
+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 eapol_test_data *e = data;
+       struct radius_hdr *hdr = radius_msg_get_hdr(msg);
+
+       /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be
+        * present when packet contains an EAP-Message attribute */
+       if (hdr->code == RADIUS_CODE_ACCESS_REJECT &&
+           radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL,
+                               0) < 0 &&
+           radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "Allowing RADIUS "
+                             "Access-Reject without Message-Authenticator "
+                             "since it does not include EAP-Message\n");
+       } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
+                                    req, 1)) {
+               printf("Incoming RADIUS packet did not have correct "
+                      "Message-Authenticator - dropped\n");
+               return RADIUS_RX_UNKNOWN;
+       }
+
+       if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
+           hdr->code != RADIUS_CODE_ACCESS_REJECT &&
+           hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
+               printf("Unknown RADIUS message code\n");
+               return RADIUS_RX_UNKNOWN;
+       }
+
+       e->radius_identifier = -1;
+       wpa_printf(MSG_DEBUG, "RADIUS packet matching with station");
+
+       radius_msg_free(e->last_recv_radius);
+       e->last_recv_radius = msg;
+
+       switch (hdr->code) {
+       case RADIUS_CODE_ACCESS_ACCEPT:
+               e->radius_access_accept_received = 1;
+               ieee802_1x_get_keys(e, msg, req, shared_secret,
+                                   shared_secret_len);
+               break;
+       case RADIUS_CODE_ACCESS_REJECT:
+               e->radius_access_reject_received = 1;
+               break;
+       }
+
+       ieee802_1x_decapsulate_radius(e);
+
+       if ((hdr->code == RADIUS_CODE_ACCESS_ACCEPT &&
+            e->eapol_test_num_reauths < 0) ||
+           hdr->code == RADIUS_CODE_ACCESS_REJECT) {
+               eloop_terminate();
+       }
+
+       return RADIUS_RX_QUEUED;
+}
+
+
+static void wpa_init_conf(struct eapol_test_data *e,
+                         struct wpa_supplicant *wpa_s, const char *authsrv,
+                         int port, const char *secret,
+                         const char *cli_addr)
+{
+       struct hostapd_radius_server *as;
+       int res;
+
+       wpa_s->bssid[5] = 1;
+       os_memcpy(wpa_s->own_addr, e->own_addr, ETH_ALEN);
+       e->own_ip_addr.s_addr = htonl((127 << 24) | 1);
+       os_strlcpy(wpa_s->ifname, "test", sizeof(wpa_s->ifname));
+
+       e->radius_conf = os_zalloc(sizeof(struct hostapd_radius_servers));
+       assert(e->radius_conf != NULL);
+       e->radius_conf->num_auth_servers = 1;
+       as = os_zalloc(sizeof(struct hostapd_radius_server));
+       assert(as != NULL);
+#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA)
+       {
+               int a[4];
+               u8 *pos;
+               sscanf(authsrv, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]);
+               pos = (u8 *) &as->addr.u.v4;
+               *pos++ = a[0];
+               *pos++ = a[1];
+               *pos++ = a[2];
+               *pos++ = a[3];
+       }
+#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+       inet_aton(authsrv, &as->addr.u.v4);
+#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
+       as->addr.af = AF_INET;
+       as->port = port;
+       as->shared_secret = (u8 *) os_strdup(secret);
+       as->shared_secret_len = os_strlen(secret);
+       e->radius_conf->auth_server = as;
+       e->radius_conf->auth_servers = as;
+       e->radius_conf->msg_dumps = 1;
+       if (cli_addr) {
+               if (hostapd_parse_ip_addr(cli_addr,
+                                         &e->radius_conf->client_addr) == 0)
+                       e->radius_conf->force_client_addr = 1;
+               else {
+                       wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
+                                  cli_addr);
+                       assert(0);
+               }
+       }
+
+       e->radius = radius_client_init(wpa_s, e->radius_conf);
+       assert(e->radius != NULL);
+
+       res = radius_client_register(e->radius, RADIUS_AUTH,
+                                    ieee802_1x_receive_auth, e);
+       assert(res == 0);
+}
+
+
+static int scard_test(void)
+{
+       struct scard_data *scard;
+       size_t len;
+       char imsi[20];
+       unsigned char _rand[16];
+#ifdef PCSC_FUNCS
+       unsigned char sres[4];
+       unsigned char kc[8];
+#endif /* PCSC_FUNCS */
+#define num_triplets 5
+       unsigned char rand_[num_triplets][16];
+       unsigned char sres_[num_triplets][4];
+       unsigned char kc_[num_triplets][8];
+       int i, res;
+       size_t j;
+
+#define AKA_RAND_LEN 16
+#define AKA_AUTN_LEN 16
+#define AKA_AUTS_LEN 14
+#define RES_MAX_LEN 16
+#define IK_LEN 16
+#define CK_LEN 16
+       unsigned char aka_rand[AKA_RAND_LEN];
+       unsigned char aka_autn[AKA_AUTN_LEN];
+       unsigned char aka_auts[AKA_AUTS_LEN];
+       unsigned char aka_res[RES_MAX_LEN];
+       size_t aka_res_len;
+       unsigned char aka_ik[IK_LEN];
+       unsigned char aka_ck[CK_LEN];
+
+       scard = scard_init(SCARD_TRY_BOTH);
+       if (scard == NULL)
+               return -1;
+       if (scard_set_pin(scard, "1234")) {
+               wpa_printf(MSG_WARNING, "PIN validation failed");
+               scard_deinit(scard);
+               return -1;
+       }
+
+       len = sizeof(imsi);
+       if (scard_get_imsi(scard, imsi, &len))
+               goto failed;
+       wpa_hexdump_ascii(MSG_DEBUG, "SCARD: IMSI", (u8 *) imsi, len);
+       /* NOTE: Permanent Username: 1 | IMSI */
+
+       os_memset(_rand, 0, sizeof(_rand));
+       if (scard_gsm_auth(scard, _rand, sres, kc))
+               goto failed;
+
+       os_memset(_rand, 0xff, sizeof(_rand));
+       if (scard_gsm_auth(scard, _rand, sres, kc))
+               goto failed;
+
+       for (i = 0; i < num_triplets; i++) {
+               os_memset(rand_[i], i, sizeof(rand_[i]));
+               if (scard_gsm_auth(scard, rand_[i], sres_[i], kc_[i]))
+                       goto failed;
+       }
+
+       for (i = 0; i < num_triplets; i++) {
+               printf("1");
+               for (j = 0; j < len; j++)
+                       printf("%c", imsi[j]);
+               printf(",");
+               for (j = 0; j < 16; j++)
+                       printf("%02X", rand_[i][j]);
+               printf(",");
+               for (j = 0; j < 4; j++)
+                       printf("%02X", sres_[i][j]);
+               printf(",");
+               for (j = 0; j < 8; j++)
+                       printf("%02X", kc_[i][j]);
+               printf("\n");
+       }
+
+       wpa_printf(MSG_DEBUG, "Trying to use UMTS authentication");
+
+       /* seq 39 (0x28) */
+       os_memset(aka_rand, 0xaa, 16);
+       os_memcpy(aka_autn, "\x86\x71\x31\xcb\xa2\xfc\x61\xdf"
+                 "\xa3\xb3\x97\x9d\x07\x32\xa2\x12", 16);
+
+       res = scard_umts_auth(scard, aka_rand, aka_autn, aka_res, &aka_res_len,
+                             aka_ik, aka_ck, aka_auts);
+       if (res == 0) {
+               wpa_printf(MSG_DEBUG, "UMTS auth completed successfully");
+               wpa_hexdump(MSG_DEBUG, "RES", aka_res, aka_res_len);
+               wpa_hexdump(MSG_DEBUG, "IK", aka_ik, IK_LEN);
+               wpa_hexdump(MSG_DEBUG, "CK", aka_ck, CK_LEN);
+       } else if (res == -2) {
+               wpa_printf(MSG_DEBUG, "UMTS auth resulted in synchronization "
+                          "failure");
+               wpa_hexdump(MSG_DEBUG, "AUTS", aka_auts, AKA_AUTS_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "UMTS auth failed");
+       }
+
+failed:
+       scard_deinit(scard);
+
+       return 0;
+#undef num_triplets
+}
+
+
+static int scard_get_triplets(int argc, char *argv[])
+{
+       struct scard_data *scard;
+       size_t len;
+       char imsi[20];
+       unsigned char _rand[16];
+       unsigned char sres[4];
+       unsigned char kc[8];
+       int num_triplets;
+       int i;
+       size_t j;
+
+       if (argc < 2 || ((num_triplets = atoi(argv[1])) <= 0)) {
+               printf("invalid parameters for sim command\n");
+               return -1;
+       }
+
+       if (argc <= 2 || os_strcmp(argv[2], "debug") != 0) {
+               /* disable debug output */
+               wpa_debug_level = 99;
+       }
+
+       scard = scard_init(SCARD_GSM_SIM_ONLY);
+       if (scard == NULL) {
+               printf("Failed to open smartcard connection\n");
+               return -1;
+       }
+       if (scard_set_pin(scard, argv[0])) {
+               wpa_printf(MSG_WARNING, "PIN validation failed");
+               scard_deinit(scard);
+               return -1;
+       }
+
+       len = sizeof(imsi);
+       if (scard_get_imsi(scard, imsi, &len)) {
+               scard_deinit(scard);
+               return -1;
+       }
+
+       for (i = 0; i < num_triplets; i++) {
+               os_memset(_rand, i, sizeof(_rand));
+               if (scard_gsm_auth(scard, _rand, sres, kc))
+                       break;
+
+               /* IMSI:Kc:SRES:RAND */
+               for (j = 0; j < len; j++)
+                       printf("%c", imsi[j]);
+               printf(":");
+               for (j = 0; j < 8; j++)
+                       printf("%02X", kc[j]);
+               printf(":");
+               for (j = 0; j < 4; j++)
+                       printf("%02X", sres[j]);
+               printf(":");
+               for (j = 0; j < 16; j++)
+                       printf("%02X", _rand[j]);
+               printf("\n");
+       }
+
+       scard_deinit(scard);
+
+       return 0;
+}
+
+
+static void eapol_test_terminate(int sig, void *signal_ctx)
+{
+       struct wpa_supplicant *wpa_s = signal_ctx;
+       wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
+       eloop_terminate();
+}
+
+
+static void usage(void)
+{
+       printf("usage:\n"
+              "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+              "[-s<AS secret>]\\\n"
+              "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
+              "           [-M<client MAC address>] \\\n"
+              "           [-N<attr spec>] \\\n"
+              "           [-A<client IP>]\n"
+              "eapol_test scard\n"
+              "eapol_test sim <PIN> <num triplets> [debug]\n"
+              "\n");
+       printf("options:\n"
+              "  -c<conf> = configuration file\n"
+              "  -a<AS IP> = IP address of the authentication server, "
+              "default 127.0.0.1\n"
+              "  -p<AS port> = UDP port of the authentication server, "
+              "default 1812\n"
+              "  -s<AS secret> = shared secret with the authentication "
+              "server, default 'radius'\n"
+              "  -A<client IP> = IP address of the client, default: select "
+              "automatically\n"
+              "  -r<count> = number of re-authentications\n"
+              "  -W = wait for a control interface monitor before starting\n"
+              "  -S = save configuration after authentication\n"
+              "  -n = no MPPE keys expected\n"
+              "  -t<timeout> = sets timeout in seconds (default: 30 s)\n"
+              "  -C<Connect-Info> = RADIUS Connect-Info (default: "
+              "CONNECT 11Mbps 802.11b)\n"
+              "  -M<client MAC address> = Set own MAC address "
+              "(Calling-Station-Id,\n"
+              "                           default: 02:00:00:00:00:01)\n"
+              "  -N<attr spec> = send arbitrary attribute specified by:\n"
+              "                  attr_id:syntax:value or attr_id\n"
+              "                  attr_id - number id of the attribute\n"
+              "                  syntax - one of: s, d, x\n"
+              "                     s = string\n"
+              "                     d = integer\n"
+              "                     x = octet string\n"
+              "                  value - attribute value.\n"
+              "       When only attr_id is specified, NULL will be used as "
+              "value.\n"
+              "       Multiple attributes can be specified by using the "
+              "option several times.\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct wpa_supplicant wpa_s;
+       int c, ret = 1, wait_for_monitor = 0, save_config = 0;
+       char *as_addr = "127.0.0.1";
+       int as_port = 1812;
+       char *as_secret = "radius";
+       char *cli_addr = NULL;
+       char *conf = NULL;
+       int timeout = 30;
+       char *pos;
+       struct extra_radius_attr *p = NULL, *p1;
+
+       if (os_program_init())
+               return -1;
+
+       hostapd_logger_register_cb(hostapd_logger_cb);
+
+       os_memset(&eapol_test, 0, sizeof(eapol_test));
+       eapol_test.connect_info = "CONNECT 11Mbps 802.11b";
+       os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN);
+
+       wpa_debug_level = 0;
+       wpa_debug_show_keys = 1;
+
+       for (;;) {
+               c = getopt(argc, argv, "a:A:c:C:M:nN:p:r:s:St:W");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'a':
+                       as_addr = optarg;
+                       break;
+               case 'A':
+                       cli_addr = optarg;
+                       break;
+               case 'c':
+                       conf = optarg;
+                       break;
+               case 'C':
+                       eapol_test.connect_info = optarg;
+                       break;
+               case 'M':
+                       if (hwaddr_aton(optarg, eapol_test.own_addr)) {
+                               usage();
+                               return -1;
+                       }
+                       break;
+               case 'n':
+                       eapol_test.no_mppe_keys++;
+                       break;
+               case 'p':
+                       as_port = atoi(optarg);
+                       break;
+               case 'r':
+                       eapol_test.eapol_test_num_reauths = atoi(optarg);
+                       break;
+               case 's':
+                       as_secret = optarg;
+                       break;
+               case 'S':
+                       save_config++;
+                       break;
+               case 't':
+                       timeout = atoi(optarg);
+                       break;
+               case 'W':
+                       wait_for_monitor++;
+                       break;
+               case 'N':
+                       p1 = os_zalloc(sizeof(p1));
+                       if (p1 == NULL)
+                               break;
+                       if (!p)
+                               eapol_test.extra_attrs = p1;
+                       else
+                               p->next = p1;
+                       p = p1;
+
+                       p->type = atoi(optarg);
+                       pos = os_strchr(optarg, ':');
+                       if (pos == NULL) {
+                               p->syntax = 'n';
+                               p->data = NULL;
+                               break;
+                       }
+
+                       pos++;
+                       if (pos[0] == '\0' || pos[1] != ':') {
+                               printf("Incorrect format of attribute "
+                                      "specification\n");
+                               break;
+                       }
+
+                       p->syntax = pos[0];
+                       p->data = pos + 2;
+                       break;
+               default:
+                       usage();
+                       return -1;
+               }
+       }
+
+       if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
+               return scard_test();
+       }
+
+       if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
+               return scard_get_triplets(argc - optind - 1,
+                                         &argv[optind + 1]);
+       }
+
+       if (conf == NULL) {
+               usage();
+               printf("Configuration file is required.\n");
+               return -1;
+       }
+
+       if (eap_register_methods()) {
+               wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+               return -1;
+       }
+
+       if (eloop_init()) {
+               wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+               return -1;
+       }
+
+       os_memset(&wpa_s, 0, sizeof(wpa_s));
+       eapol_test.wpa_s = &wpa_s;
+       wpa_s.conf = wpa_config_read(conf);
+       if (wpa_s.conf == NULL) {
+               printf("Failed to parse configuration file '%s'.\n", conf);
+               return -1;
+       }
+       if (wpa_s.conf->ssid == NULL) {
+               printf("No networks defined.\n");
+               return -1;
+       }
+
+       wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
+                     cli_addr);
+       wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
+       if (wpa_s.ctrl_iface == NULL) {
+               printf("Failed to initialize control interface '%s'.\n"
+                      "You may have another eapol_test process already "
+                      "running or the file was\n"
+                      "left by an unclean termination of eapol_test in "
+                      "which case you will need\n"
+                      "to manually remove this file before starting "
+                      "eapol_test again.\n",
+                      wpa_s.conf->ctrl_interface);
+               return -1;
+       }
+       if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+               return -1;
+
+       if (test_eapol(&eapol_test, &wpa_s, wpa_s.conf->ssid))
+               return -1;
+
+       if (wait_for_monitor)
+               wpa_supplicant_ctrl_iface_wait(wpa_s.ctrl_iface);
+
+       eloop_register_timeout(timeout, 0, eapol_test_timeout, &eapol_test,
+                              NULL);
+       eloop_register_timeout(0, 0, send_eap_request_identity, &wpa_s, NULL);
+       eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
+       eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
+       eloop_run();
+
+       eloop_cancel_timeout(eapol_test_timeout, &eapol_test, NULL);
+       eloop_cancel_timeout(eapol_sm_reauth, &eapol_test, NULL);
+
+       if (eapol_test_compare_pmk(&eapol_test) == 0 ||
+           eapol_test.no_mppe_keys)
+               ret = 0;
+       if (eapol_test.auth_timed_out)
+               ret = -2;
+       if (eapol_test.radius_access_reject_received)
+               ret = -3;
+
+       if (save_config)
+               wpa_config_write(conf, wpa_s.conf);
+
+       test_eapol_clean(&eapol_test, &wpa_s);
+
+       eap_peer_unregister_methods();
+
+       eloop_destroy();
+
+       printf("MPPE keys OK: %d  mismatch: %d\n",
+              eapol_test.num_mppe_ok, eapol_test.num_mppe_mismatch);
+       if (eapol_test.num_mppe_mismatch)
+               ret = -4;
+       if (ret)
+               printf("FAILURE\n");
+       else
+               printf("SUCCESS\n");
+
+       os_program_deinit();
+
+       return ret;
+}
diff --git a/wpa_supplicant/events.c b/wpa_supplicant/events.c
new file mode 100644 (file)
index 0000000..85dcfb2
--- /dev/null
@@ -0,0 +1,1729 @@
+/*
+ * WPA Supplicant - Driver event processing
+ * Copyright (c) 2003-2010, 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 "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "pcsc_funcs.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "common/wpa_ctrl.h"
+#include "eap_peer/eap.h"
+#include "ap/hostapd.h"
+#include "notify.h"
+#include "common/ieee802_11_defs.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "sme.h"
+#include "bgscan.h"
+#include "ap.h"
+#include "bss.h"
+#include "mlme.h"
+#include "scan.h"
+
+
+static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid, *old_ssid;
+
+       if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "Select network based on association "
+                  "information");
+       ssid = wpa_supplicant_get_ssid(wpa_s);
+       if (ssid == NULL) {
+               wpa_printf(MSG_INFO, "No network configuration found for the "
+                          "current AP");
+               return -1;
+       }
+
+       if (ssid->disabled) {
+               wpa_printf(MSG_DEBUG, "Selected network is disabled");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "Network configuration found for the current "
+                  "AP");
+       if (ssid->key_mgmt & (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
+                             WPA_KEY_MGMT_WPA_NONE |
+                             WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_FT_IEEE8021X |
+                             WPA_KEY_MGMT_PSK_SHA256 |
+                             WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+               u8 wpa_ie[80];
+               size_t wpa_ie_len = sizeof(wpa_ie);
+               wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+                                         wpa_ie, &wpa_ie_len);
+       } else {
+               wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+       }
+
+       if (wpa_s->current_ssid && wpa_s->current_ssid != ssid)
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = ssid;
+       wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+       wpa_supplicant_initiate_eapol(wpa_s);
+       if (old_ssid != wpa_s->current_ssid)
+               wpas_notify_network_changed(wpa_s);
+
+       return 0;
+}
+
+
+static void wpa_supplicant_stop_countermeasures(void *eloop_ctx,
+                                               void *sock_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       if (wpa_s->countermeasures) {
+               wpa_s->countermeasures = 0;
+               wpa_drv_set_countermeasures(wpa_s, 0);
+               wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       }
+}
+
+
+void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
+{
+       int bssid_changed;
+
+       wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+       bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+       os_memset(wpa_s->bssid, 0, ETH_ALEN);
+       os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+       wpa_s->current_bss = NULL;
+       if (bssid_changed)
+               wpas_notify_bssid_changed(wpa_s);
+
+       eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+       eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+       if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt))
+               eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+       wpa_s->ap_ies_from_associnfo = 0;
+}
+
+
+static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ie_data ie;
+       int pmksa_set = -1;
+       size_t i;
+
+       if (wpa_sm_parse_own_wpa_ie(wpa_s->wpa, &ie) < 0 ||
+           ie.pmkid == NULL)
+               return;
+
+       for (i = 0; i < ie.num_pmkid; i++) {
+               pmksa_set = pmksa_cache_set_current(wpa_s->wpa,
+                                                   ie.pmkid + i * PMKID_LEN,
+                                                   NULL, NULL, 0);
+               if (pmksa_set == 0) {
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+                       break;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "RSN: PMKID from assoc IE %sfound from PMKSA "
+                  "cache", pmksa_set == 0 ? "" : "not ");
+}
+
+
+static void wpa_supplicant_event_pmkid_candidate(struct wpa_supplicant *wpa_s,
+                                                union wpa_event_data *data)
+{
+       if (data == NULL) {
+               wpa_printf(MSG_DEBUG, "RSN: No data in PMKID candidate event");
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "RSN: PMKID candidate event - bssid=" MACSTR
+                  " index=%d preauth=%d",
+                  MAC2STR(data->pmkid_candidate.bssid),
+                  data->pmkid_candidate.index,
+                  data->pmkid_candidate.preauth);
+
+       pmksa_candidate_add(wpa_s->wpa, data->pmkid_candidate.bssid,
+                           data->pmkid_candidate.index,
+                           data->pmkid_candidate.preauth);
+}
+
+
+static int wpa_supplicant_dynamic_keys(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+               return 0;
+
+#ifdef IEEE8021X_EAPOL
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+           wpa_s->current_ssid &&
+           !(wpa_s->current_ssid->eapol_flags &
+             (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+              EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) {
+               /* IEEE 802.1X, but not using dynamic WEP keys (i.e., either
+                * plaintext or static WEP keys). */
+               return 0;
+       }
+#endif /* IEEE8021X_EAPOL */
+
+       return 1;
+}
+
+
+/**
+ * wpa_supplicant_scard_init - Initialize SIM/USIM access with PC/SC
+ * @wpa_s: pointer to wpa_supplicant data
+ * @ssid: Configuration data for the network
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called when starting authentication with a network that is
+ * configured to use PC/SC for SIM/USIM access (EAP-SIM or EAP-AKA).
+ */
+int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid)
+{
+#ifdef IEEE8021X_EAPOL
+       int aka = 0, sim = 0, type;
+
+       if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
+               return 0;
+
+       if (ssid->eap.eap_methods == NULL) {
+               sim = 1;
+               aka = 1;
+       } else {
+               struct eap_method_type *eap = ssid->eap.eap_methods;
+               while (eap->vendor != EAP_VENDOR_IETF ||
+                      eap->method != EAP_TYPE_NONE) {
+                       if (eap->vendor == EAP_VENDOR_IETF) {
+                               if (eap->method == EAP_TYPE_SIM)
+                                       sim = 1;
+                               else if (eap->method == EAP_TYPE_AKA)
+                                       aka = 1;
+                       }
+                       eap++;
+               }
+       }
+
+       if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_SIM) == NULL)
+               sim = 0;
+       if (eap_peer_get_eap_method(EAP_VENDOR_IETF, EAP_TYPE_AKA) == NULL)
+               aka = 0;
+
+       if (!sim && !aka) {
+               wpa_printf(MSG_DEBUG, "Selected network is configured to use "
+                          "SIM, but neither EAP-SIM nor EAP-AKA are enabled");
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "Selected network is configured to use SIM "
+                  "(sim=%d aka=%d) - initialize PCSC", sim, aka);
+       if (sim && aka)
+               type = SCARD_TRY_BOTH;
+       else if (aka)
+               type = SCARD_USIM_ONLY;
+       else
+               type = SCARD_GSM_SIM_ONLY;
+
+       wpa_s->scard = scard_init(type);
+       if (wpa_s->scard == NULL) {
+               wpa_printf(MSG_WARNING, "Failed to initialize SIM "
+                          "(pcsc-lite)");
+               return -1;
+       }
+       wpa_sm_set_scard_ctx(wpa_s->wpa, wpa_s->scard);
+       eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
+#endif /* IEEE8021X_EAPOL */
+
+       return 0;
+}
+
+
+#ifndef CONFIG_NO_SCAN_PROCESSING
+static int wpa_supplicant_match_privacy(struct wpa_scan_res *bss,
+                                       struct wpa_ssid *ssid)
+{
+       int i, privacy = 0;
+
+       if (ssid->mixed_cell)
+               return 1;
+
+#ifdef CONFIG_WPS
+       if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+               return 1;
+#endif /* CONFIG_WPS */
+
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (ssid->wep_key_len[i]) {
+                       privacy = 1;
+                       break;
+               }
+       }
+#ifdef IEEE8021X_EAPOL
+       if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+           ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+                                EAPOL_FLAG_REQUIRE_KEY_BROADCAST))
+               privacy = 1;
+#endif /* IEEE8021X_EAPOL */
+
+       if (bss->caps & IEEE80211_CAP_PRIVACY)
+               return privacy;
+       return !privacy;
+}
+
+
+static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
+                                        struct wpa_ssid *ssid,
+                                        struct wpa_scan_res *bss)
+{
+       struct wpa_ie_data ie;
+       int proto_match = 0;
+       const u8 *rsn_ie, *wpa_ie;
+       int ret;
+
+       ret = wpas_wps_ssid_bss_match(wpa_s, ssid, bss);
+       if (ret >= 0)
+               return ret;
+
+       rsn_ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+       while ((ssid->proto & WPA_PROTO_RSN) && rsn_ie) {
+               proto_match++;
+
+               if (wpa_parse_wpa_ie(rsn_ie, 2 + rsn_ie[1], &ie)) {
+                       wpa_printf(MSG_DEBUG, "   skip RSN IE - parse failed");
+                       break;
+               }
+               if (!(ie.proto & ssid->proto)) {
+                       wpa_printf(MSG_DEBUG, "   skip RSN IE - proto "
+                                  "mismatch");
+                       break;
+               }
+
+               if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+                       wpa_printf(MSG_DEBUG, "   skip RSN IE - PTK cipher "
+                                  "mismatch");
+                       break;
+               }
+
+               if (!(ie.group_cipher & ssid->group_cipher)) {
+                       wpa_printf(MSG_DEBUG, "   skip RSN IE - GTK cipher "
+                                  "mismatch");
+                       break;
+               }
+
+               if (!(ie.key_mgmt & ssid->key_mgmt)) {
+                       wpa_printf(MSG_DEBUG, "   skip RSN IE - key mgmt "
+                                  "mismatch");
+                       break;
+               }
+
+#ifdef CONFIG_IEEE80211W
+               if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
+                   ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+                       wpa_printf(MSG_DEBUG, "   skip RSN IE - no mgmt frame "
+                                  "protection");
+                       break;
+               }
+#endif /* CONFIG_IEEE80211W */
+
+               wpa_printf(MSG_DEBUG, "   selected based on RSN IE");
+               return 1;
+       }
+
+       wpa_ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       while ((ssid->proto & WPA_PROTO_WPA) && wpa_ie) {
+               proto_match++;
+
+               if (wpa_parse_wpa_ie(wpa_ie, 2 + wpa_ie[1], &ie)) {
+                       wpa_printf(MSG_DEBUG, "   skip WPA IE - parse failed");
+                       break;
+               }
+               if (!(ie.proto & ssid->proto)) {
+                       wpa_printf(MSG_DEBUG, "   skip WPA IE - proto "
+                                  "mismatch");
+                       break;
+               }
+
+               if (!(ie.pairwise_cipher & ssid->pairwise_cipher)) {
+                       wpa_printf(MSG_DEBUG, "   skip WPA IE - PTK cipher "
+                                  "mismatch");
+                       break;
+               }
+
+               if (!(ie.group_cipher & ssid->group_cipher)) {
+                       wpa_printf(MSG_DEBUG, "   skip WPA IE - GTK cipher "
+                                  "mismatch");
+                       break;
+               }
+
+               if (!(ie.key_mgmt & ssid->key_mgmt)) {
+                       wpa_printf(MSG_DEBUG, "   skip WPA IE - key mgmt "
+                                  "mismatch");
+                       break;
+               }
+
+               wpa_printf(MSG_DEBUG, "   selected based on WPA IE");
+               return 1;
+       }
+
+       if (proto_match == 0)
+               wpa_printf(MSG_DEBUG, "   skip - no WPA/RSN proto match");
+
+       return 0;
+}
+
+
+static int freq_allowed(int *freqs, int freq)
+{
+       int i;
+
+       if (freqs == NULL)
+               return 1;
+
+       for (i = 0; freqs[i]; i++)
+               if (freqs[i] == freq)
+                       return 1;
+       return 0;
+}
+
+
+static struct wpa_bss *
+wpa_supplicant_select_bss_wpa(struct wpa_supplicant *wpa_s,
+                             struct wpa_scan_results *scan_res,
+                             struct wpa_ssid *group,
+                             struct wpa_ssid **selected_ssid)
+{
+       struct wpa_ssid *ssid;
+       struct wpa_scan_res *bss;
+       size_t i;
+       struct wpa_blacklist *e;
+       const u8 *ie;
+
+       wpa_printf(MSG_DEBUG, "Try to find WPA-enabled AP");
+       for (i = 0; i < scan_res->num; i++) {
+               const u8 *ssid_;
+               u8 wpa_ie_len, rsn_ie_len, ssid_len;
+               bss = scan_res->res[i];
+
+               ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+               ssid_ = ie ? ie + 2 : (u8 *) "";
+               ssid_len = ie ? ie[1] : 0;
+
+               ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+               wpa_ie_len = ie ? ie[1] : 0;
+
+               ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+               rsn_ie_len = ie ? ie[1] : 0;
+
+               wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+                          "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
+                          (int) i, MAC2STR(bss->bssid),
+                          wpa_ssid_txt(ssid_, ssid_len),
+                          wpa_ie_len, rsn_ie_len, bss->caps);
+
+               e = wpa_blacklist_get(wpa_s, bss->bssid);
+               if (e && e->count > 1) {
+                       wpa_printf(MSG_DEBUG, "   skip - blacklisted");
+                       continue;
+               }
+
+               if (ssid_len == 0) {
+                       wpa_printf(MSG_DEBUG, "   skip - SSID not known");
+                       continue;
+               }
+
+               if (wpa_ie_len == 0 && rsn_ie_len == 0) {
+                       wpa_printf(MSG_DEBUG, "   skip - no WPA/RSN IE");
+                       continue;
+               }
+
+               for (ssid = group; ssid; ssid = ssid->pnext) {
+                       int check_ssid = 1;
+
+                       if (ssid->disabled) {
+                               wpa_printf(MSG_DEBUG, "   skip - disabled");
+                               continue;
+                       }
+
+#ifdef CONFIG_WPS
+                       if (ssid->ssid_len == 0 &&
+                           wpas_wps_ssid_wildcard_ok(wpa_s, ssid, bss))
+                               check_ssid = 0;
+#endif /* CONFIG_WPS */
+
+                       if (check_ssid &&
+                           (ssid_len != ssid->ssid_len ||
+                            os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "SSID mismatch");
+                               continue;
+                       }
+
+                       if (ssid->bssid_set &&
+                           os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
+                       {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "BSSID mismatch");
+                               continue;
+                       }
+
+                       if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
+                               continue;
+
+                       if (!freq_allowed(ssid->freq_list, bss->freq)) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "frequency not allowed");
+                               continue;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "   selected WPA AP "
+                                  MACSTR " ssid='%s'",
+                                  MAC2STR(bss->bssid),
+                                  wpa_ssid_txt(ssid_, ssid_len));
+                       *selected_ssid = ssid;
+                       return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
+               }
+       }
+
+       return NULL;
+}
+
+
+static struct wpa_bss *
+wpa_supplicant_select_bss_non_wpa(struct wpa_supplicant *wpa_s,
+                                 struct wpa_scan_results *scan_res,
+                                 struct wpa_ssid *group,
+                                 struct wpa_ssid **selected_ssid)
+{
+       struct wpa_ssid *ssid;
+       struct wpa_scan_res *bss;
+       size_t i;
+       struct wpa_blacklist *e;
+       const u8 *ie;
+
+       wpa_printf(MSG_DEBUG, "Try to find non-WPA AP");
+       for (i = 0; i < scan_res->num; i++) {
+               const u8 *ssid_;
+               u8 wpa_ie_len, rsn_ie_len, ssid_len;
+               bss = scan_res->res[i];
+
+               ie = wpa_scan_get_ie(bss, WLAN_EID_SSID);
+               ssid_ = ie ? ie + 2 : (u8 *) "";
+               ssid_len = ie ? ie[1] : 0;
+
+               ie = wpa_scan_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+               wpa_ie_len = ie ? ie[1] : 0;
+
+               ie = wpa_scan_get_ie(bss, WLAN_EID_RSN);
+               rsn_ie_len = ie ? ie[1] : 0;
+
+               wpa_printf(MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
+                          "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x",
+                          (int) i, MAC2STR(bss->bssid),
+                          wpa_ssid_txt(ssid_, ssid_len),
+                          wpa_ie_len, rsn_ie_len, bss->caps);
+
+               e = wpa_blacklist_get(wpa_s, bss->bssid);
+               if (e && e->count > 1) {
+                       wpa_printf(MSG_DEBUG, "   skip - blacklisted");
+                       continue;
+               }
+
+               if (ssid_len == 0) {
+                       wpa_printf(MSG_DEBUG, "   skip - SSID not known");
+                       continue;
+               }
+
+               for (ssid = group; ssid; ssid = ssid->pnext) {
+                       int check_ssid = ssid->ssid_len != 0;
+
+                       if (ssid->disabled) {
+                               wpa_printf(MSG_DEBUG, "   skip - disabled");
+                               continue;
+                       }
+
+#ifdef CONFIG_WPS
+                       if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+                               /* Only allow wildcard SSID match if an AP
+                                * advertises active WPS operation that matches
+                                * with our mode. */
+                               check_ssid = 1;
+                               if (ssid->ssid_len == 0 &&
+                                   wpas_wps_ssid_wildcard_ok(wpa_s, ssid,
+                                                             bss))
+                                       check_ssid = 0;
+                       }
+#endif /* CONFIG_WPS */
+
+                       if (check_ssid &&
+                           (ssid_len != ssid->ssid_len ||
+                            os_memcmp(ssid_, ssid->ssid, ssid_len) != 0)) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "SSID mismatch");
+                               continue;
+                       }
+
+                       if (ssid->bssid_set &&
+                           os_memcmp(bss->bssid, ssid->bssid, ETH_ALEN) != 0)
+                       {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "BSSID mismatch");
+                               continue;
+                       }
+
+                       if (!(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
+                           !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
+                           !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA))
+                       {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "non-WPA network not allowed");
+                               continue;
+                       }
+
+                       if ((ssid->key_mgmt &
+                            (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
+                             WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK |
+                             WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                             WPA_KEY_MGMT_PSK_SHA256)) &&
+                           (wpa_ie_len != 0 || rsn_ie_len != 0)) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "WPA network");
+                               continue;
+                       }
+
+                       if (!wpa_supplicant_match_privacy(bss, ssid)) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "privacy mismatch");
+                               continue;
+                       }
+
+                       if (bss->caps & IEEE80211_CAP_IBSS) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "IBSS (adhoc) network");
+                               continue;
+                       }
+
+                       if (!freq_allowed(ssid->freq_list, bss->freq)) {
+                               wpa_printf(MSG_DEBUG, "   skip - "
+                                          "frequency not allowed");
+                               continue;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "   selected non-WPA AP "
+                                  MACSTR " ssid='%s'",
+                                  MAC2STR(bss->bssid),
+                                  wpa_ssid_txt(ssid_, ssid_len));
+                       *selected_ssid = ssid;
+                       return wpa_bss_get(wpa_s, bss->bssid, ssid_, ssid_len);
+               }
+       }
+
+       return NULL;
+}
+
+
+static struct wpa_bss *
+wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
+                         struct wpa_scan_results *scan_res,
+                         struct wpa_ssid *group,
+                         struct wpa_ssid **selected_ssid)
+{
+       struct wpa_bss *selected;
+
+       wpa_printf(MSG_DEBUG, "Selecting BSS from priority group %d",
+                  group->priority);
+
+       /* First, try to find WPA-enabled AP */
+       selected = wpa_supplicant_select_bss_wpa(wpa_s, scan_res, group,
+                                                selected_ssid);
+       if (selected)
+               return selected;
+
+       /* If no WPA-enabled AP found, try to find non-WPA AP, if configuration
+        * allows this. */
+       return wpa_supplicant_select_bss_non_wpa(wpa_s, scan_res, group,
+                                                selected_ssid);
+}
+
+
+static struct wpa_bss *
+wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
+                           struct wpa_scan_results *scan_res,
+                           struct wpa_ssid **selected_ssid)
+{
+       struct wpa_bss *selected = NULL;
+       int prio;
+
+       while (selected == NULL) {
+               for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+                       selected = wpa_supplicant_select_bss(
+                               wpa_s, scan_res, wpa_s->conf->pssid[prio],
+                               selected_ssid);
+                       if (selected)
+                               break;
+               }
+
+               if (selected == NULL && wpa_s->blacklist) {
+                       wpa_printf(MSG_DEBUG, "No APs found - clear blacklist "
+                                  "and try again");
+                       wpa_blacklist_clear(wpa_s);
+                       wpa_s->blacklist_cleared++;
+               } else if (selected == NULL)
+                       break;
+       }
+
+       return selected;
+}
+
+
+static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
+                                       int timeout_sec, int timeout_usec)
+{
+       if (!wpa_supplicant_enabled_networks(wpa_s->conf)) {
+               /*
+                * No networks are enabled; short-circuit request so
+                * we don't wait timeout seconds before transitioning
+                * to INACTIVE state.
+                */
+               wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+               return;
+       }
+       wpa_supplicant_req_scan(wpa_s, timeout_sec, timeout_usec);
+}
+
+
+void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+                           struct wpa_bss *selected,
+                           struct wpa_ssid *ssid)
+{
+       if (wpas_wps_scan_pbc_overlap(wpa_s, selected, ssid)) {
+               wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
+                       "PBC session overlap");
+               wpa_supplicant_req_new_scan(wpa_s, 10, 0);
+               return;
+       }
+
+       /*
+        * Do not trigger new association unless the BSSID has changed or if
+        * reassociation is requested. If we are in process of associating with
+        * the selected BSSID, do not trigger new attempt.
+        */
+       if (wpa_s->reassociate ||
+           (os_memcmp(selected->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
+            (wpa_s->wpa_state != WPA_ASSOCIATING ||
+             os_memcmp(selected->bssid, wpa_s->pending_bssid, ETH_ALEN) !=
+             0))) {
+               if (wpa_supplicant_scard_init(wpa_s, ssid)) {
+                       wpa_supplicant_req_new_scan(wpa_s, 10, 0);
+                       return;
+               }
+               wpa_supplicant_associate(wpa_s, selected, ssid);
+       } else {
+               wpa_printf(MSG_DEBUG, "Already associated with the selected "
+                          "AP");
+       }
+}
+
+
+static struct wpa_ssid *
+wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
+{
+       int prio;
+       struct wpa_ssid *ssid;
+
+       for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+               for (ssid = wpa_s->conf->pssid[prio]; ssid; ssid = ssid->pnext)
+               {
+                       if (ssid->disabled)
+                               continue;
+                       if (ssid->mode == IEEE80211_MODE_IBSS ||
+                           ssid->mode == IEEE80211_MODE_AP)
+                               return ssid;
+               }
+       }
+       return NULL;
+}
+
+
+/* TODO: move the rsn_preauth_scan_result*() to be called from notify.c based
+ * on BSS added and BSS changed events */
+static void wpa_supplicant_rsn_preauth_scan_results(
+       struct wpa_supplicant *wpa_s, struct wpa_scan_results *scan_res)
+{
+       int i;
+
+       if (rsn_preauth_scan_results(wpa_s->wpa) < 0)
+               return;
+
+       for (i = scan_res->num - 1; i >= 0; i--) {
+               const u8 *ssid, *rsn;
+               struct wpa_scan_res *r;
+
+               r = scan_res->res[i];
+
+               ssid = wpa_scan_get_ie(r, WLAN_EID_SSID);
+               if (ssid == NULL)
+                       continue;
+
+               rsn = wpa_scan_get_ie(r, WLAN_EID_RSN);
+               if (rsn == NULL)
+                       continue;
+
+               rsn_preauth_scan_result(wpa_s->wpa, r->bssid, ssid, rsn);
+       }
+
+}
+
+
+static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
+                                      struct wpa_bss *selected,
+                                      struct wpa_ssid *ssid,
+                                      struct wpa_scan_results *scan_res)
+{
+       size_t i;
+       struct wpa_scan_res *current_bss = NULL;
+       int min_diff;
+
+       if (wpa_s->reassociate)
+               return 1; /* explicit request to reassociate */
+       if (wpa_s->wpa_state < WPA_ASSOCIATED)
+               return 1; /* we are not associated; continue */
+       if (wpa_s->current_ssid == NULL)
+               return 1; /* unknown current SSID */
+       if (wpa_s->current_ssid != ssid)
+               return 1; /* different network block */
+
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *res = scan_res->res[i];
+               const u8 *ie;
+               if (os_memcmp(res->bssid, wpa_s->bssid, ETH_ALEN) != 0)
+                       continue;
+
+               ie = wpa_scan_get_ie(res, WLAN_EID_SSID);
+               if (ie == NULL)
+                       continue;
+               if (ie[1] != wpa_s->current_ssid->ssid_len ||
+                   os_memcmp(ie + 2, wpa_s->current_ssid->ssid, ie[1]) != 0)
+                       continue;
+               current_bss = res;
+               break;
+       }
+
+       if (!current_bss)
+               return 1; /* current BSS not seen in scan results */
+
+       wpa_printf(MSG_DEBUG, "Considering within-ESS reassociation");
+       wpa_printf(MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
+                  MAC2STR(current_bss->bssid), current_bss->level);
+       wpa_printf(MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
+                  MAC2STR(selected->bssid), selected->level);
+
+       if (wpa_s->current_ssid->bssid_set &&
+           os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
+           0) {
+               wpa_printf(MSG_DEBUG, "Allow reassociation - selected BSS has "
+                          "preferred BSSID");
+               return 1;
+       }
+
+       min_diff = 2;
+       if (current_bss->level < 0) {
+               if (current_bss->level < -85)
+                       min_diff = 1;
+               else if (current_bss->level < -80)
+                       min_diff = 2;
+               else if (current_bss->level < -75)
+                       min_diff = 3;
+               else if (current_bss->level < -70)
+                       min_diff = 4;
+               else
+                       min_diff = 5;
+       }
+       if (abs(current_bss->level - selected->level) < min_diff) {
+               wpa_printf(MSG_DEBUG, "Skip roam - too small difference in "
+                          "signal level");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+                                             union wpa_event_data *data)
+{
+       struct wpa_bss *selected;
+       struct wpa_ssid *ssid = NULL;
+       struct wpa_scan_results *scan_res;
+       int ap = 0;
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface)
+               ap = 1;
+#endif /* CONFIG_AP */
+
+       wpa_supplicant_notify_scanning(wpa_s, 0);
+
+       scan_res = wpa_supplicant_get_scan_results(wpa_s,
+                                                  data ? &data->scan_info :
+                                                  NULL, 1);
+       if (scan_res == NULL) {
+               if (wpa_s->conf->ap_scan == 2 || ap)
+                       return;
+               wpa_printf(MSG_DEBUG, "Failed to get scan results - try "
+                          "scanning again");
+               wpa_supplicant_req_new_scan(wpa_s, 1, 0);
+               return;
+       }
+
+       if (wpa_s->scan_res_handler) {
+               wpa_s->scan_res_handler(wpa_s, scan_res);
+               wpa_s->scan_res_handler = NULL;
+               wpa_scan_results_free(scan_res);
+               return;
+       }
+
+       if (ap) {
+               wpa_printf(MSG_DEBUG, "Ignore scan results in AP mode");
+               wpa_scan_results_free(scan_res);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "New scan results available");
+       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+       wpas_notify_scan_results(wpa_s);
+
+       wpas_notify_scan_done(wpa_s, 1);
+
+       if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
+               wpa_scan_results_free(scan_res);
+               return;
+       }
+
+       if (wpa_s->disconnected) {
+               wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+               wpa_scan_results_free(scan_res);
+               return;
+       }
+
+       if (bgscan_notify_scan(wpa_s) == 1) {
+               wpa_scan_results_free(scan_res);
+               return;
+       }
+
+       wpa_supplicant_rsn_preauth_scan_results(wpa_s, scan_res);
+
+       selected = wpa_supplicant_pick_network(wpa_s, scan_res, &ssid);
+
+       if (selected) {
+               int skip;
+               skip = !wpa_supplicant_need_to_roam(wpa_s, selected, ssid,
+                                                   scan_res);
+               wpa_scan_results_free(scan_res);
+               if (skip)
+                       return;
+               wpa_supplicant_connect(wpa_s, selected, ssid);
+       } else {
+               wpa_scan_results_free(scan_res);
+               wpa_printf(MSG_DEBUG, "No suitable network found");
+               ssid = wpa_supplicant_pick_new_network(wpa_s);
+               if (ssid) {
+                       wpa_printf(MSG_DEBUG, "Setup a new network");
+                       wpa_supplicant_associate(wpa_s, NULL, ssid);
+               } else {
+                       int timeout_sec = 5;
+                       int timeout_usec = 0;
+                       wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+                                                   timeout_usec);
+               }
+       }
+}
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+
+
+static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
+                                         union wpa_event_data *data)
+{
+       int l, len, found = 0, wpa_found, rsn_found;
+       const u8 *p;
+
+       wpa_printf(MSG_DEBUG, "Association info event");
+       if (data->assoc_info.req_ies)
+               wpa_hexdump(MSG_DEBUG, "req_ies", data->assoc_info.req_ies,
+                           data->assoc_info.req_ies_len);
+       if (data->assoc_info.resp_ies)
+               wpa_hexdump(MSG_DEBUG, "resp_ies", data->assoc_info.resp_ies,
+                           data->assoc_info.resp_ies_len);
+       if (data->assoc_info.beacon_ies)
+               wpa_hexdump(MSG_DEBUG, "beacon_ies",
+                           data->assoc_info.beacon_ies,
+                           data->assoc_info.beacon_ies_len);
+       if (data->assoc_info.freq)
+               wpa_printf(MSG_DEBUG, "freq=%u MHz", data->assoc_info.freq);
+
+       p = data->assoc_info.req_ies;
+       l = data->assoc_info.req_ies_len;
+
+       /* Go through the IEs and make a copy of the WPA/RSN IE, if present. */
+       while (p && l >= 2) {
+               len = p[1] + 2;
+               if (len > l) {
+                       wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+                                   p, l);
+                       break;
+               }
+               if ((p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+                    (os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0)) ||
+                   (p[0] == WLAN_EID_RSN && p[1] >= 2)) {
+                       if (wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, p, len))
+                               break;
+                       found = 1;
+                       wpa_find_assoc_pmkid(wpa_s);
+                       break;
+               }
+               l -= len;
+               p += len;
+       }
+       if (!found && data->assoc_info.req_ies)
+               wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef CONFIG_IEEE80211R
+#ifdef CONFIG_SME
+       if (wpa_s->sme.auth_alg == WPA_AUTH_ALG_FT) {
+               u8 bssid[ETH_ALEN];
+               if (wpa_drv_get_bssid(wpa_s, bssid) < 0 ||
+                   wpa_ft_validate_reassoc_resp(wpa_s->wpa,
+                                                data->assoc_info.resp_ies,
+                                                data->assoc_info.resp_ies_len,
+                                                bssid) < 0) {
+                       wpa_printf(MSG_DEBUG, "FT: Validation of "
+                                  "Reassociation Response failed");
+                       wpa_supplicant_deauthenticate(
+                               wpa_s, WLAN_REASON_INVALID_IE);
+                       return -1;
+               }
+       }
+
+       p = data->assoc_info.resp_ies;
+       l = data->assoc_info.resp_ies_len;
+
+       /* Go through the IEs and make a copy of the MDIE, if present. */
+       while (p && l >= 2) {
+               len = p[1] + 2;
+               if (len > l) {
+                       wpa_hexdump(MSG_DEBUG, "Truncated IE in assoc_info",
+                                   p, l);
+                       break;
+               }
+               if (p[0] == WLAN_EID_MOBILITY_DOMAIN &&
+                   p[1] >= MOBILITY_DOMAIN_ID_LEN) {
+                       wpa_s->sme.ft_used = 1;
+                       os_memcpy(wpa_s->sme.mobility_domain, p + 2,
+                                 MOBILITY_DOMAIN_ID_LEN);
+                       break;
+               }
+               l -= len;
+               p += len;
+       }
+#endif /* CONFIG_SME */
+
+       wpa_sm_set_ft_params(wpa_s->wpa, data->assoc_info.resp_ies,
+                            data->assoc_info.resp_ies_len);
+#endif /* CONFIG_IEEE80211R */
+
+       /* WPA/RSN IE from Beacon/ProbeResp */
+       p = data->assoc_info.beacon_ies;
+       l = data->assoc_info.beacon_ies_len;
+
+       /* Go through the IEs and make a copy of the WPA/RSN IEs, if present.
+        */
+       wpa_found = rsn_found = 0;
+       while (p && l >= 2) {
+               len = p[1] + 2;
+               if (len > l) {
+                       wpa_hexdump(MSG_DEBUG, "Truncated IE in beacon_ies",
+                                   p, l);
+                       break;
+               }
+               if (!wpa_found &&
+                   p[0] == WLAN_EID_VENDOR_SPECIFIC && p[1] >= 6 &&
+                   os_memcmp(&p[2], "\x00\x50\xF2\x01\x01\x00", 6) == 0) {
+                       wpa_found = 1;
+                       wpa_sm_set_ap_wpa_ie(wpa_s->wpa, p, len);
+               }
+
+               if (!rsn_found &&
+                   p[0] == WLAN_EID_RSN && p[1] >= 2) {
+                       rsn_found = 1;
+                       wpa_sm_set_ap_rsn_ie(wpa_s->wpa, p, len);
+               }
+
+               l -= len;
+               p += len;
+       }
+
+       if (!wpa_found && data->assoc_info.beacon_ies)
+               wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+       if (!rsn_found && data->assoc_info.beacon_ies)
+               wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+       if (wpa_found || rsn_found)
+               wpa_s->ap_ies_from_associnfo = 1;
+
+       wpa_s->assoc_freq = data->assoc_info.freq;
+
+       return 0;
+}
+
+
+static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
+                                      union wpa_event_data *data)
+{
+       u8 bssid[ETH_ALEN];
+       int ft_completed;
+       int bssid_changed;
+       struct wpa_driver_capa capa;
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
+                                   data->assoc_info.addr,
+                                   data->assoc_info.req_ies,
+                                   data->assoc_info.req_ies_len);
+               return;
+       }
+#endif /* CONFIG_AP */
+
+       ft_completed = wpa_ft_is_completed(wpa_s->wpa);
+       if (data && wpa_supplicant_event_associnfo(wpa_s, data) < 0)
+               return;
+
+       wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATED);
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
+       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
+           (wpa_drv_get_bssid(wpa_s, bssid) >= 0 &&
+            os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)) {
+               wpa_msg(wpa_s, MSG_DEBUG, "Associated to a new BSS: BSSID="
+                       MACSTR, MAC2STR(bssid));
+               bssid_changed = os_memcmp(wpa_s->bssid, bssid, ETH_ALEN);
+               os_memcpy(wpa_s->bssid, bssid, ETH_ALEN);
+               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+               if (bssid_changed)
+                       wpas_notify_bssid_changed(wpa_s);
+
+               if (wpa_supplicant_dynamic_keys(wpa_s) && !ft_completed) {
+                       wpa_clear_keys(wpa_s, bssid);
+               }
+               if (wpa_supplicant_select_config(wpa_s) < 0) {
+                       wpa_supplicant_disassociate(
+                               wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+                       return;
+               }
+               if (wpa_s->current_ssid) {
+                       struct wpa_bss *bss = NULL;
+                       struct wpa_ssid *ssid = wpa_s->current_ssid;
+                       if (ssid->ssid_len > 0)
+                               bss = wpa_bss_get(wpa_s, bssid,
+                                                 ssid->ssid, ssid->ssid_len);
+                       if (!bss)
+                               bss = wpa_bss_get_bssid(wpa_s, bssid);
+                       if (bss)
+                               wpa_s->current_bss = bss;
+               }
+       }
+
+#ifdef CONFIG_SME
+       os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
+       wpa_s->sme.prev_bssid_set = 1;
+#endif /* CONFIG_SME */
+
+       wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
+       if (wpa_s->current_ssid) {
+               /* When using scanning (ap_scan=1), SIM PC/SC interface can be
+                * initialized before association, but for other modes,
+                * initialize PC/SC here, if the current configuration needs
+                * smartcard or SIM/USIM. */
+               wpa_supplicant_scard_init(wpa_s, wpa_s->current_ssid);
+       }
+       wpa_sm_notify_assoc(wpa_s->wpa, bssid);
+       if (wpa_s->l2)
+               l2_packet_notify_auth_start(wpa_s->l2);
+
+       /*
+        * Set portEnabled first to FALSE in order to get EAP state machine out
+        * of the SUCCESS state and eapSuccess cleared. Without this, EAPOL PAE
+        * state machine may transit to AUTHENTICATING state based on obsolete
+        * eapSuccess and then trigger BE_AUTH to SUCCESS and PAE to
+        * AUTHENTICATED without ever giving chance to EAP state machine to
+        * reset the state.
+        */
+       if (!ft_completed) {
+               eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+               eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+       }
+       if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) || ft_completed)
+               eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+       /* 802.1X::portControl = Auto */
+       eapol_sm_notify_portEnabled(wpa_s->eapol, TRUE);
+       wpa_s->eapol_received = 0;
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
+           (wpa_s->current_ssid &&
+            wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+       } else if (!ft_completed) {
+               /* Timeout for receiving the first EAPOL packet */
+               wpa_supplicant_req_auth_timeout(wpa_s, 10, 0);
+       }
+       wpa_supplicant_cancel_scan(wpa_s);
+
+       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+           wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+               /*
+                * We are done; the driver will take care of RSN 4-way
+                * handshake.
+                */
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+               eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+               eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+       }
+
+       if (wpa_s->pending_eapol_rx) {
+               struct os_time now, age;
+               os_get_time(&now);
+               os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
+               if (age.sec == 0 && age.usec < 100000 &&
+                   os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
+                   0) {
+                       wpa_printf(MSG_DEBUG, "Process pending EAPOL frame "
+                                  "that was received just before association "
+                                  "notification");
+                       wpa_supplicant_rx_eapol(
+                               wpa_s, wpa_s->pending_eapol_rx_src,
+                               wpabuf_head(wpa_s->pending_eapol_rx),
+                               wpabuf_len(wpa_s->pending_eapol_rx));
+               }
+               wpabuf_free(wpa_s->pending_eapol_rx);
+               wpa_s->pending_eapol_rx = NULL;
+       }
+
+#ifdef CONFIG_BGSCAN
+       if (wpa_s->current_ssid != wpa_s->bgscan_ssid) {
+               bgscan_deinit(wpa_s);
+               if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
+                       if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+                               wpa_printf(MSG_DEBUG, "Failed to initialize "
+                                          "bgscan");
+                               /*
+                                * Live without bgscan; it is only used as a
+                                * roaming optimization, so the initial
+                                * connection is not affected.
+                                */
+                       } else
+                               wpa_s->bgscan_ssid = wpa_s->current_ssid;
+               } else
+                       wpa_s->bgscan_ssid = NULL;
+       }
+#endif /* CONFIG_BGSCAN */
+
+       if ((wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+            wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
+           wpa_s->current_ssid && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
+           capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE) {
+               /* Set static WEP keys again */
+               wpa_set_wep_keys(wpa_s, wpa_s->current_ssid);
+       }
+}
+
+
+static void wpa_supplicant_event_disassoc(struct wpa_supplicant *wpa_s,
+                                         u16 reason_code)
+{
+       const u8 *bssid;
+#ifdef CONFIG_SME
+       int authenticating;
+       u8 prev_pending_bssid[ETH_ALEN];
+
+       authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
+       os_memcpy(prev_pending_bssid, wpa_s->pending_bssid, ETH_ALEN);
+#endif /* CONFIG_SME */
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+               /*
+                * At least Host AP driver and a Prism3 card seemed to be
+                * generating streams of disconnected events when configuring
+                * IBSS for WPA-None. Ignore them for now.
+                */
+               wpa_printf(MSG_DEBUG, "Disconnect event - ignore in "
+                          "IBSS/WPA-None mode");
+               return;
+       }
+
+       if (wpa_s->wpa_state == WPA_4WAY_HANDSHAKE &&
+           wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+               wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
+                       "pre-shared key may be incorrect");
+       }
+       if (wpa_s->wpa_state >= WPA_ASSOCIATED)
+               wpa_supplicant_req_scan(wpa_s, 0, 100000);
+       bssid = wpa_s->bssid;
+       if (is_zero_ether_addr(bssid))
+               bssid = wpa_s->pending_bssid;
+       wpa_blacklist_add(wpa_s, bssid);
+       wpa_sm_notify_disassoc(wpa_s->wpa);
+       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+               " reason=%d",
+               MAC2STR(bssid), reason_code);
+       if (wpa_supplicant_dynamic_keys(wpa_s)) {
+               wpa_printf(MSG_DEBUG, "Disconnect event - remove keys");
+               wpa_s->keys_cleared = 0;
+               wpa_clear_keys(wpa_s, wpa_s->bssid);
+       }
+       wpa_supplicant_mark_disassoc(wpa_s);
+       bgscan_deinit(wpa_s);
+       wpa_s->bgscan_ssid = NULL;
+#ifdef CONFIG_SME
+       if (authenticating &&
+           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)) {
+               /*
+                * mac80211-workaround to force deauth on failed auth cmd,
+                * requires us to remain in authenticating state to allow the
+                * second authentication attempt to be continued properly.
+                */
+               wpa_printf(MSG_DEBUG, "SME: Allow pending authentication to "
+                          "proceed after disconnection event");
+               wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+               os_memcpy(wpa_s->pending_bssid, prev_pending_bssid, ETH_ALEN);
+       }
+#endif /* CONFIG_SME */
+}
+
+
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+static void wpa_supplicant_delayed_mic_error_report(void *eloop_ctx,
+                                                   void *sock_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       if (!wpa_s->pending_mic_error_report)
+               return;
+
+       wpa_printf(MSG_DEBUG, "WPA: Sending pending MIC error report");
+       wpa_sm_key_request(wpa_s->wpa, 1, wpa_s->pending_mic_error_pairwise);
+       wpa_s->pending_mic_error_report = 0;
+}
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+
+
+static void
+wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
+                                        union wpa_event_data *data)
+{
+       int pairwise;
+       struct os_time t;
+
+       wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
+       pairwise = (data && data->michael_mic_failure.unicast);
+       os_get_time(&t);
+       if ((wpa_s->last_michael_mic_error &&
+            t.sec - wpa_s->last_michael_mic_error <= 60) ||
+           wpa_s->pending_mic_error_report) {
+               if (wpa_s->pending_mic_error_report) {
+                       /*
+                        * Send the pending MIC error report immediately since
+                        * we are going to start countermeasures and AP better
+                        * do the same.
+                        */
+                       wpa_sm_key_request(wpa_s->wpa, 1,
+                                          wpa_s->pending_mic_error_pairwise);
+               }
+
+               /* Send the new MIC error report immediately since we are going
+                * to start countermeasures and AP better do the same.
+                */
+               wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+
+               /* initialize countermeasures */
+               wpa_s->countermeasures = 1;
+               wpa_msg(wpa_s, MSG_WARNING, "TKIP countermeasures started");
+
+               /*
+                * Need to wait for completion of request frame. We do not get
+                * any callback for the message completion, so just wait a
+                * short while and hope for the best. */
+               os_sleep(0, 10000);
+
+               wpa_drv_set_countermeasures(wpa_s, 1);
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_MICHAEL_MIC_FAILURE);
+               eloop_cancel_timeout(wpa_supplicant_stop_countermeasures,
+                                    wpa_s, NULL);
+               eloop_register_timeout(60, 0,
+                                      wpa_supplicant_stop_countermeasures,
+                                      wpa_s, NULL);
+               /* TODO: mark the AP rejected for 60 second. STA is
+                * allowed to associate with another AP.. */
+       } else {
+#ifdef CONFIG_DELAYED_MIC_ERROR_REPORT
+               if (wpa_s->mic_errors_seen) {
+                       /*
+                        * Reduce the effectiveness of Michael MIC error
+                        * reports as a means for attacking against TKIP if
+                        * more than one MIC failure is noticed with the same
+                        * PTK. We delay the transmission of the reports by a
+                        * random time between 0 and 60 seconds in order to
+                        * force the attacker wait 60 seconds before getting
+                        * the information on whether a frame resulted in a MIC
+                        * failure.
+                        */
+                       u8 rval[4];
+                       int sec;
+
+                       if (os_get_random(rval, sizeof(rval)) < 0)
+                               sec = os_random() % 60;
+                       else
+                               sec = WPA_GET_BE32(rval) % 60;
+                       wpa_printf(MSG_DEBUG, "WPA: Delay MIC error report %d "
+                                  "seconds", sec);
+                       wpa_s->pending_mic_error_report = 1;
+                       wpa_s->pending_mic_error_pairwise = pairwise;
+                       eloop_cancel_timeout(
+                               wpa_supplicant_delayed_mic_error_report,
+                               wpa_s, NULL);
+                       eloop_register_timeout(
+                               sec, os_random() % 1000000,
+                               wpa_supplicant_delayed_mic_error_report,
+                               wpa_s, NULL);
+               } else {
+                       wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+               }
+#else /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+               wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
+#endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
+       }
+       wpa_s->last_michael_mic_error = t.sec;
+       wpa_s->mic_errors_seen++;
+}
+
+
+#ifdef CONFIG_TERMINATE_ONLASTIF
+static int any_interfaces(struct wpa_supplicant *head)
+{
+       struct wpa_supplicant *wpa_s;
+
+       for (wpa_s = head; wpa_s != NULL; wpa_s = wpa_s->next)
+               if (!wpa_s->interface_removed)
+                       return 1;
+       return 0;
+}
+#endif /* CONFIG_TERMINATE_ONLASTIF */
+
+
+static void
+wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
+                                     union wpa_event_data *data)
+{
+       if (os_strcmp(wpa_s->ifname, data->interface_status.ifname) != 0)
+               return;
+
+       switch (data->interface_status.ievent) {
+       case EVENT_INTERFACE_ADDED:
+               if (!wpa_s->interface_removed)
+                       break;
+               wpa_s->interface_removed = 0;
+               wpa_printf(MSG_DEBUG, "Configured interface was added.");
+               if (wpa_supplicant_driver_init(wpa_s) < 0) {
+                       wpa_printf(MSG_INFO, "Failed to initialize the driver "
+                                  "after interface was added.");
+               }
+               break;
+       case EVENT_INTERFACE_REMOVED:
+               wpa_printf(MSG_DEBUG, "Configured interface was removed.");
+               wpa_s->interface_removed = 1;
+               wpa_supplicant_mark_disassoc(wpa_s);
+               l2_packet_deinit(wpa_s->l2);
+               wpa_s->l2 = NULL;
+#ifdef CONFIG_TERMINATE_ONLASTIF
+               /* check if last interface */
+               if (!any_interfaces(wpa_s->global->ifaces))
+                       eloop_terminate();
+#endif /* CONFIG_TERMINATE_ONLASTIF */
+               break;
+       }
+}
+
+
+#ifdef CONFIG_PEERKEY
+static void
+wpa_supplicant_event_stkstart(struct wpa_supplicant *wpa_s,
+                             union wpa_event_data *data)
+{
+       if (data == NULL)
+               return;
+       wpa_sm_stkstart(wpa_s->wpa, data->stkstart.peer);
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_IEEE80211R
+static void
+wpa_supplicant_event_ft_response(struct wpa_supplicant *wpa_s,
+                                union wpa_event_data *data)
+{
+       if (data == NULL)
+               return;
+
+       if (wpa_ft_process_response(wpa_s->wpa, data->ft_ies.ies,
+                                   data->ft_ies.ies_len,
+                                   data->ft_ies.ft_action,
+                                   data->ft_ies.target_ap,
+                                   data->ft_ies.ric_ies,
+                                   data->ft_ies.ric_ies_len) < 0) {
+               /* TODO: prevent MLME/driver from trying to associate? */
+       }
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_IBSS_RSN
+static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
+                                               union wpa_event_data *data)
+{
+       if (data == NULL)
+               return;
+       ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
+}
+#endif /* CONFIG_IBSS_RSN */
+
+
+#ifdef CONFIG_IEEE80211R
+static void ft_rx_action(struct wpa_supplicant *wpa_s, const u8 *data,
+                        size_t len)
+{
+       const u8 *sta_addr, *target_ap_addr;
+       u16 status;
+
+       wpa_hexdump(MSG_MSGDUMP, "FT: RX Action", data, len);
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME))
+               return; /* only SME case supported for now */
+       if (len < 1 + 2 * ETH_ALEN + 2)
+               return;
+       if (data[0] != 2)
+               return; /* Only FT Action Response is supported for now */
+       sta_addr = data + 1;
+       target_ap_addr = data + 1 + ETH_ALEN;
+       status = WPA_GET_LE16(data + 1 + 2 * ETH_ALEN);
+       wpa_printf(MSG_DEBUG, "FT: Received FT Action Response: STA " MACSTR
+                  " TargetAP " MACSTR " status %u",
+                  MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+
+       if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "FT: Foreign STA Address " MACSTR
+                          " in FT Action Response", MAC2STR(sta_addr));
+               return;
+       }
+
+       if (status) {
+               wpa_printf(MSG_DEBUG, "FT: FT Action Response indicates "
+                          "failure (status code %d)", status);
+               /* TODO: report error to FT code(?) */
+               return;
+       }
+
+       if (wpa_ft_process_response(wpa_s->wpa, data + 1 + 2 * ETH_ALEN + 2,
+                                   len - (1 + 2 * ETH_ALEN + 2), 1,
+                                   target_ap_addr, NULL, 0) < 0)
+               return;
+
+#ifdef CONFIG_SME
+       {
+               struct wpa_bss *bss;
+               bss = wpa_bss_get_bssid(wpa_s, target_ap_addr);
+               if (bss)
+                       wpa_s->sme.freq = bss->freq;
+               wpa_s->sme.auth_alg = WPA_AUTH_ALG_FT;
+               sme_associate(wpa_s, WPAS_MODE_INFRA, target_ap_addr,
+                             WLAN_AUTH_FT);
+       }
+#endif /* CONFIG_SME */
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
+                         union wpa_event_data *data)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       u16 reason_code = 0;
+
+       switch (event) {
+       case EVENT_AUTH:
+               sme_event_auth(wpa_s, data);
+               break;
+       case EVENT_ASSOC:
+               wpa_supplicant_event_assoc(wpa_s, data);
+               break;
+       case EVENT_DISASSOC:
+               wpa_printf(MSG_DEBUG, "Disassociation notification");
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface && data && data->disassoc_info.addr) {
+                       hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
+                                              data->disassoc_info.addr);
+                       break;
+               }
+#endif /* CONFIG_AP */
+               if (data)
+                       reason_code = data->deauth_info.reason_code;
+               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+                       sme_event_disassoc(wpa_s, data);
+               /* fall through */
+       case EVENT_DEAUTH:
+               if (event == EVENT_DEAUTH) {
+                       wpa_printf(MSG_DEBUG, "Deauthentication notification");
+                       if (data)
+                               reason_code = data->deauth_info.reason_code;
+               }
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface && data && data->deauth_info.addr) {
+                       hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
+                                              data->deauth_info.addr);
+                       break;
+               }
+#endif /* CONFIG_AP */
+               wpa_supplicant_event_disassoc(wpa_s, reason_code);
+               break;
+       case EVENT_MICHAEL_MIC_FAILURE:
+               wpa_supplicant_event_michael_mic_failure(wpa_s, data);
+               break;
+#ifndef CONFIG_NO_SCAN_PROCESSING
+       case EVENT_SCAN_RESULTS:
+               wpa_supplicant_event_scan_results(wpa_s, data);
+               break;
+#endif /* CONFIG_NO_SCAN_PROCESSING */
+       case EVENT_ASSOCINFO:
+               wpa_supplicant_event_associnfo(wpa_s, data);
+               break;
+       case EVENT_INTERFACE_STATUS:
+               wpa_supplicant_event_interface_status(wpa_s, data);
+               break;
+       case EVENT_PMKID_CANDIDATE:
+               wpa_supplicant_event_pmkid_candidate(wpa_s, data);
+               break;
+#ifdef CONFIG_PEERKEY
+       case EVENT_STKSTART:
+               wpa_supplicant_event_stkstart(wpa_s, data);
+               break;
+#endif /* CONFIG_PEERKEY */
+#ifdef CONFIG_IEEE80211R
+       case EVENT_FT_RESPONSE:
+               wpa_supplicant_event_ft_response(wpa_s, data);
+               break;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IBSS_RSN
+       case EVENT_IBSS_RSN_START:
+               wpa_supplicant_event_ibss_rsn_start(wpa_s, data);
+               break;
+#endif /* CONFIG_IBSS_RSN */
+       case EVENT_ASSOC_REJECT:
+               sme_event_assoc_reject(wpa_s, data);
+               break;
+       case EVENT_AUTH_TIMED_OUT:
+               sme_event_auth_timed_out(wpa_s, data);
+               break;
+       case EVENT_ASSOC_TIMED_OUT:
+               sme_event_assoc_timed_out(wpa_s, data);
+               break;
+#ifdef CONFIG_AP
+       case EVENT_TX_STATUS:
+               if (wpa_s->ap_iface == NULL)
+                       break;
+               switch (data->tx_status.type) {
+               case WLAN_FC_TYPE_MGMT:
+                       ap_mgmt_tx_cb(wpa_s, data->tx_status.data,
+                                     data->tx_status.data_len,
+                                     data->tx_status.stype,
+                                     data->tx_status.ack);
+                       break;
+               case WLAN_FC_TYPE_DATA:
+                       ap_tx_status(wpa_s, data->tx_status.dst,
+                                    data->tx_status.data,
+                                    data->tx_status.data_len,
+                                    data->tx_status.ack);
+                       break;
+               }
+               break;
+       case EVENT_RX_FROM_UNKNOWN:
+               if (wpa_s->ap_iface == NULL)
+                       break;
+               ap_rx_from_unknown_sta(wpa_s, data->rx_from_unknown.frame,
+                                      data->rx_from_unknown.len);
+               break;
+       case EVENT_RX_MGMT:
+               if (wpa_s->ap_iface == NULL)
+                       break;
+               ap_mgmt_rx(wpa_s, &data->rx_mgmt);
+               break;
+#endif /* CONFIG_AP */
+       case EVENT_RX_ACTION:
+               wpa_printf(MSG_DEBUG, "Received Action frame: SA=" MACSTR
+                          " Category=%u DataLen=%d freq=%d MHz",
+                          MAC2STR(data->rx_action.sa),
+                          data->rx_action.category, (int) data->rx_action.len,
+                          data->rx_action.freq);
+#ifdef CONFIG_IEEE80211R
+               if (data->rx_action.category == WLAN_ACTION_FT) {
+                       ft_rx_action(wpa_s, data->rx_action.data,
+                                    data->rx_action.len);
+                       break;
+               }
+#endif /* CONFIG_IEEE80211R */
+               break;
+#ifdef CONFIG_CLIENT_MLME
+       case EVENT_MLME_RX: {
+               struct ieee80211_rx_status rx_status;
+               os_memset(&rx_status, 0, sizeof(rx_status));
+               rx_status.freq = data->mlme_rx.freq;
+               rx_status.channel = data->mlme_rx.channel;
+               rx_status.ssi = data->mlme_rx.ssi;
+               ieee80211_sta_rx(wpa_s, data->mlme_rx.buf, data->mlme_rx.len,
+                                &rx_status);
+               break;
+       }
+#endif /* CONFIG_CLIENT_MLME */
+       case EVENT_EAPOL_RX:
+               wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
+                                       data->eapol_rx.data,
+                                       data->eapol_rx.data_len);
+               break;
+       case EVENT_SIGNAL_CHANGE:
+               bgscan_notify_signal_change(
+                       wpa_s, data->signal_change.above_threshold);
+               break;
+       default:
+               wpa_printf(MSG_INFO, "Unknown event %d", event);
+               break;
+       }
+}
diff --git a/wpa_supplicant/examples/60_wpa_supplicant b/wpa_supplicant/examples/60_wpa_supplicant
new file mode 100755 (executable)
index 0000000..39bd8e0
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+# /etc/pm/sleep.d/60_wpa_supplicant
+# Action script to notify wpa_supplicant of pm-action events.
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+
+WPACLI=wpa_cli
+
+case "$1" in
+       suspend|hibernate)
+               $WPACLI suspend
+               ;;
+       resume|thaw)
+               $WPACLI resume
+               ;;
+esac
+
+exit 0
diff --git a/wpa_supplicant/examples/ieee8021x.conf b/wpa_supplicant/examples/ieee8021x.conf
new file mode 100644 (file)
index 0000000..e8a5503
--- /dev/null
@@ -0,0 +1,13 @@
+# IEEE 802.1X with dynamic WEP keys using EAP-PEAP/MSCHAPv2
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+       ssid="example 802.1x network"
+       key_mgmt=IEEE8021X
+       eap=PEAP
+       phase2="auth=MSCHAPV2"
+       identity="user name"
+       password="password"
+       ca_cert="/etc/cert/ca.pem"
+}
diff --git a/wpa_supplicant/examples/openCryptoki.conf b/wpa_supplicant/examples/openCryptoki.conf
new file mode 100644 (file)
index 0000000..e2301a6
--- /dev/null
@@ -0,0 +1,41 @@
+# EAP-TLS using private key and certificates via OpenSSL PKCS#11 engine and
+# openCryptoki (e.g., with TPM token)
+
+# This example uses following PKCS#11 objects:
+# $ pkcs11-tool --module /usr/lib/opencryptoki/libopencryptoki.so  -O -l
+# Please enter User PIN:
+# Private Key Object; RSA
+#   label:      rsakey
+#   ID:         04
+#   Usage:      decrypt, sign, unwrap
+# Certificate Object, type = X.509 cert
+#   label:      ca
+#   ID:         01
+# Certificate Object, type = X.509 cert
+#   label:      cert
+#   ID:         04
+
+# Configure OpenSSL to load the PKCS#11 engine and openCryptoki module
+pkcs11_engine_path=/usr/lib/engines/engine_pkcs11.so
+pkcs11_module_path=/usr/lib/opencryptoki/libopencryptoki.so
+
+network={
+       ssid="test network"
+       key_mgmt=WPA-EAP
+       eap=TLS
+       identity="User"
+
+       # use OpenSSL PKCS#11 engine for this network
+       engine=1
+       engine_id="pkcs11"
+
+       # select the private key and certificates based on ID (see pkcs11-tool
+       # output above)
+       key_id="4"
+       cert_id="4"
+       ca_cert_id="1"
+
+       # set the PIN code; leave this out to configure the PIN to be requested
+       # interactively when needed (e.g., via wpa_gui or wpa_cli)
+       pin="123456"
+}
diff --git a/wpa_supplicant/examples/plaintext.conf b/wpa_supplicant/examples/plaintext.conf
new file mode 100644 (file)
index 0000000..542ac1d
--- /dev/null
@@ -0,0 +1,8 @@
+# Plaintext (no encryption) network
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+       ssid="example open network"
+       key_mgmt=NONE
+}
diff --git a/wpa_supplicant/examples/wep.conf b/wpa_supplicant/examples/wep.conf
new file mode 100644 (file)
index 0000000..9c7b55f
--- /dev/null
@@ -0,0 +1,11 @@
+# Static WEP keys
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+       ssid="example wep network"
+       key_mgmt=NONE
+       wep_key0="abcde"
+       wep_key1=0102030405
+       wep_tx_keyidx=0
+}
diff --git a/wpa_supplicant/examples/wpa-psk-tkip.conf b/wpa_supplicant/examples/wpa-psk-tkip.conf
new file mode 100644 (file)
index 0000000..93d7fc2
--- /dev/null
@@ -0,0 +1,12 @@
+# WPA-PSK/TKIP
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+       ssid="example wpa-psk network"
+       key_mgmt=WPA-PSK
+       proto=WPA
+       pairwise=TKIP
+       group=TKIP
+       psk="secret passphrase"
+}
diff --git a/wpa_supplicant/examples/wpa2-eap-ccmp.conf b/wpa_supplicant/examples/wpa2-eap-ccmp.conf
new file mode 100644 (file)
index 0000000..d7a64d8
--- /dev/null
@@ -0,0 +1,15 @@
+# WPA2-EAP/CCMP using EAP-TLS
+
+ctrl_interface=/var/run/wpa_supplicant
+
+network={
+       ssid="example wpa2-eap network"
+       key_mgmt=WPA-EAP
+       proto=WPA2
+       pairwise=CCMP
+       group=CCMP
+       eap=TLS
+       ca_cert="/etc/cert/ca.pem"
+       private_key="/etc/cert/user.p12"
+       private_key_passwd="PKCS#12 passhrase"
+}
diff --git a/wpa_supplicant/examples/wpas-dbus-new-getall.py b/wpa_supplicant/examples/wpas-dbus-new-getall.py
new file mode 100755 (executable)
index 0000000..03da187
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+
+def main():
+       bus = dbus.SystemBus()
+       wpas_obj = bus.get_object("fi.w1.wpa_supplicant1",
+                                 "/fi/w1/wpa_supplicant1")
+       props = wpas_obj.GetAll("fi.w1.wpa_supplicant1",
+                               dbus_interface=dbus.PROPERTIES_IFACE)
+       print "GetAll(fi.w1.wpa_supplicant1, /fi/w1/wpa_supplicant1):"
+       print props
+
+       if len(sys.argv) != 2:
+               os._exit(1)
+
+       ifname = sys.argv[1]
+
+       wpas = dbus.Interface(wpas_obj, "fi.w1.wpa_supplicant1")
+       path = wpas.GetInterface(ifname)
+       if_obj = bus.get_object("fi.w1.wpa_supplicant1", path)
+       props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface",
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+       print
+       print "GetAll(fi.w1.wpa_supplicant1.Interface, %s):" % (path)
+       print props
+
+       props = if_obj.GetAll("fi.w1.wpa_supplicant1.Interface.WPS",
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+       print
+       print "GetAll(fi.w1.wpa_supplicant1.Interface.WPS, %s):" % (path)
+       print props
+
+       res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'BSSs',
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+       if len(res) > 0:
+               bss_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0])
+               props = bss_obj.GetAll("fi.w1.wpa_supplicant1.BSS",
+                                      dbus_interface=dbus.PROPERTIES_IFACE)
+               print
+               print "GetAll(fi.w1.wpa_supplicant1.BSS, %s):" % (res[0])
+               print props
+
+       res = if_obj.Get("fi.w1.wpa_supplicant1.Interface", 'Networks',
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+       if len(res) > 0:
+               net_obj = bus.get_object("fi.w1.wpa_supplicant1", res[0])
+               props = net_obj.GetAll("fi.w1.wpa_supplicant1.Network",
+                                      dbus_interface=dbus.PROPERTIES_IFACE)
+               print
+               print "GetAll(fi.w1.wpa_supplicant1.Network, %s):" % (res[0])
+               print props
+
+if __name__ == "__main__":
+       main()
+
diff --git a/wpa_supplicant/examples/wpas-dbus-new-signals.py b/wpa_supplicant/examples/wpas-dbus-new-signals.py
new file mode 100755 (executable)
index 0000000..b040e0a
--- /dev/null
@@ -0,0 +1,203 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_INTERFACES_OPATH = "/fi/w1/wpa_supplicant1/Interfaces"
+WPAS_DBUS_BSS_INTERFACE = "fi.w1.wpa_supplicant1.BSS"
+WPAS_DBUS_NETWORK_INTERFACE = "fi.w1.wpa_supplicant1.Network"
+
+def byte_array_to_string(s):
+       import urllib
+       r = ""    
+       for c in s:
+               if c >= 32 and c < 127:
+                       r += "%c" % c
+               else:
+                       r += urllib.quote(chr(c))
+       return r
+
+def list_interfaces(wpas_obj):
+       ifaces = wpas_obj.Get(WPAS_DBUS_INTERFACE, 'Interfaces',
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+       for path in ifaces:
+               if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+               ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname',
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+               print ifname
+
+def interfaceAdded(interface, properties):
+       print "InterfaceAdded(%s): Ifname=%s" % (interface, properties['Ifname'])
+
+def interfaceRemoved(interface):
+       print "InterfaceRemoved(%s)" % (interface)
+
+def propertiesChanged(properties):
+       for i in properties:
+               print "PropertiesChanged: %s=%s" % (i, properties[i])
+
+def showBss(bss):
+       net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss)
+       net = dbus.Interface(net_obj, WPAS_DBUS_BSS_INTERFACE)
+
+       # Convert the byte-array for SSID and BSSID to printable strings
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'BSSID',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       bssid = ""
+       for item in val:
+               bssid = bssid + ":%02x" % item
+       bssid = bssid[1:]
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'SSID',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       ssid = byte_array_to_string(val)
+
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPAIE',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       wpa = "no"
+       if val != None:
+               wpa = "yes"
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSNIE',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       wpa2 = "no"
+       if val != None:
+               wpa2 = "yes"
+       freq = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Frequency',
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+       signal = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Signal',
+                            dbus_interface=dbus.PROPERTIES_IFACE)
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Rates',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       if len(val) > 0:
+               maxrate = val[0] / 1000000
+       else:
+               maxrate = 0
+
+       print "  %s  ::  ssid='%s'  wpa=%s  wpa2=%s  signal=%d  rate=%d  freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq)
+
+def scanDone(success):
+       gobject.MainLoop().quit()
+       print "Scan done: success=%s" % success
+
+def scanDone2(success, path=None):
+       print "Scan done: success=%s [path=%s]" % (success, path)
+
+def bssAdded(bss, properties):
+       print "BSS added: %s" % (bss)
+       showBss(bss)
+
+def bssRemoved(bss):
+       print "BSS removed: %s" % (bss)
+
+def blobAdded(blob):
+       print "BlobAdded(%s)" % (blob)
+
+def blobRemoved(blob):
+       print "BlobRemoved(%s)" % (blob)
+
+def networkAdded(network, properties):
+       print "NetworkAdded(%s)" % (network)
+
+def networkRemoved(network):
+       print "NetworkRemoved(%s)" % (network)
+
+def networkSelected(network):
+       print "NetworkSelected(%s)" % (network)
+
+def propertiesChangedInterface(properties):
+       for i in properties:
+               print "PropertiesChanged(interface): %s=%s" % (i, properties[i])
+
+def propertiesChangedBss(properties):
+       for i in properties:
+               print "PropertiesChanged(BSS): %s=%s" % (i, properties[i])
+
+def propertiesChangedNetwork(properties):
+       for i in properties:
+               print "PropertiesChanged(Network): %s=%s" % (i, properties[i])
+
+def main():
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+       global bus
+       bus = dbus.SystemBus()
+       wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+       if len(sys.argv) != 2:
+               list_interfaces(wpas_obj)
+               os._exit(1)
+
+       wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+       bus.add_signal_receiver(interfaceAdded,
+                               dbus_interface=WPAS_DBUS_INTERFACE,
+                               signal_name="InterfaceAdded")
+       bus.add_signal_receiver(interfaceRemoved,
+                               dbus_interface=WPAS_DBUS_INTERFACE,
+                               signal_name="InterfaceRemoved")
+       bus.add_signal_receiver(propertiesChanged,
+                               dbus_interface=WPAS_DBUS_INTERFACE,
+                               signal_name="PropertiesChanged")
+
+       ifname = sys.argv[1]
+       path = wpas.GetInterface(ifname)
+       if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+       iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+       iface.connect_to_signal("ScanDone", scanDone2,
+                               path_keyword='path')
+
+       bus.add_signal_receiver(scanDone,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="ScanDone",
+                               path=path)
+       bus.add_signal_receiver(bssAdded,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BSSAdded",
+                               path=path)
+       bus.add_signal_receiver(bssRemoved,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BSSRemoved",
+                               path=path)
+       bus.add_signal_receiver(blobAdded,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BlobAdded",
+                               path=path)
+       bus.add_signal_receiver(blobRemoved,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BlobRemoved",
+                               path=path)
+       bus.add_signal_receiver(networkAdded,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="NetworkAdded",
+                               path=path)
+       bus.add_signal_receiver(networkRemoved,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="NetworkRemoved",
+                               path=path)
+       bus.add_signal_receiver(networkSelected,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="NetworkSelected",
+                               path=path)
+       bus.add_signal_receiver(propertiesChangedInterface,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="PropertiesChanged",
+                               path=path)
+
+       bus.add_signal_receiver(propertiesChangedBss,
+                               dbus_interface=WPAS_DBUS_BSS_INTERFACE,
+                               signal_name="PropertiesChanged")
+
+       bus.add_signal_receiver(propertiesChangedNetwork,
+                               dbus_interface=WPAS_DBUS_NETWORK_INTERFACE,
+                               signal_name="PropertiesChanged")
+
+       gobject.MainLoop().run()
+
+if __name__ == "__main__":
+       main()
+
diff --git a/wpa_supplicant/examples/wpas-dbus-new-wps.py b/wpa_supplicant/examples/wpas-dbus-new-wps.py
new file mode 100755 (executable)
index 0000000..b886385
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_WPS_INTERFACE = "fi.w1.wpa_supplicant1.Interface.WPS"
+
+def propertiesChanged(properties):
+       if properties.has_key("State"):
+               print "PropertiesChanged: State: %s" % (properties["State"])
+
+def scanDone(success):
+       print "Scan done: success=%s" % success
+
+def bssAdded(bss, properties):
+       print "BSS added: %s" % (bss)
+
+def bssRemoved(bss):
+       print "BSS removed: %s" % (bss)
+
+def wpsEvent(name, args):
+       print "WPS event: %s" % (name)
+       print args
+
+def credentials(cred):
+       print "WPS credentials: %s" % (cred)
+
+def main():
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+       global bus
+       bus = dbus.SystemBus()
+       wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+       if len(sys.argv) != 2:
+               print "Missing ifname argument"
+               os._exit(1)
+
+       wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+       bus.add_signal_receiver(scanDone,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="ScanDone")
+       bus.add_signal_receiver(bssAdded,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BSSAdded")
+       bus.add_signal_receiver(bssRemoved,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BSSRemoved")
+       bus.add_signal_receiver(propertiesChanged,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="PropertiesChanged")
+       bus.add_signal_receiver(wpsEvent,
+                               dbus_interface=WPAS_DBUS_WPS_INTERFACE,
+                               signal_name="Event")
+       bus.add_signal_receiver(credentials,
+                               dbus_interface=WPAS_DBUS_WPS_INTERFACE,
+                               signal_name="Credentials")
+
+       ifname = sys.argv[1]
+
+       path = wpas.GetInterface(ifname)
+       if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+       if_obj.Set(WPAS_DBUS_WPS_INTERFACE, 'ProcessCredentials',
+                  dbus.Boolean(1),
+                  dbus_interface=dbus.PROPERTIES_IFACE)
+       wps = dbus.Interface(if_obj, WPAS_DBUS_WPS_INTERFACE)
+       wps.Start({'Role': 'enrollee', 'Type': 'pbc'})
+
+       gobject.MainLoop().run()
+
+if __name__ == "__main__":
+       main()
+
diff --git a/wpa_supplicant/examples/wpas-dbus-new.py b/wpa_supplicant/examples/wpas-dbus-new.py
new file mode 100755 (executable)
index 0000000..25072ce
--- /dev/null
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+import gobject
+from dbus.mainloop.glib import DBusGMainLoop
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_INTERFACE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_OPATH = "/fi/w1/wpa_supplicant1"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_INTERFACES_OPATH = "/fi/w1/wpa_supplicant1/Interfaces"
+WPAS_DBUS_BSS_INTERFACE = "fi.w1.wpa_supplicant1.BSS"
+
+def byte_array_to_string(s):
+       import urllib
+       r = ""    
+       for c in s:
+               if c >= 32 and c < 127:
+                       r += "%c" % c
+               else:
+                       r += urllib.quote(chr(c))
+       return r
+
+def list_interfaces(wpas_obj):
+       ifaces = wpas_obj.Get(WPAS_DBUS_INTERFACE, 'Interfaces',
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+       for path in ifaces:
+               if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+               ifname = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'Ifname',
+                             dbus_interface=dbus.PROPERTIES_IFACE)
+               print ifname
+
+def propertiesChanged(properties):
+       if properties.has_key("State"):
+               print "PropertiesChanged: State: %s" % (properties["State"])
+
+def showBss(bss):
+       net_obj = bus.get_object(WPAS_DBUS_SERVICE, bss)
+       net = dbus.Interface(net_obj, WPAS_DBUS_BSS_INTERFACE)
+
+       # Convert the byte-array for SSID and BSSID to printable strings
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'BSSID',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       bssid = ""
+       for item in val:
+               bssid = bssid + ":%02x" % item
+       bssid = bssid[1:]
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'SSID',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       ssid = byte_array_to_string(val)
+
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'WPA',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       wpa = "no"
+       if len(val["KeyMgmt"]) > 0:
+               wpa = "yes"
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'RSN',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       wpa2 = "no"
+       if len(val["KeyMgmt"]) > 0:
+               wpa2 = "yes"
+       freq = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Frequency',
+                          dbus_interface=dbus.PROPERTIES_IFACE)
+       signal = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Signal',
+                            dbus_interface=dbus.PROPERTIES_IFACE)
+       val = net_obj.Get(WPAS_DBUS_BSS_INTERFACE, 'Rates',
+                         dbus_interface=dbus.PROPERTIES_IFACE)
+       if len(val) > 0:
+               maxrate = val[0] / 1000000
+       else:
+               maxrate = 0
+
+       print "  %s  ::  ssid='%s'  wpa=%s  wpa2=%s  signal=%d  rate=%d  freq=%d" % (bssid, ssid, wpa, wpa2, signal, maxrate, freq)
+
+def scanDone(success):
+       print "Scan done: success=%s" % success
+       
+       res = if_obj.Get(WPAS_DBUS_INTERFACES_INTERFACE, 'BSSs',
+                        dbus_interface=dbus.PROPERTIES_IFACE)
+
+       print "Scanned wireless networks:"
+       for opath in res:
+               print opath
+               showBss(opath)
+
+def bssAdded(bss, properties):
+       print "BSS added: %s" % (bss)
+       showBss(bss)
+
+def bssRemoved(bss):
+       print "BSS removed: %s" % (bss)
+
+def main():
+       dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+       global bus
+       bus = dbus.SystemBus()
+       wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+
+       if len(sys.argv) != 2:
+               list_interfaces(wpas_obj)
+               os._exit(1)
+
+       wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+       bus.add_signal_receiver(scanDone,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="ScanDone")
+       bus.add_signal_receiver(bssAdded,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BSSAdded")
+       bus.add_signal_receiver(bssRemoved,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="BSSRemoved")
+       bus.add_signal_receiver(propertiesChanged,
+                               dbus_interface=WPAS_DBUS_INTERFACES_INTERFACE,
+                               signal_name="PropertiesChanged")
+
+       ifname = sys.argv[1]
+
+       # See if wpa_supplicant already knows about this interface
+       path = None
+       try:
+               path = wpas.GetInterface(ifname)
+       except dbus.DBusException, exc:
+               if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceUnknown:"):
+                       raise exc
+               try:
+                       path = wpas.CreateInterface({'Ifname': ifname, 'Driver': 'test'})
+                       time.sleep(1)
+
+               except dbus.DBusException, exc:
+                       if not str(exc).startswith("fi.w1.wpa_supplicant1.InterfaceExists:"):
+                               raise exc
+
+       global if_obj
+       if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+       global iface
+       iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+       iface.Scan({'Type': 'active'})
+
+       gobject.MainLoop().run()
+
+       wpas.RemoveInterface(dbus.ObjectPath(path))
+
+if __name__ == "__main__":
+       main()
+
diff --git a/wpa_supplicant/examples/wpas-test.py b/wpa_supplicant/examples/wpas-test.py
new file mode 100755 (executable)
index 0000000..fd7f73d
--- /dev/null
@@ -0,0 +1,91 @@
+#!/usr/bin/python
+
+import dbus
+import sys, os
+import time
+
+WPAS_DBUS_SERVICE = "fi.epitest.hostap.WPASupplicant"
+WPAS_DBUS_INTERFACE = "fi.epitest.hostap.WPASupplicant"
+WPAS_DBUS_OPATH = "/fi/epitest/hostap/WPASupplicant"
+
+WPAS_DBUS_INTERFACES_INTERFACE = "fi.epitest.hostap.WPASupplicant.Interface"
+WPAS_DBUS_INTERFACES_OPATH = "/fi/epitest/hostap/WPASupplicant/Interfaces"
+WPAS_DBUS_BSSID_INTERFACE = "fi.epitest.hostap.WPASupplicant.BSSID"
+
+def byte_array_to_string(s):
+       import urllib
+       r = ""    
+       for c in s:
+               if c >= 32 and c < 127:
+                       r += "%c" % c
+               else:
+                       r += urllib.quote(chr(c))
+       return r
+
+def main():
+       if len(sys.argv) != 2:
+               print "Usage: wpas-test.py <interface>"
+               os._exit(1)
+
+       ifname = sys.argv[1]
+
+       bus = dbus.SystemBus()
+       wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_OPATH)
+       wpas = dbus.Interface(wpas_obj, WPAS_DBUS_INTERFACE)
+
+       # See if wpa_supplicant already knows about this interface
+       path = None
+       try:
+               path = wpas.getInterface(ifname)
+       except dbus.dbus_bindings.DBusException, exc:
+               if str(exc) != "wpa_supplicant knows nothing about this interface.":
+                       raise exc
+               try:
+                       path = wpas.addInterface(ifname, {'driver': dbus.Variant('wext')})
+               except dbus.dbus_bindings.DBusException, exc:
+                       if str(exc) != "wpa_supplicant already controls this interface.":
+                               raise exc
+
+       if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+       iface = dbus.Interface(if_obj, WPAS_DBUS_INTERFACES_INTERFACE)
+       iface.scan()
+       # Should really wait for the "scanResults" signal instead of sleeping
+       time.sleep(5)
+       res = iface.scanResults()
+
+       print "Scanned wireless networks:"
+       for opath in res:
+               net_obj = bus.get_object(WPAS_DBUS_SERVICE, opath)
+               net = dbus.Interface(net_obj, WPAS_DBUS_BSSID_INTERFACE)
+               props = net.properties()
+
+               # Convert the byte-array for SSID and BSSID to printable strings
+               bssid = ""
+               for item in props["bssid"]:
+                       bssid = bssid + ":%02x" % item
+               bssid = bssid[1:]
+               ssid = byte_array_to_string(props["ssid"])
+               wpa = "no"
+               if props.has_key("wpaie"):
+                       wpa = "yes"
+               wpa2 = "no"
+               if props.has_key("rsnie"):
+                       wpa2 = "yes"
+               freq = 0
+               if props.has_key("frequency"):
+                       freq = props["frequency"]
+               caps = props["capabilities"]
+               qual = props["quality"]
+               level = props["level"]
+               noise = props["noise"]
+               maxrate = props["maxrate"] / 1000000
+
+               print "  %s  ::  ssid='%s'  wpa=%s  wpa2=%s  quality=%d%%  rate=%d  freq=%d" % (bssid, ssid, wpa, wpa2, qual, maxrate, freq)
+
+       wpas.removeInterface(dbus.ObjectPath(path))
+       # Should fail here with unknown interface error
+       iface.scan()
+
+if __name__ == "__main__":
+       main()
+
diff --git a/wpa_supplicant/ibss_rsn.c b/wpa_supplicant/ibss_rsn.c
new file mode 100644 (file)
index 0000000..0e33253
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * wpa_supplicant - IBSS RSN
+ * Copyright (c) 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 "common.h"
+#include "l2_packet/l2_packet.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "ap/wpa_auth.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ibss_rsn.h"
+
+
+static void ibss_rsn_free(struct ibss_rsn_peer *peer)
+{
+       wpa_auth_sta_deinit(peer->auth);
+       wpa_sm_deinit(peer->supp);
+       os_free(peer);
+}
+
+
+static void supp_set_state(void *ctx, enum wpa_states state)
+{
+       struct ibss_rsn_peer *peer = ctx;
+       peer->supp_state = state;
+}
+
+
+static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+                          size_t len)
+{
+       struct ibss_rsn_peer *peer = ctx;
+       struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
+                  "len=%lu)",
+                  __func__, MAC2STR(dest), proto, (unsigned long) len);
+
+       if (wpa_s->l2)
+               return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
+
+       return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+}
+
+
+static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
+                            u16 data_len, size_t *msg_len, void **data_pos)
+{
+       struct ieee802_1x_hdr *hdr;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
+                  __func__, type, data_len);
+
+       *msg_len = sizeof(*hdr) + data_len;
+       hdr = os_malloc(*msg_len);
+       if (hdr == NULL)
+               return NULL;
+
+       hdr->version = 2;
+       hdr->type = type;
+       hdr->length = host_to_be16(data_len);
+
+       if (data)
+               os_memcpy(hdr + 1, data, data_len);
+       else
+               os_memset(hdr + 1, 0, data_len);
+
+       if (data_pos)
+               *data_pos = hdr + 1;
+
+       return (u8 *) hdr;
+}
+
+
+static int supp_get_beacon_ie(void *ctx)
+{
+       struct ibss_rsn_peer *peer = ctx;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+       /* TODO: get correct RSN IE */
+       return wpa_sm_set_ap_rsn_ie(peer->supp,
+                                   (u8 *) "\x30\x14\x01\x00"
+                                   "\x00\x0f\xac\x04"
+                                   "\x01\x00\x00\x0f\xac\x04"
+                                   "\x01\x00\x00\x0f\xac\x02"
+                                   "\x00\x00", 22);
+}
+
+
+static int supp_set_key(void *ctx, enum wpa_alg alg,
+                       const u8 *addr, int key_idx, int set_tx,
+                       const u8 *seq, size_t seq_len,
+                       const u8 *key, size_t key_len)
+{
+       struct ibss_rsn_peer *peer = ctx;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
+                  "set_tx=%d)",
+                  __func__, alg, MAC2STR(addr), key_idx, set_tx);
+       wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
+       wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
+
+       if (key_idx == 0) {
+               /*
+                * In IBSS RSN, the pairwise key from the 4-way handshake
+                * initiated by the peer with highest MAC address is used.
+                */
+               if (os_memcmp(peer->ibss_rsn->wpa_s->own_addr, peer->addr,
+                             ETH_ALEN) > 0) {
+                       wpa_printf(MSG_DEBUG, "SUPP: Do not use this PTK");
+                       return 0;
+               }
+       }
+
+       return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
+                              set_tx, seq, seq_len, key, key_len);
+}
+
+
+static void * supp_get_network_ctx(void *ctx)
+{
+       struct ibss_rsn_peer *peer = ctx;
+       return wpa_supplicant_get_ssid(peer->ibss_rsn->wpa_s);
+}
+
+
+static int supp_mlme_setprotection(void *ctx, const u8 *addr,
+                                  int protection_type, int key_type)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
+                  "key_type=%d)",
+                  __func__, MAC2STR(addr), protection_type, key_type);
+       return 0;
+}
+
+
+static void supp_cancel_auth_timeout(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+}
+
+
+int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
+                      const u8 *psk)
+{
+       struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return -1;
+
+       ctx->ctx = peer;
+       ctx->msg_ctx = peer->ibss_rsn->wpa_s;
+       ctx->set_state = supp_set_state;
+       ctx->ether_send = supp_ether_send;
+       ctx->get_beacon_ie = supp_get_beacon_ie;
+       ctx->alloc_eapol = supp_alloc_eapol;
+       ctx->set_key = supp_set_key;
+       ctx->get_network_ctx = supp_get_network_ctx;
+       ctx->mlme_setprotection = supp_mlme_setprotection;
+       ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+       peer->supp = wpa_sm_init(ctx);
+       if (peer->supp == NULL) {
+               wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
+               return -1;
+       }
+
+       wpa_sm_set_own_addr(peer->supp, own_addr);
+       wpa_sm_set_param(peer->supp, WPA_PARAM_RSN_ENABLED, 1);
+       wpa_sm_set_param(peer->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+       wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
+       wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+       wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
+       wpa_sm_set_pmk(peer->supp, psk, PMK_LEN);
+
+       peer->supp_ie_len = sizeof(peer->supp_ie);
+       if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
+                                           &peer->supp_ie_len) < 0) {
+               wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
+                          " failed");
+               return -1;
+       }
+
+       wpa_sm_notify_assoc(peer->supp, peer->addr);
+
+       return 0;
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+                       const char *txt)
+{
+       if (addr)
+               wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+                          MAC2STR(addr), txt);
+       else
+               wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
+{
+       struct ibss_rsn *ibss_rsn = ctx;
+       wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+                  __func__, MAC2STR(addr), prev_psk);
+       if (prev_psk)
+               return NULL;
+       return ibss_rsn->psk;
+}
+
+
+static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
+                          size_t data_len, int encrypt)
+{
+       struct ibss_rsn *ibss_rsn = ctx;
+       struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
+
+       wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
+                  "encrypt=%d)",
+                  __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
+
+       if (wpa_s->l2)
+               return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data,
+                                     data_len);
+
+       return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len);
+}
+
+
+static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+                       const u8 *addr, int idx, u8 *key, size_t key_len)
+{
+       struct ibss_rsn *ibss_rsn = ctx;
+       u8 seq[6];
+
+       os_memset(seq, 0, sizeof(seq));
+
+       if (addr) {
+               wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR
+                          " key_idx=%d)",
+                          __func__, alg, MAC2STR(addr), idx);
+       } else {
+               wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)",
+                          __func__, alg, idx);
+       }
+       wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
+
+       if (idx == 0) {
+               /*
+                * In IBSS RSN, the pairwise key from the 4-way handshake
+                * initiated by the peer with highest MAC address is used.
+                */
+               if (addr == NULL ||
+                   os_memcmp(ibss_rsn->wpa_s->own_addr, addr, ETH_ALEN) < 0) {
+                       wpa_printf(MSG_DEBUG, "AUTH: Do not use this PTK");
+                       return 0;
+               }
+       }
+
+       return wpa_drv_set_key(ibss_rsn->wpa_s, alg, addr, idx,
+                              1, seq, 6, key, key_len);
+}
+
+
+static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
+                                   const u8 *own_addr)
+{
+       struct wpa_auth_config conf;
+       struct wpa_auth_callbacks cb;
+
+       wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+       os_memset(&conf, 0, sizeof(conf));
+       conf.wpa = 2;
+       conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+       conf.wpa_pairwise = WPA_CIPHER_CCMP;
+       conf.rsn_pairwise = WPA_CIPHER_CCMP;
+       conf.wpa_group = WPA_CIPHER_CCMP;
+       conf.eapol_version = 2;
+
+       os_memset(&cb, 0, sizeof(cb));
+       cb.ctx = ibss_rsn;
+       cb.logger = auth_logger;
+       cb.send_eapol = auth_send_eapol;
+       cb.get_psk = auth_get_psk;
+       cb.set_key = auth_set_key;
+
+       ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
+       if (ibss_rsn->auth_group == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
+                             struct ibss_rsn_peer *peer)
+{
+       peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr);
+       if (peer->auth == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
+               return -1;
+       }
+
+       /* TODO: get peer RSN IE with Probe Request */
+       if (wpa_validate_wpa_ie(ibss_rsn->auth_group, peer->auth,
+                               (u8 *) "\x30\x14\x01\x00"
+                               "\x00\x0f\xac\x04"
+                               "\x01\x00\x00\x0f\xac\x04"
+                               "\x01\x00\x00\x0f\xac\x02"
+                               "\x00\x00", 22, NULL, 0) !=
+           WPA_IE_OK) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+               return -1;
+       }
+
+       if (wpa_auth_sm_event(peer->auth, WPA_ASSOC))
+               return -1;
+
+       if (wpa_auth_sta_associated(ibss_rsn->auth_group, peer->auth))
+               return -1;
+
+       return 0;
+}
+
+
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+       struct ibss_rsn_peer *peer;
+
+       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
+                  "Supplicant for peer " MACSTR, MAC2STR(addr));
+
+       peer = os_zalloc(sizeof(*peer));
+       if (peer == NULL)
+               return -1;
+
+       peer->ibss_rsn = ibss_rsn;
+       os_memcpy(peer->addr, addr, ETH_ALEN);
+
+       if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk)
+           < 0) {
+               ibss_rsn_free(peer);
+               return -1;
+       }
+
+       if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) {
+               ibss_rsn_free(peer);
+               return -1;
+       }
+
+       peer->next = ibss_rsn->peers;
+       ibss_rsn->peers = peer;
+
+       return 0;
+}
+
+
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s)
+{
+       struct ibss_rsn *ibss_rsn;
+
+       ibss_rsn = os_zalloc(sizeof(*ibss_rsn));
+       if (ibss_rsn == NULL)
+               return NULL;
+       ibss_rsn->wpa_s = wpa_s;
+
+       if (ibss_rsn_auth_init_group(ibss_rsn, wpa_s->own_addr) < 0) {
+               ibss_rsn_deinit(ibss_rsn);
+               return NULL;
+       }
+
+       return ibss_rsn;
+}
+
+
+void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn)
+{
+       struct ibss_rsn_peer *peer, *prev;
+
+       if (ibss_rsn == NULL)
+               return;
+
+       peer = ibss_rsn->peers;
+       while (peer) {
+               prev = peer;
+               peer = peer->next;
+               ibss_rsn_free(prev);
+       }
+
+       wpa_deinit(ibss_rsn->auth_group);
+       os_free(ibss_rsn);
+
+}
+
+
+static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len)
+{
+       const struct ieee802_1x_hdr *hdr;
+       const struct wpa_eapol_key *key;
+       u16 key_info;
+       size_t plen;
+
+       /* TODO: Support other EAPOL packets than just EAPOL-Key */
+
+       if (len < sizeof(*hdr) + sizeof(*key))
+               return -1;
+
+       hdr = (const struct ieee802_1x_hdr *) buf;
+       key = (const struct wpa_eapol_key *) (hdr + 1);
+       plen = be_to_host16(hdr->length);
+
+       if (hdr->version < EAPOL_VERSION) {
+               /* TODO: backwards compatibility */
+       }
+       if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+               wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, "
+                       "not a Key frame", hdr->type);
+               return -1;
+       }
+       if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+               wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu "
+                          "invalid (frame size %lu)",
+                          (unsigned long) plen, (unsigned long) len);
+               return -1;
+       }
+
+       if (key->type != EAPOL_KEY_TYPE_RSN) {
+               wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, "
+                          "discarded", key->type);
+               return -1;
+       }
+
+       key_info = WPA_GET_BE16(key->key_info);
+
+       return !!(key_info & WPA_KEY_INFO_ACK);
+}
+
+
+static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
+                                    struct ibss_rsn_peer *peer,
+                                    const u8 *buf, size_t len)
+{
+       int supp;
+       u8 *tmp;
+
+       supp = ibss_rsn_eapol_dst_supp(buf, len);
+       if (supp < 0)
+               return -1;
+
+       tmp = os_malloc(len);
+       if (tmp == NULL)
+               return -1;
+       os_memcpy(tmp, buf, len);
+       if (supp) {
+               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant");
+               wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len);
+       } else {
+               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator");
+               wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len);
+       }
+       os_free(tmp);
+
+       return 1;
+}
+
+
+int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
+                     const u8 *buf, size_t len)
+{
+       struct ibss_rsn_peer *peer;
+
+       for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+               if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0)
+                       return ibss_rsn_process_rx_eapol(ibss_rsn, peer,
+                                                        buf, len);
+       }
+
+       if (ibss_rsn_eapol_dst_supp(buf, len) > 0) {
+               /*
+                * Create new IBSS peer based on an EAPOL message from the peer
+                * Authenticator.
+                */
+               if (ibss_rsn_start(ibss_rsn, src_addr) < 0)
+                       return -1;
+               return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers,
+                                                buf, len);
+       }
+
+       return 0;
+}
+
+
+void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
+{
+       os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
+}
diff --git a/wpa_supplicant/ibss_rsn.h b/wpa_supplicant/ibss_rsn.h
new file mode 100644 (file)
index 0000000..11e63ad
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * wpa_supplicant - IBSS RSN
+ * Copyright (c) 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.
+ */
+
+#ifndef IBSS_RSN_H
+#define IBSS_RSN_H
+
+struct ibss_rsn;
+
+struct ibss_rsn_peer {
+       struct ibss_rsn_peer *next;
+       struct ibss_rsn *ibss_rsn;
+
+       u8 addr[ETH_ALEN];
+
+       struct wpa_sm *supp;
+       enum wpa_states supp_state;
+       u8 supp_ie[80];
+       size_t supp_ie_len;
+
+       struct wpa_state_machine *auth;
+};
+
+struct ibss_rsn {
+       struct wpa_supplicant *wpa_s;
+       struct wpa_authenticator *auth_group;
+       struct ibss_rsn_peer *peers;
+       u8 psk[PMK_LEN];
+};
+
+
+struct ibss_rsn * ibss_rsn_init(struct wpa_supplicant *wpa_s);
+void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn);
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr);
+int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
+                     const u8 *buf, size_t len);
+void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
+
+#endif /* IBSS_RSN_H */
diff --git a/wpa_supplicant/main.c b/wpa_supplicant/main.c
new file mode 100644 (file)
index 0000000..c0aa59c
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * WPA Supplicant / main() function for UNIX like OSes and MinGW
+ * 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.
+ */
+
+#include "includes.h"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+
+extern struct wpa_driver_ops *wpa_drivers[];
+
+
+static void usage(void)
+{
+       int i;
+       printf("%s\n\n%s\n"
+              "usage:\n"
+              "  wpa_supplicant [-BddhKLqqstuvW] [-P<pid file>] "
+              "[-g<global ctrl>] \\\n"
+              "        -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
+              "[-p<driver_param>] \\\n"
+              "        [-b<br_ifname>] [-f<debug file>] \\\n"
+              "        [-o<override driver>] [-O<override ctrl>] \\\n"
+              "        [-N -i<ifname> -c<conf> [-C<ctrl>] "
+              "[-D<driver>] \\\n"
+              "        [-p<driver_param>] [-b<br_ifname>] ...]\n"
+              "\n"
+              "drivers:\n",
+              wpa_supplicant_version, wpa_supplicant_license);
+
+       for (i = 0; wpa_drivers[i]; i++) {
+               printf("  %s = %s\n",
+                      wpa_drivers[i]->name,
+                      wpa_drivers[i]->desc);
+       }
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       printf("options:\n"
+              "  -b = optional bridge interface name\n"
+              "  -B = run daemon in the background\n"
+              "  -c = Configuration file\n"
+              "  -C = ctrl_interface parameter (only used if -c is not)\n"
+              "  -i = interface name\n"
+              "  -d = increase debugging verbosity (-dd even more)\n"
+              "  -D = driver name (can be multiple drivers: nl80211,wext)\n");
+#ifdef CONFIG_DEBUG_FILE
+       printf("  -f = log output to debug file instead of stdout\n");
+#endif /* CONFIG_DEBUG_FILE */
+       printf("  -g = global ctrl_interface\n"
+              "  -K = include keys (passwords, etc.) in debug output\n");
+#ifdef CONFIG_DEBUG_SYSLOG
+       printf("  -s = log output to syslog instead of stdout\n");
+#endif /* CONFIG_DEBUG_SYSLOG */
+       printf("  -t = include timestamp in debug messages\n"
+              "  -h = show this help text\n"
+              "  -L = show license (GPL and BSD)\n"
+              "  -o = override driver parameter for new interfaces\n"
+              "  -O = override ctrl_interface parameter for new interfaces\n"
+              "  -p = driver parameters\n"
+              "  -P = PID file\n"
+              "  -q = decrease debugging verbosity (-qq even less)\n");
+#ifdef CONFIG_DBUS
+       printf("  -u = enable DBus control interface\n");
+#endif /* CONFIG_DBUS */
+       printf("  -v = show version\n"
+              "  -W = wait for a control interface monitor before starting\n"
+              "  -N = start describing new interface\n");
+
+       printf("example:\n"
+              "  wpa_supplicant -D%s -iwlan0 -c/etc/wpa_supplicant.conf\n",
+              wpa_drivers[i] ? wpa_drivers[i]->name : "wext");
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static void license(void)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       printf("%s\n\n%s%s%s%s%s\n",
+              wpa_supplicant_version,
+              wpa_supplicant_full_license1,
+              wpa_supplicant_full_license2,
+              wpa_supplicant_full_license3,
+              wpa_supplicant_full_license4,
+              wpa_supplicant_full_license5);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
+static void wpa_supplicant_fd_workaround(void)
+{
+#ifdef __linux__
+       int s, i;
+       /* When started from pcmcia-cs scripts, wpa_supplicant might start with
+        * fd 0, 1, and 2 closed. This will cause some issues because many
+        * places in wpa_supplicant are still printing out to stdout. As a
+        * workaround, make sure that fd's 0, 1, and 2 are not used for other
+        * sockets. */
+       for (i = 0; i < 3; i++) {
+               s = open("/dev/null", O_RDWR);
+               if (s > 2) {
+                       close(s);
+                       break;
+               }
+       }
+#endif /* __linux__ */
+}
+
+
+int main(int argc, char *argv[])
+{
+       int c, i;
+       struct wpa_interface *ifaces, *iface;
+       int iface_count, exitcode = -1;
+       struct wpa_params params;
+       struct wpa_global *global;
+
+       if (os_program_init())
+               return -1;
+
+       os_memset(&params, 0, sizeof(params));
+       params.wpa_debug_level = MSG_INFO;
+
+       iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
+       if (ifaces == NULL)
+               return -1;
+       iface_count = 1;
+
+       wpa_supplicant_fd_workaround();
+
+       for (;;) {
+               c = getopt(argc, argv, "b:Bc:C:D:df:g:hi:KLNo:O:p:P:qstuvW");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'b':
+                       iface->bridge_ifname = optarg;
+                       break;
+               case 'B':
+                       params.daemonize++;
+                       break;
+               case 'c':
+                       iface->confname = optarg;
+                       break;
+               case 'C':
+                       iface->ctrl_interface = optarg;
+                       break;
+               case 'D':
+                       iface->driver = optarg;
+                       break;
+               case 'd':
+#ifdef CONFIG_NO_STDOUT_DEBUG
+                       printf("Debugging disabled with "
+                              "CONFIG_NO_STDOUT_DEBUG=y build time "
+                              "option.\n");
+                       goto out;
+#else /* CONFIG_NO_STDOUT_DEBUG */
+                       params.wpa_debug_level--;
+                       break;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+#ifdef CONFIG_DEBUG_FILE
+               case 'f':
+                       params.wpa_debug_file_path = optarg;
+                       break;
+#endif /* CONFIG_DEBUG_FILE */
+               case 'g':
+                       params.ctrl_interface = optarg;
+                       break;
+               case 'h':
+                       usage();
+                       exitcode = 0;
+                       goto out;
+               case 'i':
+                       iface->ifname = optarg;
+                       break;
+               case 'K':
+                       params.wpa_debug_show_keys++;
+                       break;
+               case 'L':
+                       license();
+                       exitcode = 0;
+                       goto out;
+               case 'o':
+                       params.override_driver = optarg;
+                       break;
+               case 'O':
+                       params.override_ctrl_interface = optarg;
+                       break;
+               case 'p':
+                       iface->driver_param = optarg;
+                       break;
+               case 'P':
+                       os_free(params.pid_file);
+                       params.pid_file = os_rel2abs_path(optarg);
+                       break;
+               case 'q':
+                       params.wpa_debug_level++;
+                       break;
+#ifdef CONFIG_DEBUG_SYSLOG
+               case 's':
+                       params.wpa_debug_syslog++;
+                       break;
+#endif /* CONFIG_DEBUG_SYSLOG */
+               case 't':
+                       params.wpa_debug_timestamp++;
+                       break;
+#ifdef CONFIG_DBUS
+               case 'u':
+                       params.dbus_ctrl_interface = 1;
+                       break;
+#endif /* CONFIG_DBUS */
+               case 'v':
+                       printf("%s\n", wpa_supplicant_version);
+                       exitcode = 0;
+                       goto out;
+               case 'W':
+                       params.wait_for_monitor++;
+                       break;
+               case 'N':
+                       iface_count++;
+                       iface = os_realloc(ifaces, iface_count *
+                                          sizeof(struct wpa_interface));
+                       if (iface == NULL)
+                               goto out;
+                       ifaces = iface;
+                       iface = &ifaces[iface_count - 1]; 
+                       os_memset(iface, 0, sizeof(*iface));
+                       break;
+               default:
+                       usage();
+                       exitcode = 0;
+                       goto out;
+               }
+       }
+
+       exitcode = 0;
+       global = wpa_supplicant_init(&params);
+       if (global == NULL) {
+               wpa_printf(MSG_ERROR, "Failed to initialize wpa_supplicant");
+               exitcode = -1;
+               goto out;
+       }
+
+       for (i = 0; exitcode == 0 && i < iface_count; i++) {
+               if ((ifaces[i].confname == NULL &&
+                    ifaces[i].ctrl_interface == NULL) ||
+                   ifaces[i].ifname == NULL) {
+                       if (iface_count == 1 && (params.ctrl_interface ||
+                                                params.dbus_ctrl_interface))
+                               break;
+                       usage();
+                       exitcode = -1;
+                       break;
+               }
+               if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL)
+                       exitcode = -1;
+       }
+
+       if (exitcode == 0)
+               exitcode = wpa_supplicant_run(global);
+
+       wpa_supplicant_deinit(global);
+
+out:
+       os_free(ifaces);
+       os_free(params.pid_file);
+
+       os_program_deinit();
+
+       return exitcode;
+}
diff --git a/wpa_supplicant/main_none.c b/wpa_supplicant/main_none.c
new file mode 100644 (file)
index 0000000..993338a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * WPA Supplicant / Example program entrypoint
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+
+int main(int argc, char *argv[])
+{
+       struct wpa_interface iface;
+       int exitcode = 0;
+       struct wpa_params params;
+       struct wpa_global *global;
+
+       memset(&params, 0, sizeof(params));
+       params.wpa_debug_level = MSG_INFO;
+
+       global = wpa_supplicant_init(&params);
+       if (global == NULL)
+               return -1;
+
+       memset(&iface, 0, sizeof(iface));
+       /* TODO: set interface parameters */
+
+       if (wpa_supplicant_add_iface(global, &iface) == NULL)
+               exitcode = -1;
+
+       if (exitcode == 0)
+               exitcode = wpa_supplicant_run(global);
+
+       wpa_supplicant_deinit(global);
+
+       return exitcode;
+}
diff --git a/wpa_supplicant/main_symbian.cpp b/wpa_supplicant/main_symbian.cpp
new file mode 100644 (file)
index 0000000..4ff364b
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * WPA Supplicant / Program entrypoint for Symbian
+ * 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.
+ */
+
+#include "includes.h"
+
+extern "C" {
+#include "common.h"
+#include "wpa_supplicant_i.h"
+}
+
+GLDEF_C TInt E32Main(void)
+{
+       struct wpa_interface iface;
+       int exitcode = 0;
+       struct wpa_params params;
+       struct wpa_global *global;
+
+       memset(&params, 0, sizeof(params));
+       params.wpa_debug_level = MSG_INFO;
+
+       global = wpa_supplicant_init(&params);
+       if (global == NULL)
+               return -1;
+
+       memset(&iface, 0, sizeof(iface));
+       /* TODO: set interface parameters */
+
+       if (wpa_supplicant_add_iface(global, &iface) == NULL)
+               exitcode = -1;
+
+       if (exitcode == 0)
+               exitcode = wpa_supplicant_run(global);
+
+       wpa_supplicant_deinit(global);
+
+       return exitcode;
+}
diff --git a/wpa_supplicant/main_winmain.c b/wpa_supplicant/main_winmain.c
new file mode 100644 (file)
index 0000000..19d9950
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * WPA Supplicant / WinMain() function for Windows-based applications
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+
+#ifdef _WIN32_WCE
+#define CMDLINE LPWSTR
+#else /* _WIN32_WCE */
+#define CMDLINE LPSTR
+#endif /* _WIN32_WCE */
+
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+                  CMDLINE lpCmdLine, int nShowCmd)
+{
+       int i;
+       struct wpa_interface *ifaces, *iface;
+       int iface_count, exitcode = -1;
+       struct wpa_params params;
+       struct wpa_global *global;
+
+       if (os_program_init())
+               return -1;
+
+       os_memset(&params, 0, sizeof(params));
+       params.wpa_debug_level = MSG_MSGDUMP;
+       params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt";
+       params.wpa_debug_show_keys = 1;
+
+       iface = ifaces = os_zalloc(sizeof(struct wpa_interface));
+       if (ifaces == NULL)
+               return -1;
+       iface_count = 1;
+
+       iface->confname = "default";
+       iface->driver = "ndis";
+       iface->ifname = "";
+
+       exitcode = 0;
+       global = wpa_supplicant_init(&params);
+       if (global == NULL) {
+               printf("Failed to initialize wpa_supplicant\n");
+               exitcode = -1;
+       }
+
+       for (i = 0; exitcode == 0 && i < iface_count; i++) {
+               if ((ifaces[i].confname == NULL &&
+                    ifaces[i].ctrl_interface == NULL) ||
+                   ifaces[i].ifname == NULL) {
+                       if (iface_count == 1 && (params.ctrl_interface ||
+                                                params.dbus_ctrl_interface))
+                               break;
+                       exitcode = -1;
+                       break;
+               }
+               if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL)
+                       exitcode = -1;
+       }
+
+       if (exitcode == 0)
+               exitcode = wpa_supplicant_run(global);
+
+       wpa_supplicant_deinit(global);
+
+       os_free(ifaces);
+
+       os_program_deinit();
+
+       return exitcode;
+}
diff --git a/wpa_supplicant/main_winsvc.c b/wpa_supplicant/main_winsvc.c
new file mode 100644 (file)
index 0000000..4a46ed5
--- /dev/null
@@ -0,0 +1,464 @@
+/*
+ * WPA Supplicant / main() function for Win32 service
+ * Copyright (c) 2003-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.
+ *
+ * The root of wpa_supplicant configuration in registry is
+ * HKEY_LOCAL_MACHINE\\SOFTWARE\\%wpa_supplicant. This level includes global
+ * parameters and a 'interfaces' subkey with all the interface configuration
+ * (adapter to confname mapping). Each such mapping is a subkey that has
+ * 'adapter' and 'config' values.
+ *
+ * This program can be run either as a normal command line application, e.g.,
+ * for debugging, with 'wpasvc.exe app' or as a Windows service. Service need
+ * to be registered with 'wpasvc.exe reg <full path to wpasvc.exe>'. After
+ * this, it can be started like any other Windows service (e.g., 'net start
+ * wpasvc') or it can be configured to start automatically through the Services
+ * tool in administrative tasks. The service can be unregistered with
+ * 'wpasvc.exe unreg'.
+ */
+
+#include "includes.h"
+#include <windows.h>
+
+#include "common.h"
+#include "wpa_supplicant_i.h"
+#include "eloop.h"
+
+#ifndef WPASVC_NAME
+#define WPASVC_NAME TEXT("wpasvc")
+#endif
+#ifndef WPASVC_DISPLAY_NAME
+#define WPASVC_DISPLAY_NAME TEXT("wpa_supplicant service")
+#endif
+#ifndef WPASVC_DESCRIPTION
+#define WPASVC_DESCRIPTION \
+TEXT("Provides IEEE 802.1X and WPA/WPA2 supplicant functionality")
+#endif
+
+static HANDLE kill_svc;
+
+static SERVICE_STATUS_HANDLE svc_status_handle;
+static SERVICE_STATUS svc_status;
+
+
+#ifndef WPA_KEY_ROOT
+#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
+#endif
+#ifndef WPA_KEY_PREFIX
+#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
+#endif
+
+#ifdef UNICODE
+#define TSTR "%S"
+#else /* UNICODE */
+#define TSTR "%s"
+#endif /* UNICODE */
+
+
+static int read_interface(struct wpa_global *global, HKEY _hk,
+                         const TCHAR *name)
+{
+       HKEY hk;
+#define TBUFLEN 255
+       TCHAR adapter[TBUFLEN], config[TBUFLEN], ctrl_interface[TBUFLEN];
+       DWORD buflen, val;
+       LONG ret;
+       struct wpa_interface iface;
+       int skip_on_error = 0;
+
+       ret = RegOpenKeyEx(_hk, name, 0, KEY_QUERY_VALUE, &hk);
+       if (ret != ERROR_SUCCESS) {
+               printf("Could not open wpa_supplicant interface key\n");
+               return -1;
+       }
+
+       os_memset(&iface, 0, sizeof(iface));
+       iface.driver = "ndis";
+
+       buflen = sizeof(ctrl_interface);
+       ret = RegQueryValueEx(hk, TEXT("ctrl_interface"), NULL, NULL,
+                             (LPBYTE) ctrl_interface, &buflen);
+       if (ret == ERROR_SUCCESS) {
+               ctrl_interface[TBUFLEN - 1] = TEXT('\0');
+               wpa_unicode2ascii_inplace(ctrl_interface);
+               printf("ctrl_interface[len=%d] '%s'\n",
+                      (int) buflen, (char *) ctrl_interface);
+               iface.ctrl_interface = (char *) ctrl_interface;
+       }
+
+       buflen = sizeof(adapter);
+       ret = RegQueryValueEx(hk, TEXT("adapter"), NULL, NULL,
+                             (LPBYTE) adapter, &buflen);
+       if (ret == ERROR_SUCCESS) {
+               adapter[TBUFLEN - 1] = TEXT('\0');
+               wpa_unicode2ascii_inplace(adapter);
+               printf("adapter[len=%d] '%s'\n",
+                      (int) buflen, (char *) adapter);
+               iface.ifname = (char *) adapter;
+       }
+
+       buflen = sizeof(config);
+       ret = RegQueryValueEx(hk, TEXT("config"), NULL, NULL,
+                             (LPBYTE) config, &buflen);
+       if (ret == ERROR_SUCCESS) {
+               config[sizeof(config) - 1] = '\0';
+               wpa_unicode2ascii_inplace(config);
+               printf("config[len=%d] '%s'\n",
+                      (int) buflen, (char *) config);
+               iface.confname = (char *) config;
+       }
+
+       buflen = sizeof(val);
+       ret = RegQueryValueEx(hk, TEXT("skip_on_error"), NULL, NULL,
+                             (LPBYTE) &val, &buflen);
+       if (ret == ERROR_SUCCESS && buflen == sizeof(val))
+               skip_on_error = val;
+
+       RegCloseKey(hk);
+
+       if (wpa_supplicant_add_iface(global, &iface) == NULL) {
+               if (skip_on_error)
+                       wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to "
+                                  "initialization failure", iface.ifname);
+               else
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+static int wpa_supplicant_thread(void)
+{
+       int exitcode;
+       struct wpa_params params;
+       struct wpa_global *global;
+       HKEY hk, ihk;
+       DWORD val, buflen, i;
+       LONG ret;
+
+       if (os_program_init())
+               return -1;
+
+       os_memset(&params, 0, sizeof(params));
+       params.wpa_debug_level = MSG_INFO;
+
+       ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX,
+                          0, KEY_QUERY_VALUE, &hk);
+       if (ret != ERROR_SUCCESS) {
+               printf("Could not open wpa_supplicant registry key\n");
+               return -1;
+       }
+
+       buflen = sizeof(val);
+       ret = RegQueryValueEx(hk, TEXT("debug_level"), NULL, NULL,
+                             (LPBYTE) &val, &buflen);
+       if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+               params.wpa_debug_level = val;
+       }
+
+       buflen = sizeof(val);
+       ret = RegQueryValueEx(hk, TEXT("debug_show_keys"), NULL, NULL,
+                             (LPBYTE) &val, &buflen);
+       if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+               params.wpa_debug_show_keys = val;
+       }
+
+       buflen = sizeof(val);
+       ret = RegQueryValueEx(hk, TEXT("debug_timestamp"), NULL, NULL,
+                             (LPBYTE) &val, &buflen);
+       if (ret == ERROR_SUCCESS && buflen == sizeof(val)) {
+               params.wpa_debug_timestamp = val;
+       }
+
+       buflen = sizeof(val);
+       ret = RegQueryValueEx(hk, TEXT("debug_use_file"), NULL, NULL,
+                             (LPBYTE) &val, &buflen);
+       if (ret == ERROR_SUCCESS && buflen == sizeof(val) && val) {
+               params.wpa_debug_file_path = "\\Temp\\wpa_supplicant-log.txt";
+       }
+
+       exitcode = 0;
+       global = wpa_supplicant_init(&params);
+       if (global == NULL) {
+               printf("Failed to initialize wpa_supplicant\n");
+               exitcode = -1;
+       }
+
+       ret = RegOpenKeyEx(hk, TEXT("interfaces"), 0, KEY_ENUMERATE_SUB_KEYS,
+                          &ihk);
+       RegCloseKey(hk);
+       if (ret != ERROR_SUCCESS) {
+               printf("Could not open wpa_supplicant interfaces registry "
+                      "key\n");
+               return -1;
+       }
+
+       for (i = 0; ; i++) {
+               TCHAR name[255];
+               DWORD namelen;
+
+               namelen = 255;
+               ret = RegEnumKeyEx(ihk, i, name, &namelen, NULL, NULL, NULL,
+                                  NULL);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS) {
+                       printf("RegEnumKeyEx failed: 0x%x\n",
+                              (unsigned int) ret);
+                       break;
+               }
+
+               if (namelen >= 255)
+                       namelen = 255 - 1;
+               name[namelen] = '\0';
+
+               wpa_printf(MSG_DEBUG, "interface %d: %s\n", (int) i, name);
+               if (read_interface(global, ihk, name) < 0)
+                       exitcode = -1;
+       }
+
+       RegCloseKey(ihk);
+
+       if (exitcode == 0)
+               exitcode = wpa_supplicant_run(global);
+
+       wpa_supplicant_deinit(global);
+
+       os_program_deinit();
+
+       return exitcode;
+}
+
+
+static DWORD svc_thread(LPDWORD param)
+{
+       int ret = wpa_supplicant_thread();
+
+       svc_status.dwCurrentState = SERVICE_STOPPED;
+       svc_status.dwWaitHint = 0;
+       if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+               printf("SetServiceStatus() failed: %d\n",
+                      (int) GetLastError());
+       }
+
+       return ret;
+}
+
+
+static int register_service(const TCHAR *exe)
+{
+       SC_HANDLE svc, scm;
+       SERVICE_DESCRIPTION sd;
+
+       printf("Registering service: " TSTR "\n", WPASVC_NAME);
+
+       scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+       if (!scm) {
+               printf("OpenSCManager failed: %d\n", (int) GetLastError());
+               return -1;
+       }
+
+       svc = CreateService(scm, WPASVC_NAME, WPASVC_DISPLAY_NAME,
+                           SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
+                           SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL,
+                           exe, NULL, NULL, NULL, NULL, NULL);
+
+       if (!svc) {
+               printf("CreateService failed: %d\n\n", (int) GetLastError());
+               CloseServiceHandle(scm);
+               return -1;
+       }
+
+       os_memset(&sd, 0, sizeof(sd));
+       sd.lpDescription = WPASVC_DESCRIPTION;
+       if (!ChangeServiceConfig2(svc, SERVICE_CONFIG_DESCRIPTION, &sd)) {
+               printf("ChangeServiceConfig2 failed: %d\n",
+                      (int) GetLastError());
+               /* This is not a fatal error, so continue anyway. */
+       }
+
+       CloseServiceHandle(svc);
+       CloseServiceHandle(scm);
+
+       printf("Service registered successfully.\n");
+
+       return 0;
+}
+
+
+static int unregister_service(void)
+{
+       SC_HANDLE svc, scm;
+       SERVICE_STATUS status;
+
+       printf("Unregistering service: " TSTR "\n", WPASVC_NAME);
+
+       scm = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+       if (!scm) {
+               printf("OpenSCManager failed: %d\n", (int) GetLastError());
+               return -1;
+       }
+
+       svc = OpenService(scm, WPASVC_NAME, SERVICE_ALL_ACCESS | DELETE);
+       if (!svc) {
+               printf("OpenService failed: %d\n\n", (int) GetLastError());
+               CloseServiceHandle(scm);
+               return -1;
+       }
+
+       if (QueryServiceStatus(svc, &status)) {
+               if (status.dwCurrentState != SERVICE_STOPPED) {
+                       printf("Service currently active - stopping "
+                              "service...\n");
+                       if (!ControlService(svc, SERVICE_CONTROL_STOP,
+                                           &status)) {
+                               printf("ControlService failed: %d\n",
+                                      (int) GetLastError());
+                       }
+                       Sleep(500);
+               }
+       }
+
+       if (DeleteService(svc)) {
+               printf("Service unregistered successfully.\n");
+       } else {
+               printf("DeleteService failed: %d\n", (int) GetLastError());
+       }
+
+       CloseServiceHandle(svc);
+       CloseServiceHandle(scm);
+
+       return 0;
+}
+
+
+static void WINAPI service_ctrl_handler(DWORD control_code)
+{
+       switch (control_code) {
+       case SERVICE_CONTROL_INTERROGATE:
+               break;
+       case SERVICE_CONTROL_SHUTDOWN:
+       case SERVICE_CONTROL_STOP:
+               svc_status.dwCurrentState = SERVICE_STOP_PENDING;
+               svc_status.dwWaitHint = 2000;
+               eloop_terminate();
+               SetEvent(kill_svc);
+               break;
+       }
+
+       if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+               printf("SetServiceStatus() failed: %d\n",
+                      (int) GetLastError());
+       }
+}
+
+
+static void WINAPI service_start(DWORD argc, LPTSTR *argv)
+{
+       DWORD id;
+
+       svc_status_handle = RegisterServiceCtrlHandler(WPASVC_NAME,
+                                                      service_ctrl_handler);
+       if (svc_status_handle == (SERVICE_STATUS_HANDLE) 0) {
+               printf("RegisterServiceCtrlHandler failed: %d\n",
+                      (int) GetLastError());
+               return;
+       }
+
+       os_memset(&svc_status, 0, sizeof(svc_status));
+       svc_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+       svc_status.dwCurrentState = SERVICE_START_PENDING;
+       svc_status.dwWaitHint = 1000;
+
+       if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+               printf("SetServiceStatus() failed: %d\n",
+                      (int) GetLastError());
+               return;
+       }
+
+       kill_svc = CreateEvent(0, TRUE, FALSE, 0);
+       if (!kill_svc) {
+               printf("CreateEvent failed: %d\n", (int) GetLastError());
+               return;
+       }
+
+       if (CreateThread(0, 0, (LPTHREAD_START_ROUTINE) svc_thread, 0, 0, &id)
+           == 0) {
+               printf("CreateThread failed: %d\n", (int) GetLastError());
+               return;
+       }
+
+       if (svc_status.dwCurrentState == SERVICE_START_PENDING) {
+               svc_status.dwCurrentState = SERVICE_RUNNING;
+               svc_status.dwWaitHint = 0;
+               svc_status.dwControlsAccepted = SERVICE_ACCEPT_STOP |
+                       SERVICE_ACCEPT_SHUTDOWN;
+       }
+
+       if (!SetServiceStatus(svc_status_handle, &svc_status)) {
+               printf("SetServiceStatus() failed: %d\n",
+                      (int) GetLastError());
+               return;
+       }
+
+       /* wait until service gets killed */
+       WaitForSingleObject(kill_svc, INFINITE);
+}
+
+
+int main(int argc, char *argv[])
+{
+       SERVICE_TABLE_ENTRY dt[] = {
+               { WPASVC_NAME, service_start },
+               { NULL, NULL }
+       };
+
+       if (argc > 1) {
+               if (os_strcmp(argv[1], "reg") == 0) {
+                       TCHAR *path;
+                       int ret;
+
+                       if (argc < 3) {
+                               path = os_malloc(MAX_PATH * sizeof(TCHAR));
+                               if (path == NULL)
+                                       return -1;
+                               if (!GetModuleFileName(NULL, path, MAX_PATH)) {
+                                       printf("GetModuleFileName failed: "
+                                              "%d\n", (int) GetLastError());
+                                       os_free(path);
+                                       return -1;
+                               }
+                       } else {
+                               path = wpa_strdup_tchar(argv[2]);
+                               if (path == NULL)
+                                       return -1;
+                       }
+                       ret = register_service(path);
+                       os_free(path);
+                       return ret;
+               } else if (os_strcmp(argv[1], "unreg") == 0) {
+                       return unregister_service();
+               } else if (os_strcmp(argv[1], "app") == 0) {
+                       return wpa_supplicant_thread();
+               }
+       }
+
+       if (!StartServiceCtrlDispatcher(dt)) {
+               printf("StartServiceCtrlDispatcher failed: %d\n",
+                      (int) GetLastError());
+       }
+
+       return 0;
+}
diff --git a/wpa_supplicant/mlme.c b/wpa_supplicant/mlme.c
new file mode 100644 (file)
index 0000000..eb60ac5
--- /dev/null
@@ -0,0 +1,3198 @@
+/*
+ * WPA Supplicant - Client mode MLME
+ * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eloop.h"
+#include "config_ssid.h"
+#include "wpa_supplicant_i.h"
+#include "notify.h"
+#include "driver_i.h"
+#include "rsn_supp/wpa.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "mlme.h"
+
+
+/* Timeouts and intervals in milliseconds */
+#define IEEE80211_AUTH_TIMEOUT (200)
+#define IEEE80211_AUTH_MAX_TRIES 3
+#define IEEE80211_ASSOC_TIMEOUT (200)
+#define IEEE80211_ASSOC_MAX_TRIES 3
+#define IEEE80211_MONITORING_INTERVAL (2000)
+#define IEEE80211_PROBE_INTERVAL (60000)
+#define IEEE80211_RETRY_AUTH_INTERVAL (1000)
+#define IEEE80211_SCAN_INTERVAL (2000)
+#define IEEE80211_SCAN_INTERVAL_SLOW (15000)
+#define IEEE80211_IBSS_JOIN_TIMEOUT (20000)
+
+#define IEEE80211_PROBE_DELAY (33)
+#define IEEE80211_CHANNEL_TIME (33)
+#define IEEE80211_PASSIVE_CHANNEL_TIME (200)
+#define IEEE80211_SCAN_RESULT_EXPIRE (10000)
+#define IEEE80211_IBSS_MERGE_INTERVAL (30000)
+#define IEEE80211_IBSS_INACTIVITY_LIMIT (60000)
+
+#define IEEE80211_IBSS_MAX_STA_ENTRIES 128
+
+
+#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4))
+
+
+struct ieee80211_sta_bss {
+       struct ieee80211_sta_bss *next;
+       struct ieee80211_sta_bss *hnext;
+
+       u8 bssid[ETH_ALEN];
+       u8 ssid[MAX_SSID_LEN];
+       size_t ssid_len;
+       u16 capability; /* host byte order */
+       int hw_mode;
+       int channel;
+       int freq;
+       int rssi;
+       u8 *ie;
+       size_t ie_len;
+       u8 *wpa_ie;
+       size_t wpa_ie_len;
+       u8 *rsn_ie;
+       size_t rsn_ie_len;
+       u8 *wmm_ie;
+       size_t wmm_ie_len;
+       u8 *mdie;
+       size_t mdie_len;
+#define IEEE80211_MAX_SUPP_RATES 32
+       u8 supp_rates[IEEE80211_MAX_SUPP_RATES];
+       size_t supp_rates_len;
+       int beacon_int;
+       u64 timestamp;
+
+       int probe_resp;
+       struct os_time last_update;
+};
+
+
+static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
+                                    const u8 *dst,
+                                    const u8 *ssid, size_t ssid_len);
+static struct ieee80211_sta_bss *
+ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid);
+static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s);
+static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s);
+static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx);
+static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx);
+static void ieee80211_build_tspec(struct wpabuf *buf);
+static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
+                                         const u8 *ies, size_t ies_len);
+
+
+static int ieee80211_sta_set_channel(struct wpa_supplicant *wpa_s,
+                                    enum hostapd_hw_mode phymode, int chan,
+                                    int freq)
+{
+       size_t i;
+       struct hostapd_hw_modes *mode;
+
+       for (i = 0; i < wpa_s->mlme.num_modes; i++) {
+               mode = &wpa_s->mlme.modes[i];
+               if (mode->mode == phymode) {
+                       wpa_s->mlme.curr_rates = mode->rates;
+                       wpa_s->mlme.num_curr_rates = mode->num_rates;
+                       break;
+               }
+       }
+
+       return wpa_drv_set_channel(wpa_s, phymode, chan, freq);
+}
+
+
+static int ecw2cw(int ecw)
+{
+       int cw = 1;
+       while (ecw > 0) {
+               cw <<= 1;
+               ecw--;
+       }
+       return cw - 1;
+}
+
+
+static void ieee80211_sta_wmm_params(struct wpa_supplicant *wpa_s,
+                                    const u8 *wmm_param, size_t wmm_param_len)
+{
+       size_t left;
+       int count;
+       const u8 *pos;
+       u8 wmm_acm;
+
+       if (wmm_param_len < 8 || wmm_param[5] /* version */ != 1)
+               return;
+       count = wmm_param[6] & 0x0f;
+       if (count == wpa_s->mlme.wmm_last_param_set)
+               return;
+       wpa_s->mlme.wmm_last_param_set = count;
+
+       pos = wmm_param + 8;
+       left = wmm_param_len - 8;
+
+       wmm_acm = 0;
+       for (; left >= 4; left -= 4, pos += 4) {
+               int aci = (pos[0] >> 5) & 0x03;
+               int acm = (pos[0] >> 4) & 0x01;
+               int aifs, cw_max, cw_min, burst_time;
+
+               switch (aci) {
+               case 1: /* AC_BK */
+                       if (acm)
+                               wmm_acm |= BIT(1) | BIT(2); /* BK/- */
+                       break;
+               case 2: /* AC_VI */
+                       if (acm)
+                               wmm_acm |= BIT(4) | BIT(5); /* CL/VI */
+                       break;
+               case 3: /* AC_VO */
+                       if (acm)
+                               wmm_acm |= BIT(6) | BIT(7); /* VO/NC */
+                       break;
+               case 0: /* AC_BE */
+               default:
+                       if (acm)
+                               wmm_acm |= BIT(0) | BIT(3); /* BE/EE */
+                       break;
+               }
+
+               aifs = pos[0] & 0x0f;
+               cw_max = ecw2cw((pos[1] & 0xf0) >> 4);
+               cw_min = ecw2cw(pos[1] & 0x0f);
+               /* TXOP is in units of 32 usec; burst_time in 0.1 ms */
+               burst_time = (pos[2] | (pos[3] << 8)) * 32 / 100;
+               wpa_printf(MSG_DEBUG, "MLME: WMM aci=%d acm=%d aifs=%d "
+                          "cWmin=%d cWmax=%d burst=%d",
+                          aci, acm, aifs, cw_min, cw_max, burst_time);
+               /* TODO: driver configuration */
+       }
+}
+
+
+static void ieee80211_set_associated(struct wpa_supplicant *wpa_s, int assoc)
+{
+       if (wpa_s->mlme.associated == assoc && !assoc)
+               return;
+
+       wpa_s->mlme.associated = assoc;
+
+       if (assoc) {
+               union wpa_event_data data;
+               os_memset(&data, 0, sizeof(data));
+               wpa_s->mlme.prev_bssid_set = 1;
+               os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
+               data.assoc_info.req_ies = wpa_s->mlme.assocreq_ies;
+               data.assoc_info.req_ies_len = wpa_s->mlme.assocreq_ies_len;
+               data.assoc_info.resp_ies = wpa_s->mlme.assocresp_ies;
+               data.assoc_info.resp_ies_len = wpa_s->mlme.assocresp_ies_len;
+               data.assoc_info.freq = wpa_s->mlme.freq;
+               wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
+       } else {
+               wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
+       }
+       os_get_time(&wpa_s->mlme.last_probe);
+}
+
+
+static int ieee80211_sta_tx(struct wpa_supplicant *wpa_s, const u8 *buf,
+                           size_t len)
+{
+       return wpa_drv_send_mlme(wpa_s, buf, len);
+}
+
+
+static void ieee80211_send_auth(struct wpa_supplicant *wpa_s,
+                               int transaction, const u8 *extra,
+                               size_t extra_len, int encrypt)
+{
+       u8 *buf;
+       size_t len;
+       struct ieee80211_mgmt *mgmt;
+
+       buf = os_malloc(sizeof(*mgmt) + 6 + extra_len);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
+                          "auth frame");
+               return;
+       }
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       len = 24 + 6;
+       os_memset(mgmt, 0, 24 + 6);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_AUTH);
+       if (encrypt)
+               mgmt->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+       mgmt->u.auth.auth_alg = host_to_le16(wpa_s->mlme.auth_alg);
+       mgmt->u.auth.auth_transaction = host_to_le16(transaction);
+       wpa_s->mlme.auth_transaction = transaction + 1;
+       mgmt->u.auth.status_code = host_to_le16(0);
+       if (extra) {
+               os_memcpy(buf + len, extra, extra_len);
+               len += extra_len;
+       }
+
+       ieee80211_sta_tx(wpa_s, buf, len);
+       os_free(buf);
+}
+
+
+static void ieee80211_reschedule_timer(struct wpa_supplicant *wpa_s, int ms)
+{
+       eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
+       eloop_register_timeout(ms / 1000, 1000 * (ms % 1000),
+                              ieee80211_sta_timer, wpa_s, NULL);
+}
+
+
+static void ieee80211_authenticate(struct wpa_supplicant *wpa_s)
+{
+       u8 *extra;
+       size_t extra_len;
+
+       wpa_s->mlme.auth_tries++;
+       if (wpa_s->mlme.auth_tries > IEEE80211_AUTH_MAX_TRIES) {
+               wpa_printf(MSG_DEBUG, "MLME: authentication with AP " MACSTR
+                          " timed out", MAC2STR(wpa_s->bssid));
+               return;
+       }
+
+       wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
+       wpa_printf(MSG_DEBUG, "MLME: authenticate with AP " MACSTR,
+                  MAC2STR(wpa_s->bssid));
+
+       extra = NULL;
+       extra_len = 0;
+
+#ifdef CONFIG_IEEE80211R
+       if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
+            wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
+           wpa_s->mlme.ft_ies) {
+               struct ieee80211_sta_bss *bss;
+               struct rsn_mdie *mdie = NULL;
+               bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
+               if (bss && bss->mdie_len >= 2 + sizeof(*mdie))
+                       mdie = (struct rsn_mdie *) (bss->mdie + 2);
+               if (mdie &&
+                   os_memcmp(mdie->mobility_domain, wpa_s->mlme.current_md,
+                             MOBILITY_DOMAIN_ID_LEN) == 0) {
+                       wpa_printf(MSG_DEBUG, "MLME: Trying to use FT "
+                                  "over-the-air");
+                       wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
+                       extra = wpa_s->mlme.ft_ies;
+                       extra_len = wpa_s->mlme.ft_ies_len;
+               }
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       ieee80211_send_auth(wpa_s, 1, extra, extra_len, 0);
+
+       ieee80211_reschedule_timer(wpa_s, IEEE80211_AUTH_TIMEOUT);
+}
+
+
+static void ieee80211_send_assoc(struct wpa_supplicant *wpa_s)
+{
+       struct ieee80211_mgmt *mgmt;
+       u8 *pos, *ies, *buf;
+       int i, len;
+       u16 capab;
+       struct ieee80211_sta_bss *bss;
+       int wmm = 0;
+       size_t blen, buflen;
+
+       if (wpa_s->mlme.curr_rates == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: curr_rates not set for assoc");
+               return;
+       }
+
+       buflen = sizeof(*mgmt) + 200 + wpa_s->mlme.extra_ie_len +
+               wpa_s->mlme.ssid_len;
+#ifdef CONFIG_IEEE80211R
+       if (wpa_s->mlme.ft_ies)
+               buflen += wpa_s->mlme.ft_ies_len;
+#endif /* CONFIG_IEEE80211R */
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
+                          "assoc frame");
+               return;
+       }
+       blen = 0;
+
+       capab = wpa_s->mlme.capab;
+       if (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G) {
+               capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME |
+                       WLAN_CAPABILITY_SHORT_PREAMBLE;
+       }
+       bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
+       if (bss) {
+               if (bss->capability & WLAN_CAPABILITY_PRIVACY)
+                       capab |= WLAN_CAPABILITY_PRIVACY;
+               if (bss->wmm_ie) {
+                       wmm = 1;
+               }
+       }
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       blen += 24;
+       os_memset(mgmt, 0, 24);
+       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+
+       if (wpa_s->mlme.prev_bssid_set) {
+               blen += 10;
+               mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                                  WLAN_FC_STYPE_REASSOC_REQ);
+               mgmt->u.reassoc_req.capab_info = host_to_le16(capab);
+               mgmt->u.reassoc_req.listen_interval = host_to_le16(1);
+               os_memcpy(mgmt->u.reassoc_req.current_ap,
+                         wpa_s->mlme.prev_bssid,
+                         ETH_ALEN);
+       } else {
+               blen += 4;
+               mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                                  WLAN_FC_STYPE_ASSOC_REQ);
+               mgmt->u.assoc_req.capab_info = host_to_le16(capab);
+               mgmt->u.assoc_req.listen_interval = host_to_le16(1);
+       }
+
+       /* SSID */
+       ies = pos = buf + blen;
+       blen += 2 + wpa_s->mlme.ssid_len;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = wpa_s->mlme.ssid_len;
+       os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
+
+       len = wpa_s->mlme.num_curr_rates;
+       if (len > 8)
+               len = 8;
+       pos = buf + blen;
+       blen += len + 2;
+       *pos++ = WLAN_EID_SUPP_RATES;
+       *pos++ = len;
+       for (i = 0; i < len; i++)
+               *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
+
+       if (wpa_s->mlme.num_curr_rates > len) {
+               pos = buf + blen;
+               blen += wpa_s->mlme.num_curr_rates - len + 2;
+               *pos++ = WLAN_EID_EXT_SUPP_RATES;
+               *pos++ = wpa_s->mlme.num_curr_rates - len;
+               for (i = len; i < wpa_s->mlme.num_curr_rates; i++)
+                       *pos++ = (u8) (wpa_s->mlme.curr_rates[i] / 5);
+       }
+
+       if (wpa_s->mlme.extra_ie && wpa_s->mlme.auth_alg != WLAN_AUTH_FT) {
+               pos = buf + blen;
+               blen += wpa_s->mlme.extra_ie_len;
+               os_memcpy(pos, wpa_s->mlme.extra_ie, wpa_s->mlme.extra_ie_len);
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
+            wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
+           wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
+           bss && bss->mdie &&
+           bss->mdie_len >= 2 + sizeof(struct rsn_mdie) &&
+           bss->mdie[1] >= sizeof(struct rsn_mdie)) {
+               pos = buf + blen;
+               blen += 2 + sizeof(struct rsn_mdie);
+               *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+               *pos++ = sizeof(struct rsn_mdie);
+               os_memcpy(pos, bss->mdie + 2, MOBILITY_DOMAIN_ID_LEN);
+               pos += MOBILITY_DOMAIN_ID_LEN;
+               *pos++ = 0; /* FIX: copy from the target AP's MDIE */
+       }
+
+       if ((wpa_s->mlme.key_mgmt == KEY_MGMT_FT_802_1X ||
+            wpa_s->mlme.key_mgmt == KEY_MGMT_FT_PSK) &&
+           wpa_s->mlme.auth_alg == WLAN_AUTH_FT && wpa_s->mlme.ft_ies) {
+               pos = buf + blen;
+               os_memcpy(pos, wpa_s->mlme.ft_ies, wpa_s->mlme.ft_ies_len);
+               pos += wpa_s->mlme.ft_ies_len;
+               blen += wpa_s->mlme.ft_ies_len;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       if (wmm && wpa_s->mlme.wmm_enabled) {
+               pos = buf + blen;
+               blen += 9;
+               *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+               *pos++ = 7; /* len */
+               *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */
+               *pos++ = 0x50;
+               *pos++ = 0xf2;
+               *pos++ = 2; /* WMM */
+               *pos++ = 0; /* WMM info */
+               *pos++ = 1; /* WMM ver */
+               *pos++ = 0;
+       }
+
+       os_free(wpa_s->mlme.assocreq_ies);
+       wpa_s->mlme.assocreq_ies_len = (buf + blen) - ies;
+       wpa_s->mlme.assocreq_ies = os_malloc(wpa_s->mlme.assocreq_ies_len);
+       if (wpa_s->mlme.assocreq_ies) {
+               os_memcpy(wpa_s->mlme.assocreq_ies, ies,
+                         wpa_s->mlme.assocreq_ies_len);
+       }
+
+       ieee80211_sta_tx(wpa_s, buf, blen);
+       os_free(buf);
+}
+
+
+static void ieee80211_send_deauth(struct wpa_supplicant *wpa_s, u16 reason)
+{
+       u8 *buf;
+       size_t len;
+       struct ieee80211_mgmt *mgmt;
+
+       buf = os_zalloc(sizeof(*mgmt));
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
+                          "deauth frame");
+               return;
+       }
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       len = 24;
+       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_DEAUTH);
+       len += 2;
+       mgmt->u.deauth.reason_code = host_to_le16(reason);
+
+       ieee80211_sta_tx(wpa_s, buf, len);
+       os_free(buf);
+}
+
+
+static void ieee80211_send_disassoc(struct wpa_supplicant *wpa_s, u16 reason)
+{
+       u8 *buf;
+       size_t len;
+       struct ieee80211_mgmt *mgmt;
+
+       buf = os_zalloc(sizeof(*mgmt));
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
+                          "disassoc frame");
+               return;
+       }
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       len = 24;
+       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_DISASSOC);
+       len += 2;
+       mgmt->u.disassoc.reason_code = host_to_le16(reason);
+
+       ieee80211_sta_tx(wpa_s, buf, len);
+       os_free(buf);
+}
+
+
+static int ieee80211_privacy_mismatch(struct wpa_supplicant *wpa_s)
+{
+       struct ieee80211_sta_bss *bss;
+       int res = 0;
+
+       if (wpa_s->mlme.mixed_cell ||
+           wpa_s->mlme.key_mgmt != KEY_MGMT_NONE)
+               return 0;
+
+       bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
+       if (bss == NULL)
+               return 0;
+
+       if (ieee80211_sta_wep_configured(wpa_s) !=
+           !!(bss->capability & WLAN_CAPABILITY_PRIVACY))
+               res = 1;
+
+       return res;
+}
+
+
+static void ieee80211_associate(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->mlme.assoc_tries++;
+       if (wpa_s->mlme.assoc_tries > IEEE80211_ASSOC_MAX_TRIES) {
+               wpa_printf(MSG_DEBUG, "MLME: association with AP " MACSTR
+                          " timed out", MAC2STR(wpa_s->bssid));
+               return;
+       }
+
+       wpa_s->mlme.state = IEEE80211_ASSOCIATE;
+       wpa_printf(MSG_DEBUG, "MLME: associate with AP " MACSTR,
+                  MAC2STR(wpa_s->bssid));
+       if (ieee80211_privacy_mismatch(wpa_s)) {
+               wpa_printf(MSG_DEBUG, "MLME: mismatch in privacy "
+                          "configuration and mixed-cell disabled - abort "
+                          "association");
+               return;
+       }
+
+       ieee80211_send_assoc(wpa_s);
+
+       ieee80211_reschedule_timer(wpa_s, IEEE80211_ASSOC_TIMEOUT);
+}
+
+
+static void ieee80211_associated(struct wpa_supplicant *wpa_s)
+{
+       int disassoc;
+
+       /* TODO: start monitoring current AP signal quality and number of
+        * missed beacons. Scan other channels every now and then and search
+        * for better APs. */
+       /* TODO: remove expired BSSes */
+
+       wpa_s->mlme.state = IEEE80211_ASSOCIATED;
+
+#if 0 /* FIX */
+       sta = sta_info_get(local, wpa_s->bssid);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG "MLME: No STA entry for own AP " MACSTR,
+                          MAC2STR(wpa_s->bssid));
+               disassoc = 1;
+       } else {
+               disassoc = 0;
+               if (time_after(jiffies,
+                              sta->last_rx + IEEE80211_MONITORING_INTERVAL)) {
+                       if (wpa_s->mlme.probereq_poll) {
+                               wpa_printf(MSG_DEBUG "MLME: No ProbeResp from "
+                                          "current AP " MACSTR " - assume "
+                                          "out of range",
+                                          MAC2STR(wpa_s->bssid));
+                               disassoc = 1;
+                       } else {
+                               ieee80211_send_probe_req(
+                                       wpa_s->bssid,
+                                       wpa_s->mlme.scan_ssid,
+                                       wpa_s->mlme.scan_ssid_len);
+                               wpa_s->mlme.probereq_poll = 1;
+                       }
+               } else {
+                       wpa_s->mlme.probereq_poll = 0;
+                       if (time_after(jiffies, wpa_s->mlme.last_probe +
+                                      IEEE80211_PROBE_INTERVAL)) {
+                               wpa_s->mlme.last_probe = jiffies;
+                               ieee80211_send_probe_req(wpa_s->bssid,
+                                                        wpa_s->mlme.ssid,
+                                                        wpa_s->mlme.ssid_len);
+                       }
+               }
+               sta_info_release(local, sta);
+       }
+#else
+       disassoc = 0;
+#endif
+       if (disassoc) {
+               wpa_supplicant_event(wpa_s, EVENT_DISASSOC, NULL);
+               ieee80211_reschedule_timer(wpa_s,
+                                          IEEE80211_MONITORING_INTERVAL +
+                                          30000);
+       } else {
+               ieee80211_reschedule_timer(wpa_s,
+                                          IEEE80211_MONITORING_INTERVAL);
+       }
+}
+
+
+static void ieee80211_send_probe_req(struct wpa_supplicant *wpa_s,
+                                    const u8 *dst,
+                                    const u8 *ssid, size_t ssid_len)
+{
+       u8 *buf;
+       size_t len;
+       struct ieee80211_mgmt *mgmt;
+       u8 *pos, *supp_rates;
+       u8 *esupp_rates = NULL;
+       int i;
+
+       buf = os_malloc(sizeof(*mgmt) + 200 + wpa_s->mlme.extra_probe_ie_len);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to allocate buffer for "
+                          "probe request");
+               return;
+       }
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       len = 24;
+       os_memset(mgmt, 0, 24);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_PROBE_REQ);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       if (dst) {
+               os_memcpy(mgmt->da, dst, ETH_ALEN);
+               os_memcpy(mgmt->bssid, dst, ETH_ALEN);
+       } else {
+               os_memset(mgmt->da, 0xff, ETH_ALEN);
+               os_memset(mgmt->bssid, 0xff, ETH_ALEN);
+       }
+       pos = buf + len;
+       len += 2 + ssid_len;
+       *pos++ = WLAN_EID_SSID;
+       *pos++ = ssid_len;
+       os_memcpy(pos, ssid, ssid_len);
+
+       supp_rates = buf + len;
+       len += 2;
+       supp_rates[0] = WLAN_EID_SUPP_RATES;
+       supp_rates[1] = 0;
+       for (i = 0; i < wpa_s->mlme.num_curr_rates; i++) {
+               if (esupp_rates) {
+                       pos = buf + len;
+                       len++;
+                       esupp_rates[1]++;
+               } else if (supp_rates[1] == 8) {
+                       esupp_rates = pos;
+                       esupp_rates[0] = WLAN_EID_EXT_SUPP_RATES;
+                       esupp_rates[1] = 1;
+                       pos = &esupp_rates[2];
+                       len += 3;
+               } else {
+                       pos = buf + len;
+                       len++;
+                       supp_rates[1]++;
+               }
+               *pos++ = wpa_s->mlme.curr_rates[i] / 5;
+       }
+
+       if (wpa_s->mlme.extra_probe_ie) {
+               os_memcpy(pos, wpa_s->mlme.extra_probe_ie,
+                         wpa_s->mlme.extra_probe_ie_len);
+               len += wpa_s->mlme.extra_probe_ie_len;
+       }
+
+       ieee80211_sta_tx(wpa_s, buf, len);
+       os_free(buf);
+}
+
+
+static int ieee80211_sta_wep_configured(struct wpa_supplicant *wpa_s)
+{
+#if 0 /* FIX */
+       if (sdata == NULL || sdata->default_key == NULL ||
+           sdata->default_key->alg != ALG_WEP)
+               return 0;
+       return 1;
+#else
+       return 0;
+#endif
+}
+
+
+static void ieee80211_auth_completed(struct wpa_supplicant *wpa_s)
+{
+       wpa_printf(MSG_DEBUG, "MLME: authenticated");
+       wpa_s->mlme.authenticated = 1;
+       ieee80211_associate(wpa_s);
+}
+
+
+static void ieee80211_auth_challenge(struct wpa_supplicant *wpa_s,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len,
+                                    struct ieee80211_rx_status *rx_status)
+{
+       u8 *pos;
+       struct ieee802_11_elems elems;
+
+       wpa_printf(MSG_DEBUG, "MLME: replying to auth challenge");
+       pos = mgmt->u.auth.variable;
+       if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
+           == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to parse Auth(challenge)");
+               return;
+       }
+       if (elems.challenge == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: no challenge IE in shared key "
+                          "auth frame");
+               return;
+       }
+       ieee80211_send_auth(wpa_s, 3, elems.challenge - 2,
+                           elems.challenge_len + 2, 1);
+}
+
+
+static void ieee80211_rx_mgmt_auth(struct wpa_supplicant *wpa_s,
+                                  struct ieee80211_mgmt *mgmt,
+                                  size_t len,
+                                  struct ieee80211_rx_status *rx_status)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       u16 auth_alg, auth_transaction, status_code;
+       int adhoc;
+
+       adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
+
+       if (wpa_s->mlme.state != IEEE80211_AUTHENTICATE && !adhoc) {
+               wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
+                          "from " MACSTR ", but not in authenticate state - "
+                          "ignored", MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (len < 24 + 6) {
+               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) authentication "
+                          "frame received from " MACSTR " - ignored",
+                          (unsigned long) len, MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (!adhoc && os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
+                          "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
+                          ") - ignored",
+                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
+               return;
+       }
+
+       if (adhoc && os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: authentication frame received "
+                          "from unknown BSSID (SA=" MACSTR " BSSID=" MACSTR
+                          ") - ignored",
+                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
+               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);
+
+       wpa_printf(MSG_DEBUG, "MLME: RX authentication from " MACSTR
+                  " (alg=%d transaction=%d status=%d)",
+                  MAC2STR(mgmt->sa), auth_alg, auth_transaction, status_code);
+
+       if (adhoc) {
+               /* IEEE 802.11 standard does not require authentication in IBSS
+                * networks and most implementations do not seem to use it.
+                * However, try to reply to authentication attempts if someone
+                * has actually implemented this.
+                * TODO: Could implement shared key authentication. */
+               if (auth_alg != WLAN_AUTH_OPEN || auth_transaction != 1) {
+                       wpa_printf(MSG_DEBUG, "MLME: unexpected IBSS "
+                                  "authentication frame (alg=%d "
+                                  "transaction=%d)",
+                                  auth_alg, auth_transaction);
+                       return;
+               }
+               ieee80211_send_auth(wpa_s, 2, NULL, 0, 0);
+       }
+
+       if (auth_alg != wpa_s->mlme.auth_alg ||
+           auth_transaction != wpa_s->mlme.auth_transaction) {
+               wpa_printf(MSG_DEBUG, "MLME: unexpected authentication frame "
+                          "(alg=%d transaction=%d)",
+                          auth_alg, auth_transaction);
+               return;
+       }
+
+       if (status_code != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "MLME: AP denied authentication "
+                          "(auth_alg=%d code=%d)", wpa_s->mlme.auth_alg,
+                          status_code);
+               if (status_code == WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG) {
+                       const int num_algs = 3;
+                       u8 algs[num_algs];
+                       int i, pos;
+                       algs[0] = algs[1] = algs[2] = 0xff;
+                       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
+                               algs[0] = WLAN_AUTH_OPEN;
+                       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
+                               algs[1] = WLAN_AUTH_SHARED_KEY;
+                       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
+                               algs[2] = WLAN_AUTH_LEAP;
+                       if (wpa_s->mlme.auth_alg == WLAN_AUTH_OPEN)
+                               pos = 0;
+                       else if (wpa_s->mlme.auth_alg == WLAN_AUTH_SHARED_KEY)
+                               pos = 1;
+                       else
+                               pos = 2;
+                       for (i = 0; i < num_algs; i++) {
+                               pos++;
+                               if (pos >= num_algs)
+                                       pos = 0;
+                               if (algs[pos] == wpa_s->mlme.auth_alg ||
+                                   algs[pos] == 0xff)
+                                       continue;
+                               if (algs[pos] == WLAN_AUTH_SHARED_KEY &&
+                                   !ieee80211_sta_wep_configured(wpa_s))
+                                       continue;
+                               wpa_s->mlme.auth_alg = algs[pos];
+                               wpa_printf(MSG_DEBUG, "MLME: set auth_alg=%d "
+                                          "for next try",
+                                          wpa_s->mlme.auth_alg);
+                               break;
+                       }
+               }
+               return;
+       }
+
+       switch (wpa_s->mlme.auth_alg) {
+       case WLAN_AUTH_OPEN:
+       case WLAN_AUTH_LEAP:
+               ieee80211_auth_completed(wpa_s);
+               break;
+       case WLAN_AUTH_SHARED_KEY:
+               if (wpa_s->mlme.auth_transaction == 4)
+                       ieee80211_auth_completed(wpa_s);
+               else
+                       ieee80211_auth_challenge(wpa_s, mgmt, len,
+                                                rx_status);
+               break;
+#ifdef CONFIG_IEEE80211R
+       case WLAN_AUTH_FT:
+       {
+               union wpa_event_data data;
+               struct wpabuf *ric = NULL;
+               os_memset(&data, 0, sizeof(data));
+               data.ft_ies.ies = mgmt->u.auth.variable;
+               data.ft_ies.ies_len = len -
+                       (mgmt->u.auth.variable - (u8 *) mgmt);
+               os_memcpy(data.ft_ies.target_ap, wpa_s->bssid, ETH_ALEN);
+               if (os_strcmp(wpa_s->driver->name, "test") == 0 &&
+                   wpa_s->mlme.wmm_enabled) {
+                       ric = wpabuf_alloc(200);
+                       if (ric) {
+                               /* Build simple RIC-Request: RDIE | TSPEC */
+
+                               /* RIC Data (RDIE) */
+                               wpabuf_put_u8(ric, WLAN_EID_RIC_DATA);
+                               wpabuf_put_u8(ric, 4);
+                               wpabuf_put_u8(ric, 0); /* RDIE Identifier */
+                               wpabuf_put_u8(ric, 1); /* Resource Descriptor
+                                                       * Count */
+                               wpabuf_put_le16(ric, 0); /* Status Code */
+
+                               /* WMM TSPEC */
+                               ieee80211_build_tspec(ric);
+
+                               data.ft_ies.ric_ies = wpabuf_head(ric);
+                               data.ft_ies.ric_ies_len = wpabuf_len(ric);
+                       }
+               }
+
+               wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
+               wpabuf_free(ric);
+               ieee80211_auth_completed(wpa_s);
+               break;
+       }
+#endif /* CONFIG_IEEE80211R */
+       }
+}
+
+
+static void ieee80211_rx_mgmt_deauth(struct wpa_supplicant *wpa_s,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len,
+                                    struct ieee80211_rx_status *rx_status)
+{
+       u16 reason_code;
+
+       if (len < 24 + 2) {
+               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) deauthentication "
+                          "frame received from " MACSTR " - ignored",
+                          (unsigned long) len, MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: deauthentication frame received "
+                          "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
+                          ") - ignored",
+                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
+               return;
+       }
+
+       reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+       wpa_printf(MSG_DEBUG, "MLME: RX deauthentication from " MACSTR
+                  " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
+
+       if (wpa_s->mlme.authenticated)
+               wpa_printf(MSG_DEBUG, "MLME: deauthenticated");
+
+       if (wpa_s->mlme.state == IEEE80211_AUTHENTICATE ||
+           wpa_s->mlme.state == IEEE80211_ASSOCIATE ||
+           wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
+               wpa_s->mlme.state = IEEE80211_AUTHENTICATE;
+               ieee80211_reschedule_timer(wpa_s,
+                                          IEEE80211_RETRY_AUTH_INTERVAL);
+       }
+
+       ieee80211_set_associated(wpa_s, 0);
+       wpa_s->mlme.authenticated = 0;
+}
+
+
+static void ieee80211_rx_mgmt_disassoc(struct wpa_supplicant *wpa_s,
+                                      struct ieee80211_mgmt *mgmt,
+                                      size_t len,
+                                      struct ieee80211_rx_status *rx_status)
+{
+       u16 reason_code;
+
+       if (len < 24 + 2) {
+               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) disassociation "
+                          "frame received from " MACSTR " - ignored",
+                          (unsigned long) len, MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: disassociation frame received "
+                          "from unknown AP (SA=" MACSTR " BSSID=" MACSTR
+                          ") - ignored",
+                          MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
+               return;
+       }
+
+       reason_code = le_to_host16(mgmt->u.disassoc.reason_code);
+
+       wpa_printf(MSG_DEBUG, "MLME: RX disassociation from " MACSTR
+                  " (reason=%d)", MAC2STR(mgmt->sa), reason_code);
+
+       if (wpa_s->mlme.associated)
+               wpa_printf(MSG_DEBUG, "MLME: disassociated");
+
+       if (wpa_s->mlme.state == IEEE80211_ASSOCIATED) {
+               wpa_s->mlme.state = IEEE80211_ASSOCIATE;
+               ieee80211_reschedule_timer(wpa_s,
+                                          IEEE80211_RETRY_AUTH_INTERVAL);
+       }
+
+       ieee80211_set_associated(wpa_s, 0);
+}
+
+
+static void ieee80211_build_tspec(struct wpabuf *buf)
+{
+       struct wmm_tspec_element *tspec;
+       int tid, up;
+
+       tspec = wpabuf_put(buf, sizeof(*tspec));
+       tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
+       tspec->length = sizeof(*tspec) - 2;
+       tspec->oui[0] = 0x00;
+       tspec->oui[1] = 0x50;
+       tspec->oui[2] = 0xf2;
+       tspec->oui_type = 2;
+       tspec->oui_subtype = 2;
+       tspec->version = 1;
+
+       tid = 1;
+       up = 6; /* Voice */
+       tspec->ts_info[0] = (tid << 1) |
+               (WMM_TSPEC_DIRECTION_BI_DIRECTIONAL << 5) |
+               BIT(7);
+       tspec->ts_info[1] = up << 3;
+       tspec->nominal_msdu_size = host_to_le16(1530);
+       tspec->mean_data_rate = host_to_le32(128000); /* bits per second */
+       tspec->minimum_phy_rate = host_to_le32(6000000);
+       tspec->surplus_bandwidth_allowance = host_to_le16(0x3000); /* 150% */
+}
+
+
+static void ieee80211_tx_addts(struct wpa_supplicant *wpa_s)
+{
+       struct wpabuf *buf;
+       struct ieee80211_mgmt *mgmt;
+       size_t alen;
+
+       wpa_printf(MSG_DEBUG, "MLME: Send ADDTS Request for Voice TSPEC");
+       mgmt = NULL;
+       alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
+
+       buf = wpabuf_alloc(alen + sizeof(struct wmm_tspec_element));
+       if (buf == NULL)
+               return;
+
+       mgmt = wpabuf_put(buf, alen);
+       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       mgmt->u.action.category = WLAN_ACTION_WMM;
+       mgmt->u.action.u.wmm_action.action_code = WMM_ACTION_CODE_ADDTS_REQ;
+       mgmt->u.action.u.wmm_action.dialog_token = 1;
+       mgmt->u.action.u.wmm_action.status_code = 0;
+
+       ieee80211_build_tspec(buf);
+
+       ieee80211_sta_tx(wpa_s, wpabuf_head(buf), wpabuf_len(buf));
+       wpabuf_free(buf);
+}
+
+
+static void ieee80211_rx_mgmt_assoc_resp(struct wpa_supplicant *wpa_s,
+                                        struct ieee80211_mgmt *mgmt,
+                                        size_t len,
+                                        struct ieee80211_rx_status *rx_status,
+                                        int reassoc)
+{
+       u8 rates[32];
+       size_t rates_len;
+       u16 capab_info, status_code, aid;
+       struct ieee802_11_elems elems;
+       u8 *pos;
+
+       /* AssocResp and ReassocResp have identical structure, so process both
+        * of them in this function. */
+
+       if (wpa_s->mlme.state != IEEE80211_ASSOCIATE) {
+               wpa_printf(MSG_DEBUG, "MLME: association frame received from "
+                          MACSTR ", but not in associate state - ignored",
+                          MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (len < 24 + 6) {
+               wpa_printf(MSG_DEBUG, "MLME: too short (%lu) association "
+                          "frame received from " MACSTR " - ignored",
+                          (unsigned long) len, MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (os_memcmp(wpa_s->bssid, mgmt->sa, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: association frame received from "
+                          "unknown AP (SA=" MACSTR " BSSID=" MACSTR ") - "
+                          "ignored", MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid));
+               return;
+       }
+
+       capab_info = le_to_host16(mgmt->u.assoc_resp.capab_info);
+       status_code = le_to_host16(mgmt->u.assoc_resp.status_code);
+       aid = le_to_host16(mgmt->u.assoc_resp.aid);
+       if ((aid & (BIT(15) | BIT(14))) != (BIT(15) | BIT(14)))
+               wpa_printf(MSG_DEBUG, "MLME: invalid aid value %d; bits 15:14 "
+                          "not set", aid);
+       aid &= ~(BIT(15) | BIT(14));
+
+       wpa_printf(MSG_DEBUG, "MLME: RX %sssocResp from " MACSTR
+                  " (capab=0x%x status=%d aid=%d)",
+                  reassoc ? "Rea" : "A", MAC2STR(mgmt->sa),
+                  capab_info, status_code, aid);
+
+       pos = mgmt->u.assoc_resp.variable;
+       if (ieee802_11_parse_elems(pos, len - (pos - (u8 *) mgmt), &elems, 0)
+           == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to parse AssocResp");
+               return;
+       }
+
+       if (status_code != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "MLME: AP denied association (code=%d)",
+                          status_code);
+#ifdef CONFIG_IEEE80211W
+               if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY &&
+                   elems.timeout_int && elems.timeout_int_len == 5 &&
+                   elems.timeout_int[0] == WLAN_TIMEOUT_ASSOC_COMEBACK) {
+                       u32 tu, ms;
+                       tu = WPA_GET_LE32(elems.timeout_int + 1);
+                       ms = tu * 1024 / 1000;
+                       wpa_printf(MSG_DEBUG, "MLME: AP rejected association "
+                                  "temporarily; comeback duration %u TU "
+                                  "(%u ms)", tu, ms);
+                       if (ms > IEEE80211_ASSOC_TIMEOUT) {
+                               wpa_printf(MSG_DEBUG, "MLME: Update timer "
+                                          "based on comeback duration");
+                               ieee80211_reschedule_timer(wpa_s, ms);
+                       }
+               }
+#endif /* CONFIG_IEEE80211W */
+               return;
+       }
+
+       if (elems.supp_rates == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: no SuppRates element in "
+                          "AssocResp");
+               return;
+       }
+
+       if (wpa_s->mlme.auth_alg == WLAN_AUTH_FT) {
+               if (!reassoc) {
+                       wpa_printf(MSG_DEBUG, "MLME: AP tried to use "
+                                  "association, not reassociation, response "
+                                  "with FT");
+                       return;
+               }
+               if (wpa_ft_validate_reassoc_resp(
+                           wpa_s->wpa, pos, len - (pos - (u8 *) mgmt),
+                           mgmt->sa) < 0) {
+                       wpa_printf(MSG_DEBUG, "MLME: FT validation of Reassoc"
+                                  "Resp failed");
+                       return;
+               }
+       } else if (wpa_sm_set_ft_params(wpa_s->wpa, pos,
+                                       len - (pos - (u8 *) mgmt)) < 0)
+               return;
+
+       wpa_printf(MSG_DEBUG, "MLME: associated");
+       wpa_s->mlme.aid = aid;
+       wpa_s->mlme.ap_capab = capab_info;
+
+       os_free(wpa_s->mlme.assocresp_ies);
+       wpa_s->mlme.assocresp_ies_len = len - (pos - (u8 *) mgmt);
+       wpa_s->mlme.assocresp_ies = os_malloc(wpa_s->mlme.assocresp_ies_len);
+       if (wpa_s->mlme.assocresp_ies) {
+               os_memcpy(wpa_s->mlme.assocresp_ies, pos,
+                         wpa_s->mlme.assocresp_ies_len);
+       }
+
+       ieee80211_set_associated(wpa_s, 1);
+
+       rates_len = elems.supp_rates_len;
+       if (rates_len > sizeof(rates))
+               rates_len = sizeof(rates);
+       os_memcpy(rates, elems.supp_rates, rates_len);
+       if (elems.ext_supp_rates) {
+               size_t _len = elems.ext_supp_rates_len;
+               if (_len > sizeof(rates) - rates_len)
+                       _len = sizeof(rates) - rates_len;
+               os_memcpy(rates + rates_len, elems.ext_supp_rates, _len);
+               rates_len += _len;
+       }
+
+       if (wpa_drv_set_bssid(wpa_s, wpa_s->bssid) < 0) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to set BSSID for the "
+                          "netstack");
+       }
+       if (wpa_drv_set_ssid(wpa_s, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) <
+           0) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to set SSID for the "
+                          "netstack");
+       }
+
+       /* Remove STA entry before adding a new one just in case to avoid
+        * problems with existing configuration (e.g., keys). */
+       wpa_drv_mlme_remove_sta(wpa_s, wpa_s->bssid);
+       if (wpa_drv_mlme_add_sta(wpa_s, wpa_s->bssid, rates, rates_len) < 0) {
+               wpa_printf(MSG_DEBUG, "MLME: failed to add STA entry to the "
+                          "netstack");
+       }
+
+       if (elems.wmm && wpa_s->mlme.wmm_enabled)
+               ieee80211_sta_wmm_params(wpa_s, elems.wmm, elems.wmm_len);
+
+       ieee80211_associated(wpa_s);
+
+       if (wpa_s->mlme.auth_alg != WLAN_AUTH_FT &&
+           os_strcmp(wpa_s->driver->name, "test") == 0 &&
+           elems.wmm && wpa_s->mlme.wmm_enabled) {
+               /* Test WMM-AC - send ADDTS for WMM TSPEC */
+               ieee80211_tx_addts(wpa_s);
+       }
+}
+
+
+/* Caller must hold local->sta_bss_lock */
+static void __ieee80211_bss_hash_add(struct wpa_supplicant *wpa_s,
+                                    struct ieee80211_sta_bss *bss)
+{
+       bss->hnext = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
+       wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)] = bss;
+}
+
+
+/* Caller must hold local->sta_bss_lock */
+static void __ieee80211_bss_hash_del(struct wpa_supplicant *wpa_s,
+                                    struct ieee80211_sta_bss *bss)
+{
+       struct ieee80211_sta_bss *b, *prev = NULL;
+       b = wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)];
+       while (b) {
+               if (b == bss) {
+                       if (prev == NULL) {
+                               wpa_s->mlme.sta_bss_hash[STA_HASH(bss->bssid)]
+                                       = bss->hnext;
+                       } else {
+                               prev->hnext = bss->hnext;
+                       }
+                       break;
+               }
+               prev = b;
+               b = b->hnext;
+       }
+}
+
+
+static struct ieee80211_sta_bss *
+ieee80211_bss_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct ieee80211_sta_bss *bss;
+
+       bss = os_zalloc(sizeof(*bss));
+       if (bss == NULL)
+               return NULL;
+       os_memcpy(bss->bssid, bssid, ETH_ALEN);
+
+       /* TODO: order by RSSI? */
+       bss->next = wpa_s->mlme.sta_bss_list;
+       wpa_s->mlme.sta_bss_list = bss;
+       __ieee80211_bss_hash_add(wpa_s, bss);
+       return bss;
+}
+
+
+static struct ieee80211_sta_bss *
+ieee80211_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct ieee80211_sta_bss *bss;
+
+       bss = wpa_s->mlme.sta_bss_hash[STA_HASH(bssid)];
+       while (bss) {
+               if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+                       break;
+               bss = bss->hnext;
+       }
+       return bss;
+}
+
+
+static void ieee80211_bss_free(struct wpa_supplicant *wpa_s,
+                              struct ieee80211_sta_bss *bss)
+{
+       __ieee80211_bss_hash_del(wpa_s, bss);
+       os_free(bss->ie);
+       os_free(bss->wpa_ie);
+       os_free(bss->rsn_ie);
+       os_free(bss->wmm_ie);
+       os_free(bss->mdie);
+       os_free(bss);
+}
+
+
+static void ieee80211_bss_list_deinit(struct wpa_supplicant *wpa_s)
+{
+       struct ieee80211_sta_bss *bss, *prev;
+
+       bss = wpa_s->mlme.sta_bss_list;
+       wpa_s->mlme.sta_bss_list = NULL;
+       while (bss) {
+               prev = bss;
+               bss = bss->next;
+               ieee80211_bss_free(wpa_s, prev);
+       }
+}
+
+
+static void ieee80211_bss_info(struct wpa_supplicant *wpa_s,
+                              struct ieee80211_mgmt *mgmt,
+                              size_t len,
+                              struct ieee80211_rx_status *rx_status,
+                              int beacon)
+{
+       struct ieee802_11_elems elems;
+       size_t baselen;
+       int channel, invalid = 0, clen;
+       struct ieee80211_sta_bss *bss;
+       u64 timestamp;
+       u8 *pos, *ie_pos;
+       size_t ie_len;
+
+       if (!beacon && os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN))
+               return; /* ignore ProbeResp to foreign address */
+
+#if 0
+       wpa_printf(MSG_MSGDUMP, "MLME: RX %s from " MACSTR " to " MACSTR,
+                  beacon ? "Beacon" : "Probe Response",
+                  MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+#endif
+
+       baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+       if (baselen > len)
+               return;
+
+       pos = mgmt->u.beacon.timestamp;
+       timestamp = WPA_GET_LE64(pos);
+
+#if 0 /* FIX */
+       if (local->conf.mode == IW_MODE_ADHOC && beacon &&
+           os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0) {
+#ifdef IEEE80211_IBSS_DEBUG
+               static unsigned long last_tsf_debug = 0;
+               u64 tsf;
+               if (local->hw->get_tsf)
+                       tsf = local->hw->get_tsf(local->mdev);
+               else
+                       tsf = -1LLU;
+               if (time_after(jiffies, last_tsf_debug + 5 * HZ)) {
+                       wpa_printf(MSG_DEBUG, "RX beacon SA=" MACSTR " BSSID="
+                                  MACSTR " TSF=0x%llx BCN=0x%llx diff=%lld "
+                                  "@%ld",
+                                  MAC2STR(mgmt->sa), MAC2STR(mgmt->bssid),
+                                  tsf, timestamp, tsf - timestamp, jiffies);
+                       last_tsf_debug = jiffies;
+               }
+#endif /* IEEE80211_IBSS_DEBUG */
+       }
+#endif
+
+       ie_pos = mgmt->u.beacon.variable;
+       ie_len = len - baselen;
+       if (ieee802_11_parse_elems(ie_pos, ie_len, &elems, 0) == ParseFailed)
+               invalid = 1;
+
+#if 0 /* FIX */
+       if (local->conf.mode == IW_MODE_ADHOC && elems.supp_rates &&
+           os_memcmp(mgmt->bssid, local->bssid, ETH_ALEN) == 0 &&
+           (sta = sta_info_get(local, mgmt->sa))) {
+               struct ieee80211_rate *rates;
+               size_t num_rates;
+               u32 supp_rates, prev_rates;
+               int i, j, oper_mode;
+
+               rates = local->curr_rates;
+               num_rates = local->num_curr_rates;
+               oper_mode = wpa_s->mlme.sta_scanning ?
+                       local->scan_oper_phymode : local->conf.phymode;
+               for (i = 0; i < local->hw->num_modes; i++) {
+                       struct ieee80211_hw_modes *mode = &local->hw->modes[i];
+                       if (oper_mode == mode->mode) {
+                               rates = mode->rates;
+                               num_rates = mode->num_rates;
+                               break;
+                       }
+               }
+
+               supp_rates = 0;
+               for (i = 0; i < elems.supp_rates_len +
+                            elems.ext_supp_rates_len; i++) {
+                       u8 rate = 0;
+                       int own_rate;
+                       if (i < elems.supp_rates_len)
+                               rate = elems.supp_rates[i];
+                       else if (elems.ext_supp_rates)
+                               rate = elems.ext_supp_rates
+                                       [i - elems.supp_rates_len];
+                       own_rate = 5 * (rate & 0x7f);
+                       if (oper_mode == MODE_ATHEROS_TURBO)
+                               own_rate *= 2;
+                       for (j = 0; j < num_rates; j++)
+                               if (rates[j].rate == own_rate)
+                                       supp_rates |= BIT(j);
+               }
+
+               prev_rates = sta->supp_rates;
+               sta->supp_rates &= supp_rates;
+               if (sta->supp_rates == 0) {
+                       /* No matching rates - this should not really happen.
+                        * Make sure that at least one rate is marked
+                        * supported to avoid issues with TX rate ctrl. */
+                       sta->supp_rates = wpa_s->mlme.supp_rates_bits;
+               }
+               if (sta->supp_rates != prev_rates) {
+                       wpa_printf(MSG_DEBUG, "MLME: updated supp_rates set "
+                                  "for " MACSTR " based on beacon info "
+                                  "(0x%x & 0x%x -> 0x%x)",
+                                  MAC2STR(sta->addr), prev_rates,
+                                  supp_rates, sta->supp_rates);
+               }
+               sta_info_release(local, sta);
+       }
+#endif
+
+       if (elems.ssid == NULL)
+               return;
+
+       if (elems.ds_params && elems.ds_params_len == 1)
+               channel = elems.ds_params[0];
+       else
+               channel = rx_status->channel;
+
+       bss = ieee80211_bss_get(wpa_s, mgmt->bssid);
+       if (bss == NULL) {
+               bss = ieee80211_bss_add(wpa_s, mgmt->bssid);
+               if (bss == NULL)
+                       return;
+       } else {
+#if 0
+               /* TODO: order by RSSI? */
+               spin_lock_bh(&local->sta_bss_lock);
+               list_move_tail(&bss->list, &local->sta_bss_list);
+               spin_unlock_bh(&local->sta_bss_lock);
+#endif
+       }
+
+       if (bss->probe_resp && beacon) {
+               /* Do not allow beacon to override data from Probe Response. */
+               return;
+       }
+
+       bss->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int);
+       bss->capability = le_to_host16(mgmt->u.beacon.capab_info);
+
+       if (bss->ie == NULL || bss->ie_len < ie_len) {
+               os_free(bss->ie);
+               bss->ie = os_malloc(ie_len);
+       }
+       if (bss->ie) {
+               os_memcpy(bss->ie, ie_pos, ie_len);
+               bss->ie_len = ie_len;
+       }
+
+       if (elems.ssid && elems.ssid_len <= MAX_SSID_LEN) {
+               os_memcpy(bss->ssid, elems.ssid, elems.ssid_len);
+               bss->ssid_len = elems.ssid_len;
+       }
+
+       bss->supp_rates_len = 0;
+       if (elems.supp_rates) {
+               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+               if (clen > elems.supp_rates_len)
+                       clen = elems.supp_rates_len;
+               os_memcpy(&bss->supp_rates[bss->supp_rates_len],
+                         elems.supp_rates, clen);
+               bss->supp_rates_len += clen;
+       }
+       if (elems.ext_supp_rates) {
+               clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
+               if (clen > elems.ext_supp_rates_len)
+                       clen = elems.ext_supp_rates_len;
+               os_memcpy(&bss->supp_rates[bss->supp_rates_len],
+                         elems.ext_supp_rates, clen);
+               bss->supp_rates_len += clen;
+       }
+
+       if (elems.wpa_ie &&
+           (bss->wpa_ie == NULL || bss->wpa_ie_len != elems.wpa_ie_len ||
+            os_memcmp(bss->wpa_ie, elems.wpa_ie, elems.wpa_ie_len))) {
+               os_free(bss->wpa_ie);
+               bss->wpa_ie = os_malloc(elems.wpa_ie_len + 2);
+               if (bss->wpa_ie) {
+                       os_memcpy(bss->wpa_ie, elems.wpa_ie - 2,
+                                 elems.wpa_ie_len + 2);
+                       bss->wpa_ie_len = elems.wpa_ie_len + 2;
+               } else
+                       bss->wpa_ie_len = 0;
+       } else if (!elems.wpa_ie && bss->wpa_ie) {
+               os_free(bss->wpa_ie);
+               bss->wpa_ie = NULL;
+               bss->wpa_ie_len = 0;
+       }
+
+       if (elems.rsn_ie &&
+           (bss->rsn_ie == NULL || bss->rsn_ie_len != elems.rsn_ie_len ||
+            os_memcmp(bss->rsn_ie, elems.rsn_ie, elems.rsn_ie_len))) {
+               os_free(bss->rsn_ie);
+               bss->rsn_ie = os_malloc(elems.rsn_ie_len + 2);
+               if (bss->rsn_ie) {
+                       os_memcpy(bss->rsn_ie, elems.rsn_ie - 2,
+                                 elems.rsn_ie_len + 2);
+                       bss->rsn_ie_len = elems.rsn_ie_len + 2;
+               } else
+                       bss->rsn_ie_len = 0;
+       } else if (!elems.rsn_ie && bss->rsn_ie) {
+               os_free(bss->rsn_ie);
+               bss->rsn_ie = NULL;
+               bss->rsn_ie_len = 0;
+       }
+
+       if (elems.wmm &&
+           (bss->wmm_ie == NULL || bss->wmm_ie_len != elems.wmm_len ||
+            os_memcmp(bss->wmm_ie, elems.wmm, elems.wmm_len))) {
+               os_free(bss->wmm_ie);
+               bss->wmm_ie = os_malloc(elems.wmm_len + 2);
+               if (bss->wmm_ie) {
+                       os_memcpy(bss->wmm_ie, elems.wmm - 2,
+                                 elems.wmm_len + 2);
+                       bss->wmm_ie_len = elems.wmm_len + 2;
+               } else
+                       bss->wmm_ie_len = 0;
+       } else if (!elems.wmm && bss->wmm_ie) {
+               os_free(bss->wmm_ie);
+               bss->wmm_ie = NULL;
+               bss->wmm_ie_len = 0;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (elems.mdie &&
+           (bss->mdie == NULL || bss->mdie_len != elems.mdie_len ||
+            os_memcmp(bss->mdie, elems.mdie, elems.mdie_len))) {
+               os_free(bss->mdie);
+               bss->mdie = os_malloc(elems.mdie_len + 2);
+               if (bss->mdie) {
+                       os_memcpy(bss->mdie, elems.mdie - 2,
+                                 elems.mdie_len + 2);
+                       bss->mdie_len = elems.mdie_len + 2;
+               } else
+                       bss->mdie_len = 0;
+       } else if (!elems.mdie && bss->mdie) {
+               os_free(bss->mdie);
+               bss->mdie = NULL;
+               bss->mdie_len = 0;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       bss->hw_mode = wpa_s->mlme.phymode;
+       bss->channel = channel;
+       bss->freq = wpa_s->mlme.freq;
+       if (channel != wpa_s->mlme.channel &&
+           (wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211G ||
+            wpa_s->mlme.phymode == HOSTAPD_MODE_IEEE80211B) &&
+           channel >= 1 && channel <= 14) {
+               static const int freq_list[] = {
+                       2412, 2417, 2422, 2427, 2432, 2437, 2442,
+                       2447, 2452, 2457, 2462, 2467, 2472, 2484
+               };
+               /* IEEE 802.11g/b mode can receive packets from neighboring
+                * channels, so map the channel into frequency. */
+               bss->freq = freq_list[channel - 1];
+       }
+       bss->timestamp = timestamp;
+       os_get_time(&bss->last_update);
+       bss->rssi = rx_status->ssi;
+       if (!beacon)
+               bss->probe_resp++;
+}
+
+
+static void ieee80211_rx_mgmt_probe_resp(struct wpa_supplicant *wpa_s,
+                                        struct ieee80211_mgmt *mgmt,
+                                        size_t len,
+                                        struct ieee80211_rx_status *rx_status)
+{
+       ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 0);
+}
+
+
+static void ieee80211_rx_mgmt_beacon(struct wpa_supplicant *wpa_s,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len,
+                                    struct ieee80211_rx_status *rx_status)
+{
+       int use_protection;
+       size_t baselen;
+       struct ieee802_11_elems elems;
+
+       ieee80211_bss_info(wpa_s, mgmt, len, rx_status, 1);
+
+       if (!wpa_s->mlme.associated ||
+           os_memcmp(wpa_s->bssid, mgmt->bssid, ETH_ALEN) != 0)
+               return;
+
+       /* Process beacon from the current BSS */
+       baselen = (u8 *) mgmt->u.beacon.variable - (u8 *) mgmt;
+       if (baselen > len)
+               return;
+
+       if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - baselen,
+                                  &elems, 0) == ParseFailed)
+               return;
+
+       use_protection = 0;
+       if (elems.erp_info && elems.erp_info_len >= 1) {
+               use_protection =
+                       (elems.erp_info[0] & ERP_INFO_USE_PROTECTION) != 0;
+       }
+
+       if (use_protection != !!wpa_s->mlme.use_protection) {
+               wpa_printf(MSG_DEBUG, "MLME: CTS protection %s (BSSID=" MACSTR
+                          ")",
+                          use_protection ? "enabled" : "disabled",
+                          MAC2STR(wpa_s->bssid));
+               wpa_s->mlme.use_protection = use_protection ? 1 : 0;
+               wpa_s->mlme.cts_protect_erp_frames = use_protection;
+       }
+
+       if (elems.wmm && wpa_s->mlme.wmm_enabled) {
+               ieee80211_sta_wmm_params(wpa_s, elems.wmm,
+                                        elems.wmm_len);
+       }
+}
+
+
+static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s,
+                                       struct ieee80211_mgmt *mgmt,
+                                       size_t len,
+                                       struct ieee80211_rx_status *rx_status)
+{
+       int tx_last_beacon, adhoc;
+#if 0 /* FIX */
+       struct ieee80211_mgmt *resp;
+#endif
+       u8 *pos, *end;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       adhoc = ssid && ssid->mode == WPAS_MODE_IBSS;
+
+       if (!adhoc || wpa_s->mlme.state != IEEE80211_IBSS_JOINED ||
+           len < 24 + 2 || wpa_s->mlme.probe_resp == NULL)
+               return;
+
+#if 0 /* FIX */
+       if (local->hw->tx_last_beacon)
+               tx_last_beacon = local->hw->tx_last_beacon(local->mdev);
+       else
+#endif
+               tx_last_beacon = 1;
+
+#ifdef IEEE80211_IBSS_DEBUG
+       wpa_printf(MSG_DEBUG, "MLME: RX ProbeReq SA=" MACSTR " DA=" MACSTR
+                  " BSSID=" MACSTR " (tx_last_beacon=%d)",
+                  MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+                  MAC2STR(mgmt->bssid), tx_last_beacon);
+#endif /* IEEE80211_IBSS_DEBUG */
+
+       if (!tx_last_beacon)
+               return;
+
+       if (os_memcmp(mgmt->bssid, wpa_s->bssid, ETH_ALEN) != 0 &&
+           os_memcmp(mgmt->bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0)
+               return;
+
+       end = ((u8 *) mgmt) + len;
+       pos = mgmt->u.probe_req.variable;
+       if (pos[0] != WLAN_EID_SSID ||
+           pos + 2 + pos[1] > end) {
+               wpa_printf(MSG_DEBUG, "MLME: Invalid SSID IE in ProbeReq from "
+                          MACSTR, MAC2STR(mgmt->sa));
+               return;
+       }
+       if (pos[1] != 0 &&
+           (pos[1] != wpa_s->mlme.ssid_len ||
+            os_memcmp(pos + 2, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len) != 0))
+       {
+               /* Ignore ProbeReq for foreign SSID */
+               return;
+       }
+
+#if 0 /* FIX */
+       /* Reply with ProbeResp */
+       skb = skb_copy(wpa_s->mlme.probe_resp, GFP_ATOMIC);
+       if (skb == NULL)
+               return;
+
+       resp = (struct ieee80211_mgmt *) skb->data;
+       os_memcpy(resp->da, mgmt->sa, ETH_ALEN);
+#ifdef IEEE80211_IBSS_DEBUG
+       wpa_printf(MSG_DEBUG, "MLME: Sending ProbeResp to " MACSTR,
+                  MAC2STR(resp->da));
+#endif /* IEEE80211_IBSS_DEBUG */
+       ieee80211_sta_tx(wpa_s, skb, 0, 1);
+#endif
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s,
+                                       struct ieee80211_mgmt *mgmt,
+                                       size_t len,
+                                       struct ieee80211_rx_status *rx_status)
+{
+       union wpa_event_data data;
+       u16 status;
+       u8 *sta_addr, *target_ap_addr;
+
+       if (len < 24 + 1 + sizeof(mgmt->u.action.u.ft_action_resp)) {
+               wpa_printf(MSG_DEBUG, "MLME: Too short FT Action frame");
+               return;
+       }
+
+       /*
+        * Only FT Action Response is needed for now since reservation
+        * protocol is not supported.
+        */
+       if (mgmt->u.action.u.ft_action_resp.action != 2) {
+               wpa_printf(MSG_DEBUG, "MLME: Unexpected FT Action %d",
+                          mgmt->u.action.u.ft_action_resp.action);
+               return;
+       }
+
+       status = le_to_host16(mgmt->u.action.u.ft_action_resp.status_code);
+       sta_addr = mgmt->u.action.u.ft_action_resp.sta_addr;
+       target_ap_addr = mgmt->u.action.u.ft_action_resp.target_ap_addr;
+       wpa_printf(MSG_DEBUG, "MLME: Received FT Action Response: STA " MACSTR
+                  " TargetAP " MACSTR " Status Code %d",
+                  MAC2STR(sta_addr), MAC2STR(target_ap_addr), status);
+       if (os_memcmp(sta_addr, wpa_s->own_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: Foreign STA Address " MACSTR
+                          " in FT Action Response", MAC2STR(sta_addr));
+               return;
+       }
+
+       if (status) {
+               wpa_printf(MSG_DEBUG, "MLME: FT Action Response indicates "
+                          "failure (status code %d)", status);
+               /* TODO: report error to FT code(?) */
+               return;
+       }
+
+       os_memset(&data, 0, sizeof(data));
+       data.ft_ies.ies = mgmt->u.action.u.ft_action_resp.variable;
+       data.ft_ies.ies_len = len - (mgmt->u.action.u.ft_action_resp.variable -
+                                    (u8 *) mgmt);
+       data.ft_ies.ft_action = 1;
+       os_memcpy(data.ft_ies.target_ap, target_ap_addr, ETH_ALEN);
+       wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &data);
+       /* TODO: should only re-associate, if EVENT_FT_RESPONSE was processed
+        * successfully */
+       wpa_s->mlme.prev_bssid_set = 1;
+       wpa_s->mlme.auth_alg = WLAN_AUTH_FT;
+       os_memcpy(wpa_s->mlme.prev_bssid, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN);
+       ieee80211_associate(wpa_s);
+}
+#endif /* CONFIG_IEEE80211R */
+
+
+#ifdef CONFIG_IEEE80211W
+
+/* MLME-SAQuery.response */
+static int ieee80211_sta_send_sa_query_resp(struct wpa_supplicant *wpa_s,
+                                           const u8 *addr, const u8 *trans_id)
+{
+       struct ieee80211_mgmt *mgmt;
+       int res;
+       size_t len;
+
+       mgmt = os_zalloc(sizeof(*mgmt));
+       if (mgmt == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+                          "SA Query action frame");
+               return -1;
+       }
+
+       len = 24;
+       os_memcpy(mgmt->da, addr, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
+       mgmt->u.action.u.sa_query_resp.action = WLAN_SA_QUERY_RESPONSE;
+       os_memcpy(mgmt->u.action.u.sa_query_resp.trans_id, trans_id,
+                 WLAN_SA_QUERY_TR_ID_LEN);
+       len += 1 + sizeof(mgmt->u.action.u.sa_query_resp);
+
+       res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len);
+       os_free(mgmt);
+
+       return res;
+}
+
+
+static void ieee80211_rx_mgmt_sa_query_action(
+       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
+       struct ieee80211_rx_status *rx_status)
+{
+       if (len < 24 + 1 + sizeof(mgmt->u.action.u.sa_query_req)) {
+               wpa_printf(MSG_DEBUG, "MLME: Too short SA Query Action frame");
+               return;
+       }
+
+       if (mgmt->u.action.u.sa_query_req.action != WLAN_SA_QUERY_REQUEST) {
+               wpa_printf(MSG_DEBUG, "MLME: Unexpected SA Query Action %d",
+                          mgmt->u.action.u.sa_query_req.action);
+               return;
+       }
+
+       if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "MLME: Ignore SA Query from unknown "
+                          "source " MACSTR, MAC2STR(mgmt->sa));
+               return;
+       }
+
+       if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) {
+               wpa_printf(MSG_DEBUG, "MLME: Ignore SA query request during "
+                          "association process");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "MLME: Replying to SA Query request");
+       ieee80211_sta_send_sa_query_resp(wpa_s, mgmt->sa, mgmt->u.action.u.
+                                        sa_query_req.trans_id);
+}
+
+#endif /* CONFIG_IEEE80211W */
+
+
+static void dump_tspec(struct wmm_tspec_element *tspec)
+{
+       int up, psb, dir, tid;
+       u16 val;
+
+       up = (tspec->ts_info[1] >> 3) & 0x07;
+       psb = (tspec->ts_info[1] >> 2) & 0x01;
+       dir = (tspec->ts_info[0] >> 5) & 0x03;
+       tid = (tspec->ts_info[0] >> 1) & 0x0f;
+       wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d",
+                  up, psb, dir, tid);
+       val = le_to_host16(tspec->nominal_msdu_size);
+       wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s",
+                  val & 0x7fff, val & 0x8000 ? " (fixed)" : "");
+       wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps",
+                  le_to_host32(tspec->mean_data_rate));
+       wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps",
+                  le_to_host32(tspec->minimum_phy_rate));
+       val = le_to_host16(tspec->surplus_bandwidth_allowance);
+       wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u",
+                  val >> 13, 10000 * (val & 0x1fff) / 0x2000);
+       val = le_to_host16(tspec->medium_time);
+       wpa_printf(MSG_DEBUG, "WMM: Medium Time: %u (= %u usec/sec)",
+                  val, 32 * val);
+}
+
+
+static int is_wmm_tspec(const u8 *ie, size_t len)
+{
+       const struct wmm_tspec_element *tspec;
+
+       if (len < sizeof(*tspec))
+               return 0;
+
+       tspec = (const struct wmm_tspec_element *) ie;
+       if (tspec->eid != WLAN_EID_VENDOR_SPECIFIC ||
+           tspec->length < sizeof(*tspec) - 2 ||
+           tspec->oui[0] != 0x00 || tspec->oui[1] != 0x50 ||
+           tspec->oui[2] != 0xf2 || tspec->oui_type != 2 ||
+           tspec->oui_subtype != 2 || tspec->version != 1)
+               return 0;
+
+       return 1;
+}
+
+
+static void ieee80211_rx_addts_resp(
+       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
+       size_t var_len)
+{
+       struct wmm_tspec_element *tspec;
+
+       wpa_printf(MSG_DEBUG, "WMM: Received ADDTS Response");
+       wpa_hexdump(MSG_MSGDUMP, "WMM: ADDTS Response IE(s)",
+                   mgmt->u.action.u.wmm_action.variable, var_len);
+       if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
+               return;
+       tspec = (struct wmm_tspec_element *)
+               mgmt->u.action.u.wmm_action.variable;
+       dump_tspec(tspec);
+}
+
+
+static void ieee80211_rx_delts(
+       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
+       size_t var_len)
+{
+       struct wmm_tspec_element *tspec;
+
+       wpa_printf(MSG_DEBUG, "WMM: Received DELTS");
+       wpa_hexdump(MSG_MSGDUMP, "WMM: DELTS IE(s)",
+                   mgmt->u.action.u.wmm_action.variable, var_len);
+       if (!is_wmm_tspec(mgmt->u.action.u.wmm_action.variable, var_len))
+               return;
+       tspec = (struct wmm_tspec_element *)
+               mgmt->u.action.u.wmm_action.variable;
+       dump_tspec(tspec);
+}
+
+
+static void ieee80211_rx_mgmt_wmm_action(
+       struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
+       struct ieee80211_rx_status *rx_status)
+{
+       size_t alen;
+
+       alen = mgmt->u.action.u.wmm_action.variable - (u8 *) mgmt;
+       if (len < alen) {
+               wpa_printf(MSG_DEBUG, "WMM: Received Action frame too short");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "WMM: Received Action frame: Action Code %d, "
+                  "Dialog Token %d, Status Code %d",
+                  mgmt->u.action.u.wmm_action.action_code,
+                  mgmt->u.action.u.wmm_action.dialog_token,
+                  mgmt->u.action.u.wmm_action.status_code);
+
+       switch (mgmt->u.action.u.wmm_action.action_code) {
+       case WMM_ACTION_CODE_ADDTS_RESP:
+               ieee80211_rx_addts_resp(wpa_s, mgmt, len, len - alen);
+               break;
+       case WMM_ACTION_CODE_DELTS:
+               ieee80211_rx_delts(wpa_s, mgmt, len, len - alen);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "WMM: Unsupported Action Code %d",
+                          mgmt->u.action.u.wmm_action.action_code);
+               break;
+       }
+}
+
+
+static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s,
+                                    struct ieee80211_mgmt *mgmt,
+                                    size_t len,
+                                    struct ieee80211_rx_status *rx_status)
+{
+       wpa_printf(MSG_DEBUG, "MLME: received Action frame");
+
+       if (len < 25)
+               return;
+
+       switch (mgmt->u.action.category) {
+#ifdef CONFIG_IEEE80211R
+       case WLAN_ACTION_FT:
+               ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status);
+               break;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       case WLAN_ACTION_SA_QUERY:
+               ieee80211_rx_mgmt_sa_query_action(wpa_s, mgmt, len, rx_status);
+               break;
+#endif /* CONFIG_IEEE80211W */
+       case WLAN_ACTION_WMM:
+               ieee80211_rx_mgmt_wmm_action(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_ACTION_PUBLIC:
+               if (wpa_s->mlme.public_action_cb) {
+                       wpa_s->mlme.public_action_cb(
+                               wpa_s->mlme.public_action_cb_ctx,
+                               (u8 *) mgmt, len, rx_status->freq);
+                       return;
+               }
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d",
+                          mgmt->u.action.category);
+               break;
+       }
+}
+
+
+static void ieee80211_sta_rx_mgmt(struct wpa_supplicant *wpa_s,
+                                 const u8 *buf, size_t len,
+                                 struct ieee80211_rx_status *rx_status)
+{
+       struct ieee80211_mgmt *mgmt;
+       u16 fc;
+
+       if (len < 24)
+               return;
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       fc = le_to_host16(mgmt->frame_control);
+
+       switch (WLAN_FC_GET_STYPE(fc)) {
+       case WLAN_FC_STYPE_PROBE_REQ:
+               ieee80211_rx_mgmt_probe_req(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_FC_STYPE_PROBE_RESP:
+               ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_FC_STYPE_BEACON:
+               ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_FC_STYPE_AUTH:
+               ieee80211_rx_mgmt_auth(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_FC_STYPE_ASSOC_RESP:
+               ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 0);
+               break;
+       case WLAN_FC_STYPE_REASSOC_RESP:
+               ieee80211_rx_mgmt_assoc_resp(wpa_s, mgmt, len, rx_status, 1);
+               break;
+       case WLAN_FC_STYPE_DEAUTH:
+               ieee80211_rx_mgmt_deauth(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_FC_STYPE_DISASSOC:
+               ieee80211_rx_mgmt_disassoc(wpa_s, mgmt, len, rx_status);
+               break;
+       case WLAN_FC_STYPE_ACTION:
+               ieee80211_rx_mgmt_action(wpa_s, mgmt, len, rx_status);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "MLME: received unknown management "
+                          "frame - stype=%d", WLAN_FC_GET_STYPE(fc));
+               break;
+       }
+}
+
+
+static void ieee80211_sta_rx_scan(struct wpa_supplicant *wpa_s,
+                                 const u8 *buf, size_t len,
+                                 struct ieee80211_rx_status *rx_status)
+{
+       struct ieee80211_mgmt *mgmt;
+       u16 fc;
+
+       if (len < 24)
+               return;
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       fc = le_to_host16(mgmt->frame_control);
+
+       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT) {
+               if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+                       ieee80211_rx_mgmt_probe_resp(wpa_s, mgmt,
+                                                    len, rx_status);
+               } else if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
+                       ieee80211_rx_mgmt_beacon(wpa_s, mgmt, len, rx_status);
+               }
+       }
+}
+
+
+static int ieee80211_sta_active_ibss(struct wpa_supplicant *wpa_s)
+{
+       int active = 0;
+
+#if 0 /* FIX */
+       list_for_each(ptr, &local->sta_list) {
+               sta = list_entry(ptr, struct sta_info, list);
+               if (sta->dev == dev &&
+                   time_after(sta->last_rx + IEEE80211_IBSS_MERGE_INTERVAL,
+                              jiffies)) {
+                       active++;
+                       break;
+               }
+       }
+#endif
+
+       return active;
+}
+
+
+static void ieee80211_sta_expire(struct wpa_supplicant *wpa_s)
+{
+#if 0 /* FIX */
+       list_for_each_safe(ptr, n, &local->sta_list) {
+               sta = list_entry(ptr, struct sta_info, list);
+               if (time_after(jiffies, sta->last_rx +
+                              IEEE80211_IBSS_INACTIVITY_LIMIT)) {
+                       wpa_printf(MSG_DEBUG, "MLME: expiring inactive STA "
+                                  MACSTR, MAC2STR(sta->addr));
+                       sta_info_free(local, sta, 1);
+               }
+       }
+#endif
+}
+
+
+static void ieee80211_sta_merge_ibss(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_driver_scan_params params;
+
+       ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
+
+       ieee80211_sta_expire(wpa_s);
+       if (ieee80211_sta_active_ibss(wpa_s))
+               return;
+
+       wpa_printf(MSG_DEBUG, "MLME: No active IBSS STAs - trying to scan for "
+                  "other IBSS networks with same SSID (merge)");
+       os_memset(&params, 0, sizeof(params));
+       params.ssids[0].ssid = wpa_s->mlme.ssid;
+       params.ssids[0].ssid_len = wpa_s->mlme.ssid_len;
+       params.num_ssids = wpa_s->mlme.ssid_len ? 1 : 0;
+       ieee80211_sta_req_scan(wpa_s, &params);
+}
+
+
+static void ieee80211_sta_timer(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       switch (wpa_s->mlme.state) {
+       case IEEE80211_DISABLED:
+               break;
+       case IEEE80211_AUTHENTICATE:
+               ieee80211_authenticate(wpa_s);
+               break;
+       case IEEE80211_ASSOCIATE:
+               ieee80211_associate(wpa_s);
+               break;
+       case IEEE80211_ASSOCIATED:
+               ieee80211_associated(wpa_s);
+               break;
+       case IEEE80211_IBSS_SEARCH:
+               ieee80211_sta_find_ibss(wpa_s);
+               break;
+       case IEEE80211_IBSS_JOINED:
+               ieee80211_sta_merge_ibss(wpa_s);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "ieee80211_sta_timer: Unknown state %d",
+                          wpa_s->mlme.state);
+               break;
+       }
+
+       if (ieee80211_privacy_mismatch(wpa_s)) {
+               wpa_printf(MSG_DEBUG, "MLME: privacy configuration mismatch "
+                          "and mixed-cell disabled - disassociate");
+
+               ieee80211_send_disassoc(wpa_s, WLAN_REASON_UNSPECIFIED);
+               ieee80211_set_associated(wpa_s, 0);
+       }
+}
+
+
+static void ieee80211_sta_new_auth(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       if (ssid && ssid->mode != WPAS_MODE_INFRA)
+               return;
+
+#if 0 /* FIX */
+       if (local->hw->reset_tsf) {
+               /* Reset own TSF to allow time synchronization work. */
+               local->hw->reset_tsf(local->mdev);
+       }
+#endif
+
+       wpa_s->mlme.wmm_last_param_set = -1; /* allow any WMM update */
+
+
+       if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_OPEN)
+               wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
+       else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_SHARED)
+               wpa_s->mlme.auth_alg = WLAN_AUTH_SHARED_KEY;
+       else if (wpa_s->mlme.auth_algs & WPA_AUTH_ALG_LEAP)
+               wpa_s->mlme.auth_alg = WLAN_AUTH_LEAP;
+       else
+               wpa_s->mlme.auth_alg = WLAN_AUTH_OPEN;
+       wpa_printf(MSG_DEBUG, "MLME: Initial auth_alg=%d",
+                  wpa_s->mlme.auth_alg);
+       wpa_s->mlme.auth_transaction = -1;
+       wpa_s->mlme.auth_tries = wpa_s->mlme.assoc_tries = 0;
+       ieee80211_authenticate(wpa_s);
+}
+
+
+static int ieee80211_ibss_allowed(struct wpa_supplicant *wpa_s)
+{
+#if 0 /* FIX */
+       int m, c;
+
+       for (m = 0; m < local->hw->num_modes; m++) {
+               struct ieee80211_hw_modes *mode = &local->hw->modes[m];
+               if (mode->mode != local->conf.phymode)
+                       continue;
+               for (c = 0; c < mode->num_channels; c++) {
+                       struct ieee80211_channel *chan = &mode->channels[c];
+                       if (chan->flag & IEEE80211_CHAN_W_SCAN &&
+                           chan->chan == local->conf.channel) {
+                               if (chan->flag & IEEE80211_CHAN_W_IBSS)
+                                       return 1;
+                               break;
+                       }
+               }
+       }
+#endif
+
+       return 0;
+}
+
+
+static int ieee80211_sta_join_ibss(struct wpa_supplicant *wpa_s,
+                                  struct ieee80211_sta_bss *bss)
+{
+       int res = 0, rates, done = 0, bssid_changed;
+       struct ieee80211_mgmt *mgmt;
+#if 0 /* FIX */
+       struct ieee80211_tx_control control;
+       struct ieee80211_rate *rate;
+       struct rate_control_extra extra;
+#endif
+       u8 *pos, *buf;
+       size_t len;
+
+       /* Remove possible STA entries from other IBSS networks. */
+#if 0 /* FIX */
+       sta_info_flush(local, NULL);
+
+       if (local->hw->reset_tsf) {
+               /* Reset own TSF to allow time synchronization work. */
+               local->hw->reset_tsf(local->mdev);
+       }
+#endif
+       bssid_changed = os_memcmp(wpa_s->bssid, bss->bssid, ETH_ALEN);
+       os_memcpy(wpa_s->bssid, bss->bssid, ETH_ALEN);
+       if (bssid_changed)
+               wpas_notify_bssid_changed(wpa_s);
+
+#if 0 /* FIX */
+       local->conf.beacon_int = bss->beacon_int >= 10 ? bss->beacon_int : 10;
+
+       sdata->drop_unencrypted = bss->capability &
+               host_to_le16(WLAN_CAPABILITY_PRIVACY) ? 1 : 0;
+#endif
+
+#if 0 /* FIX */
+       os_memset(&rq, 0, sizeof(rq));
+       rq.m = bss->freq * 100000;
+       rq.e = 1;
+       res = ieee80211_ioctl_siwfreq(wpa_s, NULL, &rq, NULL);
+#endif
+
+       if (!ieee80211_ibss_allowed(wpa_s)) {
+#if 0 /* FIX */
+               wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed on channel %d "
+                          "(%d MHz)", local->conf.channel,
+                          local->conf.freq);
+#endif
+               return -1;
+       }
+
+       /* Set beacon template based on scan results */
+       buf = os_malloc(400);
+       len = 0;
+       do {
+               if (buf == NULL)
+                       break;
+
+               mgmt = (struct ieee80211_mgmt *) buf;
+               len += 24 + sizeof(mgmt->u.beacon);
+               os_memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+               mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                                  WLAN_FC_STYPE_BEACON);
+               os_memset(mgmt->da, 0xff, ETH_ALEN);
+               os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+               os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+#if 0 /* FIX */
+               mgmt->u.beacon.beacon_int =
+                       host_to_le16(local->conf.beacon_int);
+#endif
+               mgmt->u.beacon.capab_info = host_to_le16(bss->capability);
+
+               pos = buf + len;
+               len += 2 + wpa_s->mlme.ssid_len;
+               *pos++ = WLAN_EID_SSID;
+               *pos++ = wpa_s->mlme.ssid_len;
+               os_memcpy(pos, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
+
+               rates = bss->supp_rates_len;
+               if (rates > 8)
+                       rates = 8;
+               pos = buf + len;
+               len += 2 + rates;
+               *pos++ = WLAN_EID_SUPP_RATES;
+               *pos++ = rates;
+               os_memcpy(pos, bss->supp_rates, rates);
+
+               pos = buf + len;
+               len += 2 + 1;
+               *pos++ = WLAN_EID_DS_PARAMS;
+               *pos++ = 1;
+               *pos++ = bss->channel;
+
+               pos = buf + len;
+               len += 2 + 2;
+               *pos++ = WLAN_EID_IBSS_PARAMS;
+               *pos++ = 2;
+               /* FIX: set ATIM window based on scan results */
+               *pos++ = 0;
+               *pos++ = 0;
+
+               if (bss->supp_rates_len > 8) {
+                       rates = bss->supp_rates_len - 8;
+                       pos = buf + len;
+                       len += 2 + rates;
+                       *pos++ = WLAN_EID_EXT_SUPP_RATES;
+                       *pos++ = rates;
+                       os_memcpy(pos, &bss->supp_rates[8], rates);
+               }
+
+#if 0 /* FIX */
+               os_memset(&control, 0, sizeof(control));
+               control.pkt_type = PKT_PROBE_RESP;
+               os_memset(&extra, 0, sizeof(extra));
+               extra.endidx = local->num_curr_rates;
+               rate = rate_control_get_rate(wpa_s, skb, &extra);
+               if (rate == NULL) {
+                       wpa_printf(MSG_DEBUG, "MLME: Failed to determine TX "
+                                  "rate for IBSS beacon");
+                       break;
+               }
+               control.tx_rate = (wpa_s->mlme.short_preamble &&
+                                  (rate->flags & IEEE80211_RATE_PREAMBLE2)) ?
+                       rate->val2 : rate->val;
+               control.antenna_sel = local->conf.antenna_sel;
+               control.power_level = local->conf.power_level;
+               control.no_ack = 1;
+               control.retry_limit = 1;
+               control.rts_cts_duration = 0;
+#endif
+
+#if 0 /* FIX */
+               wpa_s->mlme.probe_resp = skb_copy(skb, GFP_ATOMIC);
+               if (wpa_s->mlme.probe_resp) {
+                       mgmt = (struct ieee80211_mgmt *)
+                               wpa_s->mlme.probe_resp->data;
+                       mgmt->frame_control =
+                               IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                            WLAN_FC_STYPE_PROBE_RESP);
+               } else {
+                       wpa_printf(MSG_DEBUG, "MLME: Could not allocate "
+                                  "ProbeResp template for IBSS");
+               }
+
+               if (local->hw->beacon_update &&
+                   local->hw->beacon_update(wpa_s, skb, &control) == 0) {
+                       wpa_printf(MSG_DEBUG, "MLME: Configured IBSS beacon "
+                                  "template based on scan results");
+                       skb = NULL;
+               }
+
+               rates = 0;
+               for (i = 0; i < bss->supp_rates_len; i++) {
+                       int rate = (bss->supp_rates[i] & 0x7f) * 5;
+                       if (local->conf.phymode == MODE_ATHEROS_TURBO)
+                               rate *= 2;
+                       for (j = 0; j < local->num_curr_rates; j++)
+                               if (local->curr_rates[j] == rate)
+                                       rates |= BIT(j);
+               }
+               wpa_s->mlme.supp_rates_bits = rates;
+#endif
+               done = 1;
+       } while (0);
+
+       os_free(buf);
+       if (!done) {
+               wpa_printf(MSG_DEBUG, "MLME: Failed to configure IBSS beacon "
+                          "template");
+       }
+
+       wpa_s->mlme.state = IEEE80211_IBSS_JOINED;
+       ieee80211_reschedule_timer(wpa_s, IEEE80211_IBSS_MERGE_INTERVAL);
+
+       return res;
+}
+
+
+#if 0 /* FIX */
+static int ieee80211_sta_create_ibss(struct wpa_supplicant *wpa_s)
+{
+       struct ieee80211_sta_bss *bss;
+       u8 bssid[ETH_ALEN], *pos;
+       int i;
+
+#if 0
+       /* Easier testing, use fixed BSSID. */
+       os_memset(bssid, 0xfe, ETH_ALEN);
+#else
+       /* Generate random, not broadcast, locally administered BSSID. Mix in
+        * own MAC address to make sure that devices that do not have proper
+        * random number generator get different BSSID. */
+       os_get_random(bssid, ETH_ALEN);
+       for (i = 0; i < ETH_ALEN; i++)
+               bssid[i] ^= wpa_s->own_addr[i];
+       bssid[0] &= ~0x01;
+       bssid[0] |= 0x02;
+#endif
+
+       wpa_printf(MSG_DEBUG, "MLME: Creating new IBSS network, BSSID "
+                  MACSTR "", MAC2STR(bssid));
+
+       bss = ieee80211_bss_add(wpa_s, bssid);
+       if (bss == NULL)
+               return -ENOMEM;
+
+#if 0 /* FIX */
+       if (local->conf.beacon_int == 0)
+               local->conf.beacon_int = 100;
+       bss->beacon_int = local->conf.beacon_int;
+       bss->hw_mode = local->conf.phymode;
+       bss->channel = local->conf.channel;
+       bss->freq = local->conf.freq;
+#endif
+       os_get_time(&bss->last_update);
+       bss->capability = host_to_le16(WLAN_CAPABILITY_IBSS);
+#if 0 /* FIX */
+       if (sdata->default_key) {
+               bss->capability |= host_to_le16(WLAN_CAPABILITY_PRIVACY);
+       } else
+               sdata->drop_unencrypted = 0;
+       bss->supp_rates_len = local->num_curr_rates;
+#endif
+       pos = bss->supp_rates;
+#if 0 /* FIX */
+       for (i = 0; i < local->num_curr_rates; i++) {
+               int rate = local->curr_rates[i];
+               if (local->conf.phymode == MODE_ATHEROS_TURBO)
+                       rate /= 2;
+               *pos++ = (u8) (rate / 5);
+       }
+#endif
+
+       return ieee80211_sta_join_ibss(wpa_s, bss);
+}
+#endif
+
+
+static int ieee80211_sta_find_ibss(struct wpa_supplicant *wpa_s)
+{
+       struct ieee80211_sta_bss *bss;
+       int found = 0;
+       u8 bssid[ETH_ALEN];
+       int active_ibss;
+       struct os_time now;
+
+       if (wpa_s->mlme.ssid_len == 0)
+               return -EINVAL;
+
+       active_ibss = ieee80211_sta_active_ibss(wpa_s);
+#ifdef IEEE80211_IBSS_DEBUG
+       wpa_printf(MSG_DEBUG, "MLME: sta_find_ibss (active_ibss=%d)",
+                  active_ibss);
+#endif /* IEEE80211_IBSS_DEBUG */
+       for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
+               if (wpa_s->mlme.ssid_len != bss->ssid_len ||
+                   os_memcmp(wpa_s->mlme.ssid, bss->ssid, bss->ssid_len) != 0
+                   || !(bss->capability & WLAN_CAPABILITY_IBSS))
+                       continue;
+#ifdef IEEE80211_IBSS_DEBUG
+               wpa_printf(MSG_DEBUG, "   bssid=" MACSTR " found",
+                          MAC2STR(bss->bssid));
+#endif /* IEEE80211_IBSS_DEBUG */
+               os_memcpy(bssid, bss->bssid, ETH_ALEN);
+               found = 1;
+               if (active_ibss ||
+                   os_memcmp(bssid, wpa_s->bssid, ETH_ALEN) != 0)
+                       break;
+       }
+
+#ifdef IEEE80211_IBSS_DEBUG
+       wpa_printf(MSG_DEBUG, "   sta_find_ibss: selected " MACSTR " current "
+                  MACSTR, MAC2STR(bssid), MAC2STR(wpa_s->bssid));
+#endif /* IEEE80211_IBSS_DEBUG */
+       if (found && os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) != 0 &&
+           (bss = ieee80211_bss_get(wpa_s, bssid))) {
+               wpa_printf(MSG_DEBUG, "MLME: Selected IBSS BSSID " MACSTR
+                          " based on configured SSID",
+                          MAC2STR(bssid));
+               return ieee80211_sta_join_ibss(wpa_s, bss);
+       }
+#ifdef IEEE80211_IBSS_DEBUG
+       wpa_printf(MSG_DEBUG, "   did not try to join ibss");
+#endif /* IEEE80211_IBSS_DEBUG */
+
+       /* Selected IBSS not found in current scan results - try to scan */
+       os_get_time(&now);
+#if 0 /* FIX */
+       if (wpa_s->mlme.state == IEEE80211_IBSS_JOINED &&
+           !ieee80211_sta_active_ibss(wpa_s)) {
+               ieee80211_reschedule_timer(wpa_s,
+                                          IEEE80211_IBSS_MERGE_INTERVAL);
+       } else if (time_after(jiffies, wpa_s->mlme.last_scan_completed +
+                             IEEE80211_SCAN_INTERVAL)) {
+               wpa_printf(MSG_DEBUG, "MLME: Trigger new scan to find an IBSS "
+                          "to join");
+               return ieee80211_sta_req_scan(wpa_s->mlme.ssid,
+                                             wpa_s->mlme.ssid_len);
+       } else if (wpa_s->mlme.state != IEEE80211_IBSS_JOINED) {
+               int interval = IEEE80211_SCAN_INTERVAL;
+
+               if (time_after(jiffies, wpa_s->mlme.ibss_join_req +
+                              IEEE80211_IBSS_JOIN_TIMEOUT)) {
+                       if (wpa_s->mlme.create_ibss &&
+                           ieee80211_ibss_allowed(wpa_s))
+                               return ieee80211_sta_create_ibss(wpa_s);
+                       if (wpa_s->mlme.create_ibss) {
+                               wpa_printf(MSG_DEBUG, "MLME: IBSS not allowed "
+                                          "on the configured channel %d "
+                                          "(%d MHz)",
+                                          local->conf.channel,
+                                          local->conf.freq);
+                       }
+
+                       /* No IBSS found - decrease scan interval and continue
+                        * scanning. */
+                       interval = IEEE80211_SCAN_INTERVAL_SLOW;
+               }
+
+               wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
+               ieee80211_reschedule_timer(wpa_s, interval);
+               return 0;
+       }
+#endif
+
+       return 0;
+}
+
+
+int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
+                          size_t *len)
+{
+       os_memcpy(ssid, wpa_s->mlme.ssid, wpa_s->mlme.ssid_len);
+       *len = wpa_s->mlme.ssid_len;
+       return 0;
+}
+
+
+int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
+                           struct wpa_driver_associate_params *params)
+{
+       struct ieee80211_sta_bss *bss;
+       int bssid_changed;
+
+       wpa_s->mlme.bssid_set = 0;
+       wpa_s->mlme.freq = params->freq;
+       if (params->bssid) {
+               bssid_changed = os_memcmp(wpa_s->bssid, params->bssid,
+                                         ETH_ALEN);
+               os_memcpy(wpa_s->bssid, params->bssid, ETH_ALEN);
+               if (bssid_changed)
+                       wpas_notify_bssid_changed(wpa_s);
+
+               if (!is_zero_ether_addr(params->bssid))
+                       wpa_s->mlme.bssid_set = 1;
+               bss = ieee80211_bss_get(wpa_s, wpa_s->bssid);
+               if (bss) {
+                       wpa_s->mlme.phymode = bss->hw_mode;
+                       wpa_s->mlme.channel = bss->channel;
+                       wpa_s->mlme.freq = bss->freq;
+               }
+       }
+
+#if 0 /* FIX */
+       /* TODO: This should always be done for IBSS, even if IEEE80211_QOS is
+        * not defined. */
+       if (local->hw->conf_tx) {
+               struct ieee80211_tx_queue_params qparam;
+               int i;
+
+               os_memset(&qparam, 0, sizeof(qparam));
+               /* TODO: are these ok defaults for all hw_modes? */
+               qparam.aifs = 2;
+               qparam.cw_min =
+                       local->conf.phymode == MODE_IEEE80211B ? 31 : 15;
+               qparam.cw_max = 1023;
+               qparam.burst_time = 0;
+               for (i = IEEE80211_TX_QUEUE_DATA0; i < NUM_TX_DATA_QUEUES; i++)
+               {
+                       local->hw->conf_tx(wpa_s, i + IEEE80211_TX_QUEUE_DATA0,
+                                          &qparam);
+               }
+               /* IBSS uses different parameters for Beacon sending */
+               qparam.cw_min++;
+               qparam.cw_min *= 2;
+               qparam.cw_min--;
+               local->hw->conf_tx(wpa_s, IEEE80211_TX_QUEUE_BEACON, &qparam);
+       }
+#endif
+
+       if (wpa_s->mlme.ssid_len != params->ssid_len ||
+           os_memcmp(wpa_s->mlme.ssid, params->ssid, params->ssid_len) != 0)
+               wpa_s->mlme.prev_bssid_set = 0;
+       os_memcpy(wpa_s->mlme.ssid, params->ssid, params->ssid_len);
+       os_memset(wpa_s->mlme.ssid + params->ssid_len, 0,
+                 MAX_SSID_LEN - params->ssid_len);
+       wpa_s->mlme.ssid_len = params->ssid_len;
+       wpa_s->mlme.ssid_set = 1;
+
+       os_free(wpa_s->mlme.extra_ie);
+       if (params->wpa_ie == NULL || params->wpa_ie_len == 0) {
+               wpa_s->mlme.extra_ie = NULL;
+               wpa_s->mlme.extra_ie_len = 0;
+       } else {
+               wpa_s->mlme.extra_ie = os_malloc(params->wpa_ie_len);
+               if (wpa_s->mlme.extra_ie == NULL) {
+                       wpa_s->mlme.extra_ie_len = 0;
+                       return -1;
+               }
+               os_memcpy(wpa_s->mlme.extra_ie, params->wpa_ie,
+                         params->wpa_ie_len);
+               wpa_s->mlme.extra_ie_len = params->wpa_ie_len;
+       }
+
+       wpa_s->mlme.key_mgmt = params->key_mgmt_suite;
+
+       ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
+                                 wpa_s->mlme.channel, wpa_s->mlme.freq);
+
+       if (params->mode == WPAS_MODE_IBSS && !wpa_s->mlme.bssid_set) {
+               os_get_time(&wpa_s->mlme.ibss_join_req);
+               wpa_s->mlme.state = IEEE80211_IBSS_SEARCH;
+               return ieee80211_sta_find_ibss(wpa_s);
+       }
+
+       if (wpa_s->mlme.bssid_set)
+               ieee80211_sta_new_auth(wpa_s);
+
+       return 0;
+}
+
+
+static void ieee80211_sta_save_oper_chan(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->mlme.scan_oper_channel = wpa_s->mlme.channel;
+       wpa_s->mlme.scan_oper_freq = wpa_s->mlme.freq;
+       wpa_s->mlme.scan_oper_phymode = wpa_s->mlme.phymode;
+}
+
+
+static int ieee80211_sta_restore_oper_chan(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->mlme.channel = wpa_s->mlme.scan_oper_channel;
+       wpa_s->mlme.freq = wpa_s->mlme.scan_oper_freq;
+       wpa_s->mlme.phymode = wpa_s->mlme.scan_oper_phymode;
+       if (wpa_s->mlme.freq == 0)
+               return 0;
+       return ieee80211_sta_set_channel(wpa_s, wpa_s->mlme.phymode,
+                                        wpa_s->mlme.channel,
+                                        wpa_s->mlme.freq);
+}
+
+
+static int ieee80211_active_scan(struct wpa_supplicant *wpa_s)
+{
+       size_t m;
+       int c;
+
+       for (m = 0; m < wpa_s->mlme.num_modes; m++) {
+               struct hostapd_hw_modes *mode = &wpa_s->mlme.modes[m];
+               if ((int) mode->mode != (int) wpa_s->mlme.phymode)
+                       continue;
+               for (c = 0; c < mode->num_channels; c++) {
+                       struct hostapd_channel_data *chan = &mode->channels[c];
+                       if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+                           chan->chan == wpa_s->mlme.channel) {
+                               if (!(chan->flag & HOSTAPD_CHAN_PASSIVE_SCAN))
+                                       return 1;
+                               break;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+static void ieee80211_sta_scan_timer(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *chan;
+       int skip = 0;
+       int timeout = 0;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       int adhoc;
+
+       if (!wpa_s->mlme.sta_scanning || wpa_s->mlme.modes == NULL)
+               return;
+
+       adhoc = ssid && ssid->mode == 1;
+
+       switch (wpa_s->mlme.scan_state) {
+       case SCAN_SET_CHANNEL:
+               mode = &wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx];
+               if (wpa_s->mlme.scan_hw_mode_idx >=
+                   (int) wpa_s->mlme.num_modes ||
+                   (wpa_s->mlme.scan_hw_mode_idx + 1 ==
+                    (int) wpa_s->mlme.num_modes
+                    && wpa_s->mlme.scan_channel_idx >= mode->num_channels)) {
+                       if (ieee80211_sta_restore_oper_chan(wpa_s)) {
+                               wpa_printf(MSG_DEBUG, "MLME: failed to "
+                                          "restore operational channel after "
+                                          "scan");
+                       }
+                       wpa_printf(MSG_DEBUG, "MLME: scan completed");
+                       wpa_s->mlme.sta_scanning = 0;
+                       os_get_time(&wpa_s->mlme.last_scan_completed);
+                       wpa_supplicant_event(wpa_s, EVENT_SCAN_RESULTS, NULL);
+                       if (adhoc) {
+                               if (!wpa_s->mlme.bssid_set ||
+                                   (wpa_s->mlme.state ==
+                                    IEEE80211_IBSS_JOINED &&
+                                    !ieee80211_sta_active_ibss(wpa_s)))
+                                       ieee80211_sta_find_ibss(wpa_s);
+                       }
+                       return;
+               }
+               skip = !(wpa_s->mlme.hw_modes & (1 << mode->mode));
+               chan = &mode->channels[wpa_s->mlme.scan_channel_idx];
+               if ((chan->flag & HOSTAPD_CHAN_DISABLED) ||
+                   (adhoc && (chan->flag & HOSTAPD_CHAN_NO_IBSS)) ||
+                   (wpa_s->mlme.hw_modes & (1 << HOSTAPD_MODE_IEEE80211G) &&
+                    mode->mode == HOSTAPD_MODE_IEEE80211B &&
+                    wpa_s->mlme.scan_skip_11b))
+                       skip = 1;
+               if (!skip && wpa_s->mlme.scan_freqs) {
+                       int i, found = 0;
+                       for (i = 0; wpa_s->mlme.scan_freqs[i]; i++) {
+                               if (wpa_s->mlme.scan_freqs[i] == chan->freq) {
+                                       found = 1;
+                                       break;
+                               }
+                       }
+                       if (!found)
+                               skip = 1;
+               }
+
+               if (!skip) {
+                       wpa_printf(MSG_MSGDUMP,
+                                  "MLME: scan channel %d (%d MHz)",
+                                  chan->chan, chan->freq);
+
+                       wpa_s->mlme.channel = chan->chan;
+                       wpa_s->mlme.freq = chan->freq;
+                       wpa_s->mlme.phymode = mode->mode;
+                       if (ieee80211_sta_set_channel(wpa_s, mode->mode,
+                                                     chan->chan, chan->freq))
+                       {
+                               wpa_printf(MSG_DEBUG, "MLME: failed to set "
+                                          "channel %d (%d MHz) for scan",
+                                          chan->chan, chan->freq);
+                               skip = 1;
+                       }
+               }
+
+               wpa_s->mlme.scan_channel_idx++;
+               if (wpa_s->mlme.scan_channel_idx >=
+                   wpa_s->mlme.modes[wpa_s->mlme.scan_hw_mode_idx].
+                   num_channels) {
+                       wpa_s->mlme.scan_hw_mode_idx++;
+                       wpa_s->mlme.scan_channel_idx = 0;
+               }
+
+               if (skip) {
+                       timeout = 0;
+                       break;
+               }
+
+               timeout = IEEE80211_PROBE_DELAY;
+               wpa_s->mlme.scan_state = SCAN_SEND_PROBE;
+               break;
+       case SCAN_SEND_PROBE:
+               if (ieee80211_active_scan(wpa_s)) {
+                       ieee80211_send_probe_req(wpa_s, NULL,
+                                                wpa_s->mlme.scan_ssid,
+                                                wpa_s->mlme.scan_ssid_len);
+                       timeout = IEEE80211_CHANNEL_TIME;
+               } else {
+                       timeout = IEEE80211_PASSIVE_CHANNEL_TIME;
+               }
+               wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
+               break;
+       }
+
+       eloop_register_timeout(timeout / 1000, 1000 * (timeout % 1000),
+                              ieee80211_sta_scan_timer, wpa_s, NULL);
+}
+
+
+int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
+                          struct wpa_driver_scan_params *params)
+{
+       const u8 *ssid = params->ssids[0].ssid;
+       size_t ssid_len = params->ssids[0].ssid_len;
+
+       if (ssid_len > MAX_SSID_LEN)
+               return -1;
+
+       /* MLME-SCAN.request (page 118)  page 144 (11.1.3.1)
+        * BSSType: INFRASTRUCTURE, INDEPENDENT, ANY_BSS
+        * BSSID: MACAddress
+        * SSID
+        * ScanType: ACTIVE, PASSIVE
+        * ProbeDelay: delay (in microseconds) to be used prior to transmitting
+        *    a Probe frame during active scanning
+        * ChannelList
+        * MinChannelTime (>= ProbeDelay), in TU
+        * MaxChannelTime: (>= MinChannelTime), in TU
+        */
+
+        /* MLME-SCAN.confirm
+         * BSSDescriptionSet
+         * ResultCode: SUCCESS, INVALID_PARAMETERS
+        */
+
+       /* TODO: if assoc, move to power save mode for the duration of the
+        * scan */
+
+       if (wpa_s->mlme.sta_scanning)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "MLME: starting scan");
+
+       ieee80211_sta_set_probe_req_ie(wpa_s, params->extra_ies,
+                                      params->extra_ies_len);
+
+       os_free(wpa_s->mlme.scan_freqs);
+       if (params->freqs) {
+               int i;
+               for (i = 0; params->freqs[i]; i++)
+                       ;
+               wpa_s->mlme.scan_freqs = os_malloc((i + 1) * sizeof(int));
+               if (wpa_s->mlme.scan_freqs)
+                       os_memcpy(wpa_s->mlme.scan_freqs, params->freqs,
+                                 (i + 1) * sizeof(int));
+       } else
+               wpa_s->mlme.scan_freqs = NULL;
+
+       ieee80211_sta_save_oper_chan(wpa_s);
+
+       wpa_s->mlme.sta_scanning = 1;
+       /* TODO: stop TX queue? */
+
+       if (ssid) {
+               wpa_s->mlme.scan_ssid_len = ssid_len;
+               os_memcpy(wpa_s->mlme.scan_ssid, ssid, ssid_len);
+       } else
+               wpa_s->mlme.scan_ssid_len = 0;
+       wpa_s->mlme.scan_skip_11b = 1; /* FIX: clear this is 11g is not
+                                       * supported */
+       wpa_s->mlme.scan_state = SCAN_SET_CHANNEL;
+       wpa_s->mlme.scan_hw_mode_idx = 0;
+       wpa_s->mlme.scan_channel_idx = 0;
+       eloop_register_timeout(0, 1, ieee80211_sta_scan_timer, wpa_s, NULL);
+
+       return 0;
+}
+
+
+struct wpa_scan_results *
+ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
+{
+       size_t ap_num = 0;
+       struct wpa_scan_results *res;
+       struct wpa_scan_res *r;
+       struct ieee80211_sta_bss *bss;
+
+       res = os_zalloc(sizeof(*res));
+       for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next)
+               ap_num++;
+       res->res = os_zalloc(ap_num * sizeof(struct wpa_scan_res *));
+       if (res->res == NULL) {
+               os_free(res);
+               return NULL;
+       }
+
+       for (bss = wpa_s->mlme.sta_bss_list; bss; bss = bss->next) {
+               r = os_zalloc(sizeof(*r) + bss->ie_len);
+               if (r == NULL)
+                       break;
+               os_memcpy(r->bssid, bss->bssid, ETH_ALEN);
+               r->freq = bss->freq;
+               r->beacon_int = bss->beacon_int;
+               r->caps = bss->capability;
+               r->level = bss->rssi;
+               r->tsf = bss->timestamp;
+               if (bss->ie) {
+                       r->ie_len = bss->ie_len;
+                       os_memcpy(r + 1, bss->ie, bss->ie_len);
+               }
+
+               res->res[res->num++] = r;
+       }
+
+       return res;
+}
+
+
+#if 0 /* FIX */
+struct sta_info * ieee80211_ibss_add_sta(struct wpa_supplicant *wpa_s,
+                                        struct sk_buff *skb, u8 *bssid,
+                                        u8 *addr)
+{
+       struct ieee80211_local *local = dev->priv;
+       struct list_head *ptr;
+       struct sta_info *sta;
+       struct wpa_supplicant *sta_dev = NULL;
+
+       /* TODO: Could consider removing the least recently used entry and
+        * allow new one to be added. */
+       if (local->num_sta >= IEEE80211_IBSS_MAX_STA_ENTRIES) {
+               if (net_ratelimit()) {
+                       wpa_printf(MSG_DEBUG, "MLME: No room for a new IBSS "
+                                  "STA entry " MACSTR, MAC2STR(addr));
+               }
+               return NULL;
+       }
+
+       spin_lock_bh(&local->sub_if_lock);
+       list_for_each(ptr, &local->sub_if_list) {
+               sdata = list_entry(ptr, struct ieee80211_sub_if_data, list);
+               if (sdata->type == IEEE80211_SUB_IF_TYPE_STA &&
+                   os_memcmp(bssid, sdata->u.sta.bssid, ETH_ALEN) == 0) {
+                       sta_dev = sdata->dev;
+                       break;
+               }
+       }
+       spin_unlock_bh(&local->sub_if_lock);
+
+       if (sta_dev == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "MLME: Adding new IBSS station " MACSTR
+                  " (dev=%s)", MAC2STR(addr), sta_dev->name);
+
+       sta = sta_info_add(wpa_s, addr);
+       if (sta == NULL) {
+               return NULL;
+       }
+
+       sta->dev = sta_dev;
+       sta->supp_rates = wpa_s->mlme.supp_rates_bits;
+
+       rate_control_rate_init(local, sta);
+
+       return sta; /* caller will call sta_info_release() */
+}
+#endif
+
+
+int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason)
+{
+       wpa_printf(MSG_DEBUG, "MLME: deauthenticate(reason=%d)", reason);
+
+       ieee80211_send_deauth(wpa_s, reason);
+       ieee80211_set_associated(wpa_s, 0);
+       return 0;
+}
+
+
+int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason)
+{
+       wpa_printf(MSG_DEBUG, "MLME: disassociate(reason=%d)", reason);
+
+       if (!wpa_s->mlme.associated)
+               return -1;
+
+       ieee80211_send_disassoc(wpa_s, reason);
+       ieee80211_set_associated(wpa_s, 0);
+       return 0;
+}
+
+
+void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
+                     struct ieee80211_rx_status *rx_status)
+{
+       struct ieee80211_mgmt *mgmt;
+       u16 fc;
+       const u8 *pos;
+
+       /* wpa_hexdump(MSG_MSGDUMP, "MLME: Received frame", buf, len); */
+
+       if (wpa_s->mlme.sta_scanning) {
+               ieee80211_sta_rx_scan(wpa_s, buf, len, rx_status);
+               return;
+       }
+
+       if (len < 24)
+               return;
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       fc = le_to_host16(mgmt->frame_control);
+
+       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+               ieee80211_sta_rx_mgmt(wpa_s, buf, len, rx_status);
+       else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+               if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) !=
+                   WLAN_FC_FROMDS)
+                       return;
+               /* mgmt->sa is actually BSSID for FromDS data frames */
+               if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0)
+                       return;
+               /* Skip IEEE 802.11 and LLC headers */
+               pos = buf + 24 + 6;
+               if (WPA_GET_BE16(pos) != ETH_P_EAPOL)
+                       return;
+               pos += 2;
+               /* mgmt->bssid is actually BSSID for SA data frames */
+               wpa_supplicant_rx_eapol(wpa_s, mgmt->bssid,
+                                       pos, buf + len - pos);
+       }
+}
+
+
+void ieee80211_sta_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 ieee80211_sta_init(struct wpa_supplicant *wpa_s)
+{
+       u16 num_modes, flags;
+
+       wpa_s->mlme.modes = wpa_drv_get_hw_feature_data(wpa_s, &num_modes,
+                                                       &flags);
+       if (wpa_s->mlme.modes == NULL) {
+               wpa_printf(MSG_ERROR, "MLME: Failed to read supported "
+                          "channels and rates from the driver");
+               return -1;
+       }
+
+       wpa_s->mlme.num_modes = num_modes;
+
+       wpa_s->mlme.hw_modes = 1 << HOSTAPD_MODE_IEEE80211A;
+       wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211B;
+       wpa_s->mlme.hw_modes |= 1 << HOSTAPD_MODE_IEEE80211G;
+
+       wpa_s->mlme.wmm_enabled = 1;
+
+       return 0;
+}
+
+
+void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(ieee80211_sta_timer, wpa_s, NULL);
+       eloop_cancel_timeout(ieee80211_sta_scan_timer, wpa_s, NULL);
+       os_free(wpa_s->mlme.extra_ie);
+       wpa_s->mlme.extra_ie = NULL;
+       os_free(wpa_s->mlme.extra_probe_ie);
+       wpa_s->mlme.extra_probe_ie = NULL;
+       os_free(wpa_s->mlme.assocreq_ies);
+       wpa_s->mlme.assocreq_ies = NULL;
+       os_free(wpa_s->mlme.assocresp_ies);
+       wpa_s->mlme.assocresp_ies = NULL;
+       ieee80211_bss_list_deinit(wpa_s);
+       ieee80211_sta_free_hw_features(wpa_s->mlme.modes,
+                                      wpa_s->mlme.num_modes);
+#ifdef CONFIG_IEEE80211R
+       os_free(wpa_s->mlme.ft_ies);
+       wpa_s->mlme.ft_ies = NULL;
+       wpa_s->mlme.ft_ies_len = 0;
+#endif /* CONFIG_IEEE80211R */
+
+       os_free(wpa_s->mlme.scan_freqs);
+       wpa_s->mlme.scan_freqs = NULL;
+}
+
+
+#ifdef CONFIG_IEEE80211R
+
+int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+                               const u8 *ies, size_t ies_len)
+{
+       if (md == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: Clear FT mobility domain");
+               os_memset(wpa_s->mlme.current_md, 0, MOBILITY_DOMAIN_ID_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "MLME: Update FT IEs for MD " MACSTR,
+                          MAC2STR(md));
+               os_memcpy(wpa_s->mlme.current_md, md, MOBILITY_DOMAIN_ID_LEN);
+       }
+
+       wpa_hexdump(MSG_DEBUG, "MLME: FT IEs", ies, ies_len);
+       os_free(wpa_s->mlme.ft_ies);
+       wpa_s->mlme.ft_ies = os_malloc(ies_len);
+       if (wpa_s->mlme.ft_ies == NULL)
+               return -1;
+       os_memcpy(wpa_s->mlme.ft_ies, ies, ies_len);
+       wpa_s->mlme.ft_ies_len = ies_len;
+
+       return 0;
+}
+
+
+int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
+                                const u8 *target_ap,
+                                const u8 *ies, size_t ies_len)
+{
+       u8 *buf;
+       size_t len;
+       struct ieee80211_mgmt *mgmt;
+       int res;
+
+       /*
+        * Action frame payload:
+        * Category[1] = 6 (Fast BSS Transition)
+        * Action[1] = 1 (Fast BSS Transition Request)
+        * STA Address
+        * Target AP Address
+        * FT IEs
+        */
+
+       buf = os_zalloc(sizeof(*mgmt) + ies_len);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
+                          "FT action frame");
+               return -1;
+       }
+
+       mgmt = (struct ieee80211_mgmt *) buf;
+       len = 24;
+       os_memcpy(mgmt->da, wpa_s->bssid, ETH_ALEN);
+       os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       mgmt->u.action.category = WLAN_ACTION_FT;
+       mgmt->u.action.u.ft_action_req.action = action;
+       os_memcpy(mgmt->u.action.u.ft_action_req.sta_addr, wpa_s->own_addr,
+                 ETH_ALEN);
+       os_memcpy(mgmt->u.action.u.ft_action_req.target_ap_addr, target_ap,
+                 ETH_ALEN);
+       os_memcpy(mgmt->u.action.u.ft_action_req.variable, ies, ies_len);
+       len += 1 + sizeof(mgmt->u.action.u.ft_action_req) + ies_len;
+
+       wpa_printf(MSG_DEBUG, "MLME: Send FT Action Frame: Action=%d "
+                  "Target AP=" MACSTR " body_len=%lu",
+                  action, MAC2STR(target_ap), (unsigned long) ies_len);
+
+       res = ieee80211_sta_tx(wpa_s, buf, len);
+       os_free(buf);
+
+       return res;
+}
+
+#endif /* CONFIG_IEEE80211R */
+
+
+static int ieee80211_sta_set_probe_req_ie(struct wpa_supplicant *wpa_s,
+                                         const u8 *ies, size_t ies_len)
+{
+       os_free(wpa_s->mlme.extra_probe_ie);
+       wpa_s->mlme.extra_probe_ie = NULL;
+       wpa_s->mlme.extra_probe_ie_len = 0;
+
+       if (ies == NULL)
+               return 0;
+
+       wpa_s->mlme.extra_probe_ie = os_malloc(ies_len);
+       if (wpa_s->mlme.extra_probe_ie == NULL)
+               return -1;
+
+       os_memcpy(wpa_s->mlme.extra_probe_ie, ies, ies_len);
+       wpa_s->mlme.extra_probe_ie_len = ies_len;
+
+       return 0;
+}
diff --git a/wpa_supplicant/mlme.h b/wpa_supplicant/mlme.h
new file mode 100644 (file)
index 0000000..5db3665
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * WPA Supplicant - Client mode MLME
+ * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 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 MLME_H
+#define MLME_H
+
+struct wpa_supplicant;
+
+struct ieee80211_rx_status {
+       int freq;
+        int channel;
+        int ssi;
+};
+
+#ifdef CONFIG_CLIENT_MLME
+
+int ieee80211_sta_init(struct wpa_supplicant *wpa_s);
+void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s);
+int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
+                          struct wpa_driver_scan_params *params);
+int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s, u16 reason);
+int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s, u16 reason);
+int ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
+                           struct wpa_driver_associate_params *params);
+int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid,
+                          size_t *len);
+void ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
+                                   size_t num_hw_features);
+void ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
+                     struct ieee80211_rx_status *rx_status);
+struct wpa_scan_results *
+ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s);
+int ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+                               const u8 *ies, size_t ies_len);
+int ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
+                                const u8 *target_ap,
+                                const u8 *ies, size_t ies_len);
+
+#else /* CONFIG_CLIENT_MLME */
+
+static inline int ieee80211_sta_init(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline void ieee80211_sta_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int ieee80211_sta_req_scan(struct wpa_supplicant *wpa_s,
+                                        struct wpa_driver_scan_params *params)
+{
+       return -1;
+}
+
+static inline int ieee80211_sta_deauthenticate(struct wpa_supplicant *wpa_s,
+                                              u16 reason)
+{
+       return -1;
+}
+
+static inline int ieee80211_sta_disassociate(struct wpa_supplicant *wpa_s,
+                                            u16 reason)
+{
+       return -1;
+}
+
+static inline int
+ieee80211_sta_associate(struct wpa_supplicant *wpa_s,
+                       struct wpa_driver_associate_params *params)
+{
+       return -1;
+}
+
+static inline int ieee80211_sta_get_ssid(struct wpa_supplicant *wpa_s,
+                                        u8 *ssid, size_t *len)
+{
+       return -1;
+}
+
+static inline void
+ieee80211_sta_free_hw_features(struct hostapd_hw_modes *hw_features,
+                              size_t num_hw_features)
+{
+}
+
+static inline void
+ieee80211_sta_rx(struct wpa_supplicant *wpa_s, const u8 *buf, size_t len,
+                struct ieee80211_rx_status *rx_status)
+{
+}
+
+static inline struct wpa_scan_results *
+ieee80211_sta_get_scan_results(struct wpa_supplicant *wpa_s)
+{
+       return NULL;
+}
+
+static inline int
+ieee80211_sta_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+                           const u8 *ies, size_t ies_len)
+{
+       return -1;
+}
+
+static inline int
+ieee80211_sta_send_ft_action(struct wpa_supplicant *wpa_s, u8 action,
+                            const u8 *target_ap,
+                            const u8 *ies, size_t ies_len)
+{
+       return -1;
+}
+
+#endif /* CONFIG_CLIENT_MLME */
+
+#endif /* MLME_H */
diff --git a/wpa_supplicant/nmake.mak b/wpa_supplicant/nmake.mak
new file mode 100644 (file)
index 0000000..80e0ac8
--- /dev/null
@@ -0,0 +1,240 @@
+# Makefile for Microsoft nmake to build wpa_supplicant
+
+# This can be run in Visual Studio 2005 Command Prompt
+
+# Note: Make sure that cl.exe is configured to include Platform SDK
+# include and lib directories (vsvars32.bat)
+
+all: wpa_supplicant.exe wpa_cli.exe wpa_passphrase.exe wpasvc.exe win_if_list.exe
+
+# Root directory for WinPcap developer's pack
+# (http://www.winpcap.org/install/bin/WpdPack_3_1.zip)
+WINPCAPDIR=C:\dev\WpdPack
+
+# Root directory for OpenSSL
+# (http://www.openssl.org/source/openssl-0.9.8a.tar.gz)
+# Build and installed following instructions in INSTALL.W32
+# Note: If EAP-FAST is included in the build, OpenSSL needs to be patched to
+# support it (openssl-tls-extensions.patch)
+# Alternatively, see README-Windows.txt for information about binary
+# installation package for OpenSSL.
+OPENSSLDIR=C:\dev\openssl
+
+CC = cl
+OBJDIR = objs
+
+CFLAGS = /DCONFIG_NATIVE_WINDOWS
+CFLAGS = $(CFLAGS) /DCONFIG_NDIS_EVENTS_INTEGRATED
+CFLAGS = $(CFLAGS) /DCONFIG_ANSI_C_EXTRA
+CFLAGS = $(CFLAGS) /DCONFIG_WINPCAP
+CFLAGS = $(CFLAGS) /DIEEE8021X_EAPOL
+CFLAGS = $(CFLAGS) /DPKCS12_FUNCS
+CFLAGS = $(CFLAGS) /DEAP_MD5
+CFLAGS = $(CFLAGS) /DEAP_TLS
+CFLAGS = $(CFLAGS) /DEAP_MSCHAPv2
+CFLAGS = $(CFLAGS) /DEAP_PEAP
+CFLAGS = $(CFLAGS) /DEAP_TTLS
+CFLAGS = $(CFLAGS) /DEAP_GTC
+CFLAGS = $(CFLAGS) /DEAP_OTP
+CFLAGS = $(CFLAGS) /DEAP_SIM
+CFLAGS = $(CFLAGS) /DEAP_LEAP
+CFLAGS = $(CFLAGS) /DEAP_PSK
+CFLAGS = $(CFLAGS) /DEAP_AKA
+#CFLAGS = $(CFLAGS) /DEAP_FAST
+CFLAGS = $(CFLAGS) /DEAP_PAX
+CFLAGS = $(CFLAGS) /DEAP_TNC
+CFLAGS = $(CFLAGS) /DPCSC_FUNCS
+CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE
+CFLAGS = $(CFLAGS) /DCONFIG_CTRL_IFACE_NAMED_PIPE
+CFLAGS = $(CFLAGS) /DCONFIG_DRIVER_NDIS
+CFLAGS = $(CFLAGS) /I..\src /I..\src\utils
+CFLAGS = $(CFLAGS) /I.
+CFLAGS = $(CFLAGS) /DWIN32
+CFLAGS = $(CFLAGS) /Fo$(OBJDIR)\\ /c
+CFLAGS = $(CFLAGS) /W3
+
+#CFLAGS = $(CFLAGS) /WX
+
+# VS 2005 complains about lot of deprecated string functions; let's ignore them
+# at least for now since snprintf and strncpy can be used in a safe way
+CFLAGS = $(CFLAGS) /D_CRT_SECURE_NO_DEPRECATE
+
+OBJS = \
+       $(OBJDIR)\os_win32.obj \
+       $(OBJDIR)\eloop_win.obj \
+       $(OBJDIR)\sha1.obj \
+       $(OBJDIR)\sha1-tlsprf.obj \
+       $(OBJDIR)\sha1-pbkdf2.obj \
+       $(OBJDIR)\md5.obj \
+       $(OBJDIR)\aes-cbc.obj \
+       $(OBJDIR)\aes-ctr.obj \
+       $(OBJDIR)\aes-eax.obj \
+       $(OBJDIR)\aes-encblock.obj \
+       $(OBJDIR)\aes-omac1.obj \
+       $(OBJDIR)\aes-unwrap.obj \
+       $(OBJDIR)\aes-wrap.obj \
+       $(OBJDIR)\common.obj \
+       $(OBJDIR)\wpa_debug.obj \
+       $(OBJDIR)\wpabuf.obj \
+       $(OBJDIR)\wpa_supplicant.obj \
+       $(OBJDIR)\wpa.obj \
+       $(OBJDIR)\wpa_common.obj \
+       $(OBJDIR)\wpa_ie.obj \
+       $(OBJDIR)\preauth.obj \
+       $(OBJDIR)\pmksa_cache.obj \
+       $(OBJDIR)\eapol_supp_sm.obj \
+       $(OBJDIR)\eap.obj \
+       $(OBJDIR)\eap_common.obj \
+       $(OBJDIR)\chap.obj \
+       $(OBJDIR)\eap_methods.obj \
+       $(OBJDIR)\eap_md5.obj \
+       $(OBJDIR)\eap_tls.obj \
+       $(OBJDIR)\eap_tls_common.obj \
+       $(OBJDIR)\eap_mschapv2.obj \
+       $(OBJDIR)\mschapv2.obj \
+       $(OBJDIR)\eap_peap.obj \
+       $(OBJDIR)\eap_peap_common.obj \
+       $(OBJDIR)\eap_ttls.obj \
+       $(OBJDIR)\eap_gtc.obj \
+       $(OBJDIR)\eap_otp.obj \
+       $(OBJDIR)\eap_leap.obj \
+       $(OBJDIR)\eap_sim.obj \
+       $(OBJDIR)\eap_sim_common.obj \
+       $(OBJDIR)\eap_aka.obj \
+       $(OBJDIR)\eap_pax.obj \
+       $(OBJDIR)\eap_pax_common.obj \
+       $(OBJDIR)\eap_psk.obj \
+       $(OBJDIR)\eap_psk_common.obj \
+       $(OBJDIR)\eap_tnc.obj \
+       $(OBJDIR)\tncc.obj \
+       $(OBJDIR)\base64.obj \
+       $(OBJDIR)\ctrl_iface.obj \
+       $(OBJDIR)\ctrl_iface_named_pipe.obj \
+       $(OBJDIR)\driver_ndis.obj \
+       $(OBJDIR)\driver_ndis_.obj \
+       $(OBJDIR)\scan_helpers.obj \
+       $(OBJDIR)\events.obj \
+       $(OBJDIR)\blacklist.obj \
+       $(OBJDIR)\scan.obj \
+       $(OBJDIR)\wpas_glue.obj \
+       $(OBJDIR)\eap_register.obj \
+       $(OBJDIR)\config.obj \
+       $(OBJDIR)\l2_packet_winpcap.obj \
+       $(OBJDIR)\tls_openssl.obj \
+       $(OBJDIR)\ms_funcs.obj \
+       $(OBJDIR)\crypto_openssl.obj \
+       $(OBJDIR)\fips_prf_openssl.obj \
+       $(OBJDIR)\pcsc_funcs.obj \
+       $(OBJDIR)\notify.obj \
+       $(OBJDIR)\ndis_events.obj
+
+# OBJS = $(OBJS) $(OBJDIR)\eap_fast.obj
+
+OBJS_t = $(OBJS) \
+       $(OBJDIR)\eapol_test.obj \
+       $(OBJDIR)\radius.obj \
+       $(OBJDIR)\radius_client.obj \
+       $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj
+
+OBJS_t2 = $(OBJS) \
+       $(OBJDIR)\preauth_test.obj \
+       $(OBJDIR)\config_file.obj $(OBJDIR)\base64.obj
+
+OBJS2 = $(OBJDIR)\drivers.obj \
+       $(OBJDIR)\config_file.obj \
+       $(OBJS2) $(OBJDIR)\main.obj
+
+OBJS3 = $(OBJDIR)\drivers.obj \
+       $(OBJDIR)\config_winreg.obj \
+       $(OBJS3) $(OBJDIR)\main_winsvc.obj
+
+OBJS_c = \
+       $(OBJDIR)\os_win32.obj \
+       $(OBJDIR)\wpa_cli.obj \
+       $(OBJDIR)\wpa_ctrl.obj \
+       $(OBJDIR)\common.obj
+
+OBJS_p = \
+       $(OBJDIR)\os_win32.obj \
+       $(OBJDIR)\common.obj \
+       $(OBJDIR)\wpa_debug.obj \
+       $(OBJDIR)\wpabuf.obj \
+       $(OBJDIR)\sha1.obj \
+       $(OBJDIR)\md5.obj \
+       $(OBJDIR)\crypto_openssl.obj \
+       $(OBJDIR)\sha1-pbkdf2.obj \
+       $(OBJDIR)\wpa_passphrase.obj
+
+LIBS = wbemuuid.lib libcmt.lib kernel32.lib uuid.lib ole32.lib oleaut32.lib \
+       ws2_32.lib Advapi32.lib Crypt32.lib Winscard.lib \
+       Packet.lib wpcap.lib \
+       libeay32.lib ssleay32.lib
+# If using Win32 OpenSSL binary installation from Shining Light Productions,
+# replace the last line with this for dynamic libraries
+#      libeay32MT.lib ssleay32MT.lib
+# and this for static libraries
+#      libeay32MT.lib ssleay32MT.lib Gdi32.lib User32.lib
+
+CFLAGS = $(CFLAGS) /I"$(WINPCAPDIR)/Include" /I"$(OPENSSLDIR)\include"
+LFLAGS = /libpath:"$(WINPCAPDIR)\Lib" /libpath:"$(OPENSSLDIR)\lib"
+
+wpa_supplicant.exe: $(OBJDIR) $(OBJS) $(OBJS2)
+       link.exe /out:wpa_supplicant.exe $(LFLAGS) $(OBJS) $(OBJS2) $(LIBS)
+
+wpasvc.exe: $(OBJDIR) $(OBJS) $(OBJS3)
+       link.exe /out:wpasvc.exe $(LFLAGS) $(OBJS) $(OBJS3) $(LIBS)
+
+wpa_cli.exe: $(OBJDIR) $(OBJS_c)
+       link.exe /out:wpa_cli.exe $(LFLAGS) $(OBJS_c) $(LIBS)
+
+wpa_passphrase.exe: $(OBJDIR) $(OBJS_p)
+       link.exe /out:wpa_passphrase.exe $(LFLAGS) $(OBJS_p) $(LIBS)
+
+eapol_test.exe: $(OBJDIR) $(OBJS_t)
+       link.exe /out:eapol_test.exe $(LFLAGS) $(OBJS_t) $(LIBS)
+
+preauth_test.exe: $(OBJDIR) $(OBJS_t2)
+       link.exe /out:preauth_test.exe $(LFLAGS) $(OBJS_t2) $(LIBS)
+
+win_if_list.exe: $(OBJDIR) $(OBJDIR)\win_if_list.obj
+       link.exe /out:win_if_list.exe $(LFLAGS) $(OBJDIR)\win_if_list.obj $(LIBS)
+
+
+{..\src\utils}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\common}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\rsn_supp}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\eapol_supp}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\crypto}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\eap_peer}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\eap_common}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\drivers}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{..\src\l2_packet}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{.\}.c{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+{.\}.cpp{$(OBJDIR)}.obj::
+       $(CC) $(CFLAGS) $<
+
+$(OBJDIR):
+       if not exist "$(OBJDIR)" mkdir "$(OBJDIR)"
+
+clean:
+       erase $(OBJDIR)\*.obj wpa_supplicant.exe
diff --git a/wpa_supplicant/notify.c b/wpa_supplicant/notify.c
new file mode 100644 (file)
index 0000000..ac65b4f
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * wpa_supplicant - Event notifications
+ * Copyright (c) 2009-2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "wps_supplicant.h"
+#include "dbus/dbus_common.h"
+#include "dbus/dbus_old.h"
+#include "dbus/dbus_new.h"
+#include "driver_i.h"
+#include "scan.h"
+#include "notify.h"
+
+int wpas_notify_supplicant_initialized(struct wpa_global *global)
+{
+#ifdef CONFIG_DBUS
+       if (global->params.dbus_ctrl_interface) {
+               global->dbus = wpas_dbus_init(global);
+               if (global->dbus == NULL)
+                       return -1;
+       }
+#endif /* CONFIG_DBUS */
+
+       return 0;
+}
+
+
+void wpas_notify_supplicant_deinitialized(struct wpa_global *global)
+{
+#ifdef CONFIG_DBUS
+       if (global->dbus)
+               wpas_dbus_deinit(global->dbus);
+#endif /* CONFIG_DBUS */
+}
+
+
+int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
+{
+       if (wpas_dbus_register_iface(wpa_s))
+               return -1;
+
+       if (wpas_dbus_register_interface(wpa_s))
+               return -1;
+
+       return 0;
+}
+
+
+void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
+{
+       /* unregister interface in old DBus ctrl iface */
+       wpas_dbus_unregister_iface(wpa_s);
+
+       /* unregister interface in new DBus ctrl iface */
+       wpas_dbus_unregister_interface(wpa_s);
+}
+
+
+void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
+                              enum wpa_states new_state,
+                              enum wpa_states old_state)
+{
+       /* notify the old DBus API */
+       wpa_supplicant_dbus_notify_state_change(wpa_s, new_state,
+                                               old_state);
+
+       /* notify the new DBus API */
+       wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
+}
+
+
+void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
+{
+       wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
+}
+
+
+void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s)
+{
+       wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN);
+}
+
+
+void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
+{
+       wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
+}
+
+
+void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
+                                        struct wpa_ssid *ssid)
+{
+       wpas_dbus_signal_network_enabled_changed(wpa_s, ssid);
+}
+
+
+void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid)
+{
+       wpas_dbus_signal_network_selected(wpa_s, ssid->id);
+}
+
+
+void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
+{
+       /* notify the old DBus API */
+       wpa_supplicant_dbus_notify_scanning(wpa_s);
+
+       /* notify the new DBus API */
+       wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_SCANNING);
+}
+
+
+void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success)
+{
+       wpas_dbus_signal_scan_done(wpa_s, success);
+}
+
+
+void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+       /* notify the old DBus API */
+       wpa_supplicant_dbus_notify_scan_results(wpa_s);
+
+       wpas_wps_notify_scan_results(wpa_s);
+}
+
+
+void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
+                               const struct wps_credential *cred)
+{
+#ifdef CONFIG_WPS
+       /* notify the old DBus API */
+       wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
+       /* notify the new DBus API */
+       wpas_dbus_signal_wps_cred(wpa_s, cred);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
+                              struct wps_event_m2d *m2d)
+{
+#ifdef CONFIG_WPS
+       wpas_dbus_signal_wps_event_m2d(wpa_s, m2d);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
+                               struct wps_event_fail *fail)
+{
+#ifdef CONFIG_WPS
+       wpas_dbus_signal_wps_event_fail(wpa_s, fail);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS
+       wpas_dbus_signal_wps_event_success(wpa_s);
+#endif /* CONFIG_WPS */
+}
+
+
+void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
+                              struct wpa_ssid *ssid)
+{
+       wpas_dbus_register_network(wpa_s, ssid);
+}
+
+
+void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
+                                struct wpa_ssid *ssid)
+{
+       wpas_dbus_unregister_network(wpa_s, ssid->id);
+}
+
+
+void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
+                          u8 bssid[], unsigned int id)
+{
+       wpas_dbus_register_bss(wpa_s, bssid, id);
+       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR,
+                    id, MAC2STR(bssid));
+}
+
+
+void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
+                            u8 bssid[], unsigned int id)
+{
+       wpas_dbus_unregister_bss(wpa_s, bssid, id);
+       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR,
+                    id, MAC2STR(bssid));
+}
+
+
+void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
+                                 unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id);
+}
+
+
+void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
+                                   unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL,
+                                         id);
+}
+
+
+void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
+                                    unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY,
+                                         id);
+}
+
+
+void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
+                                 unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id);
+}
+
+
+void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id);
+}
+
+
+void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id);
+}
+
+
+void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
+                                unsigned int id)
+{
+}
+
+
+void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id);
+}
+
+
+void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id)
+{
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id);
+}
+
+
+void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
+{
+       wpas_dbus_signal_blob_added(wpa_s, name);
+}
+
+
+void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name)
+{
+       wpas_dbus_signal_blob_removed(wpa_s, name);
+}
+
+
+void wpas_notify_debug_level_changed(struct wpa_global *global)
+{
+       wpas_dbus_signal_debug_level_changed(global);
+}
+
+
+void wpas_notify_debug_timestamp_changed(struct wpa_global *global)
+{
+       wpas_dbus_signal_debug_timestamp_changed(global);
+}
+
+
+void wpas_notify_debug_show_keys_changed(struct wpa_global *global)
+{
+       wpas_dbus_signal_debug_show_keys_changed(global);
+}
+
+
+void wpas_notify_suspend(struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s;
+
+       os_get_time(&global->suspend_time);
+       wpa_printf(MSG_DEBUG, "System suspend notification");
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+               wpa_drv_suspend(wpa_s);
+}
+
+
+void wpas_notify_resume(struct wpa_global *global)
+{
+       struct os_time now;
+       int slept;
+       struct wpa_supplicant *wpa_s;
+
+       if (global->suspend_time.sec == 0)
+               slept = -1;
+       else {
+               os_get_time(&now);
+               slept = now.sec - global->suspend_time.sec;
+       }
+       wpa_printf(MSG_DEBUG, "System resume notification (slept %d seconds)",
+                  slept);
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               wpa_drv_resume(wpa_s);
+               if (wpa_s->wpa_state == WPA_DISCONNECTED)
+                       wpa_supplicant_req_scan(wpa_s, 0, 100000);
+       }
+}
diff --git a/wpa_supplicant/notify.h b/wpa_supplicant/notify.h
new file mode 100644 (file)
index 0000000..2e70bdb
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * wpa_supplicant - Event notifications
+ * Copyright (c) 2009-2010, 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 NOTIFY_H
+#define NOTIFY_H
+
+struct wps_credential;
+struct wps_event_m2d;
+struct wps_event_fail;
+
+int wpas_notify_supplicant_initialized(struct wpa_global *global);
+void wpas_notify_supplicant_deinitialized(struct wpa_global *global);
+int wpas_notify_iface_added(struct wpa_supplicant *wpa_s);
+void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s);
+void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
+                              enum wpa_states new_state,
+                              enum wpa_states old_state);
+void wpas_notify_network_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
+                                        struct wpa_ssid *ssid);
+void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid);
+void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
+void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
+void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
+                               const struct wps_credential *cred);
+void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
+                              struct wps_event_m2d *m2d);
+void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
+                               struct wps_event_fail *fail);
+void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s);
+void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
+                              struct wpa_ssid *ssid);
+void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
+                                struct wpa_ssid *ssid);
+void wpas_notify_bss_added(struct wpa_supplicant *wpa_s, u8 bssid[],
+                          unsigned int id);
+void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s, u8 bssid[],
+                            unsigned int id);
+void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
+                                 unsigned int id);
+void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
+                                   unsigned int id);
+void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
+                                    unsigned int id);
+void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
+                                 unsigned int id);
+void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id);
+void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id);
+void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
+                                unsigned int id);
+void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
+                                unsigned int id);
+void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
+                                  unsigned int id);
+void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
+void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
+
+void wpas_notify_debug_level_changed(struct wpa_global *global);
+void wpas_notify_debug_timestamp_changed(struct wpa_global *global);
+void wpas_notify_debug_show_keys_changed(struct wpa_global *global);
+void wpas_notify_suspend(struct wpa_global *global);
+void wpas_notify_resume(struct wpa_global *global);
+
+#endif /* NOTIFY_H */
diff --git a/wpa_supplicant/preauth_test.c b/wpa_supplicant/preauth_test.c
new file mode 100644 (file)
index 0000000..d38a6bb
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * WPA Supplicant - test code for pre-authentication
+ * 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.
+ *
+ * IEEE 802.1X Supplicant test code (to be used in place of wpa_supplicant.c.
+ * Not used in production version.
+ */
+
+#include "includes.h"
+#include <assert.h>
+
+#include "common.h"
+#include "config.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eloop.h"
+#include "rsn_supp/wpa.h"
+#include "eap_peer/eap.h"
+#include "wpa_supplicant_i.h"
+#include "l2_packet/l2_packet.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "drivers/driver.h"
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+
+struct wpa_driver_ops *wpa_drivers[] = { NULL };
+
+
+struct preauth_test_data {
+       int auth_timed_out;
+};
+
+
+static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code)
+{
+       wpa_supplicant_disassociate(wpa_s, reason_code);
+}
+
+
+static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+{
+       wpa_supplicant_deauthenticate(wpa_s, reason_code);
+}
+
+
+static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
+                           const void *data, u16 data_len,
+                           size_t *msg_len, void **data_pos)
+{
+       struct ieee802_1x_hdr *hdr;
+
+       *msg_len = sizeof(*hdr) + data_len;
+       hdr = os_malloc(*msg_len);
+       if (hdr == NULL)
+               return NULL;
+
+       hdr->version = wpa_s->conf->eapol_version;
+       hdr->type = type;
+       hdr->length = htons(data_len);
+
+       if (data)
+               os_memcpy(hdr + 1, data, data_len);
+       else
+               os_memset(hdr + 1, 0, data_len);
+
+       if (data_pos)
+               *data_pos = hdr + 1;
+
+       return (u8 *) hdr;
+}
+
+
+static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
+                            const void *data, u16 data_len,
+                            size_t *msg_len, void **data_pos)
+{
+       return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
+}
+
+
+static void _wpa_supplicant_set_state(void *ctx, enum wpa_states state)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_s->wpa_state = state;
+}
+
+
+static enum wpa_states _wpa_supplicant_get_state(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpa_s->wpa_state;
+}
+
+
+static int wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
+                         const u8 *buf, size_t len)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static void * wpa_supplicant_get_network_ctx(void *wpa_s)
+{
+       return wpa_supplicant_get_ssid(wpa_s);
+}
+
+
+static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
+{
+       wpa_supplicant_cancel_auth_timeout(wpa_s);
+}
+
+
+static int wpa_supplicant_get_beacon_ie(void *wpa_s)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static int wpa_supplicant_get_bssid(void *wpa_s, u8 *bssid)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static int wpa_supplicant_set_key(void *wpa_s, enum wpa_alg alg,
+                                 const u8 *addr, int key_idx, int set_tx,
+                                 const u8 *seq, size_t seq_len,
+                                 const u8 *key, size_t key_len)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
+                                            int protection_type,
+                                            int key_type)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static int wpa_supplicant_add_pmkid(void *wpa_s,
+                                   const u8 *bssid, const u8 *pmkid)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static int wpa_supplicant_remove_pmkid(void *wpa_s,
+                                      const u8 *bssid, const u8 *pmkid)
+{
+       printf("%s - not implemented\n", __func__);
+       return -1;
+}
+
+
+static void wpa_supplicant_set_config_blob(void *ctx,
+                                          struct wpa_config_blob *blob)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_config_set_blob(wpa_s->conf, blob);
+}
+
+
+static const struct wpa_config_blob *
+wpa_supplicant_get_config_blob(void *ctx, const char *name)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpa_config_get_blob(wpa_s->conf, name);
+}
+
+
+static void test_eapol_clean(struct wpa_supplicant *wpa_s)
+{
+       rsn_preauth_deinit(wpa_s->wpa);
+       pmksa_candidate_free(wpa_s->wpa);
+       wpa_sm_deinit(wpa_s->wpa);
+       scard_deinit(wpa_s->scard);
+       if (wpa_s->ctrl_iface) {
+               wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+               wpa_s->ctrl_iface = NULL;
+       }
+       wpa_config_free(wpa_s->conf);
+}
+
+
+static void eapol_test_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct preauth_test_data *p = eloop_ctx;
+       printf("EAPOL test timed out\n");
+       p->auth_timed_out = 1;
+       eloop_terminate();
+}
+
+
+static void eapol_test_poll(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       if (!rsn_preauth_in_progress(wpa_s->wpa))
+               eloop_terminate();
+       else {
+               eloop_register_timeout(0, 100000, eapol_test_poll, eloop_ctx,
+                                      timeout_ctx);
+       }
+}
+
+
+static struct wpa_driver_ops dummy_driver;
+
+
+static void wpa_init_conf(struct wpa_supplicant *wpa_s, const char *ifname)
+{
+       struct l2_packet_data *l2;
+       struct wpa_sm_ctx *ctx;
+
+       os_memset(&dummy_driver, 0, sizeof(dummy_driver));
+       wpa_s->driver = &dummy_driver;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       assert(ctx != NULL);
+
+       ctx->ctx = wpa_s;
+       ctx->msg_ctx = wpa_s;
+       ctx->set_state = _wpa_supplicant_set_state;
+       ctx->get_state = _wpa_supplicant_get_state;
+       ctx->deauthenticate = _wpa_supplicant_deauthenticate;
+       ctx->disassociate = _wpa_supplicant_disassociate;
+       ctx->set_key = wpa_supplicant_set_key;
+       ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
+       ctx->get_bssid = wpa_supplicant_get_bssid;
+       ctx->ether_send = wpa_ether_send;
+       ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
+       ctx->alloc_eapol = _wpa_alloc_eapol;
+       ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
+       ctx->add_pmkid = wpa_supplicant_add_pmkid;
+       ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
+       ctx->set_config_blob = wpa_supplicant_set_config_blob;
+       ctx->get_config_blob = wpa_supplicant_get_config_blob;
+       ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
+
+       wpa_s->wpa = wpa_sm_init(ctx);
+       assert(wpa_s->wpa != NULL);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+
+       os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+       wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname, NULL);
+
+       l2 = l2_packet_init(wpa_s->ifname, NULL, ETH_P_RSN_PREAUTH, NULL,
+                           NULL, 0);
+       assert(l2 != NULL);
+       if (l2_packet_get_own_addr(l2, wpa_s->own_addr)) {
+               wpa_printf(MSG_WARNING, "Failed to get own L2 address\n");
+               exit(-1);
+       }
+       l2_packet_deinit(l2);
+       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+}
+
+
+static void eapol_test_terminate(int sig, void *signal_ctx)
+{
+       struct wpa_supplicant *wpa_s = signal_ctx;
+       wpa_msg(wpa_s, MSG_INFO, "Signal %d received - terminating", sig);
+       eloop_terminate();
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct wpa_supplicant wpa_s;
+       int ret = 1;
+       u8 bssid[ETH_ALEN];
+       struct preauth_test_data preauth_test;
+
+       if (os_program_init())
+               return -1;
+
+       os_memset(&preauth_test, 0, sizeof(preauth_test));
+
+       wpa_debug_level = 0;
+       wpa_debug_show_keys = 1;
+
+       if (argc != 4) {
+               printf("usage: preauth_test <conf> <target MAC address> "
+                      "<ifname>\n");
+               return -1;
+       }
+
+       if (hwaddr_aton(argv[2], bssid)) {
+               printf("Failed to parse target address '%s'.\n", argv[2]);
+               return -1;
+       }
+
+       if (eap_register_methods()) {
+               wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+               return -1;
+       }
+
+       if (eloop_init()) {
+               wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+               return -1;
+       }
+
+       os_memset(&wpa_s, 0, sizeof(wpa_s));
+       wpa_s.conf = wpa_config_read(argv[1]);
+       if (wpa_s.conf == NULL) {
+               printf("Failed to parse configuration file '%s'.\n", argv[1]);
+               return -1;
+       }
+       if (wpa_s.conf->ssid == NULL) {
+               printf("No networks defined.\n");
+               return -1;
+       }
+
+       wpa_init_conf(&wpa_s, argv[3]);
+       wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
+       if (wpa_s.ctrl_iface == NULL) {
+               printf("Failed to initialize control interface '%s'.\n"
+                      "You may have another preauth_test process already "
+                      "running or the file was\n"
+                      "left by an unclean termination of preauth_test in "
+                      "which case you will need\n"
+                      "to manually remove this file before starting "
+                      "preauth_test again.\n",
+                      wpa_s.conf->ctrl_interface);
+               return -1;
+       }
+       if (wpa_supplicant_scard_init(&wpa_s, wpa_s.conf->ssid))
+               return -1;
+
+       if (rsn_preauth_init(wpa_s.wpa, bssid, &wpa_s.conf->ssid->eap))
+               return -1;
+
+       eloop_register_timeout(30, 0, eapol_test_timeout, &preauth_test, NULL);
+       eloop_register_timeout(0, 100000, eapol_test_poll, &wpa_s, NULL);
+       eloop_register_signal_terminate(eapol_test_terminate, &wpa_s);
+       eloop_register_signal_reconfig(eapol_test_terminate, &wpa_s);
+       eloop_run();
+
+       if (preauth_test.auth_timed_out)
+               ret = -2;
+       else {
+               ret = pmksa_cache_set_current(wpa_s.wpa, NULL, bssid, NULL, 0)
+                       ? 0 : -3;
+       }
+
+       test_eapol_clean(&wpa_s);
+
+       eap_peer_unregister_methods();
+
+       eloop_destroy();
+
+       os_program_deinit();
+
+       return ret;
+}
diff --git a/wpa_supplicant/scan.c b/wpa_supplicant/scan.c
new file mode 100644 (file)
index 0000000..edc8c83
--- /dev/null
@@ -0,0 +1,689 @@
+/*
+ * WPA Supplicant - Scanning
+ * Copyright (c) 2003-2010, 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 "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "mlme.h"
+#include "wps_supplicant.h"
+#include "notify.h"
+#include "bss.h"
+#include "scan.h"
+
+
+static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid;
+       union wpa_event_data data;
+
+       ssid = wpa_supplicant_get_ssid(wpa_s);
+       if (ssid == NULL)
+               return;
+
+       if (wpa_s->current_ssid == NULL) {
+               wpa_s->current_ssid = ssid;
+               if (wpa_s->current_ssid != NULL)
+                       wpas_notify_network_changed(wpa_s);
+       }
+       wpa_supplicant_initiate_eapol(wpa_s);
+       wpa_printf(MSG_DEBUG, "Already associated with a configured network - "
+                  "generating associated event");
+       os_memset(&data, 0, sizeof(data));
+       wpa_supplicant_event(wpa_s, EVENT_ASSOC, &data);
+}
+
+
+#ifdef CONFIG_WPS
+static int wpas_wps_in_use(struct wpa_config *conf,
+                          enum wps_request_type *req_type)
+{
+       struct wpa_ssid *ssid;
+       int wps = 0;
+
+       for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+               if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+                       continue;
+
+               wps = 1;
+               *req_type = wpas_wps_get_req_type(ssid);
+               if (!ssid->eap.phase1)
+                       continue;
+
+               if (os_strstr(ssid->eap.phase1, "pbc=1"))
+                       return 2;
+       }
+
+       return wps;
+}
+#endif /* CONFIG_WPS */
+
+
+int wpa_supplicant_enabled_networks(struct wpa_config *conf)
+{
+       struct wpa_ssid *ssid = conf->ssid;
+       while (ssid) {
+               if (!ssid->disabled)
+                       return 1;
+               ssid = ssid->next;
+       }
+       return 0;
+}
+
+
+static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
+                                    struct wpa_ssid *ssid)
+{
+       while (ssid) {
+               if (!ssid->disabled)
+                       break;
+               ssid = ssid->next;
+       }
+
+       /* ap_scan=2 mode - try to associate with each SSID. */
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "wpa_supplicant_scan: Reached "
+                          "end of scan list - go back to beginning");
+               wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               return;
+       }
+       if (ssid->next) {
+               /* Continue from the next SSID on the next attempt. */
+               wpa_s->prev_scan_ssid = ssid;
+       } else {
+               /* Start from the beginning of the SSID list. */
+               wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+       }
+       wpa_supplicant_associate(wpa_s, NULL, ssid);
+}
+
+
+static int int_array_len(const int *a)
+{
+       int i;
+       for (i = 0; a && a[i]; i++)
+               ;
+       return i;
+}
+
+
+static void int_array_concat(int **res, const int *a)
+{
+       int reslen, alen, i;
+       int *n;
+
+       reslen = int_array_len(*res);
+       alen = int_array_len(a);
+
+       n = os_realloc(*res, (reslen + alen + 1) * sizeof(int));
+       if (n == NULL) {
+               os_free(*res);
+               *res = NULL;
+               return;
+       }
+       for (i = 0; i <= alen; i++)
+               n[reslen + i] = a[i];
+       *res = n;
+}
+
+
+static int freq_cmp(const void *a, const void *b)
+{
+       int _a = *(int *) a;
+       int _b = *(int *) b;
+
+       if (_a == 0)
+               return 1;
+       if (_b == 0)
+               return -1;
+       return _a - _b;
+}
+
+
+static void int_array_sort_unique(int *a)
+{
+       int alen;
+       int i, j;
+
+       if (a == NULL)
+               return;
+
+       alen = int_array_len(a);
+       qsort(a, alen, sizeof(int), freq_cmp);
+
+       i = 0;
+       j = 1;
+       while (a[i] && a[j]) {
+               if (a[i] == a[j]) {
+                       j++;
+                       continue;
+               }
+               a[++i] = a[j++];
+       }
+       if (a[i])
+               i++;
+       a[i] = 0;
+}
+
+
+int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
+                               struct wpa_driver_scan_params *params)
+{
+       int ret;
+
+       wpa_supplicant_notify_scanning(wpa_s, 1);
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               ret = ieee80211_sta_req_scan(wpa_s, params);
+       else
+               ret = wpa_drv_scan(wpa_s, params);
+
+       if (ret) {
+               wpa_supplicant_notify_scanning(wpa_s, 0);
+               wpas_notify_scan_done(wpa_s, 0);
+       } else
+               wpa_s->scan_runs++;
+
+       return ret;
+}
+
+
+static struct wpa_driver_scan_filter *
+wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
+{
+       struct wpa_driver_scan_filter *ssids;
+       struct wpa_ssid *ssid;
+       size_t count;
+
+       *num_ssids = 0;
+       if (!conf->filter_ssids)
+               return NULL;
+
+       for (count = 0, ssid = conf->ssid; ssid; ssid = ssid->next) {
+               if (ssid->ssid && ssid->ssid_len)
+                       count++;
+       }
+       if (count == 0)
+               return NULL;
+       ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter));
+       if (ssids == NULL)
+               return NULL;
+
+       for (ssid = conf->ssid; ssid; ssid = ssid->next) {
+               if (!ssid->ssid || !ssid->ssid_len)
+                       continue;
+               os_memcpy(ssids[*num_ssids].ssid, ssid->ssid, ssid->ssid_len);
+               ssids[*num_ssids].ssid_len = ssid->ssid_len;
+               (*num_ssids)++;
+       }
+
+       return ssids;
+}
+
+
+static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wpa_ssid *ssid;
+       int scan_req = 0, ret;
+       struct wpabuf *wps_ie = NULL;
+#ifdef CONFIG_WPS
+       int wps = 0;
+       enum wps_request_type req_type = WPS_REQ_ENROLLEE_INFO;
+#endif /* CONFIG_WPS */
+       struct wpa_driver_scan_params params;
+       size_t max_ssids;
+       enum wpa_states prev_state;
+
+       if (wpa_s->disconnected && !wpa_s->scan_req) {
+               wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+               return;
+       }
+
+       if (!wpa_supplicant_enabled_networks(wpa_s->conf) &&
+           !wpa_s->scan_req) {
+               wpa_printf(MSG_DEBUG, "No enabled networks - do not scan");
+               wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+               return;
+       }
+
+       if (wpa_s->conf->ap_scan != 0 &&
+           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)) {
+               wpa_printf(MSG_DEBUG, "Using wired authentication - "
+                          "overriding ap_scan configuration");
+               wpa_s->conf->ap_scan = 0;
+               wpas_notify_ap_scan_changed(wpa_s);
+       }
+
+       if (wpa_s->conf->ap_scan == 0) {
+               wpa_supplicant_gen_assoc_event(wpa_s);
+               return;
+       }
+
+       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) ||
+           wpa_s->conf->ap_scan == 2)
+               max_ssids = 1;
+       else {
+               max_ssids = wpa_s->max_scan_ssids;
+               if (max_ssids > WPAS_MAX_SCAN_SSIDS)
+                       max_ssids = WPAS_MAX_SCAN_SSIDS;
+       }
+
+#ifdef CONFIG_WPS
+       wps = wpas_wps_in_use(wpa_s->conf, &req_type);
+#endif /* CONFIG_WPS */
+
+       scan_req = wpa_s->scan_req;
+       wpa_s->scan_req = 0;
+
+       os_memset(&params, 0, sizeof(params));
+
+       prev_state = wpa_s->wpa_state;
+       if (wpa_s->wpa_state == WPA_DISCONNECTED ||
+           wpa_s->wpa_state == WPA_INACTIVE)
+               wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
+
+       /* Find the starting point from which to continue scanning */
+       ssid = wpa_s->conf->ssid;
+       if (wpa_s->prev_scan_ssid != WILDCARD_SSID_SCAN) {
+               while (ssid) {
+                       if (ssid == wpa_s->prev_scan_ssid) {
+                               ssid = ssid->next;
+                               break;
+                       }
+                       ssid = ssid->next;
+               }
+       }
+
+       if (scan_req != 2 && (wpa_s->conf->ap_scan == 2 ||
+                             wpa_s->connect_without_scan)) {
+               wpa_s->connect_without_scan = 0;
+               wpa_supplicant_assoc_try(wpa_s, ssid);
+               return;
+       } else if (wpa_s->conf->ap_scan == 2) {
+               /*
+                * User-initiated scan request in ap_scan == 2; scan with
+                * wildcard SSID.
+                */
+               ssid = NULL;
+       } else {
+               struct wpa_ssid *start = ssid, *tssid;
+               int freqs_set = 0;
+               if (ssid == NULL && max_ssids > 1)
+                       ssid = wpa_s->conf->ssid;
+               while (ssid) {
+                       if (!ssid->disabled && ssid->scan_ssid) {
+                               wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+                                                 ssid->ssid, ssid->ssid_len);
+                               params.ssids[params.num_ssids].ssid =
+                                       ssid->ssid;
+                               params.ssids[params.num_ssids].ssid_len =
+                                       ssid->ssid_len;
+                               params.num_ssids++;
+                               if (params.num_ssids + 1 >= max_ssids)
+                                       break;
+                       }
+                       ssid = ssid->next;
+                       if (ssid == start)
+                               break;
+                       if (ssid == NULL && max_ssids > 1 &&
+                           start != wpa_s->conf->ssid)
+                               ssid = wpa_s->conf->ssid;
+               }
+
+               for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
+                       if (tssid->disabled)
+                               continue;
+                       if ((params.freqs || !freqs_set) && tssid->scan_freq) {
+                               int_array_concat(&params.freqs,
+                                                tssid->scan_freq);
+                       } else {
+                               os_free(params.freqs);
+                               params.freqs = NULL;
+                       }
+                       freqs_set = 1;
+               }
+               int_array_sort_unique(params.freqs);
+       }
+
+       if (ssid) {
+               wpa_s->prev_scan_ssid = ssid;
+               if (max_ssids > 1) {
+                       wpa_printf(MSG_DEBUG, "Include wildcard SSID in the "
+                                  "scan request");
+                       params.num_ssids++;
+               }
+               wpa_printf(MSG_DEBUG, "Starting AP scan for specific SSID(s)");
+       } else {
+               wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+               params.num_ssids++;
+               wpa_printf(MSG_DEBUG, "Starting AP scan for wildcard SSID");
+       }
+
+#ifdef CONFIG_WPS
+       if (params.freqs == NULL && wpa_s->after_wps && wpa_s->wps_freq) {
+               /*
+                * Optimize post-provisioning scan based on channel used
+                * during provisioning.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Scan only frequency %u MHz that "
+                          "was used during provisioning", wpa_s->wps_freq);
+               params.freqs = os_zalloc(2 * sizeof(int));
+               if (params.freqs)
+                       params.freqs[0] = wpa_s->wps_freq;
+               wpa_s->after_wps--;
+       }
+
+       if (wps) {
+               wps_ie = wps_build_probe_req_ie(wps == 2, &wpa_s->wps->dev,
+                                               wpa_s->wps->uuid, req_type);
+               if (wps_ie) {
+                       params.extra_ies = wpabuf_head(wps_ie);
+                       params.extra_ies_len = wpabuf_len(wps_ie);
+               }
+       }
+#endif /* CONFIG_WPS */
+
+       params.filter_ssids = wpa_supplicant_build_filter_ssids(
+               wpa_s->conf, &params.num_filter_ssids);
+
+       ret = wpa_supplicant_trigger_scan(wpa_s, &params);
+
+       wpabuf_free(wps_ie);
+       os_free(params.freqs);
+       os_free(params.filter_ssids);
+
+       if (ret) {
+               wpa_printf(MSG_WARNING, "Failed to initiate AP scan.");
+               if (prev_state != wpa_s->wpa_state)
+                       wpa_supplicant_set_state(wpa_s, prev_state);
+               wpa_supplicant_req_scan(wpa_s, 1, 0);
+       }
+}
+
+
+/**
+ * wpa_supplicant_req_scan - Schedule a scan for neighboring access points
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to scan
+ * @usec: Number of microseconds after which to scan
+ *
+ * This function is used to schedule a scan for neighboring access points after
+ * the specified time.
+ */
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
+{
+       /* If there's at least one network that should be specifically scanned
+        * then don't cancel the scan and reschedule.  Some drivers do
+        * background scanning which generates frequent scan results, and that
+        * causes the specific SSID scan to get continually pushed back and
+        * never happen, which causes hidden APs to never get probe-scanned.
+        */
+       if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) &&
+           wpa_s->conf->ap_scan == 1) {
+               struct wpa_ssid *ssid = wpa_s->conf->ssid;
+
+               while (ssid) {
+                       if (!ssid->disabled && ssid->scan_ssid)
+                               break;
+                       ssid = ssid->next;
+               }
+               if (ssid) {
+                       wpa_msg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
+                               "ensure that specific SSID scans occur");
+                       return;
+               }
+       }
+
+       wpa_msg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
+               sec, usec);
+       eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+       eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_scan - Cancel a scheduled scan request
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel a scan request scheduled with
+ * wpa_supplicant_req_scan().
+ */
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
+{
+       wpa_msg(wpa_s, MSG_DEBUG, "Cancelling scan request");
+       eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
+}
+
+
+void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
+                                   int scanning)
+{
+       if (wpa_s->scanning != scanning) {
+               wpa_s->scanning = scanning;
+               wpas_notify_scanning(wpa_s);
+       }
+}
+
+
+static int wpa_scan_get_max_rate(const struct wpa_scan_res *res)
+{
+       int rate = 0;
+       const u8 *ie;
+       int i;
+
+       ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES);
+       for (i = 0; ie && i < ie[1]; i++) {
+               if ((ie[i + 2] & 0x7f) > rate)
+                       rate = ie[i + 2] & 0x7f;
+       }
+
+       ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES);
+       for (i = 0; ie && i < ie[1]; i++) {
+               if ((ie[i + 2] & 0x7f) > rate)
+                       rate = ie[i + 2] & 0x7f;
+       }
+
+       return rate;
+}
+
+
+const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie)
+{
+       const u8 *end, *pos;
+
+       pos = (const u8 *) (res + 1);
+       end = pos + res->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
+                                 u32 vendor_type)
+{
+       const u8 *end, *pos;
+
+       pos = (const u8 *) (res + 1);
+       end = pos + res->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   vendor_type == WPA_GET_BE32(&pos[2]))
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
+                                            u32 vendor_type)
+{
+       struct wpabuf *buf;
+       const u8 *end, *pos;
+
+       buf = wpabuf_alloc(res->ie_len);
+       if (buf == NULL)
+               return NULL;
+
+       pos = (const u8 *) (res + 1);
+       end = pos + res->ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   vendor_type == WPA_GET_BE32(&pos[2]))
+                       wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4);
+               pos += 2 + pos[1];
+       }
+
+       if (wpabuf_len(buf) == 0) {
+               wpabuf_free(buf);
+               buf = NULL;
+       }
+
+       return buf;
+}
+
+
+/* Compare function for sorting scan results. Return >0 if @b is considered
+ * better. */
+static int wpa_scan_result_compar(const void *a, const void *b)
+{
+       struct wpa_scan_res **_wa = (void *) a;
+       struct wpa_scan_res **_wb = (void *) b;
+       struct wpa_scan_res *wa = *_wa;
+       struct wpa_scan_res *wb = *_wb;
+       int wpa_a, wpa_b, maxrate_a, maxrate_b;
+
+       /* WPA/WPA2 support preferred */
+       wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
+               wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL;
+       wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL ||
+               wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL;
+
+       if (wpa_b && !wpa_a)
+               return 1;
+       if (!wpa_b && wpa_a)
+               return -1;
+
+       /* privacy support preferred */
+       if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 &&
+           (wb->caps & IEEE80211_CAP_PRIVACY))
+               return 1;
+       if ((wa->caps & IEEE80211_CAP_PRIVACY) &&
+           (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
+               return -1;
+
+       /* best/max rate preferred if signal level close enough XXX */
+       if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) ||
+           (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
+               maxrate_a = wpa_scan_get_max_rate(wa);
+               maxrate_b = wpa_scan_get_max_rate(wb);
+               if (maxrate_a != maxrate_b)
+                       return maxrate_b - maxrate_a;
+       }
+
+       /* use freq for channel preference */
+
+       /* all things being equal, use signal level; if signal levels are
+        * identical, use quality values since some drivers may only report
+        * that value and leave the signal level zero */
+       if (wb->level == wa->level)
+               return wb->qual - wa->qual;
+       return wb->level - wa->level;
+}
+
+
+/**
+ * wpa_supplicant_get_scan_results - Get scan results
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @info: Information about what was scanned or %NULL if not available
+ * @new_scan: Whether a new scan was performed
+ * Returns: Scan results, %NULL on failure
+ *
+ * This function request the current scan results from the driver and updates
+ * the local BSS list wpa_s->bss. The caller is responsible for freeing the
+ * results with wpa_scan_results_free().
+ */
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+                               struct scan_info *info, int new_scan)
+{
+       struct wpa_scan_results *scan_res;
+       size_t i;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               scan_res = ieee80211_sta_get_scan_results(wpa_s);
+       else
+               scan_res = wpa_drv_get_scan_results2(wpa_s);
+       if (scan_res == NULL) {
+               wpa_printf(MSG_DEBUG, "Failed to get scan results");
+               return NULL;
+       }
+
+       qsort(scan_res->res, scan_res->num, sizeof(struct wpa_scan_res *),
+             wpa_scan_result_compar);
+
+       wpa_bss_update_start(wpa_s);
+       for (i = 0; i < scan_res->num; i++)
+               wpa_bss_update_scan_res(wpa_s, scan_res->res[i]);
+       wpa_bss_update_end(wpa_s, info, new_scan);
+
+       return scan_res;
+}
+
+
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_scan_results *scan_res;
+       scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+       if (scan_res == NULL)
+               return -1;
+       wpa_scan_results_free(scan_res);
+
+       return 0;
+}
+
+
+void wpa_scan_results_free(struct wpa_scan_results *res)
+{
+       size_t i;
+
+       if (res == NULL)
+               return;
+
+       for (i = 0; i < res->num; i++)
+               os_free(res->res[i]);
+       os_free(res->res);
+       os_free(res);
+}
diff --git a/wpa_supplicant/scan.h b/wpa_supplicant/scan.h
new file mode 100644 (file)
index 0000000..441fdbb
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * WPA Supplicant - Scanning
+ * Copyright (c) 2003-2010, 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 SCAN_H
+#define SCAN_H
+
+int wpa_supplicant_enabled_networks(struct wpa_config *conf);
+void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec);
+void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
+                                   int scanning);
+struct wpa_driver_scan_params;
+int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
+                               struct wpa_driver_scan_params *params);
+struct wpa_scan_results *
+wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
+                               struct scan_info *info, int new_scan);
+int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
+const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
+const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
+                                 u32 vendor_type);
+struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
+                                            u32 vendor_type);
+void wpa_scan_results_free(struct wpa_scan_results *res);
+
+#endif /* SCAN_H */
diff --git a/wpa_supplicant/sme.c b/wpa_supplicant/sme.c
new file mode 100644 (file)
index 0000000..5604e97
--- /dev/null
@@ -0,0 +1,490 @@
+/*
+ * wpa_supplicant - SME
+ * Copyright (c) 2009-2010, 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 "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "common/wpa_common.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "notify.h"
+#include "blacklist.h"
+#include "bss.h"
+#include "scan.h"
+#include "sme.h"
+
+void sme_authenticate(struct wpa_supplicant *wpa_s,
+                     struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+       struct wpa_driver_auth_params params;
+       struct wpa_ssid *old_ssid;
+#ifdef CONFIG_IEEE80211R
+       const u8 *ie;
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211R
+       const u8 *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+       int i, bssid_changed;
+
+       if (bss == NULL) {
+               wpa_printf(MSG_ERROR, "SME: No scan result available for the "
+                          "network");
+               return;
+       }
+
+       wpa_s->current_bss = bss;
+
+       os_memset(&params, 0, sizeof(params));
+       wpa_s->reassociate = 0;
+
+       params.freq = bss->freq;
+       params.bssid = bss->bssid;
+       params.ssid = bss->ssid;
+       params.ssid_len = bss->ssid_len;
+
+       if (wpa_s->sme.ssid_len != params.ssid_len ||
+           os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
+               wpa_s->sme.prev_bssid_set = 0;
+
+       wpa_s->sme.freq = params.freq;
+       os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
+       wpa_s->sme.ssid_len = params.ssid_len;
+
+       params.auth_alg = WPA_AUTH_ALG_OPEN;
+#ifdef IEEE8021X_EAPOL
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               if (ssid->leap) {
+                       if (ssid->non_leap == 0)
+                               params.auth_alg = WPA_AUTH_ALG_LEAP;
+                       else
+                               params.auth_alg |= WPA_AUTH_ALG_LEAP;
+               }
+       }
+#endif /* IEEE8021X_EAPOL */
+       wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
+                  params.auth_alg);
+       if (ssid->auth_alg) {
+               params.auth_alg = ssid->auth_alg;
+               wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
+                          params.auth_alg);
+       }
+
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (ssid->wep_key_len[i])
+                       params.wep_key[i] = ssid->wep_key[i];
+               params.wep_key_len[i] = ssid->wep_key_len[i];
+       }
+       params.wep_tx_keyidx = ssid->wep_tx_keyidx;
+
+       bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+       os_memset(wpa_s->bssid, 0, ETH_ALEN);
+       os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+       if (bssid_changed)
+               wpas_notify_bssid_changed(wpa_s);
+
+       if ((wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+            wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+           (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
+                              WPA_KEY_MGMT_FT_IEEE8021X |
+                              WPA_KEY_MGMT_FT_PSK |
+                              WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                              WPA_KEY_MGMT_PSK_SHA256))) {
+               int try_opportunistic;
+               try_opportunistic = ssid->proactive_key_caching &&
+                       (ssid->proto & WPA_PROTO_RSN);
+               if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+                                           wpa_s->current_ssid,
+                                           try_opportunistic) == 0)
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+               wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+               if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+                                             wpa_s->sme.assoc_req_ie,
+                                             &wpa_s->sme.assoc_req_ie_len)) {
+                       wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
+                                  "management and encryption suites");
+                       return;
+               }
+       } else if (ssid->key_mgmt &
+                  (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
+                   WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK |
+                   WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 |
+                   WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+               wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
+               if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+                                             wpa_s->sme.assoc_req_ie,
+                                             &wpa_s->sme.assoc_req_ie_len)) {
+                       wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
+                                  "management and encryption suites (no scan "
+                                  "results)");
+                       return;
+               }
+#ifdef CONFIG_WPS
+       } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+               struct wpabuf *wps_ie;
+               wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+               if (wps_ie && wpabuf_len(wps_ie) <=
+                   sizeof(wpa_s->sme.assoc_req_ie)) {
+                       wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
+                       os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
+                                 wpa_s->sme.assoc_req_ie_len);
+               } else
+                       wpa_s->sme.assoc_req_ie_len = 0;
+               wpabuf_free(wps_ie);
+               wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+#endif /* CONFIG_WPS */
+       } else {
+               wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+               wpa_s->sme.assoc_req_ie_len = 0;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+       if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+               md = ie + 2;
+       wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+       if (md) {
+               /* Prepare for the next transition */
+               wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+       }
+
+       if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK |
+                                   WPA_KEY_MGMT_FT_IEEE8021X)) {
+               if (wpa_s->sme.assoc_req_ie_len + 5 <
+                   sizeof(wpa_s->sme.assoc_req_ie)) {
+                       struct rsn_mdie *mdie;
+                       u8 *pos = wpa_s->sme.assoc_req_ie +
+                               wpa_s->sme.assoc_req_ie_len;
+                       *pos++ = WLAN_EID_MOBILITY_DOMAIN;
+                       *pos++ = sizeof(*mdie);
+                       mdie = (struct rsn_mdie *) pos;
+                       os_memcpy(mdie->mobility_domain, md,
+                                 MOBILITY_DOMAIN_ID_LEN);
+                       mdie->ft_capab = md[MOBILITY_DOMAIN_ID_LEN];
+                       wpa_s->sme.assoc_req_ie_len += 5;
+               }
+
+               if (wpa_s->sme.ft_used &&
+                   os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0 &&
+                   wpa_sm_has_ptk(wpa_s->wpa)) {
+                       wpa_printf(MSG_DEBUG, "SME: Trying to use FT "
+                                  "over-the-air");
+                       params.auth_alg = WPA_AUTH_ALG_FT;
+                       params.ie = wpa_s->sme.ft_ies;
+                       params.ie_len = wpa_s->sme.ft_ies_len;
+               }
+       }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+       wpa_s->sme.mfp = ssid->ieee80211w;
+       if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+               struct wpa_ie_data _ie;
+               if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
+                   _ie.capabilities &
+                   (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
+                       wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
+                                  "require MFP");
+                       wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
+               }
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       wpa_supplicant_cancel_scan(wpa_s);
+
+       wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR
+               " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
+               wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
+
+       wpa_clear_keys(wpa_s, bss->bssid);
+       wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = ssid;
+       wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+       wpa_supplicant_initiate_eapol(wpa_s);
+       if (old_ssid != wpa_s->current_ssid)
+               wpas_notify_network_changed(wpa_s);
+
+       wpa_s->sme.auth_alg = params.auth_alg;
+       if (wpa_drv_authenticate(wpa_s, &params) < 0) {
+               wpa_msg(wpa_s, MSG_INFO, "Authentication request to the "
+                       "driver failed");
+               wpa_supplicant_req_scan(wpa_s, 1, 0);
+               return;
+       }
+
+       /* TODO: add timeout on authentication */
+
+       /*
+        * Association will be started based on the authentication event from
+        * the driver.
+        */
+}
+
+
+void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
+                          "network is not selected");
+               return;
+       }
+
+       if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
+               wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
+                          "not in authenticating state");
+               return;
+       }
+
+       if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "SME: Ignore authentication with "
+                          "unexpected peer " MACSTR,
+                          MAC2STR(data->auth.peer));
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
+                  " auth_type=%d status_code=%d",
+                  MAC2STR(data->auth.peer), data->auth.auth_type,
+                  data->auth.status_code);
+       wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
+                   data->auth.ies, data->auth.ies_len);
+
+       if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "SME: Authentication failed (status "
+                          "code %d)", data->auth.status_code);
+
+               if (data->auth.status_code !=
+                   WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG ||
+                   wpa_s->sme.auth_alg == data->auth.auth_type ||
+                   wpa_s->current_ssid->auth_alg == WPA_AUTH_ALG_LEAP)
+                       return;
+
+               switch (data->auth.auth_type) {
+               case WLAN_AUTH_OPEN:
+                       wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
+
+                       wpa_printf(MSG_DEBUG, "SME: Trying SHARED auth");
+                       wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
+                                                wpa_s->current_ssid);
+                       return;
+
+               case WLAN_AUTH_SHARED_KEY:
+                       wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_LEAP;
+
+                       wpa_printf(MSG_DEBUG, "SME: Trying LEAP auth");
+                       wpa_supplicant_associate(wpa_s, wpa_s->current_bss,
+                                                wpa_s->current_ssid);
+                       return;
+
+               default:
+                       return;
+               }
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (data->auth.auth_type == WLAN_AUTH_FT) {
+               union wpa_event_data edata;
+               os_memset(&edata, 0, sizeof(edata));
+               edata.ft_ies.ies = data->auth.ies;
+               edata.ft_ies.ies_len = data->auth.ies_len;
+               os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN);
+               wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata);
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       sme_associate(wpa_s, ssid->mode, data->auth.peer,
+                     data->auth.auth_type);
+}
+
+
+void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
+                  const u8 *bssid, u16 auth_type)
+{
+       struct wpa_driver_associate_params params;
+       struct ieee802_11_elems elems;
+
+       os_memset(&params, 0, sizeof(params));
+       params.bssid = bssid;
+       params.ssid = wpa_s->sme.ssid;
+       params.ssid_len = wpa_s->sme.ssid_len;
+       params.freq = wpa_s->sme.freq;
+       params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
+               wpa_s->sme.assoc_req_ie : NULL;
+       params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
+#ifdef CONFIG_IEEE80211R
+       if (auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
+               params.wpa_ie = wpa_s->sme.ft_ies;
+               params.wpa_ie_len = wpa_s->sme.ft_ies_len;
+       }
+#endif /* CONFIG_IEEE80211R */
+       params.mode = mode;
+       params.mgmt_frame_protection = wpa_s->sme.mfp;
+       if (wpa_s->sme.prev_bssid_set)
+               params.prev_bssid = wpa_s->sme.prev_bssid;
+
+       wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+               " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
+               params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "",
+               params.freq);
+
+       wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
+
+       if (params.wpa_ie == NULL ||
+           ieee802_11_parse_elems(params.wpa_ie, params.wpa_ie_len, &elems, 0)
+           < 0) {
+               wpa_printf(MSG_DEBUG, "SME: Could not parse own IEs?!");
+               os_memset(&elems, 0, sizeof(elems));
+       }
+       if (elems.rsn_ie)
+               wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.rsn_ie - 2,
+                                       elems.rsn_ie_len + 2);
+       else if (elems.wpa_ie)
+               wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
+                                       elems.wpa_ie_len + 2);
+       else
+               wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+       if (wpa_drv_associate(wpa_s, &params) < 0) {
+               wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
+                       "failed");
+               wpa_supplicant_req_scan(wpa_s, 5, 0);
+               return;
+       }
+
+       /* TODO: add timeout on association */
+}
+
+
+int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+                     const u8 *ies, size_t ies_len)
+{
+       if (md == NULL || ies == NULL) {
+               wpa_printf(MSG_DEBUG, "SME: Remove mobility domain");
+               os_free(wpa_s->sme.ft_ies);
+               wpa_s->sme.ft_ies = NULL;
+               wpa_s->sme.ft_ies_len = 0;
+               wpa_s->sme.ft_used = 0;
+               return 0;
+       }
+
+       os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
+       wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
+       os_free(wpa_s->sme.ft_ies);
+       wpa_s->sme.ft_ies = os_malloc(ies_len);
+       if (wpa_s->sme.ft_ies == NULL)
+               return -1;
+       os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
+       wpa_s->sme.ft_ies_len = ies_len;
+       return 0;
+}
+
+
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+                           union wpa_event_data *data)
+{
+       int bssid_changed;
+       int timeout = 5000;
+
+       wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: "
+                  "status code %d", MAC2STR(wpa_s->pending_bssid),
+                  data->assoc_reject.status_code);
+
+       bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+
+       /*
+        * For now, unconditionally terminate the previous authentication. In
+        * theory, this should not be needed, but mac80211 gets quite confused
+        * if the authentication is left pending.. Some roaming cases might
+        * benefit from using the previous authentication, so this could be
+        * optimized in the future.
+        */
+       if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+                                  WLAN_REASON_DEAUTH_LEAVING) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Deauth request to the driver failed");
+       }
+       wpa_s->sme.prev_bssid_set = 0;
+
+       if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) {
+               struct wpa_blacklist *b;
+               b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid);
+               if (b && b->count < 3) {
+                       /*
+                        * Speed up next attempt if there could be other APs
+                        * that could accept association.
+                        */
+                       timeout = 100;
+               }
+       }
+       wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+       os_memset(wpa_s->bssid, 0, ETH_ALEN);
+       os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+       if (bssid_changed)
+               wpas_notify_bssid_changed(wpa_s);
+
+       /*
+        * TODO: if more than one possible AP is available in scan results,
+        * could try the other ones before requesting a new scan.
+        */
+       wpa_supplicant_req_scan(wpa_s, timeout / 1000,
+                               1000 * (timeout % 1000));
+}
+
+
+void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+                             union wpa_event_data *data)
+{
+       wpa_printf(MSG_DEBUG, "SME: Authentication timed out");
+       wpa_supplicant_req_scan(wpa_s, 5, 0);
+}
+
+
+void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+                              union wpa_event_data *data)
+{
+       wpa_printf(MSG_DEBUG, "SME: Association timed out");
+       wpa_supplicant_mark_disassoc(wpa_s);
+       wpa_supplicant_req_scan(wpa_s, 5, 0);
+}
+
+
+void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+                       union wpa_event_data *data)
+{
+       wpa_printf(MSG_DEBUG, "SME: Disassociation event received");
+       if (wpa_s->sme.prev_bssid_set &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) {
+               /*
+                * cfg80211/mac80211 can get into somewhat confused state if
+                * the AP only disassociates us and leaves us in authenticated
+                * state. For now, force the state to be cleared to avoid
+                * confusing errors if we try to associate with the AP again.
+                */
+               wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver "
+                          "state");
+               wpa_drv_deauthenticate(wpa_s, wpa_s->sme.prev_bssid,
+                                      WLAN_REASON_DEAUTH_LEAVING);
+       }
+}
diff --git a/wpa_supplicant/sme.h b/wpa_supplicant/sme.h
new file mode 100644 (file)
index 0000000..3ec8cc9
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * wpa_supplicant - SME
+ * Copyright (c) 2009-2010, 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 SME_H
+#define SME_H
+
+#ifdef CONFIG_SME
+
+void sme_authenticate(struct wpa_supplicant *wpa_s,
+                     struct wpa_bss *bss, struct wpa_ssid *ssid);
+void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
+                  const u8 *bssid, u16 auth_type);
+void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data);
+int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+                     const u8 *ies, size_t ies_len);
+void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+                           union wpa_event_data *data);
+void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+                             union wpa_event_data *data);
+void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+                              union wpa_event_data *data);
+void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+                       union wpa_event_data *data);
+
+#else /* CONFIG_SME */
+
+static inline void sme_authenticate(struct wpa_supplicant *wpa_s,
+                                   struct wpa_bss *bss,
+                                   struct wpa_ssid *ssid)
+{
+}
+
+static inline void sme_event_auth(struct wpa_supplicant *wpa_s,
+                                 union wpa_event_data *data)
+{
+}
+
+static inline int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
+                                   const u8 *ies, size_t ies_len)
+{
+       return -1;
+}
+
+
+static inline void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
+                                         union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
+                                           union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
+                                            union wpa_event_data *data)
+{
+}
+
+static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s,
+                                     union wpa_event_data *data)
+{
+}
+
+#endif /* CONFIG_SME */
+
+#endif /* SME_H */
diff --git a/wpa_supplicant/symbian/README.symbian b/wpa_supplicant/symbian/README.symbian
new file mode 100644 (file)
index 0000000..9d3b811
--- /dev/null
@@ -0,0 +1,24 @@
+wpa_supplicant for Symbian
+==========================
+
+Copyright (c) 2003-2007, 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.
+
+
+This directory includes project files for testing experimental Symbian
+(e.g., Nokia S60 3rd Ed) builds. The Symbian port is not really
+complete or expected to work, but these files can be used to verify
+that the build itself can be completed successfully.
+
+These files have been successfully tested with Nokia S60 3rd Edition
+MR SDK.
+
+Build files can be created and a phone release build can be run with
+following commands:
+
+bldmake bldfiles
+abld build gcce urel
diff --git a/wpa_supplicant/symbian/bld.inf b/wpa_supplicant/symbian/bld.inf
new file mode 100644 (file)
index 0000000..a1fc582
--- /dev/null
@@ -0,0 +1,8 @@
+PRJ_PLATFORMS
+WINSCW GCCE
+
+PRJ_EXPORTS
+
+PRJ_MMPFILES
+
+wpa_supplicant.mmp
diff --git a/wpa_supplicant/symbian/wpa_supplicant.mmp b/wpa_supplicant/symbian/wpa_supplicant.mmp
new file mode 100644 (file)
index 0000000..217908e
--- /dev/null
@@ -0,0 +1,38 @@
+TARGET         wpa_supplicant.exe
+UID            0x0 0x0
+VENDORID       0
+TARGETTYPE     exe
+
+SYSTEMINCLUDE \epoc32\include \epoc32\include\variant \epoc32\include\ecom \epoc32\include\libc
+
+USERINCLUDE    .. ..\..\src ..\..\src\utils
+
+SOURCEPATH     ..
+SOURCE         main_symbian.cpp
+SOURCE         config.c config_file.c
+SOURCE         eapol_sm.c
+SOURCE         wpa_supplicant.c events.c
+SOURCEPATH     ..\..\src\rsn_supp
+SOURCE         wpa.c preauth.c pmksa_cache.c peerkey.c wpa_ie.c
+SOURCEPATH     ..\..\src\drivers
+SOURCE         drivers.c
+SOURCEPATH     ..\..\src\common
+SOURCE         wpa_common.c
+SOURCEPATH     ..\..\src\utils
+SOURCE         os_none.c common.c wpa_debug.c eloop_none.c base64.c
+SOURCEPATH     ..\..\src\crypto
+SOURCE         sha1.c md5.c rc4.c des.c aes-cbc.c aes-ctr.c aes-eax.c aes-encblock.c aes-omac1.c aes-unwrap.c aes-wrap.c aes.c ms_funcs.c
+SOURCE         tls_internal.c crypto_internal.c
+SOURCEPATH     ..\..\src\tls
+SOURCE         asn1.c bignum.c rsa.c x509v3.c tlsv1_client.c tlsv1_common.c
+SOURCEPATH     ..\..\src\l2_packet
+SOURCE         l2_packet_none.c
+SOURCEPATH     ..\..\src\eap_peer
+SOURCE         eap.c eap_methods.c
+SOURCE         eap_md5.c eap_tls.c eap_mschapv2.c eap_peap.c eap_gtc.c
+SOURCE         eap_ttls.c eap_otp.c eap_leap.c eap_tls_common.c eap_tlv.c
+SOURCE         eap_fast.c eap_fast_pac.c
+SOURCEPATH     ..\..\src\eap_common
+SOURCE         eap_common.c
+
+LIBRARY                euser.lib estlib.lib
diff --git a/wpa_supplicant/tests/link_test.c b/wpa_supplicant/tests/link_test.c
new file mode 100644 (file)
index 0000000..3bfbed5
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Dummy functions to allow link_test to be linked. The need for these
+ * functions should be removed to allow IEEE 802.1X/EAPOL authenticator to
+ * be built outside hostapd.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+
+
+struct hostapd_data;
+struct sta_info;
+struct rsn_pmksa_cache_entry;
+struct eapol_state_machine;
+struct hostapd_eap_user;
+struct hostapd_bss_config;
+struct hostapd_vlan;
+
+
+struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
+{
+       return NULL;
+}
+
+
+int ap_for_each_sta(struct hostapd_data *hapd,
+                   int (*cb)(struct hostapd_data *hapd, struct sta_info *sta,
+                             void *ctx),
+                   void *ctx)
+{
+       return 0;
+}
+
+
+void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+                           u32 session_timeout)
+{
+}
+
+
+int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
+                    int old_vlanid)
+{
+       return 0;
+}
+
+
+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 accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
+{
+}
+
+
+void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
+                              struct eapol_state_machine *eapol)
+{
+}
+
+
+const struct hostapd_eap_user *
+hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity,
+                    size_t identity_len, int phase2)
+{
+       return NULL;
+}
+
+
+const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
+{
+       return NULL;
+}
diff --git a/wpa_supplicant/tests/test_eap_sim_common.c b/wpa_supplicant/tests/test_eap_sim_common.c
new file mode 100644 (file)
index 0000000..deb19f6
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Test program for EAP-SIM PRF
+ * Copyright (c) 2004-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.
+ */
+
+#include "eap_common/eap_sim_common.c"
+
+
+static int test_eap_sim_prf(void)
+{
+       /* http://csrc.nist.gov/encryption/dss/Examples-1024bit.pdf */
+       u8 xkey[] = {
+               0xbd, 0x02, 0x9b, 0xbe, 0x7f, 0x51, 0x96, 0x0b,
+               0xcf, 0x9e, 0xdb, 0x2b, 0x61, 0xf0, 0x6f, 0x0f,
+               0xeb, 0x5a, 0x38, 0xb6
+       };
+       u8 w[] = {
+               0x20, 0x70, 0xb3, 0x22, 0x3d, 0xba, 0x37, 0x2f,
+               0xde, 0x1c, 0x0f, 0xfc, 0x7b, 0x2e, 0x3b, 0x49,
+               0x8b, 0x26, 0x06, 0x14, 0x3c, 0x6c, 0x18, 0xba,
+               0xcb, 0x0f, 0x6c, 0x55, 0xba, 0xbb, 0x13, 0x78,
+               0x8e, 0x20, 0xd7, 0x37, 0xa3, 0x27, 0x51, 0x16
+       };
+       u8 buf[40];
+
+       printf("Testing EAP-SIM PRF (FIPS 186-2 + change notice 1)\n");
+       eap_sim_prf(xkey, buf, sizeof(buf));
+       if (memcmp(w, buf, sizeof(w) != 0)) {
+               printf("eap_sim_prf failed\n");
+               return 1;
+       }
+
+       return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+       int errors = 0;
+
+       errors += test_eap_sim_prf();
+
+       return errors;
+}
diff --git a/wpa_supplicant/tests/test_wpa.c b/wpa_supplicant/tests/test_wpa.c
new file mode 100644 (file)
index 0000000..7947137
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Test program for combined WPA authenticator/supplicant
+ * Copyright (c) 2006-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 "eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "../config.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_ie.h"
+#include "../hostapd/wpa.h"
+
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+
+
+struct wpa {
+       u8 auth_addr[ETH_ALEN];
+       u8 supp_addr[ETH_ALEN];
+       u8 psk[PMK_LEN];
+
+       /* from authenticator */
+       u8 auth_eapol_dst[ETH_ALEN];
+       u8 *auth_eapol;
+       size_t auth_eapol_len;
+
+       /* from supplicant */
+       u8 *supp_eapol;
+       size_t supp_eapol_len;
+
+       struct wpa_sm *supp;
+       struct wpa_authenticator *auth_group;
+       struct wpa_state_machine *auth;
+
+       struct wpa_ssid ssid;
+       u8 supp_ie[80];
+       size_t supp_ie_len;
+};
+
+
+static int supp_get_bssid(void *ctx, u8 *bssid)
+{
+       struct wpa *wpa = ctx;
+       wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+       os_memcpy(bssid, wpa->auth_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static void supp_set_state(void *ctx, enum wpa_states state)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state);
+}
+
+
+static void auth_eapol_rx(void *eloop_data, void *user_ctx)
+{
+       struct wpa *wpa = eloop_data;
+
+       wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame");
+       wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol,
+                   wpa->supp_eapol_len);
+}
+
+
+static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+                          size_t len)
+{
+       struct wpa *wpa = ctx;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
+                  "len=%lu)",
+                  __func__, MAC2STR(dest), proto, (unsigned long) len);
+
+       os_free(wpa->supp_eapol);
+       wpa->supp_eapol = os_malloc(len);
+       if (wpa->supp_eapol == NULL)
+               return -1;
+       os_memcpy(wpa->supp_eapol, buf, len);
+       wpa->supp_eapol_len = len;
+       eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
+
+       return 0;
+}
+
+
+static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
+                            u16 data_len, size_t *msg_len, void **data_pos)
+{
+       struct ieee802_1x_hdr *hdr;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
+                  __func__, type, data_len);
+
+       *msg_len = sizeof(*hdr) + data_len;
+       hdr = os_malloc(*msg_len);
+       if (hdr == NULL)
+               return NULL;
+
+       hdr->version = 2;
+       hdr->type = type;
+       hdr->length = host_to_be16(data_len);
+
+       if (data)
+               os_memcpy(hdr + 1, data, data_len);
+       else
+               os_memset(hdr + 1, 0, data_len);
+
+       if (data_pos)
+               *data_pos = hdr + 1;
+
+       return (u8 *) hdr;
+}
+
+
+static int supp_get_beacon_ie(void *ctx)
+{
+       struct wpa *wpa = ctx;
+       const u8 *ie;
+       size_t ielen;
+
+       wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+
+       ie = wpa_auth_get_wpa_ie(wpa->auth_group, &ielen);
+       if (ie == NULL || ielen < 1)
+               return -1;
+       if (ie[0] == WLAN_EID_RSN)
+               return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
+       return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
+}
+
+
+static int supp_set_key(void *ctx, enum wpa_alg alg,
+                       const u8 *addr, int key_idx, int set_tx,
+                       const u8 *seq, size_t seq_len,
+                       const u8 *key, size_t key_len)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
+                  "set_tx=%d)",
+                  __func__, alg, MAC2STR(addr), key_idx, set_tx);
+       wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
+       wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
+       return 0;
+}
+
+
+static int supp_mlme_setprotection(void *ctx, const u8 *addr,
+                                  int protection_type, int key_type)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
+                  "key_type=%d)",
+                  __func__, MAC2STR(addr), protection_type, key_type);
+       return 0;
+}
+
+
+static void supp_cancel_auth_timeout(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+}
+
+
+static int supp_init(struct wpa *wpa)
+{
+       struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return -1;
+
+       ctx->ctx = wpa;
+       ctx->msg_ctx = wpa;
+       ctx->set_state = supp_set_state;
+       ctx->get_bssid = supp_get_bssid;
+       ctx->ether_send = supp_ether_send;
+       ctx->get_beacon_ie = supp_get_beacon_ie;
+       ctx->alloc_eapol = supp_alloc_eapol;
+       ctx->set_key = supp_set_key;
+       ctx->mlme_setprotection = supp_mlme_setprotection;
+       ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+       wpa->supp = wpa_sm_init(ctx);
+       if (wpa->supp == NULL) {
+               wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
+               return -1;
+       }
+
+       wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr);
+       wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1);
+       wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+       wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
+       wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+       wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
+       wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN);
+
+       wpa->supp_ie_len = sizeof(wpa->supp_ie);
+       if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie,
+                                           &wpa->supp_ie_len) < 0) {
+               wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
+                          " failed");
+               return -1;
+       }
+
+       wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr);
+
+       return 0;
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+                       const char *txt)
+{
+       if (addr)
+               wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+                          MAC2STR(addr), txt);
+       else
+               wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static void supp_eapol_rx(void *eloop_data, void *user_ctx)
+{
+       struct wpa *wpa = eloop_data;
+
+       wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame");
+       wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol,
+                       wpa->auth_eapol_len);
+}
+
+
+static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
+                          size_t data_len, int encrypt)
+{
+       struct wpa *wpa = ctx;
+
+       wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
+                  "encrypt=%d)",
+                  __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
+
+       os_free(wpa->auth_eapol);
+       wpa->auth_eapol = os_malloc(data_len);
+       if (wpa->auth_eapol == NULL)
+               return -1;
+       os_memcpy(wpa->auth_eapol_dst, addr, ETH_ALEN);
+       os_memcpy(wpa->auth_eapol, data, data_len);
+       wpa->auth_eapol_len = data_len;
+       eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
+
+       return 0;
+}
+
+
+static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
+{
+       struct wpa *wpa = ctx;
+       wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+                  __func__, MAC2STR(addr), prev_psk);
+       if (prev_psk)
+               return NULL;
+       return wpa->psk;
+}
+
+
+static int auth_init_group(struct wpa *wpa)
+{
+       struct wpa_auth_config conf;
+       struct wpa_auth_callbacks cb;
+
+       wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+       os_memset(&conf, 0, sizeof(conf));
+       conf.wpa = 2;
+       conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+       conf.wpa_pairwise = WPA_CIPHER_CCMP;
+       conf.rsn_pairwise = WPA_CIPHER_CCMP;
+       conf.wpa_group = WPA_CIPHER_CCMP;
+       conf.eapol_version = 2;
+
+       os_memset(&cb, 0, sizeof(cb));
+       cb.ctx = wpa;
+       cb.logger = auth_logger;
+       cb.send_eapol = auth_send_eapol;
+       cb.get_psk = auth_get_psk;
+
+       wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &cb);
+       if (wpa->auth_group == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int auth_init(struct wpa *wpa)
+{
+       wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr);
+       if (wpa->auth == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
+               return -1;
+       }
+
+       if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, wpa->supp_ie,
+                               wpa->supp_ie_len, NULL, 0) != WPA_IE_OK) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+               return -1;
+       }
+
+       wpa_auth_sm_event(wpa->auth, WPA_ASSOC);
+
+       wpa_auth_sta_associated(wpa->auth_group, wpa->auth);
+
+       return 0;
+}
+
+
+static void deinit(struct wpa *wpa)
+{
+       wpa_auth_sta_deinit(wpa->auth);
+       wpa_sm_deinit(wpa->supp);
+       wpa_deinit(wpa->auth_group);
+       os_free(wpa->auth_eapol);
+       wpa->auth_eapol = NULL;
+       os_free(wpa->supp_eapol);
+       wpa->supp_eapol = NULL;
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct wpa wpa;
+
+       if (os_program_init())
+               return -1;
+
+       os_memset(&wpa, 0, sizeof(wpa));
+       os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
+       os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
+       os_memset(wpa.psk, 0x44, PMK_LEN);
+
+       wpa_debug_level = 0;
+       wpa_debug_show_keys = 1;
+
+       if (eloop_init()) {
+               wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+               return -1;
+       }
+
+       if (auth_init_group(&wpa) < 0)
+               return -1;
+
+       if (supp_init(&wpa) < 0)
+               return -1;
+
+       if (auth_init(&wpa) < 0)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "Starting eloop");
+       eloop_run();
+       wpa_printf(MSG_DEBUG, "eloop done");
+
+       deinit(&wpa);
+
+       eloop_destroy();
+
+       os_program_deinit();
+
+       return 0;
+}
diff --git a/wpa_supplicant/todo.txt b/wpa_supplicant/todo.txt
new file mode 100644 (file)
index 0000000..b84cccc
--- /dev/null
@@ -0,0 +1,85 @@
+To do:
+- add support for WPA with ap_scan=0 (update selected cipher etc. based on
+  AssocInfo; make sure these match with configuration)
+- consider closing smart card / PCSC connection when EAP-SIM/EAP-AKA
+  authentication has been completed (cache scard data based on serial#(?)
+  and try to optimize next connection if the same card is present for next
+  auth)
+- on disconnect event, could try to associate with another AP if one is
+  present in scan results; would need to update scan results periodically..
+- if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from
+  ssid->proto fields to avoid detecting downgrade attacks when the driver
+  is not reporting RSN IE, but msg 3/4 has one
+- Cisco AP and non-zero keyidx for unicast -> map to broadcast
+  (actually, this already works with driver_ndis; so maybe just change
+  driver_*.c to do the mapping for drivers that cannot handle non-zero keyidx
+  for unicast); worked also with Host AP driver and madwifi
+- IEEE 802.1X and key update with driver_ndis?? wpa_supplicant did not seem
+  to see unencrypted EAPOL-Key frames at all..
+- EAP-PAX with PAX_SEC
+- EAP (RFC 3748)
+  * OTP Extended Responses (Sect. 5.5)
+- test what happens if authenticator sends EAP-Success before real EAP
+  authentication ("canned" Success); this should be ignored based on
+  RFC 3748 Sect. 4.2
+- test compilation with gcc -W options (more warnings?)
+  (Done once; number of unused function arguments still present)
+- add proper support for using dot11RSNAConfigSATimeout
+- ctrl_iface: get/set/remove blob
+- use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and
+  web pages including the same information.. i.e., have this information only
+  in one page; how to build a PDF file with all the SGML included?
+- EAP-POTP/RSA SecurID profile (RFC 4793)
+- document wpa_gui build and consider adding it to 'make install'
+- test madwifi with pairwise=TKIP group=WEP104
+- consider merging hostapd and wpa_supplicant PMKSA cache implementations
+- consider redesigning pending EAP requests (identity/password/otp from
+  ctrl_iface) by moving the retrying of the previous request into EAP
+  state machine so that EAPOL state machine is not needed for this
+- rfc4284.txt (network selection for eap)
+- www pages about configuring wpa_supplicant:
+  * global options (ap_scan, ctrl_interfaces) based on OS/driver
+  * network block
+  * key_mgmt selection
+  * WPA parameters
+  * EAP options (one page for each method)
+  * "configuration wizard" (step 1: select OS, step 2: select driver, ...) to
+    generate example configuration
+- error path in rsn_preauth_init: should probably deinit l2_packet handlers
+  if something fails; does something else need deinit?
+- consider moving SIM card functionality (IMSI fetching) away from eap.c;
+  this should likely happen before EAP is initialized for authentication;
+  now IMSI is read only after receiving EAP-Identity/Request, but since it is
+  really needed for all cases, reading IMSI and generating Identity string
+  could very well be done before EAP has been started
+- try to work around race in receiving association event and first EAPOL
+  message
+- try to work around race in configuring PTK and sending msg 4/4 (some NDIS
+  drivers with ndiswrapper end up not being able to complete 4-way handshake
+  in some cases; extra delay before setting the key seems to help)
+- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to
+  clear memory; this would be used to clear temporary buffers containing
+  private data (e.g., keys); the macro can be defined to NOP in order to save
+  space (i.e., no code should depend on the macro doing something)
+- make sure that TLS session cache is not shared between EAP types or if it
+  is, that the cache entries are bound to only one EAP type; e.g., cache entry
+  created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS
+- consider moving eap_tls_build_ack() call into eap_tls_process_helper()
+  (it seems to be called always if helper returns 1)
+  * could need to modify eap_{ttls,peap,fast}_decrypt to do same
+- add support for fetching full user cert chain from Windows certificate
+  stores even when there are intermediate CA certs that are not in the
+  configured ca_cert store (e.g., ROOT) (they could be, e.g., in CA store)
+- clean up common.[ch]
+- change TLS/crypto library interface to use a structure of function
+  pointers and helper inline functions (like driver_ops) instead of
+  requiring every TLS wrapper to implement all functions
+- add support for encrypted configuration fields (e.g., password, psk,
+  passphrase, pin)
+- wpa_gui: add support for setting and showing priority
+- cleanup TLS/PEAP/TTLS/FAST fragmentation: both the handshake and Appl. Data
+  phases should be able to use the same functions for this;
+  the last step in processing sent should be this code and rest of the code
+  should not need to care about fragmentation at all
+- test EAP-FAST peer with OpenSSL and verify that fallback to full handshake
+  (ServerHello followed by something else than ChangeCipherSpec)
diff --git a/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj b/wpa_supplicant/vs2005/win_if_list/win_if_list.vcproj
new file mode 100755 (executable)
index 0000000..e79fc0f
--- /dev/null
@@ -0,0 +1,203 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="win_if_list"\r
+       ProjectGUID="{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}"\r
+       RootNamespace="win_if_list"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wpcap.lib"\r
+                               LinkIncremental="2"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..\..\src\utils;C:\dev\WpdPack\include"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wpcap.lib"\r
+                               LinkIncremental="1"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\win_if_list.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/wpa_supplicant/vs2005/wpa_supplicant.sln b/wpa_supplicant/vs2005/wpa_supplicant.sln
new file mode 100755 (executable)
index 0000000..df89e31
--- /dev/null
@@ -0,0 +1,52 @@
+\r
+Microsoft Visual Studio Solution File, Format Version 9.00\r
+# Visual Studio 2005\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_supplicant", "wpa_supplicant\wpa_supplicant.vcproj", "{8BCFDA77-AEDC-4168-8897-5B73105BBB87}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_cli", "wpa_cli\wpa_cli.vcproj", "{E3A7B181-22CC-4DA3-8410-6AD69879A9EC}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpasvc", "wpasvc\wpasvc.vcproj", "{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "wpa_passphrase", "wpa_passphrase\wpa_passphrase.vcproj", "{ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "win_if_list", "win_if_list\win_if_list.vcproj", "{9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}"\r
+EndProject\r
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "eapol_test", "eapol_test\eapol_test.vcproj", "{0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}"\r
+EndProject\r
+Global\r
+       GlobalSection(DPCodeReviewSolutionGUID) = preSolution\r
+               DPCodeReviewSolutionGUID = {00000000-0000-0000-0000-000000000000}\r
+       EndGlobalSection\r
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+               Debug|Win32 = Debug|Win32\r
+               Release|Win32 = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+               {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Debug|Win32.Build.0 = Debug|Win32\r
+               {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.ActiveCfg = Release|Win32\r
+               {8BCFDA77-AEDC-4168-8897-5B73105BBB87}.Release|Win32.Build.0 = Release|Win32\r
+               {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Debug|Win32.Build.0 = Debug|Win32\r
+               {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.ActiveCfg = Release|Win32\r
+               {E3A7B181-22CC-4DA3-8410-6AD69879A9EC}.Release|Win32.Build.0 = Release|Win32\r
+               {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Debug|Win32.Build.0 = Debug|Win32\r
+               {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.ActiveCfg = Release|Win32\r
+               {E2A4A85F-CA77-406D-8ABF-63EF94545ACC}.Release|Win32.Build.0 = Release|Win32\r
+               {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Debug|Win32.Build.0 = Debug|Win32\r
+               {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.ActiveCfg = Release|Win32\r
+               {ADBE4EA8-F0C5-40C2-AE89-C56D0F2EC1DF}.Release|Win32.Build.0 = Release|Win32\r
+               {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Debug|Win32.Build.0 = Debug|Win32\r
+               {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.ActiveCfg = Release|Win32\r
+               {9E87CD9C-60CE-4533-85CF-85CA3A9BF26A}.Release|Win32.Build.0 = Release|Win32\r
+               {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.ActiveCfg = Debug|Win32\r
+               {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Debug|Win32.Build.0 = Debug|Win32\r
+               {0E3F2C6D-1372-48D6-BCAB-E584917C4DE3}.Release|Win32.ActiveCfg = Release|Win32\r
+       EndGlobalSection\r
+       GlobalSection(SolutionProperties) = preSolution\r
+               HideSolutionNode = FALSE\r
+       EndGlobalSection\r
+EndGlobal\r
diff --git a/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj b/wpa_supplicant/vs2005/wpasvc/wpasvc.vcproj
new file mode 100755 (executable)
index 0000000..4d402e5
--- /dev/null
@@ -0,0 +1,453 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="wpasvc"\r
+       ProjectGUID="{E2A4A85F-CA77-406D-8ABF-63EF94545ACC}"\r
+       RootNamespace="wpasvc"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                               DisableSpecificWarnings="4244;4267;4311"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+                               LinkIncremental="2"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="1"\r
+                       CharacterSet="0"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="..\..;..\..\..\src;..\..\..\src\utils;C:\dev\WpdPack\include;C:\dev\openssl\include"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;CONFIG_WIN32_DEFAULTS"\r
+                               RuntimeLibrary="2"\r
+                               UsePrecompiledHeader="0"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                               DisableSpecificWarnings="4244;4267;4311"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="wbemuuid.lib ws2_32.lib Crypt32.lib Winscard.lib Packet.lib wpcap.lib libeay32MT.lib ssleay32Mt.lib"\r
+                               LinkIncremental="1"\r
+                               AdditionalLibraryDirectories="C:\dev\WpdPack\lib;C:\dev\openssl\lib"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="1"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <Filter\r
+                       Name="Source Files"\r
+                       Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"\r
+                       UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"\r
+                       >\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-cbc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-ctr.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-eax.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-encblock.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-omac1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-unwrap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\aes-wrap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\base64.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\blacklist.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\bss.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\chap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\config.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\config_winreg.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\crypto_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ctrl_iface.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\ctrl_iface_named_pipe.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\driver_ndis.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\driver_ndis_.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\drivers.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_gtc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_leap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_methods.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_mschapv2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_otp.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_peap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_common\eap_peap_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\eap_register.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tls.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tls_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_tnc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\eap_ttls.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eapol_supp\eapol_supp_sm.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\eloop_win.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\events.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\l2_packet\l2_packet_winpcap.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\main_winsvc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\md5.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\ms_funcs.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\mschapv2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\drivers\ndis_events.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\notify.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\os_win32.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\pcsc_funcs.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\peerkey.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\pmksa_cache.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\preauth.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\scan.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-pbkdf2.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\sha1-tlsprf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\crypto\tls_openssl.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\eap_peer\tncc.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\wpa.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\common\wpa_common.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\wpa_debug.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\rsn_supp\wpa_ie.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpa_supplicant.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\..\src\utils\wpabuf.c"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\wpas_glue.c"\r
+                               >\r
+                       </File>\r
+               </Filter>\r
+               <Filter\r
+                       Name="Header Files"\r
+                       Filter="h;hpp;hxx;hm;inl;inc;xsd"\r
+                       UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"\r
+                       >\r
+               </Filter>\r
+               <Filter\r
+                       Name="Resource Files"\r
+                       Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"\r
+                       UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"\r
+                       >\r
+               </Filter>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/wpa_supplicant/win_example.reg b/wpa_supplicant/win_example.reg
new file mode 100755 (executable)
index 0000000..875d4ef
--- /dev/null
@@ -0,0 +1,42 @@
+REGEDIT4\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant]\r
+"debug_level"=dword:00000000\r
+"debug_show_keys"=dword:00000001\r
+"debug_timestamp"=dword:00000000\r
+"debug_use_file"=dword:00000000\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs]\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test]\r
+"ap_scan"=dword:00000002\r
+"update_config"=dword:00000001\r
+"uuid"="12345678-9abc-def0-1234-56789abcdef0"\r
+"device_name"="Wireless Client"\r
+"manufacturer"="Company"\r
+"model_name"="cmodel"\r
+"serial_number"="12345"\r
+"device_type"="1-0050F204-1"\r
+"os_version"="01020300"\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\blobs]\r
+"testblob"=hex:01,02,03,04,05\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks]\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\configs\test\networks\0000]\r
+"ssid"="\"example network\""\r
+"key_mgmt"="WPA-PSK"\r
+"psk"="\"secret password\""\r
+"pairwise"="CCMP"\r
+"group"="CCMP"\r
+"proto"="WPA"\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces]\r
+\r
+[HKEY_LOCAL_MACHINE\SOFTWARE\wpa_supplicant\interfaces\0000]\r
+"adapter"="{A7627643-C310-49E5-BD89-7E77709C04AB}"\r
+"config"="test"\r
+"ctrl_interface"=""\r
+"skip_on_error"=dword:00000000\r
+\r
diff --git a/wpa_supplicant/win_if_list.c b/wpa_supplicant/win_if_list.c
new file mode 100644 (file)
index 0000000..0e1532e
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * win_if_list - Display network interfaces with description (for Windows)
+ * Copyright (c) 2004-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.
+ *
+ * This small tool is for the Windows build to provide an easy way of fetching
+ * a list of available network interfaces.
+ */
+
+#include "includes.h"
+#include <stdio.h>
+#ifdef CONFIG_USE_NDISUIO
+#include <winsock2.h>
+#include <ntddndis.h>
+#else /* CONFIG_USE_NDISUIO */
+#include "pcap.h"
+#include <winsock.h>
+#endif /* CONFIG_USE_NDISUIO */
+
+#ifdef CONFIG_USE_NDISUIO
+
+/* from nuiouser.h */
+#define FSCTL_NDISUIO_BASE      FILE_DEVICE_NETWORK
+
+#define _NDISUIO_CTL_CODE(_Function, _Method, _Access) \
+       CTL_CODE(FSCTL_NDISUIO_BASE, _Function, _Method, _Access)
+
+#define IOCTL_NDISUIO_QUERY_BINDING \
+       _NDISUIO_CTL_CODE(0x203, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+#define IOCTL_NDISUIO_BIND_WAIT \
+       _NDISUIO_CTL_CODE(0x204, METHOD_BUFFERED, \
+                         FILE_READ_ACCESS | FILE_WRITE_ACCESS)
+
+typedef struct _NDISUIO_QUERY_BINDING
+{
+       ULONG BindingIndex;
+       ULONG DeviceNameOffset;
+       ULONG DeviceNameLength;
+       ULONG DeviceDescrOffset;
+       ULONG DeviceDescrLength;
+} NDISUIO_QUERY_BINDING, *PNDISUIO_QUERY_BINDING;
+
+
+static HANDLE ndisuio_open(void)
+{
+       DWORD written;
+       HANDLE h;
+
+       h = CreateFile(TEXT("\\\\.\\\\Ndisuio"),
+                      GENERIC_READ | GENERIC_WRITE, 0, NULL,
+                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+                      INVALID_HANDLE_VALUE);
+       if (h == INVALID_HANDLE_VALUE)
+               return h;
+
+#ifndef _WIN32_WCE
+       if (!DeviceIoControl(h, IOCTL_NDISUIO_BIND_WAIT, NULL, 0, NULL, 0,
+                            &written, NULL)) {
+               printf("IOCTL_NDISUIO_BIND_WAIT failed: %d",
+                      (int) GetLastError());
+               CloseHandle(h);
+               return INVALID_HANDLE_VALUE;
+       }
+#endif /* _WIN32_WCE */
+
+       return h;
+}
+
+
+static void ndisuio_query_bindings(HANDLE ndisuio)
+{
+       NDISUIO_QUERY_BINDING *b;
+       size_t blen = sizeof(*b) + 1024;
+       int i, error;
+       DWORD written;
+       char name[256], desc[256];
+       WCHAR *pos;
+       size_t j, len;
+
+       b = malloc(blen);
+       if (b == NULL)
+               return;
+
+       for (i = 0; ; i++) {
+               memset(b, 0, blen);
+               b->BindingIndex = i;
+               if (!DeviceIoControl(ndisuio, IOCTL_NDISUIO_QUERY_BINDING,
+                                    b, sizeof(NDISUIO_QUERY_BINDING), b,
+                                    (DWORD) blen, &written, NULL)) {
+                       error = (int) GetLastError();
+                       if (error == ERROR_NO_MORE_ITEMS)
+                               break;
+                       printf("IOCTL_NDISUIO_QUERY_BINDING failed: %d",
+                              error);
+                       break;
+               }
+
+               pos = (WCHAR *) ((char *) b + b->DeviceNameOffset);
+               len = b->DeviceNameLength;
+               if (len >= sizeof(name))
+                       len = sizeof(name) - 1;
+               for (j = 0; j < len; j++)
+                       name[j] = (char) pos[j];
+               name[len] = '\0';
+
+               pos = (WCHAR *) ((char *) b + b->DeviceDescrOffset);
+               len = b->DeviceDescrLength;
+               if (len >= sizeof(desc))
+                       len = sizeof(desc) - 1;
+               for (j = 0; j < len; j++)
+                       desc[j] = (char) pos[j];
+               desc[len] = '\0';
+
+               printf("ifname: %s\ndescription: %s\n\n", name, desc);
+       }
+
+       free(b);
+}
+
+
+static void ndisuio_enum_bindings(void)
+{
+       HANDLE ndisuio = ndisuio_open();
+       if (ndisuio == INVALID_HANDLE_VALUE)
+               return;
+
+       ndisuio_query_bindings(ndisuio);
+       CloseHandle(ndisuio);
+}
+
+#else /* CONFIG_USE_NDISUIO */
+
+static void show_dev(pcap_if_t *dev)
+{
+       printf("ifname: %s\ndescription: %s\n\n",
+              dev->name, dev->description);
+}
+
+
+static void pcap_enum_devs(void)
+{
+       pcap_if_t *devs, *dev;
+       char err[PCAP_ERRBUF_SIZE + 1];
+
+       if (pcap_findalldevs(&devs, err) < 0) {
+               fprintf(stderr, "Error - pcap_findalldevs: %s\n", err);
+               return;
+       }
+
+       for (dev = devs; dev; dev = dev->next) {
+               show_dev(dev);
+       }
+
+       pcap_freealldevs(devs);
+}
+
+#endif /* CONFIG_USE_NDISUIO */
+
+
+int main(int argc, char *argv[])
+{
+#ifdef CONFIG_USE_NDISUIO
+       ndisuio_enum_bindings();
+#else /* CONFIG_USE_NDISUIO */
+       pcap_enum_devs();
+#endif /* CONFIG_USE_NDISUIO */
+
+       return 0;
+}
diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c
new file mode 100644 (file)
index 0000000..162a0b8
--- /dev/null
@@ -0,0 +1,2407 @@
+/*
+ * WPA Supplicant - command line interface for wpa_supplicant daemon
+ * Copyright (c) 2004-2010, 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_CTRL_IFACE
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+#include <dirent.h>
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_READLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif /* CONFIG_READLINE */
+#ifdef CONFIG_WPA_CLI_FORK
+#include <sys/wait.h>
+#endif /* CONFIG_WPA_CLI_FORK */
+
+#include "common/wpa_ctrl.h"
+#include "common.h"
+#include "common/version.h"
+
+
+static const char *wpa_cli_version =
+"wpa_cli v" VERSION_STR "\n"
+"Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> and contributors";
+
+
+static const char *wpa_cli_license =
+"This program is free software. You can distribute it and/or modify it\n"
+"under the terms of the GNU General Public License version 2.\n"
+"\n"
+"Alternatively, this software may be distributed under the terms of the\n"
+"BSD license. See README and COPYING for more details.\n";
+
+static const char *wpa_cli_full_license =
+"This program is free software; you can redistribute it and/or modify\n"
+"it under the terms of the GNU General Public License version 2 as\n"
+"published by the Free Software Foundation.\n"
+"\n"
+"This program is distributed in the hope that it will be useful,\n"
+"but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
+"MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n"
+"GNU General Public License for more details.\n"
+"\n"
+"You should have received a copy of the GNU General Public License\n"
+"along with this program; if not, write to the Free Software\n"
+"Foundation, Inc., 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 struct wpa_ctrl *ctrl_conn;
+static struct wpa_ctrl *mon_conn;
+#ifdef CONFIG_WPA_CLI_FORK
+static pid_t mon_pid = 0;
+#endif /* CONFIG_WPA_CLI_FORK */
+static int wpa_cli_quit = 0;
+static int wpa_cli_attached = 0;
+static int wpa_cli_connected = 0;
+static int wpa_cli_last_id = 0;
+static const char *ctrl_iface_dir = "/var/run/wpa_supplicant";
+static char *ctrl_ifname = NULL;
+static const char *pid_file = NULL;
+static const char *action_file = NULL;
+static int ping_interval = 5;
+static int interactive = 0;
+
+
+static void print_help();
+
+
+static void usage(void)
+{
+       printf("wpa_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hvB] "
+              "[-a<action file>] \\\n"
+              "        [-P<pid file>] [-g<global ctrl>] [-G<ping interval>]  "
+              "[command..]\n"
+              "  -h = help (show this usage text)\n"
+              "  -v = shown version information\n"
+              "  -a = run in daemon mode executing the action file based on "
+              "events from\n"
+              "       wpa_supplicant\n"
+              "  -B = run a daemon in the background\n"
+              "  default path: /var/run/wpa_supplicant\n"
+              "  default interface: first interface found in socket path\n");
+       print_help();
+}
+
+
+#ifdef CONFIG_WPA_CLI_FORK
+static int in_query = 0;
+
+static void wpa_cli_monitor_sig(int sig)
+{
+       if (sig == SIGUSR1)
+               in_query = 1;
+       else if (sig == SIGUSR2)
+               in_query = 0;
+}
+
+static void wpa_cli_monitor(void)
+{
+       char buf[256];
+       size_t len = sizeof(buf) - 1;
+       struct timeval tv;
+       fd_set rfds;
+
+       signal(SIGUSR1, wpa_cli_monitor_sig);
+       signal(SIGUSR2, wpa_cli_monitor_sig);
+
+       while (mon_conn) {
+               int s = wpa_ctrl_get_fd(mon_conn);
+               tv.tv_sec = 5;
+               tv.tv_usec = 0;
+               FD_ZERO(&rfds);
+               FD_SET(s, &rfds);
+               if (select(s + 1, &rfds, NULL, NULL, &tv) < 0) {
+                       if (errno == EINTR)
+                               continue;
+                       perror("select");
+                       break;
+               }
+               if (mon_conn == NULL)
+                       break;
+               if (FD_ISSET(s, &rfds)) {
+                       len = sizeof(buf) - 1;
+                       int res = wpa_ctrl_recv(mon_conn, buf, &len);
+                       if (res < 0) {
+                               perror("wpa_ctrl_recv");
+                               break;
+                       }
+                       buf[len] = '\0';
+                       if (in_query)
+                               printf("\r");
+                       printf("%s\n", buf);
+                       kill(getppid(), SIGUSR1);
+               }
+       }
+}
+#endif /* CONFIG_WPA_CLI_FORK */
+
+
+static int wpa_cli_open_connection(const char *ifname, int attach)
+{
+#if defined(CONFIG_CTRL_IFACE_UDP) || defined(CONFIG_CTRL_IFACE_NAMED_PIPE)
+       ctrl_conn = wpa_ctrl_open(ifname);
+       if (ctrl_conn == NULL)
+               return -1;
+
+       if (attach && interactive)
+               mon_conn = wpa_ctrl_open(ifname);
+       else
+               mon_conn = NULL;
+#else /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
+       char *cfile;
+       int flen, res;
+
+       if (ifname == NULL)
+               return -1;
+
+       flen = os_strlen(ctrl_iface_dir) + os_strlen(ifname) + 2;
+       cfile = os_malloc(flen);
+       if (cfile == NULL)
+               return -1L;
+       res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname);
+       if (res < 0 || res >= flen) {
+               os_free(cfile);
+               return -1;
+       }
+
+       ctrl_conn = wpa_ctrl_open(cfile);
+       if (ctrl_conn == NULL) {
+               os_free(cfile);
+               return -1;
+       }
+
+       if (attach && interactive)
+               mon_conn = wpa_ctrl_open(cfile);
+       else
+               mon_conn = NULL;
+       os_free(cfile);
+#endif /* CONFIG_CTRL_IFACE_UDP || CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+       if (mon_conn) {
+               if (wpa_ctrl_attach(mon_conn) == 0) {
+                       wpa_cli_attached = 1;
+               } else {
+                       printf("Warning: Failed to attach to "
+                              "wpa_supplicant.\n");
+                       return -1;
+               }
+
+#ifdef CONFIG_WPA_CLI_FORK
+               {
+                       pid_t p = fork();
+                       if (p < 0) {
+                               perror("fork");
+                               return -1;
+                       }
+                       if (p == 0) {
+                               wpa_cli_monitor();
+                               exit(0);
+                       } else
+                               mon_pid = p;
+               }
+#endif /* CONFIG_WPA_CLI_FORK */
+       }
+
+       return 0;
+}
+
+
+static void wpa_cli_close_connection(void)
+{
+       if (ctrl_conn == NULL)
+               return;
+
+#ifdef CONFIG_WPA_CLI_FORK
+       if (mon_pid) {
+               int status;
+               kill(mon_pid, SIGPIPE);
+               wait(&status);
+               mon_pid = 0;
+       }
+#endif /* CONFIG_WPA_CLI_FORK */
+
+       if (wpa_cli_attached) {
+               wpa_ctrl_detach(interactive ? mon_conn : ctrl_conn);
+               wpa_cli_attached = 0;
+       }
+       wpa_ctrl_close(ctrl_conn);
+       ctrl_conn = NULL;
+       if (mon_conn) {
+               wpa_ctrl_close(mon_conn);
+               mon_conn = NULL;
+       }
+}
+
+
+static void wpa_cli_msg_cb(char *msg, size_t len)
+{
+       printf("%s\n", msg);
+}
+
+
+static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print)
+{
+       char buf[2048];
+       size_t len;
+       int ret;
+
+       if (ctrl_conn == NULL) {
+               printf("Not connected to wpa_supplicant - command dropped.\n");
+               return -1;
+       }
+       len = sizeof(buf) - 1;
+       ret = wpa_ctrl_request(ctrl, cmd, os_strlen(cmd), buf, &len,
+                              wpa_cli_msg_cb);
+       if (ret == -2) {
+               printf("'%s' command timed out.\n", cmd);
+               return -2;
+       } else if (ret < 0) {
+               printf("'%s' command failed.\n", cmd);
+               return -1;
+       }
+       if (print) {
+               buf[len] = '\0';
+               printf("%s", buf);
+       }
+       return 0;
+}
+
+
+static int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd)
+{
+       return _wpa_ctrl_command(ctrl, cmd, 1);
+}
+
+
+static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       int verbose = argc > 0 && os_strcmp(argv[0], "verbose") == 0;
+       return wpa_ctrl_command(ctrl, verbose ? "STATUS-VERBOSE" : "STATUS");
+}
+
+
+static int wpa_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "PING");
+}
+
+
+static int wpa_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "MIB");
+}
+
+
+static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "PMKSA");
+}
+
+
+static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       print_help();
+       return 0;
+}
+
+
+static int wpa_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       printf("%s\n\n%s\n", wpa_cli_version, wpa_cli_full_license);
+       return 0;
+}
+
+
+static int wpa_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       wpa_cli_quit = 1;
+       return 0;
+}
+
+
+static void wpa_cli_show_variables(void)
+{
+       printf("set variables:\n"
+              "  EAPOL::heldPeriod (EAPOL state machine held period, "
+              "in seconds)\n"
+              "  EAPOL::authPeriod (EAPOL state machine authentication "
+              "period, in seconds)\n"
+              "  EAPOL::startPeriod (EAPOL state machine start period, in "
+              "seconds)\n"
+              "  EAPOL::maxStart (EAPOL state machine maximum start "
+              "attempts)\n");
+       printf("  dot11RSNAConfigPMKLifetime (WPA/WPA2 PMK lifetime in "
+              "seconds)\n"
+              "  dot11RSNAConfigPMKReauthThreshold (WPA/WPA2 reauthentication"
+              " threshold\n\tpercentage)\n"
+              "  dot11RSNAConfigSATimeout (WPA/WPA2 timeout for completing "
+              "security\n\tassociation in seconds)\n");
+}
+
+
+static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc == 0) {
+               wpa_cli_show_variables();
+               return 0;
+       }
+
+       if (argc != 2) {
+               printf("Invalid SET command: needs two arguments (variable "
+                      "name and value)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long SET command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_logoff(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "LOGOFF");
+}
+
+
+static int wpa_cli_cmd_logon(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "LOGON");
+}
+
+
+static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "REASSOCIATE");
+}
+
+
+static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid PREAUTH command: needs one argument "
+                      "(BSSID)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "PREAUTH %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long PREAUTH command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_ap_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid AP_SCAN command: needs one argument (ap_scan "
+                      "value)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "AP_SCAN %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long AP_SCAN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_stkstart(struct wpa_ctrl *ctrl, int argc,
+                               char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid STKSTART command: needs one argument "
+                      "(Peer STA MAC address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "STKSTART %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long STKSTART command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_ft_ds(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid FT_DS command: needs one argument "
+                      "(Target AP MAC address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "FT_DS %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long FT_DS command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc == 0) {
+               /* Any BSSID */
+               return wpa_ctrl_command(ctrl, "WPS_PBC");
+       }
+
+       /* Specific BSSID */
+       res = os_snprintf(cmd, sizeof(cmd), "WPS_PBC %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_PBC command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc == 0) {
+               printf("Invalid WPS_PIN command: need one or two arguments:\n"
+                      "- BSSID: use 'any' to select any\n"
+                      "- PIN: optional, used only with devices that have no "
+                      "display\n");
+               return -1;
+       }
+
+       if (argc == 1) {
+               /* Use dynamically generated PIN (returned as reply) */
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s", argv[0]);
+               if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+                       printf("Too long WPS_PIN command.\n");
+                       return -1;
+               }
+               return wpa_ctrl_command(ctrl, cmd);
+       }
+
+       /* Use hardcoded PIN from a label */
+       res = os_snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s", argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_PIN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+#ifdef CONFIG_WPS_OOB
+static int wpa_cli_cmd_wps_oob(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 3 && argc != 4) {
+               printf("Invalid WPS_OOB command: need three or four "
+                      "arguments:\n"
+                      "- DEV_TYPE: use 'ufd' or 'nfc'\n"
+                      "- PATH: path of OOB device like '/mnt'\n"
+                      "- METHOD: OOB method 'pin-e' or 'pin-r', "
+                      "'cred'\n"
+                      "- DEV_NAME: (only for NFC) device name like "
+                      "'pn531'\n");
+               return -1;
+       }
+
+       if (argc == 3)
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       else
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_OOB %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_OOB command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc == 2)
+               res = os_snprintf(cmd, sizeof(cmd), "WPS_REG %s %s",
+                                 argv[0], argv[1]);
+       else if (argc == 6) {
+               char ssid_hex[2 * 32 + 1];
+               char key_hex[2 * 64 + 1];
+               int i;
+
+               ssid_hex[0] = '\0';
+               for (i = 0; i < 32; i++) {
+                       if (argv[2][i] == '\0')
+                               break;
+                       os_snprintf(&ssid_hex[i * 2], 3, "%02x", argv[2][i]);
+               }
+
+               key_hex[0] = '\0';
+               for (i = 0; i < 64; i++) {
+                       if (argv[5][i] == '\0')
+                               break;
+                       os_snprintf(&key_hex[i * 2], 3, "%02x", argv[5][i]);
+               }
+
+               res = os_snprintf(cmd, sizeof(cmd),
+                                 "WPS_REG %s %s %s %s %s %s",
+                                 argv[0], argv[1], ssid_hex, argv[3], argv[4],
+                                 key_hex);
+       } else {
+               printf("Invalid WPS_REG command: need two arguments:\n"
+                      "- BSSID: use 'any' to select any\n"
+                      "- AP PIN\n");
+               printf("Alternatively, six arguments can be used to "
+                      "reconfigure the AP:\n"
+                      "- BSSID: use 'any' to select any\n"
+                      "- AP PIN\n"
+                      "- new SSID\n"
+                      "- new auth (OPEN, WPAPSK, WPA2PSK)\n"
+                      "- new encr (NONE, WEP, TKIP, CCMP)\n"
+                      "- new key\n");
+               return -1;
+       }
+
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_REG command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_er_start(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "WPS_ER_START");
+
+}
+
+
+static int wpa_cli_cmd_wps_er_stop(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "WPS_ER_STOP");
+
+}
+
+
+static int wpa_cli_cmd_wps_er_pin(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 2) {
+               printf("Invalid WPS_ER_PIN command: need two arguments:\n"
+                      "- UUID: use 'any' to select any\n"
+                      "- PIN: Enrollee PIN\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+                         argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_ER_PIN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_er_pbc(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid WPS_ER_PBC command: need one argument:\n"
+                      "- UUID: Specify the Enrollee\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
+                         argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_ER_PBC command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_wps_er_learn(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 2) {
+               printf("Invalid WPS_ER_LEARN command: need two arguments:\n"
+                      "- UUID: specify which AP to use\n"
+                      "- PIN: AP PIN\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
+                         argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long WPS_ER_LEARN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_ibss_rsn(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid IBSS_RSN command: needs one argument "
+                      "(Peer STA MAC address)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "IBSS_RSN %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long IBSS_RSN command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid LEVEL command: needs one argument (debug "
+                      "level)\n");
+               return -1;
+       }
+       res = os_snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long LEVEL command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid IDENTITY command: needs two arguments "
+                      "(network id and identity)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
+                         argv[0], argv[1]);
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long IDENTITY command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long IDENTITY command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid PASSWORD command: needs two arguments "
+                      "(network id and password)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
+                         argv[0], argv[1]);
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long PASSWORD command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long PASSWORD command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid NEW_PASSWORD command: needs two arguments "
+                      "(network id and password)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
+                         argv[0], argv[1]);
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long NEW_PASSWORD command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long NEW_PASSWORD command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid PIN command: needs two arguments "
+                      "(network id and pin)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
+                         argv[0], argv[1]);
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long PIN command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long PIN command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid OTP command: needs two arguments (network "
+                      "id and password)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
+                         argv[0], argv[1]);
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long OTP command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long OTP command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid PASSPHRASE command: needs two arguments "
+                      "(network id and passphrase)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
+                         argv[0], argv[1]);
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long PASSPHRASE command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long PASSPHRASE command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_bssid(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid BSSID command: needs two arguments (network "
+                      "id and BSSID)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, "BSSID");
+       if (ret < 0 || ret >= end - pos) {
+               printf("Too long BSSID command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 0; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (ret < 0 || ret >= end - pos) {
+                       printf("Too long BSSID command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_list_networks(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "LIST_NETWORKS");
+}
+
+
+static int wpa_cli_cmd_select_network(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char cmd[32];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid SELECT_NETWORK command: needs one argument "
+                      "(network id)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "SELECT_NETWORK %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_enable_network(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char cmd[32];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid ENABLE_NETWORK command: needs one argument "
+                      "(network id)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_disable_network(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
+{
+       char cmd[32];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid DISABLE_NETWORK command: needs one argument "
+                      "(network id)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "DISABLE_NETWORK %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_add_network(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "ADD_NETWORK");
+}
+
+
+static int wpa_cli_cmd_remove_network(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char cmd[32];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid REMOVE_NETWORK command: needs one argument "
+                      "(network id)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static void wpa_cli_show_network_variables(void)
+{
+       printf("set_network variables:\n"
+              "  ssid (network name, SSID)\n"
+              "  psk (WPA passphrase or pre-shared key)\n"
+              "  key_mgmt (key management protocol)\n"
+              "  identity (EAP identity)\n"
+              "  password (EAP password)\n"
+              "  ...\n"
+              "\n"
+              "Note: Values are entered in the same format as the "
+              "configuration file is using,\n"
+              "i.e., strings values need to be inside double quotation "
+              "marks.\n"
+              "For example: set_network 1 ssid \"network name\"\n"
+              "\n"
+              "Please see wpa_supplicant.conf documentation for full list "
+              "of\navailable variables.\n");
+}
+
+
+static int wpa_cli_cmd_set_network(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc == 0) {
+               wpa_cli_show_network_variables();
+               return 0;
+       }
+
+       if (argc != 3) {
+               printf("Invalid SET_NETWORK command: needs three arguments\n"
+                      "(network id, variable name, and value)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "SET_NETWORK %s %s %s",
+                         argv[0], argv[1], argv[2]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long SET_NETWORK command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc == 0) {
+               wpa_cli_show_network_variables();
+               return 0;
+       }
+
+       if (argc != 2) {
+               printf("Invalid GET_NETWORK command: needs two arguments\n"
+                      "(network id and variable name)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "GET_NETWORK %s %s",
+                         argv[0], argv[1]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long GET_NETWORK command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "DISCONNECT");
+}
+
+
+static int wpa_cli_cmd_reconnect(struct wpa_ctrl *ctrl, int argc,
+                                 char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "RECONNECT");
+}
+
+
+static int wpa_cli_cmd_save_config(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "SAVE_CONFIG");
+}
+
+
+static int wpa_cli_cmd_scan(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "SCAN");
+}
+
+
+static int wpa_cli_cmd_scan_results(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "SCAN_RESULTS");
+}
+
+
+static int wpa_cli_cmd_bss(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[64];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid BSS command: need one argument (index or "
+                      "BSSID)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "BSS %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_get_capability(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char cmd[64];
+       int res;
+
+       if (argc < 1 || argc > 2) {
+               printf("Invalid GET_CAPABILITY command: need either one or "
+                      "two arguments\n");
+               return -1;
+       }
+
+       if ((argc == 2) && os_strcmp(argv[1], "strict") != 0) {
+               printf("Invalid GET_CAPABILITY command: second argument, "
+                      "if any, must be 'strict'\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "GET_CAPABILITY %s%s", argv[0],
+                         (argc == 2) ? " strict" : "");
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_list_interfaces(struct wpa_ctrl *ctrl)
+{
+       printf("Available interfaces:\n");
+       return wpa_ctrl_command(ctrl, "INTERFACES");
+}
+
+
+static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       if (argc < 1) {
+               wpa_cli_list_interfaces(ctrl);
+               return 0;
+       }
+
+       wpa_cli_close_connection();
+       os_free(ctrl_ifname);
+       ctrl_ifname = os_strdup(argv[0]);
+
+       if (wpa_cli_open_connection(ctrl_ifname, 1)) {
+               printf("Connected to interface '%s.\n", ctrl_ifname);
+       } else {
+               printf("Could not connect to interface '%s' - re-trying\n",
+                      ctrl_ifname);
+       }
+       return 0;
+}
+
+
+static int wpa_cli_cmd_reconfigure(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "RECONFIGURE");
+}
+
+
+static int wpa_cli_cmd_terminate(struct wpa_ctrl *ctrl, int argc,
+                                char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "TERMINATE");
+}
+
+
+static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc < 1) {
+               printf("Invalid INTERFACE_ADD command: needs at least one "
+                      "argument (interface name)\n"
+                      "All arguments: ifname confname driver ctrl_interface "
+                      "driver_param bridge_name\n");
+               return -1;
+       }
+
+       /*
+        * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
+        * <driver_param>TAB<bridge_name>
+        */
+       res = os_snprintf(cmd, sizeof(cmd),
+                         "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
+                         argv[0],
+                         argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
+                         argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
+                         argc > 5 ? argv[5] : "");
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_interface_remove(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid INTERFACE_REMOVE command: needs one argument "
+                      "(interface name)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "INTERFACE_REMOVE %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd))
+               return -1;
+       cmd[sizeof(cmd) - 1] = '\0';
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_interface_list(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "INTERFACE_LIST");
+}
+
+
+#ifdef CONFIG_AP
+static int wpa_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;
+       }
+       os_snprintf(buf, sizeof(buf), "STA %s", argv[0]);
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd,
+                               char *addr, size_t addr_len)
+{
+       char buf[4096], *pos;
+       size_t len;
+       int ret;
+
+       if (ctrl_conn == NULL) {
+               printf("Not connected to hostapd - command dropped.\n");
+               return -1;
+       }
+       len = sizeof(buf) - 1;
+       ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len,
+                              wpa_cli_msg_cb);
+       if (ret == -2) {
+               printf("'%s' command timed out.\n", cmd);
+               return -2;
+       } else if (ret < 0) {
+               printf("'%s' command failed.\n", cmd);
+               return -1;
+       }
+
+       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 wpa_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 {
+               os_snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr);
+       } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0);
+
+       return -1;
+}
+#endif /* CONFIG_AP */
+
+
+static int wpa_cli_cmd_suspend(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "SUSPEND");
+}
+
+
+static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "RESUME");
+}
+
+
+static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "DROP_SA");
+}
+
+
+static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[128];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid ROAM command: needs one argument "
+                      "(target AP's BSSID)\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "ROAM %s", argv[0]);
+       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               printf("Too long ROAM command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+enum wpa_cli_cmd_flags {
+       cli_cmd_flag_none               = 0x00,
+       cli_cmd_flag_sensitive          = 0x01
+};
+
+struct wpa_cli_cmd {
+       const char *cmd;
+       int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
+       enum wpa_cli_cmd_flags flags;
+       const char *usage;
+};
+
+static struct wpa_cli_cmd wpa_cli_commands[] = {
+       { "status", wpa_cli_cmd_status,
+         cli_cmd_flag_none,
+         "[verbose] = get current WPA/EAPOL/EAP status" },
+       { "ping", wpa_cli_cmd_ping,
+         cli_cmd_flag_none,
+         "= pings wpa_supplicant" },
+       { "mib", wpa_cli_cmd_mib,
+         cli_cmd_flag_none,
+         "= get MIB variables (dot1x, dot11)" },
+       { "help", wpa_cli_cmd_help,
+         cli_cmd_flag_none,
+         "= show this usage help" },
+       { "interface", wpa_cli_cmd_interface,
+         cli_cmd_flag_none,
+         "[ifname] = show interfaces/select interface" },
+       { "level", wpa_cli_cmd_level,
+         cli_cmd_flag_none,
+         "<debug level> = change debug level" },
+       { "license", wpa_cli_cmd_license,
+         cli_cmd_flag_none,
+         "= show full wpa_cli license" },
+       { "quit", wpa_cli_cmd_quit,
+         cli_cmd_flag_none,
+         "= exit wpa_cli" },
+       { "set", wpa_cli_cmd_set,
+         cli_cmd_flag_none,
+         "= set variables (shows list of variables when run without "
+         "arguments)" },
+       { "logon", wpa_cli_cmd_logon,
+         cli_cmd_flag_none,
+         "= IEEE 802.1X EAPOL state machine logon" },
+       { "logoff", wpa_cli_cmd_logoff,
+         cli_cmd_flag_none,
+         "= IEEE 802.1X EAPOL state machine logoff" },
+       { "pmksa", wpa_cli_cmd_pmksa,
+         cli_cmd_flag_none,
+         "= show PMKSA cache" },
+       { "reassociate", wpa_cli_cmd_reassociate,
+         cli_cmd_flag_none,
+         "= force reassociation" },
+       { "preauthenticate", wpa_cli_cmd_preauthenticate,
+         cli_cmd_flag_none,
+         "<BSSID> = force preauthentication" },
+       { "identity", wpa_cli_cmd_identity,
+         cli_cmd_flag_none,
+         "<network id> <identity> = configure identity for an SSID" },
+       { "password", wpa_cli_cmd_password,
+         cli_cmd_flag_sensitive,
+         "<network id> <password> = configure password for an SSID" },
+       { "new_password", wpa_cli_cmd_new_password,
+         cli_cmd_flag_sensitive,
+         "<network id> <password> = change password for an SSID" },
+       { "pin", wpa_cli_cmd_pin,
+         cli_cmd_flag_sensitive,
+         "<network id> <pin> = configure pin for an SSID" },
+       { "otp", wpa_cli_cmd_otp,
+         cli_cmd_flag_sensitive,
+         "<network id> <password> = configure one-time-password for an SSID"
+       },
+       { "passphrase", wpa_cli_cmd_passphrase,
+         cli_cmd_flag_sensitive,
+         "<network id> <passphrase> = configure private key passphrase\n"
+         "  for an SSID" },
+       { "bssid", wpa_cli_cmd_bssid,
+         cli_cmd_flag_none,
+         "<network id> <BSSID> = set preferred BSSID for an SSID" },
+       { "list_networks", wpa_cli_cmd_list_networks,
+         cli_cmd_flag_none,
+         "= list configured networks" },
+       { "select_network", wpa_cli_cmd_select_network,
+         cli_cmd_flag_none,
+         "<network id> = select a network (disable others)" },
+       { "enable_network", wpa_cli_cmd_enable_network,
+         cli_cmd_flag_none,
+         "<network id> = enable a network" },
+       { "disable_network", wpa_cli_cmd_disable_network,
+         cli_cmd_flag_none,
+         "<network id> = disable a network" },
+       { "add_network", wpa_cli_cmd_add_network,
+         cli_cmd_flag_none,
+         "= add a network" },
+       { "remove_network", wpa_cli_cmd_remove_network,
+         cli_cmd_flag_none,
+         "<network id> = remove a network" },
+       { "set_network", wpa_cli_cmd_set_network,
+         cli_cmd_flag_sensitive,
+         "<network id> <variable> <value> = set network variables (shows\n"
+         "  list of variables when run without arguments)" },
+       { "get_network", wpa_cli_cmd_get_network,
+         cli_cmd_flag_none,
+         "<network id> <variable> = get network variables" },
+       { "save_config", wpa_cli_cmd_save_config,
+         cli_cmd_flag_none,
+         "= save the current configuration" },
+       { "disconnect", wpa_cli_cmd_disconnect,
+         cli_cmd_flag_none,
+         "= disconnect and wait for reassociate/reconnect command before\n"
+         "  connecting" },
+       { "reconnect", wpa_cli_cmd_reconnect,
+         cli_cmd_flag_none,
+         "= like reassociate, but only takes effect if already disconnected"
+       },
+       { "scan", wpa_cli_cmd_scan,
+         cli_cmd_flag_none,
+         "= request new BSS scan" },
+       { "scan_results", wpa_cli_cmd_scan_results,
+         cli_cmd_flag_none,
+         "= get latest scan results" },
+       { "bss", wpa_cli_cmd_bss,
+         cli_cmd_flag_none,
+         "<<idx> | <bssid>> = get detailed scan result info" },
+       { "get_capability", wpa_cli_cmd_get_capability,
+         cli_cmd_flag_none,
+         "<eap/pairwise/group/key_mgmt/proto/auth_alg> = get capabilies" },
+       { "reconfigure", wpa_cli_cmd_reconfigure,
+         cli_cmd_flag_none,
+         "= force wpa_supplicant to re-read its configuration file" },
+       { "terminate", wpa_cli_cmd_terminate,
+         cli_cmd_flag_none,
+         "= terminate wpa_supplicant" },
+       { "interface_add", wpa_cli_cmd_interface_add,
+         cli_cmd_flag_none,
+         "<ifname> <confname> <driver> <ctrl_interface> <driver_param>\n"
+         "  <bridge_name> = adds new interface, all parameters but <ifname>\n"
+         "  are optional" },
+       { "interface_remove", wpa_cli_cmd_interface_remove,
+         cli_cmd_flag_none,
+         "<ifname> = removes the interface" },
+       { "interface_list", wpa_cli_cmd_interface_list,
+         cli_cmd_flag_none,
+         "= list available interfaces" },
+       { "ap_scan", wpa_cli_cmd_ap_scan,
+         cli_cmd_flag_none,
+         "<value> = set ap_scan parameter" },
+       { "stkstart", wpa_cli_cmd_stkstart,
+         cli_cmd_flag_none,
+         "<addr> = request STK negotiation with <addr>" },
+       { "ft_ds", wpa_cli_cmd_ft_ds,
+         cli_cmd_flag_none,
+         "<addr> = request over-the-DS FT with <addr>" },
+       { "wps_pbc", wpa_cli_cmd_wps_pbc,
+         cli_cmd_flag_none,
+         "[BSSID] = start Wi-Fi Protected Setup: Push Button Configuration" },
+       { "wps_pin", wpa_cli_cmd_wps_pin,
+         cli_cmd_flag_sensitive,
+         "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
+         "hardcoded)" },
+#ifdef CONFIG_WPS_OOB
+       { "wps_oob", wpa_cli_cmd_wps_oob,
+         cli_cmd_flag_sensitive,
+         "<DEV_TYPE> <PATH> <METHOD> [DEV_NAME] = start WPS OOB" },
+#endif /* CONFIG_WPS_OOB */
+       { "wps_reg", wpa_cli_cmd_wps_reg,
+         cli_cmd_flag_sensitive,
+         "<BSSID> <AP PIN> = start WPS Registrar to configure an AP" },
+       { "wps_er_start", wpa_cli_cmd_wps_er_start,
+         cli_cmd_flag_none,
+         "= start Wi-Fi Protected Setup External Registrar" },
+       { "wps_er_stop", wpa_cli_cmd_wps_er_stop,
+         cli_cmd_flag_none,
+         "= stop Wi-Fi Protected Setup External Registrar" },
+       { "wps_er_pin", wpa_cli_cmd_wps_er_pin,
+         cli_cmd_flag_sensitive,
+         "<UUID> <PIN> = add an Enrollee PIN to External Registrar" },
+       { "wps_er_pbc", wpa_cli_cmd_wps_er_pbc,
+         cli_cmd_flag_none,
+         "<UUID> = accept an Enrollee PBC using External Registrar" },
+       { "wps_er_learn", wpa_cli_cmd_wps_er_learn,
+         cli_cmd_flag_sensitive,
+         "<UUID> <PIN> = learn AP configuration" },
+       { "ibss_rsn", wpa_cli_cmd_ibss_rsn,
+         cli_cmd_flag_none,
+         "<addr> = request RSN authentication with <addr> in IBSS" },
+#ifdef CONFIG_AP
+       { "sta", wpa_cli_cmd_sta,
+         cli_cmd_flag_none,
+         "<addr> = get information about an associated station (AP)" },
+       { "all_sta", wpa_cli_cmd_all_sta,
+         cli_cmd_flag_none,
+         "= get information about all associated stations (AP)" },
+#endif /* CONFIG_AP */
+       { "suspend", wpa_cli_cmd_suspend, cli_cmd_flag_none,
+         "= notification of suspend/hibernate" },
+       { "resume", wpa_cli_cmd_resume, cli_cmd_flag_none,
+         "= notification of resume/thaw" },
+       { "drop_sa", wpa_cli_cmd_drop_sa, cli_cmd_flag_none,
+         "= drop SA without deauth/disassoc (test command)" },
+       { "roam", wpa_cli_cmd_roam,
+         cli_cmd_flag_none,
+         "<addr> = roam to the specified BSS" },
+       { NULL, NULL, cli_cmd_flag_none, NULL }
+};
+
+
+/*
+ * Prints command usage, lines are padded with the specified string.
+ */
+static void print_cmd_help(struct wpa_cli_cmd *cmd, const char *pad)
+{
+       char c;
+       size_t n;
+
+       printf("%s%s ", pad, cmd->cmd);
+       for (n = 0; (c = cmd->usage[n]); n++) {
+               printf("%c", c);
+               if (c == '\n')
+                       printf("%s", pad);
+       }
+       printf("\n");
+}
+
+
+static void print_help(void)
+{
+       int n;
+       printf("commands:\n");
+       for (n = 0; wpa_cli_commands[n].cmd; n++)
+               print_cmd_help(&wpa_cli_commands[n], "  ");
+}
+
+
+#ifdef CONFIG_READLINE
+static int cmd_has_sensitive_data(const char *cmd)
+{
+       const char *c, *delim;
+       int n;
+       size_t len;
+
+       delim = os_strchr(cmd, ' ');
+       if (delim)
+               len = delim - cmd;
+       else
+               len = os_strlen(cmd);
+
+       for (n = 0; (c = wpa_cli_commands[n].cmd); n++) {
+               if (os_strncasecmp(cmd, c, len) == 0 && len == os_strlen(c))
+                       return (wpa_cli_commands[n].flags &
+                               cli_cmd_flag_sensitive);
+       }
+       return 0;
+}
+#endif /* CONFIG_READLINE */
+
+
+static int wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       struct wpa_cli_cmd *cmd, *match = NULL;
+       int count;
+       int ret = 0;
+
+       count = 0;
+       cmd = wpa_cli_commands;
+       while (cmd->cmd) {
+               if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
+               {
+                       match = cmd;
+                       if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+                               /* we have an exact match */
+                               count = 1;
+                               break;
+                       }
+                       count++;
+               }
+               cmd++;
+       }
+
+       if (count > 1) {
+               printf("Ambiguous command '%s'; possible commands:", argv[0]);
+               cmd = wpa_cli_commands;
+               while (cmd->cmd) {
+                       if (os_strncasecmp(cmd->cmd, argv[0],
+                                          os_strlen(argv[0])) == 0) {
+                               printf(" %s", cmd->cmd);
+                       }
+                       cmd++;
+               }
+               printf("\n");
+               ret = 1;
+       } else if (count == 0) {
+               printf("Unknown command '%s'\n", argv[0]);
+               ret = 1;
+       } else {
+               ret = match->handler(ctrl, argc - 1, &argv[1]);
+       }
+
+       return ret;
+}
+
+
+static int str_match(const char *a, const char *b)
+{
+       return os_strncmp(a, b, os_strlen(b)) == 0;
+}
+
+
+static int wpa_cli_exec(const char *program, const char *arg1,
+                       const char *arg2)
+{
+       char *cmd;
+       size_t len;
+       int res;
+       int ret = 0;
+
+       len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
+       cmd = os_malloc(len);
+       if (cmd == NULL)
+               return -1;
+       res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
+       if (res < 0 || (size_t) res >= len) {
+               os_free(cmd);
+               return -1;
+       }
+       cmd[len - 1] = '\0';
+#ifndef _WIN32_WCE
+       if (system(cmd) < 0)
+               ret = -1;
+#endif /* _WIN32_WCE */
+       os_free(cmd);
+
+       return ret;
+}
+
+
+static void wpa_cli_action_process(const char *msg)
+{
+       const char *pos;
+       char *copy = NULL, *id, *pos2;
+
+       pos = msg;
+       if (*pos == '<') {
+               /* skip priority */
+               pos = os_strchr(pos, '>');
+               if (pos)
+                       pos++;
+               else
+                       pos = msg;
+       }
+
+       if (str_match(pos, WPA_EVENT_CONNECTED)) {
+               int new_id = -1;
+               os_unsetenv("WPA_ID");
+               os_unsetenv("WPA_ID_STR");
+               os_unsetenv("WPA_CTRL_DIR");
+
+               pos = os_strstr(pos, "[id=");
+               if (pos)
+                       copy = os_strdup(pos + 4);
+
+               if (copy) {
+                       pos2 = id = copy;
+                       while (*pos2 && *pos2 != ' ')
+                               pos2++;
+                       *pos2++ = '\0';
+                       new_id = atoi(id);
+                       os_setenv("WPA_ID", id, 1);
+                       while (*pos2 && *pos2 != '=')
+                               pos2++;
+                       if (*pos2 == '=')
+                               pos2++;
+                       id = pos2;
+                       while (*pos2 && *pos2 != ']')
+                               pos2++;
+                       *pos2 = '\0';
+                       os_setenv("WPA_ID_STR", id, 1);
+                       os_free(copy);
+               }
+
+               os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
+
+               if (!wpa_cli_connected || new_id != wpa_cli_last_id) {
+                       wpa_cli_connected = 1;
+                       wpa_cli_last_id = new_id;
+                       wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED");
+               }
+       } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) {
+               if (wpa_cli_connected) {
+                       wpa_cli_connected = 0;
+                       wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED");
+               }
+       } else if (str_match(pos, WPA_EVENT_TERMINATING)) {
+               printf("wpa_supplicant is terminating - stop monitoring\n");
+               wpa_cli_quit = 1;
+       }
+}
+
+
+#ifndef CONFIG_ANSI_C_EXTRA
+static void wpa_cli_action_cb(char *msg, size_t len)
+{
+       wpa_cli_action_process(msg);
+}
+#endif /* CONFIG_ANSI_C_EXTRA */
+
+
+static void wpa_cli_reconnect(void)
+{
+       wpa_cli_close_connection();
+       wpa_cli_open_connection(ctrl_ifname, 1);
+}
+
+
+static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read,
+                                int action_monitor)
+{
+       int first = 1;
+       if (ctrl_conn == NULL) {
+               wpa_cli_reconnect();
+               return;
+       }
+       while (wpa_ctrl_pending(ctrl) > 0) {
+               char buf[256];
+               size_t len = sizeof(buf) - 1;
+               if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
+                       buf[len] = '\0';
+                       if (action_monitor)
+                               wpa_cli_action_process(buf);
+                       else {
+                               if (in_read && first)
+                                       printf("\r");
+                               first = 0;
+                               printf("%s\n", buf);
+#ifdef CONFIG_READLINE
+                               rl_on_new_line();
+                               rl_redisplay();
+#endif /* CONFIG_READLINE */
+                       }
+               } else {
+                       printf("Could not read pending message.\n");
+                       break;
+               }
+       }
+
+       if (wpa_ctrl_pending(ctrl) < 0) {
+               printf("Connection to wpa_supplicant lost - trying to "
+                      "reconnect\n");
+               wpa_cli_reconnect();
+       }
+}
+
+
+#ifdef CONFIG_READLINE
+static char * wpa_cli_cmd_gen(const char *text, int state)
+{
+       static int i, len;
+       const char *cmd;
+
+       if (state == 0) {
+               i = 0;
+               len = os_strlen(text);
+       }
+
+       while ((cmd = wpa_cli_commands[i].cmd)) {
+               i++;
+               if (os_strncasecmp(cmd, text, len) == 0)
+                       return strdup(cmd);
+       }
+
+       return NULL;
+}
+
+
+static char * wpa_cli_dummy_gen(const char *text, int state)
+{
+       int i;
+
+       for (i = 0; wpa_cli_commands[i].cmd; i++) {
+               const char *cmd = wpa_cli_commands[i].cmd;
+               size_t len = os_strlen(cmd);
+               if (os_strncasecmp(rl_line_buffer, cmd, len) == 0 &&
+                   rl_line_buffer[len] == ' ') {
+                       printf("\n%s\n", wpa_cli_commands[i].usage);
+                       rl_on_new_line();
+                       rl_redisplay();
+                       break;
+               }
+       }
+
+       rl_attempted_completion_over = 1;
+       return NULL;
+}
+
+
+static char * wpa_cli_status_gen(const char *text, int state)
+{
+       static int i, len;
+       char *options[] = {
+               "verbose", NULL
+       };
+       char *t;
+
+       if (state == 0) {
+               i = 0;
+               len = os_strlen(text);
+       }
+
+       while ((t = options[i])) {
+               i++;
+               if (os_strncasecmp(t, text, len) == 0)
+                       return strdup(t);
+       }
+
+       rl_attempted_completion_over = 1;
+       return NULL;
+}
+
+
+static char ** wpa_cli_completion(const char *text, int start, int end)
+{
+       char * (*func)(const char *text, int state);
+
+       if (start == 0)
+               func = wpa_cli_cmd_gen;
+       else if (os_strncasecmp(rl_line_buffer, "status ", 7) == 0)
+               func = wpa_cli_status_gen;
+       else
+               func = wpa_cli_dummy_gen;
+       return rl_completion_matches(text, func);
+}
+#endif /* CONFIG_READLINE */
+
+
+static void wpa_cli_interactive(void)
+{
+#define max_args 10
+       char cmdbuf[256], *cmd, *argv[max_args], *pos;
+       int argc;
+#ifdef CONFIG_READLINE
+       char *home, *hfile = NULL;
+#endif /* CONFIG_READLINE */
+
+       printf("\nInteractive mode\n\n");
+
+#ifdef CONFIG_READLINE
+       rl_attempted_completion_function = wpa_cli_completion;
+       home = getenv("HOME");
+       if (home) {
+               const char *fname = ".wpa_cli_history";
+               int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+               hfile = os_malloc(hfile_len);
+               if (hfile) {
+                       int res;
+                       res = os_snprintf(hfile, hfile_len, "%s/%s", home,
+                                         fname);
+                       if (res >= 0 && res < hfile_len) {
+                               hfile[hfile_len - 1] = '\0';
+                               read_history(hfile);
+                               stifle_history(100);
+                       }
+               }
+       }
+#endif /* CONFIG_READLINE */
+
+       do {
+               wpa_cli_recv_pending(mon_conn, 0, 0);
+#ifndef CONFIG_NATIVE_WINDOWS
+               alarm(ping_interval);
+#endif /* CONFIG_NATIVE_WINDOWS */
+#ifdef CONFIG_WPA_CLI_FORK
+               if (mon_pid)
+                       kill(mon_pid, SIGUSR1);
+#endif /* CONFIG_WPA_CLI_FORK */
+#ifdef CONFIG_READLINE
+               cmd = readline("> ");
+               if (cmd && *cmd) {
+                       HIST_ENTRY *h;
+                       while (next_history())
+                               ;
+                       h = previous_history();
+                       if (h == NULL || os_strcmp(cmd, h->line) != 0)
+                               add_history(cmd);
+                       next_history();
+               }
+#else /* CONFIG_READLINE */
+               printf("> ");
+               cmd = fgets(cmdbuf, sizeof(cmdbuf), stdin);
+#endif /* CONFIG_READLINE */
+#ifndef CONFIG_NATIVE_WINDOWS
+               alarm(0);
+#endif /* CONFIG_NATIVE_WINDOWS */
+               if (cmd == NULL)
+                       break;
+               wpa_cli_recv_pending(mon_conn, 0, 0);
+               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;
+                       if (*pos == '"') {
+                               char *pos2 = os_strrchr(pos, '"');
+                               if (pos2)
+                                       pos = pos2 + 1;
+                       }
+                       while (*pos != '\0' && *pos != ' ')
+                               pos++;
+                       if (*pos == ' ')
+                               *pos++ = '\0';
+               }
+               if (argc)
+                       wpa_request(ctrl_conn, argc, argv);
+
+               if (cmd != cmdbuf)
+                       free(cmd);
+#ifdef CONFIG_WPA_CLI_FORK
+               if (mon_pid)
+                       kill(mon_pid, SIGUSR2);
+#endif /* CONFIG_WPA_CLI_FORK */
+       } while (!wpa_cli_quit);
+
+#ifdef CONFIG_READLINE
+       if (hfile) {
+               /* Save command history, excluding lines that may contain
+                * passwords. */
+               HIST_ENTRY *h;
+               history_set_pos(0);
+               while ((h = current_history())) {
+                       char *p = h->line;
+                       while (*p == ' ' || *p == '\t')
+                               p++;
+                       if (cmd_has_sensitive_data(p)) {
+                               h = remove_history(where_history());
+                               if (h) {
+                                       os_free(h->line);
+                                       os_free(h->data);
+                                       os_free(h);
+                               } else
+                                       next_history();
+                       } else
+                               next_history();
+               }
+               write_history(hfile);
+               os_free(hfile);
+       }
+#endif /* CONFIG_READLINE */
+}
+
+
+static void wpa_cli_action(struct wpa_ctrl *ctrl)
+{
+#ifdef CONFIG_ANSI_C_EXTRA
+       /* TODO: ANSI C version(?) */
+       printf("Action processing not supported in ANSI C build.\n");
+#else /* CONFIG_ANSI_C_EXTRA */
+       fd_set rfds;
+       int fd, res;
+       struct timeval tv;
+       char buf[256]; /* note: large enough to fit in unsolicited messages */
+       size_t len;
+
+       fd = wpa_ctrl_get_fd(ctrl);
+
+       while (!wpa_cli_quit) {
+               FD_ZERO(&rfds);
+               FD_SET(fd, &rfds);
+               tv.tv_sec = ping_interval;
+               tv.tv_usec = 0;
+               res = select(fd + 1, &rfds, NULL, NULL, &tv);
+               if (res < 0 && errno != EINTR) {
+                       perror("select");
+                       break;
+               }
+
+               if (FD_ISSET(fd, &rfds))
+                       wpa_cli_recv_pending(ctrl, 0, 1);
+               else {
+                       /* verify that connection is still working */
+                       len = sizeof(buf) - 1;
+                       if (wpa_ctrl_request(ctrl, "PING", 4, buf, &len,
+                                            wpa_cli_action_cb) < 0 ||
+                           len < 4 || os_memcmp(buf, "PONG", 4) != 0) {
+                               printf("wpa_supplicant did not reply to PING "
+                                      "command - exiting\n");
+                               break;
+                       }
+               }
+       }
+#endif /* CONFIG_ANSI_C_EXTRA */
+}
+
+
+static void wpa_cli_cleanup(void)
+{
+       wpa_cli_close_connection();
+       if (pid_file)
+               os_daemonize_terminate(pid_file);
+
+       os_program_deinit();
+}
+
+static void wpa_cli_terminate(int sig)
+{
+       wpa_cli_cleanup();
+       exit(0);
+}
+
+
+#ifdef CONFIG_WPA_CLI_FORK
+static void wpa_cli_usr1(int sig)
+{
+#ifdef CONFIG_READLINE
+       rl_on_new_line();
+       rl_redisplay();
+#endif /* CONFIG_READLINE */
+}
+#endif /* CONFIG_WPA_CLI_FORK */
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static void wpa_cli_alarm(int sig)
+{
+       if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
+               printf("Connection to wpa_supplicant lost - trying to "
+                      "reconnect\n");
+               wpa_cli_close_connection();
+       }
+       if (!ctrl_conn)
+               wpa_cli_reconnect();
+       if (mon_conn)
+               wpa_cli_recv_pending(mon_conn, 1, 0);
+       alarm(ping_interval);
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static char * wpa_cli_get_default_ifname(void)
+{
+       char *ifname = NULL;
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+       struct dirent *dent;
+       DIR *dir = opendir(ctrl_iface_dir);
+       if (!dir)
+               return NULL;
+       while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+               /*
+                * Skip the file if it is not a socket. Also accept
+                * DT_UNKNOWN (0) in case the C library or underlying
+                * file system does not support d_type.
+                */
+               if (dent->d_type != DT_SOCK && dent->d_type != DT_UNKNOWN)
+                       continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+               if (os_strcmp(dent->d_name, ".") == 0 ||
+                   os_strcmp(dent->d_name, "..") == 0)
+                       continue;
+               printf("Selected interface '%s'\n", dent->d_name);
+               ifname = os_strdup(dent->d_name);
+               break;
+       }
+       closedir(dir);
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+       char buf[2048], *pos;
+       size_t len;
+       struct wpa_ctrl *ctrl;
+       int ret;
+
+       ctrl = wpa_ctrl_open(NULL);
+       if (ctrl == NULL)
+               return NULL;
+
+       len = sizeof(buf) - 1;
+       ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL);
+       if (ret >= 0) {
+               buf[len] = '\0';
+               pos = os_strchr(buf, '\n');
+               if (pos)
+                       *pos = '\0';
+               ifname = os_strdup(buf);
+       }
+       wpa_ctrl_close(ctrl);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+       return ifname;
+}
+
+
+int main(int argc, char *argv[])
+{
+       int warning_displayed = 0;
+       int c;
+       int daemonize = 0;
+       int ret = 0;
+       const char *global = NULL;
+
+       if (os_program_init())
+               return -1;
+
+       for (;;) {
+               c = getopt(argc, argv, "a:Bg:G:hi:p:P:v");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'a':
+                       action_file = optarg;
+                       break;
+               case 'B':
+                       daemonize = 1;
+                       break;
+               case 'g':
+                       global = optarg;
+                       break;
+               case 'G':
+                       ping_interval = atoi(optarg);
+                       break;
+               case 'h':
+                       usage();
+                       return 0;
+               case 'v':
+                       printf("%s\n", wpa_cli_version);
+                       return 0;
+               case 'i':
+                       os_free(ctrl_ifname);
+                       ctrl_ifname = os_strdup(optarg);
+                       break;
+               case 'p':
+                       ctrl_iface_dir = optarg;
+                       break;
+               case 'P':
+                       pid_file = optarg;
+                       break;
+               default:
+                       usage();
+                       return -1;
+               }
+       }
+
+       interactive = (argc == optind) && (action_file == NULL);
+
+       if (interactive)
+               printf("%s\n\n%s\n\n", wpa_cli_version, wpa_cli_license);
+
+       if (global) {
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+               ctrl_conn = wpa_ctrl_open(NULL);
+#else /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+               ctrl_conn = wpa_ctrl_open(global);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+               if (ctrl_conn == NULL) {
+                       perror("Failed to connect to wpa_supplicant - "
+                              "wpa_ctrl_open");
+                       return -1;
+               }
+       }
+
+#ifndef _WIN32_WCE
+       signal(SIGINT, wpa_cli_terminate);
+       signal(SIGTERM, wpa_cli_terminate);
+#endif /* _WIN32_WCE */
+#ifndef CONFIG_NATIVE_WINDOWS
+       signal(SIGALRM, wpa_cli_alarm);
+#endif /* CONFIG_NATIVE_WINDOWS */
+#ifdef CONFIG_WPA_CLI_FORK
+       signal(SIGUSR1, wpa_cli_usr1);
+#endif /* CONFIG_WPA_CLI_FORK */
+
+       if (ctrl_ifname == NULL)
+               ctrl_ifname = wpa_cli_get_default_ifname();
+
+       if (interactive) {
+               for (; !global;) {
+                       if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
+                               if (warning_displayed)
+                                       printf("Connection established.\n");
+                               break;
+                       }
+
+                       if (!warning_displayed) {
+                               printf("Could not connect to wpa_supplicant - "
+                                      "re-trying\n");
+                               warning_displayed = 1;
+                       }
+                       os_sleep(1, 0);
+                       continue;
+               }
+       } else {
+               if (!global &&
+                   wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
+                       perror("Failed to connect to wpa_supplicant - "
+                              "wpa_ctrl_open");
+                       return -1;
+               }
+
+               if (action_file) {
+                       if (wpa_ctrl_attach(ctrl_conn) == 0) {
+                               wpa_cli_attached = 1;
+                       } else {
+                               printf("Warning: Failed to attach to "
+                                      "wpa_supplicant.\n");
+                               return -1;
+                       }
+               }
+       }
+
+       if (daemonize && os_daemonize(pid_file))
+               return -1;
+
+       if (interactive)
+               wpa_cli_interactive();
+       else if (action_file)
+               wpa_cli_action(ctrl_conn);
+       else
+               ret = wpa_request(ctrl_conn, argc - optind, &argv[optind]);
+
+       os_free(ctrl_ifname);
+       wpa_cli_cleanup();
+
+       return ret;
+}
+
+#else /* CONFIG_CTRL_IFACE */
+int main(int argc, char *argv[])
+{
+       printf("CONFIG_CTRL_IFACE not defined - wpa_cli disabled\n");
+       return -1;
+}
+#endif /* CONFIG_CTRL_IFACE */
diff --git a/wpa_supplicant/wpa_gui-qt4/.gitignore b/wpa_supplicant/wpa_gui-qt4/.gitignore
new file mode 100644 (file)
index 0000000..efcb666
--- /dev/null
@@ -0,0 +1,6 @@
+.moc
+.obj
+.ui
+Makefile
+wpa_gui
+qrc_icons.cpp
diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.cpp b/wpa_supplicant/wpa_gui-qt4/addinterface.cpp
new file mode 100644 (file)
index 0000000..88d4603
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * wpa_gui - AddInterface class
+ * 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 <cstdio>
+#include "common/wpa_ctrl.h"
+
+#include <QMessageBox>
+
+#include "wpagui.h"
+#include "addinterface.h"
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <windows.h>
+
+#ifndef WPA_KEY_ROOT
+#define WPA_KEY_ROOT HKEY_LOCAL_MACHINE
+#endif
+#ifndef WPA_KEY_PREFIX
+#define WPA_KEY_PREFIX TEXT("SOFTWARE\\wpa_supplicant")
+#endif
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+AddInterface::AddInterface(WpaGui *_wpagui, QWidget *parent)
+       : QDialog(parent), wpagui(_wpagui)
+{
+       setWindowTitle(tr("Select network interface to add"));
+       resize(400, 200);
+       vboxLayout = new QVBoxLayout(this);
+
+       interfaceWidget = new QTreeWidget(this);
+       interfaceWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
+       interfaceWidget->setUniformRowHeights(true);
+       interfaceWidget->setSortingEnabled(true);
+       interfaceWidget->setColumnCount(3);
+       interfaceWidget->headerItem()->setText(0, tr("driver"));
+       interfaceWidget->headerItem()->setText(1, tr("interface"));
+       interfaceWidget->headerItem()->setText(2, tr("description"));
+       interfaceWidget->setItemsExpandable(FALSE);
+       interfaceWidget->setRootIsDecorated(FALSE);
+       vboxLayout->addWidget(interfaceWidget);
+
+       connect(interfaceWidget,
+               SIGNAL(itemActivated(QTreeWidgetItem *, int)), this,
+               SLOT(interfaceSelected(QTreeWidgetItem *)));
+
+       addInterfaces();
+}
+
+
+void AddInterface::addInterfaces()
+{
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+       struct wpa_ctrl *ctrl;
+       int ret;
+       char buf[2048];
+       size_t len;
+
+       ctrl = wpa_ctrl_open(NULL);
+       if (ctrl == NULL)
+               return;
+
+       len = sizeof(buf) - 1;
+       ret = wpa_ctrl_request(ctrl, "INTERFACE_LIST", 14, buf, &len, NULL);
+       if (ret < 0) {
+               wpa_ctrl_close(ctrl);
+               return;
+       }
+       buf[len] = '\0';
+
+       wpa_ctrl_close(ctrl);
+
+       QString ifaces(buf);
+       QStringList lines = ifaces.split(QRegExp("\\n"));
+       for (QStringList::Iterator it = lines.begin();
+            it != lines.end(); it++) {
+               QStringList arg = (*it).split(QChar('\t'));
+               if (arg.size() < 3)
+                       continue;
+               QTreeWidgetItem *item = new QTreeWidgetItem(interfaceWidget);
+               if (!item)
+                       break;
+
+               item->setText(0, arg[0]);
+               item->setText(1, arg[1]);
+               item->setText(2, arg[2]);
+       }
+
+       interfaceWidget->resizeColumnToContents(0);
+       interfaceWidget->resizeColumnToContents(1);
+       interfaceWidget->resizeColumnToContents(2);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+bool AddInterface::addRegistryInterface(const QString &ifname)
+{
+       HKEY hk, ihk;
+       LONG ret;
+       int id, tmp;
+       TCHAR name[10];
+       DWORD val, i;
+
+       ret = RegOpenKeyEx(WPA_KEY_ROOT, WPA_KEY_PREFIX TEXT("\\interfaces"),
+                          0, KEY_ENUMERATE_SUB_KEYS | KEY_CREATE_SUB_KEY,
+                          &hk);
+       if (ret != ERROR_SUCCESS)
+               return false;
+
+       id = -1;
+
+       for (i = 0; ; i++) {
+               TCHAR name[255];
+               DWORD namelen;
+
+               namelen = 255;
+               ret = RegEnumKeyEx(hk, i, name, &namelen, NULL, NULL, NULL,
+                                  NULL);
+
+               if (ret == ERROR_NO_MORE_ITEMS)
+                       break;
+
+               if (ret != ERROR_SUCCESS)
+                       break;
+
+               if (namelen >= 255)
+                       namelen = 255 - 1;
+               name[namelen] = '\0';
+
+#ifdef UNICODE
+               QString s((QChar *) name, namelen);
+#else /* UNICODE */
+               QString s(name);
+#endif /* UNICODE */
+               tmp = s.toInt();
+               if (tmp > id)
+                       id = tmp;
+       }
+
+       id += 1;
+
+#ifdef UNICODE
+       wsprintf(name, L"%04d", id);
+#else /* UNICODE */
+       os_snprintf(name, sizeof(name), "%04d", id);
+#endif /* UNICODE */
+       ret = RegCreateKeyEx(hk, name, 0, NULL, 0, KEY_WRITE, NULL, &ihk,
+                            NULL);
+       RegCloseKey(hk);
+       if (ret != ERROR_SUCCESS)
+               return false;
+
+#ifdef UNICODE
+       RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ,
+                     (LPBYTE) ifname.unicode(),
+                     (ifname.length() + 1) * sizeof(TCHAR));
+
+#else /* UNICODE */
+       RegSetValueEx(ihk, TEXT("adapter"), 0, REG_SZ,
+                     (LPBYTE) ifname.toLocal8Bit(), ifname.length() + 1);
+#endif /* UNICODE */
+       RegSetValueEx(ihk, TEXT("config"), 0, REG_SZ,
+                     (LPBYTE) TEXT("default"), 8 * sizeof(TCHAR));
+       RegSetValueEx(ihk, TEXT("ctrl_interface"), 0, REG_SZ,
+                     (LPBYTE) TEXT(""), 1 * sizeof(TCHAR));
+       val = 1;
+       RegSetValueEx(ihk, TEXT("skip_on_error"), 0, REG_DWORD, (LPBYTE) &val,
+                     sizeof(val));
+
+       RegCloseKey(ihk);
+       return true;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void AddInterface::interfaceSelected(QTreeWidgetItem *sel)
+{
+       if (!sel)
+               return;
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+       struct wpa_ctrl *ctrl;
+       int ret;
+       char buf[20], cmd[256];
+       size_t len;
+
+       /*
+        * INTERFACE_ADD <ifname>TAB<confname>TAB<driver>TAB<ctrl_interface>TAB
+        * <driver_param>TAB<bridge_name>
+        */
+       snprintf(cmd, sizeof(cmd),
+                "INTERFACE_ADD %s\t%s\t%s\t%s\t%s\t%s",
+                sel->text(1).toAscii().constData(),
+                "default",
+                sel->text(0).toAscii().constData(),
+                "yes", "", "");
+       cmd[sizeof(cmd) - 1] = '\0';
+
+       ctrl = wpa_ctrl_open(NULL);
+       if (ctrl == NULL)
+               return;
+
+       len = sizeof(buf) - 1;
+       ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL);
+       wpa_ctrl_close(ctrl);
+
+       if (ret < 0) {
+               QMessageBox::warning(this, "wpa_gui",
+                                    tr("Add interface command could not be "
+                                       "completed."));
+               return;
+       }
+
+       buf[len] = '\0';
+       if (buf[0] != 'O' || buf[1] != 'K') {
+               QMessageBox::warning(this, "wpa_gui",
+                                    tr("Failed to add the interface."));
+               return;
+       }
+
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+       if (!addRegistryInterface(sel->text(1))) {
+               QMessageBox::information(this, "wpa_gui",
+                                        tr("Failed to add the interface into "
+                                           "registry."));
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       wpagui->selectAdapter(sel->text(1));
+       close();
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/addinterface.h b/wpa_supplicant/wpa_gui-qt4/addinterface.h
new file mode 100644 (file)
index 0000000..9d9476a
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * wpa_gui - AddInterface class
+ * 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 ADDINTERFACE_H
+#define ADDINTERFACE_H
+
+#include <QObject>
+
+#include <QtGui/QDialog>
+#include <QtGui/QTreeWidget>
+#include <QtGui/QVBoxLayout>
+
+class WpaGui;
+
+class AddInterface : public QDialog
+{
+       Q_OBJECT
+
+public:
+       AddInterface(WpaGui *_wpagui, QWidget *parent = 0);
+
+public slots:
+       virtual void interfaceSelected(QTreeWidgetItem *sel);
+
+private:
+       void addInterfaces();
+       bool addRegistryInterface(const QString &ifname);
+
+       QVBoxLayout *vboxLayout;
+       QTreeWidget *interfaceWidget;
+       WpaGui *wpagui;
+};
+
+#endif /* ADDINTERFACE_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp b/wpa_supplicant/wpa_gui-qt4/eventhistory.cpp
new file mode 100644 (file)
index 0000000..1eb0b7b
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * wpa_gui - EventHistory class
+ * Copyright (c) 2005-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.
+ */
+
+#include <QHeaderView>
+#include <QScrollBar>
+
+#include "eventhistory.h"
+
+
+int EventListModel::rowCount(const QModelIndex &) const
+{
+       return msgList.count();
+}
+
+
+int EventListModel::columnCount(const QModelIndex &) const
+{
+       return 2;
+}
+
+
+QVariant EventListModel::data(const QModelIndex &index, int role) const
+{
+       if (!index.isValid())
+               return QVariant();
+
+        if (role == Qt::DisplayRole)
+               if (index.column() == 0) {
+                       if (index.row() >= timeList.size())
+                               return QVariant();
+                       return timeList.at(index.row());
+               } else {
+                       if (index.row() >= msgList.size())
+                               return QVariant();
+                       return msgList.at(index.row());
+               }
+        else
+               return QVariant();
+}
+
+
+QVariant EventListModel::headerData(int section, Qt::Orientation orientation,
+                                   int role) const
+{
+       if (role != Qt::DisplayRole)
+               return QVariant();
+
+       if (orientation == Qt::Horizontal) {
+               switch (section) {
+               case 0:
+                       return QString(tr("Timestamp"));
+               case 1:
+                       return QString(tr("Message"));
+               default:
+                       return QVariant();
+               }
+       } else
+               return QString("%1").arg(section);
+}
+
+
+void EventListModel::addEvent(QString time, QString msg)
+{
+       beginInsertRows(QModelIndex(), msgList.size(), msgList.size() + 1);
+       timeList << time;
+       msgList << msg;
+       endInsertRows();
+}
+
+
+EventHistory::EventHistory(QWidget *parent, const char *, bool, Qt::WFlags)
+       : QDialog(parent)
+{
+       setupUi(this);
+
+       connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
+
+       eventListView->setItemsExpandable(FALSE);
+       eventListView->setRootIsDecorated(FALSE);
+       elm = new EventListModel(parent);
+       eventListView->setModel(elm);
+}
+
+
+EventHistory::~EventHistory()
+{
+       destroy();
+       delete elm;
+}
+
+
+void EventHistory::languageChange()
+{
+       retranslateUi(this);
+}
+
+
+void EventHistory::addEvents(WpaMsgList msgs)
+{
+       WpaMsgList::iterator it;
+       for (it = msgs.begin(); it != msgs.end(); it++)
+               addEvent(*it);
+}
+
+
+void EventHistory::addEvent(WpaMsg msg)
+{
+       bool scroll = true;
+
+       if (eventListView->verticalScrollBar()->value() <
+           eventListView->verticalScrollBar()->maximum())
+               scroll = false;
+
+       elm->addEvent(msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"),
+                     msg.getMsg());
+
+       if (scroll)
+               eventListView->scrollToBottom();
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.h b/wpa_supplicant/wpa_gui-qt4/eventhistory.h
new file mode 100644 (file)
index 0000000..40dff6d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * wpa_gui - EventHistory class
+ * Copyright (c) 2005-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 EVENTHISTORY_H
+#define EVENTHISTORY_H
+
+#include <QObject>
+#include "ui_eventhistory.h"
+
+
+class EventListModel : public QAbstractTableModel
+{
+       Q_OBJECT
+
+public:
+       EventListModel(QObject *parent = 0)
+               : QAbstractTableModel(parent) {}
+
+        int rowCount(const QModelIndex &parent = QModelIndex()) const;
+        int columnCount(const QModelIndex &parent = QModelIndex()) const;
+        QVariant data(const QModelIndex &index, int role) const;
+        QVariant headerData(int section, Qt::Orientation orientation,
+                            int role = Qt::DisplayRole) const;
+       void addEvent(QString time, QString msg);
+
+private:
+       QStringList timeList;
+       QStringList msgList;
+};
+
+
+class EventHistory : public QDialog, public Ui::EventHistory
+{
+       Q_OBJECT
+
+public:
+       EventHistory(QWidget *parent = 0, const char *name = 0,
+                    bool modal = false, Qt::WFlags fl = 0);
+       ~EventHistory();
+
+public slots:
+       virtual void addEvents(WpaMsgList msgs);
+       virtual void addEvent(WpaMsg msg);
+
+protected slots:
+       virtual void languageChange();
+
+private:
+       EventListModel *elm;
+};
+
+#endif /* EVENTHISTORY_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/eventhistory.ui b/wpa_supplicant/wpa_gui-qt4/eventhistory.ui
new file mode 100644 (file)
index 0000000..afe9149
--- /dev/null
@@ -0,0 +1,61 @@
+<ui version="4.0" >
+ <class>EventHistory</class>
+ <widget class="QDialog" name="EventHistory" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>533</width>
+    <height>285</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Event history</string>
+  </property>
+  <layout class="QGridLayout" >
+   <item row="0" column="0" colspan="2" >
+    <widget class="QTreeView" name="eventListView" >
+     <property name="sizePolicy" >
+      <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="verticalScrollBarPolicy" >
+      <enum>Qt::ScrollBarAlwaysOn</enum>
+     </property>
+     <property name="selectionMode" >
+      <enum>QAbstractItemView::NoSelection</enum>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" >
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>40</width>
+       <height>20</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="1" >
+    <widget class="QPushButton" name="closeButton" >
+     <property name="text" >
+      <string>Close</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <includes>
+  <include location="local" >wpamsg.h</include>
+ </includes>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons.qrc b/wpa_supplicant/wpa_gui-qt4/icons.qrc
new file mode 100644 (file)
index 0000000..316ee89
--- /dev/null
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/icons" >
+  <file alias="wpa_gui.svg">icons/wpa_gui.svg</file>
+  <file alias="ap.svg">icons/ap.svg</file>
+  <file alias="laptop.svg">icons/laptop.svg</file>
+ </qresource>
+</RCC>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/README b/wpa_supplicant/wpa_gui-qt4/icons/README
new file mode 100644 (file)
index 0000000..d73eed5
--- /dev/null
@@ -0,0 +1,39 @@
+wpa_gui icon files
+
+To convert the svg icons to other formats, make sure inkscape and imagemagick
+are installed and use `make' to create various sized png and xpm icons.
+
+
+wpa_gui.svg
+-----------
+
+Copyright (c) 2008 Bernard Gray <bernard.gray@gmail.com>
+
+The wpa_gui icon is licensed under the GPL version 2. Alternatively, the icon
+may be distributed under the terms of BSD license.
+
+
+ap.svg
+------
+
+mystica_Wireless_Router.svg
+
+http://openclipart.org/media/files/mystica/8390
+Wireless Router
+by:     mystica
+last change:    April 20, 2008 10:32 pm (File added)
+date:   April 20, 2008 10:31 pm
+license: PD
+
+
+laptop.svg
+----------
+
+metalmarious_Laptop.svg
+
+http://openclipart.org/media/files/metalmarious/4056
+Laptop
+by:      metalmarious
+last change:    May 18, 2008 07:04 pm (File added)
+date:   August 27, 2007 04:44 am
+license: PD
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/ap.svg b/wpa_supplicant/wpa_gui-qt4/icons/ap.svg
new file mode 100644 (file)
index 0000000..51cc8ce
--- /dev/null
@@ -0,0 +1,832 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="546"
+   height="482.67157"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1+0.46pre1+devel"
+   sodipodi:docname="Wireless Router.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   version="1.0"
+   inkscape:export-filename="C:\Documents and Settings\Dan\Skrivbord\Clipart egna (InkScape)\Original\Kanske Upload\Wireless Router.png"
+   inkscape:export-xdpi="310"
+   inkscape:export-ydpi="310">
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="-50 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       id="perspective148" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="-50 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       id="perspective138" />
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="-50 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       id="perspective10" />
+    <inkscape:perspective
+       id="perspective2395"
+       inkscape:persp3d-origin="300 : 400 : 1"
+       inkscape:vp_z="700 : 600 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_x="-50 : 600 : 1"
+       sodipodi:type="inkscape:persp3d" />
+    <filter
+       inkscape:collect="always"
+       id="filter3304">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.27999283"
+         id="feGaussianBlur3306" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3336">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.5315371"
+         id="feGaussianBlur3338" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3368">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.26473573"
+         id="feGaussianBlur3370" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3564"
+       x="-0.37202433"
+       width="1.7440487"
+       y="-0.43252525"
+       height="1.8650506">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.1017616"
+         id="feGaussianBlur3566" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3748"
+       x="-0.41952851"
+       width="1.839057"
+       y="-0.39121628"
+       height="1.7824326">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.1235829"
+         id="feGaussianBlur3750" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3862"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3864" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3866"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-0.20756502"
+       height="1.4151301">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3868" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3870"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3872" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3874"
+       x="-0.3380883"
+       width="1.6761765"
+       y="-0.21154897"
+       height="1.4230978">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3876" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3878"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3880" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3882"
+       x="-0.36018598"
+       width="1.720372"
+       y="-0.20953795"
+       height="1.4190758">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3884" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3886"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3888" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3890"
+       x="-0.35439494"
+       width="1.7087899"
+       y="-0.20953795"
+       height="1.4190758">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3892" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3894"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3896" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3898"
+       x="-0.38537359"
+       width="1.7707472"
+       y="-0.20562869"
+       height="1.4112574">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3900" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3902"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3904" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3906"
+       x="-0.38537359"
+       width="1.7707472"
+       y="-0.21359873"
+       height="1.4271975">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3908" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3910"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3912" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3914"
+       x="-0.36018598"
+       width="1.720372"
+       y="-0.20562869"
+       height="1.4112574">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3916" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3918"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3920" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3922"
+       x="-0.36616942"
+       width="1.7323389"
+       y="-0.20953795"
+       height="1.4190758">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3924" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3926"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3928" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3930"
+       x="-0.34878778"
+       width="1.6975756"
+       y="-0.20186263"
+       height="1.4037253">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3932" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3934"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3936" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3938"
+       x="-0.32802677"
+       width="1.6560535"
+       y="-0.21568884"
+       height="1.4313776">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3940" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3942"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3944" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3946"
+       x="-0.32321677"
+       width="1.6464336"
+       y="-0.21568884"
+       height="1.4313776">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3948" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3950"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3952" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3954"
+       x="-0.3185463"
+       width="1.6370926"
+       y="-0.21359873"
+       height="1.4271975">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3956" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3958"
+       x="-0.33298156"
+       width="1.6659631"
+       y="-1.6699424"
+       height="4.3398848">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3960" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3962"
+       x="-0.28553614"
+       width="1.5710723"
+       y="-0.21568884"
+       height="1.4313776">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.82006695"
+         id="feGaussianBlur3964" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3982"
+       x="-0.0048889387"
+       width="1.0097779"
+       y="-0.26385465"
+       height="1.5277092">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.0547249"
+         id="feGaussianBlur3984" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3996">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.8032234"
+         id="feGaussianBlur3998" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3517"
+       x="-0.25713229"
+       width="1.5142646"
+       y="-0.087099633"
+       height="1.1741993">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.33984317"
+         id="feGaussianBlur3519" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3329"
+       x="-0.18025071"
+       width="1.3605014"
+       y="-1.1780664"
+       height="3.3561328">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3331" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3333"
+       x="-0.15131117"
+       width="1.3026223"
+       y="-0.1853139"
+       height="1.3706278">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3335" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3337"
+       x="-0.14412392"
+       width="1.2882478"
+       y="-0.18013415"
+       height="1.3602683">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3339" />
+    </filter>
+    <filter
+       inkscape:collect="always"
+       id="filter3341"
+       x="-1.1780664"
+       width="3.3561328"
+       y="-0.23067047"
+       height="1.4613409">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="0.49086099"
+         id="feGaussianBlur3343" />
+    </filter>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.98994949"
+     inkscape:cx="233.05018"
+     inkscape:cy="176.49031"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer2"
+     showgrid="false"
+     inkscape:window-width="1152"
+     inkscape:window-height="838"
+     inkscape:window-x="0"
+     inkscape:window-y="0" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:groupmode="layer"
+     id="layer2"
+     transform="translate(-45.788597,-496.6196)">
+    <path
+       style="fill:#606060;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 75.574315,977.86259 L 535.56828,979.20564 L 564.86002,979.29116 C 564.86002,979.29116 573.43146,977.86259 573.43146,973.57687 C 573.43146,969.29116 574.14573,959.29117 574.14573,959.29117 L 566.03832,959.7296 L 72.464255,956.66717 L 58.640665,955.63307 C 58.640665,955.63307 59.860025,973.57688 65.574315,975.71973 C 71.288595,977.86259 75.574315,977.14831 75.574315,977.86259 z"
+       id="path2402"
+       sodipodi:nodetypes="cccsccccsc" />
+    <path
+       style="fill:#dddddd;fill-opacity:1;fill-rule:evenodd;stroke:#b2b2b2;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 67.002885,957.14831 L 566.28859,960.00545 C 566.28859,960.00545 577.71717,959.29116 583.43146,955.71973 C 589.14574,952.14831 588.43146,942.86259 588.43146,942.86259 C 588.43146,942.86259 591.28859,907.14831 591.28859,902.14831 C 591.28859,897.14831 574.86002,839.29116 572.00288,827.86259 C 569.14574,816.43402 557.00288,757.1483 557.00288,757.1483 C 557.00288,757.1483 555.57431,749.29116 552.71717,748.57688 C 549.86002,747.86259 548.43146,748.57688 548.43146,748.57688 L 558.43146,797.86259 L 577.71717,880.00545 L 578.07431,881.34473 L 579.05004,882.28742 L 584.86002,897.14831 C 584.86002,897.14831 584.93925,904.12528 581.70701,906.43402 C 576.70701,910.00545 557.71717,910.71973 557.71717,910.71973 L 68.431455,907.86259 C 68.431455,907.86259 57.002885,906.43402 54.145745,903.57688 C 51.288595,900.71973 52.298745,895.51053 52.298745,895.51053 L 94.860025,745.00545 C 94.860025,745.00545 86.288605,747.86259 84.145745,752.1483 C 82.002885,756.43402 71.288595,811.43402 68.431455,820.00545 C 65.574315,828.57688 47.002885,893.57688 47.002885,893.57688 C 47.002885,893.57688 46.288597,900.00545 46.288597,903.57688 C 46.288597,907.14831 48.431455,946.43402 48.431455,946.43402 C 48.431455,946.43402 52.002885,953.57688 55.574315,955.00545 C 59.145745,956.43402 68.431455,957.14831 67.002885,957.14831 z"
+       id="path2404"
+       sodipodi:nodetypes="ccscsscsccccccsccsccsscscsc" />
+    <path
+       style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3336)"
+       d="M 562.71717,958.57688 C 562.71717,958.57688 572.00288,957.86259 572.00288,951.43402 C 572.00288,945.00545 575.57431,922.14831 572.00288,918.57688 C 568.43146,915.00545 564.86002,912.86259 564.86002,912.86259 C 564.86002,912.86259 586.28859,903.57688 585.57431,907.86259 C 584.86002,912.14831 581.28859,914.29116 581.28859,920.00545 C 581.28859,925.71973 580.57431,948.57688 580.57431,948.57688 C 580.57431,948.57688 578.43146,952.86259 581.28859,953.57688 C 584.14574,954.29116 582.71717,955.71973 582.71717,955.71973 L 576.28859,957.86259 L 570.57431,958.57688 L 562.71717,958.57688 z"
+       id="path2406" />
+    <path
+       style="fill:#ededed;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 75.574315,913.57688 L 560.57431,915.71973 L 557.71717,955.71973 L 77.002885,954.29116 L 75.574315,913.57688 z"
+       id="path2408" />
+    <path
+       style="fill:#020202;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 100.57432,925.00545 L 541.28859,927.86259 C 541.28859,927.86259 548.43146,932.14831 548.43146,936.43402 C 548.43146,940.71973 540.57431,945.71973 540.57431,945.71973 L 98.431455,942.86259 C 98.431455,942.86259 89.145745,938.57688 89.860025,932.86259 C 90.574315,927.14831 101.2886,924.29116 100.57432,925.00545 z"
+       id="path2410" />
+    <path
+       style="fill:#121212;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 71.497795,907.90591 L 557.85419,910.32545 L 573.61002,909.11259 C 573.61002,909.11259 581.28967,908.48718 583.43146,904.46973 C 584.17421,903.07651 585.22722,901.79095 585.2275,899.8245 C 585.22862,892.20247 577.89573,880.63045 577.89573,880.63045 C 577.89573,880.63045 578.20783,882.29016 577.30114,882.74322 C 575.50038,883.64304 573.664,884.53037 571.28859,885.00545 C 567.71717,885.71973 66.036055,879.91879 66.036055,879.91879 L 59.860025,880.00545 L 56.288605,877.68402 L 52.002885,896.43402 C 52.002885,896.43402 51.828665,903.29437 57.673845,905.51053 C 62.003235,907.15199 72.212085,908.6202 71.497795,907.90591 z"
+       id="path2412"
+       sodipodi:nodetypes="cccsscssccccsc" />
+    <path
+       style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:#252525;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 93.431455,743.57688 L 59.145745,868.57688 C 59.145745,868.57688 57.538605,871.96973 60.574315,873.57688 C 65.381975,876.12212 74.681455,875.18402 74.681455,875.18402 C 74.681455,875.18402 567.00288,879.29116 569.86002,878.57688 C 572.71717,877.86259 575.03859,876.96974 575.03859,876.96974 L 575.21717,868.75545 C 575.21717,868.75545 579.61685,882.27622 576.28859,883.75545 C 573.07431,885.18402 566.28859,885.00545 566.28859,885.00545 L 64.860025,880.71973 L 58.431455,879.29116 L 56.645745,876.79116 L 57.861935,870.85997 L 93.431455,743.57688 z"
+       id="path2414"
+       sodipodi:nodetypes="ccscsccscccccc" />
+    <path
+       style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#d1d1d1;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3748)"
+       d="M 62.002885,879.82688 L 62.181455,874.29116 L 60.217165,873.93402 L 57.360025,874.82688 L 56.824315,876.25545 C 56.824315,876.25545 56.467165,877.86259 57.360025,878.21973 C 58.252885,878.57688 59.860025,879.11259 59.860025,879.11259 L 62.002885,879.82688 z"
+       id="path2416" />
+    <path
+       style="fill:#d5d5d5;fill-opacity:1;fill-rule:evenodd;stroke:#dadada;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3564)"
+       d="M 571.82431,883.04116 C 571.82431,882.32688 570.57431,879.82688 572.18146,878.93402 C 573.78859,878.04116 575.03859,878.57688 575.03859,878.57688 C 575.03859,878.57688 578.07431,881.25545 577.36002,881.43402 C 576.64574,881.61259 576.46717,882.50545 574.68146,883.21973 C 572.89574,883.93402 572.36002,883.21973 571.82431,883.04116 z"
+       id="path2418" />
+    <path
+       style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3341)"
+       d="M 194.42891,930.61513 L 194.68145,934.46973"
+       id="path2420"
+       sodipodi:nodetypes="cc" />
+    <path
+       style="fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#2aea00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3337)"
+       d="M 192.18145,932.32688 C 192.18145,932.32688 189.18906,937.68402 194.50288,937.86259 C 200.57352,938.06659 197.36003,932.50545 197.36003,932.50545"
+       id="path2422"
+       sodipodi:nodetypes="csc" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#444444;stroke-width:0.92299998;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2424"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="translate(-57.282828,191.92898)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#101010;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:1.92626119;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path2439"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.4791666,0,0,0.4791666,93.755115,578.94427)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3211"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.5109914,0,0,0.5109914,116.22806,556.99816)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3213"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.5109914,0,0,0.5109914,122.47805,556.99816)" />
+    <path
+       sodipodi:type="arc"
+       style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#7b7b7b;stroke-width:1.70648665;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+       id="path3215"
+       sodipodi:cx="289.82144"
+       sodipodi:cy="742.89789"
+       sodipodi:rx="4.2857141"
+       sodipodi:ry="4.2857141"
+       d="M 294.10716,742.89789 A 4.2857141,4.2857141 0 1 1 285.53573,742.89789 A 4.2857141,4.2857141 0 1 1 294.10716,742.89789 z"
+       transform="matrix(0.5109914,0,0,0.5109914,119.44234,552.71244)" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#606060;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 308.07431,934.29116 C 308.07431,934.29116 307.00288,935.71973 306.28859,936.61259 C 305.57431,937.50544 305.57431,939.11259 304.32431,938.57688 C 303.07431,938.04116 301.11002,936.07688 301.28859,934.64831 C 301.46717,933.21974 304.32431,931.07688 304.32431,931.07688 C 304.32431,931.07688 305.03859,932.1483 306.11002,932.86259 C 307.18145,933.57688 307.89574,934.11259 308.07431,934.29116 z"
+       id="path3217"
+       sodipodi:nodetypes="cssscsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3333)"
+       d="M 336.28859,931.43402 L 340.57431,931.43402 L 341.82431,932.68402 L 341.82431,935.00545 L 340.93145,936.79116 L 336.46717,936.79116 L 335.03859,935.71973 L 335.03859,932.50545 L 336.28859,931.43402 z"
+       id="path3221"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3329)"
+       d="M 335.75288,938.57688 L 341.28859,938.57688"
+       id="path3223" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 369.86002,931.43402 L 374.14574,931.43402 L 375.39574,932.68402 L 375.39574,935.00545 L 374.50288,936.79116 L 370.0386,936.79116 L 368.61002,935.71973 L 368.61002,932.50545 L 369.86002,931.43402 z"
+       id="path3225"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 369.32431,938.57688 L 374.86002,938.57688"
+       id="path3227" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 407.00288,931.79116 L 411.2886,931.79116 L 412.5386,933.04116 L 412.5386,935.36259 L 411.64574,937.1483 L 407.18147,937.1483 L 405.75288,936.07687 L 405.75288,932.86259 L 407.00288,931.79116 z"
+       id="path3229"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 406.46717,938.93402 L 412.00288,938.93402"
+       id="path3231" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 444.14574,931.96973 L 448.43145,931.96973 L 449.68145,933.21973 L 449.68145,935.54116 L 448.78859,937.32687 L 444.32431,937.32687 L 442.89574,936.25544 L 442.89574,933.04116 L 444.14574,931.96973 z"
+       id="path3233"
+       sodipodi:nodetypes="ccccccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#4d4d4d;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 443.61002,939.11259 L 449.14574,939.11259"
+       id="path3235" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#28cc03;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican;filter:url(#filter3517)"
+       d="M 329.05831,937.86898 L 329.10629,932.30075 L 328.72213,932.54098 C 328.65816,932.59689 328.60226,932.62485 328.55441,932.62484 C 328.52242,932.62485 328.47243,932.59085 328.40444,932.52285 C 328.33646,932.45487 328.30245,932.38486 328.30245,932.31283 C 328.30245,932.22495 328.34841,932.12497 328.44034,932.0129 C 328.53225,931.90085 328.62618,931.78879 328.72213,931.67672 L 329.68234,930.33273 L 330.47445,930.78903 L 330.46236,938.5527 C 330.46236,938.64889 330.38631,938.69699 330.23421,938.69699 C 330.1783,938.69699 330.13437,938.69296 330.10238,938.6849 C 329.86239,938.62094 329.6224,938.56088 329.38241,938.50472 C 329.16634,938.40072 329.05831,938.18881 329.05831,937.86898 L 329.05831,937.86898 z"
+       id="text3237" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 364.94248,932.84854 C 364.94246,933.5126 364.7065,934.32059 364.23458,935.27249 C 363.84249,936.05667 363.33443,936.86868 362.71041,937.70852 L 362.50643,937.97256 L 363.8984,937.94876 C 364.14644,937.94876 364.37044,937.97476 364.5704,938.02676 C 364.77034,938.07876 364.91036,938.16067 364.99044,938.27249 C 365.16646,938.51248 365.25447,938.70449 365.25448,938.84854 C 365.25447,938.98452 365.19448,939.06851 365.07449,939.10049 C 364.95449,939.13247 364.85042,939.14846 364.76229,939.14846 L 360.97054,939.34072 L 360.5743,937.93667 C 360.83846,937.62466 361.10653,937.31265 361.3785,937.00064 C 361.96248,936.20865 362.44246,935.46463 362.81844,934.76858 C 363.3304,933.83255 363.58638,933.12857 363.58639,932.65664 C 363.58638,932.55264 363.5784,932.46866 363.5624,932.40469 C 363.54641,932.34073 363.51046,932.30875 363.45455,932.30874 C 363.30245,932.30875 362.99044,932.50064 362.51852,932.88442 C 362.21456,933.13248 361.81051,933.50052 361.30636,933.98855 C 360.90646,934.38065 360.70248,934.58462 360.69442,934.60049 L 360.02242,933.65273 C 360.02242,933.58853 360.19845,933.3605 360.5505,932.96865 C 360.95846,932.51261 361.36642,932.1326 361.77438,931.82864 C 362.32638,931.42069 362.79439,931.21671 363.17843,931.2167 C 363.40255,931.21671 363.59053,931.27664 363.74239,931.39651 C 364.29439,931.80448 364.60237,932.04849 364.66634,932.12856 C 364.85042,932.35269 364.94246,932.59268 364.94248,932.84854 L 364.94248,932.84854 z"
+       id="text3241" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 398.55154,939.8793 L 397.90371,938.83523 L 398.62368,938.71511 C 399.16762,938.61917 399.70754,938.33914 400.24343,937.87502 C 400.73952,937.44314 400.98757,937.09915 400.98757,936.84304 C 400.98757,936.69119 400.84755,936.55526 400.56753,936.43527 C 400.2875,936.31527 399.97951,936.25528 399.64358,936.25527 C 399.42751,936.25528 399.23348,936.28128 399.06149,936.33328 C 398.88949,936.38528 398.74355,936.47525 398.62368,936.60317 L 398.17947,936.27908 L 398.17947,935.23501 L 398.50356,935.079 C 399.04751,934.83902 399.59548,934.46304 400.14748,933.95107 C 400.69948,933.43912 400.97548,933.05521 400.97549,932.79934 C 400.97548,932.7432 400.95547,932.6951 400.91544,932.65505 C 400.81948,932.55911 400.65957,932.51114 400.43569,932.51113 C 400.10756,932.51114 399.69155,932.61112 399.18765,932.81106 C 398.7797,932.97122 398.5436,933.08328 398.47939,933.14724 L 397.97549,932.00723 C 398.06363,931.91129 398.32363,931.77127 398.75552,931.58718 C 399.29165,931.36307 399.75564,931.25101 400.14748,931.251 C 400.25955,931.25101 400.3716,931.26309 400.48367,931.28726 C 400.85965,931.37516 401.16762,931.52713 401.40762,931.74319 C 401.59169,931.90311 401.78371,932.15506 401.98367,932.49905 C 402.03151,932.57913 402.06149,932.6572 402.07357,932.73324 C 402.08565,932.8093 402.09169,932.88333 402.0917,932.95535 C 402.09169,933.25125 401.98366,933.5712 401.76761,933.91519 C 401.55154,934.25919 401.28347,934.56717 400.9634,934.83914 L 400.50747,935.21121 C 400.8915,935.21121 401.28951,935.43722 401.7015,935.88925 C 402.11348,936.34128 402.31948,936.8273 402.31948,937.34731 C 402.31948,937.65127 402.24947,937.91726 402.10947,938.14529 C 401.96944,938.37332 401.76748,938.6033 401.50356,938.83523 C 401.16762,939.13113 400.79567,939.37112 400.38772,939.5552 C 399.88357,939.78713 399.3915,939.9031 398.91152,939.9031 C 398.84756,939.9031 398.78158,939.89913 398.71359,939.8912 C 398.64559,939.88326 398.59157,939.8793 398.55154,939.8793 L 398.55154,939.8793 z"
+       id="text3245" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:#939393;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 433.97515,936.58822 L 436.87884,931.5964 L 438.17486,932.11239 L 438.01885,936.03634 L 438.42718,936.13229 C 438.62712,936.1882 438.73503,936.22018 438.75091,936.22824 C 438.799,936.26022 438.83904,936.31222 438.87103,936.38424 C 438.93499,936.52829 438.98094,936.63431 439.0089,936.7023 C 439.03686,936.77029 439.05083,936.84823 439.05083,936.93612 C 439.05083,937.00033 439.03887,937.06442 439.01495,937.12838 L 437.92291,937.20016 L 437.93499,939.73214 C 437.93499,939.94015 437.86296,940.03219 437.71893,940.00826 C 437.31902,939.95236 437.08709,939.91232 437.02312,939.88815 C 436.78313,939.78414 436.66314,939.57223 436.66314,939.25241 L 436.66314,937.28439 L 434.71893,937.28439 L 433.97515,936.58822 z M 435.48687,936.08431 L 436.80706,936.10812 L 437.01104,933.13229 L 435.48687,936.08431 z"
+       id="text3249" />
+    <path
+       style="font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#979797;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Dominican;-inkscape-font-specification:Dominican"
+       d="M 109.34409,932.3674 C 109.08016,933.58322 108.9001,934.43124 108.80392,934.91146 C 108.65206,935.67123 108.49617,936.52719 108.33628,937.47933 L 107.04026,936.98715 L 106.27194,932.81124 L 105.39597,937.73129 L 104.01609,937.28744 L 103.3082,929.55929 C 103.3082,929.47141 103.38021,929.42746 103.52426,929.42745 C 103.63608,929.43527 103.71604,929.43918 103.76413,929.43917 C 103.91622,929.43918 104.04623,929.46518 104.15414,929.51718 C 104.26206,929.56919 104.32004,929.65927 104.3281,929.78744 L 104.77194,935.69113 L 105.64792,929.97933 L 107.04026,930.03939 L 107.65219,935.5234 C 107.76425,934.39523 107.95627,933.16318 108.22824,931.82723 C 108.35617,931.17124 108.46811,930.65927 108.56405,930.29135 C 108.63607,930.00327 108.78011,929.49521 108.99618,928.76718 C 109.02011,928.7352 109.04806,928.69919 109.08005,928.65914 C 109.15206,928.58713 109.23213,928.55918 109.32028,928.57528 C 109.55221,928.61533 109.73819,928.69132 109.87821,928.80325 C 110.0182,928.91519 110.08821,929.06723 110.08822,929.25936 C 109.91219,929.97129 109.78413,930.48728 109.70407,930.80734 C 109.57613,931.32736 109.45614,931.84738 109.34409,932.3674 L 109.34409,932.3674 z M 111.18136,931.43136 L 111.18136,930.65133 C 111.18136,930.49924 111.22733,930.36521 111.31924,930.24923 C 111.41116,930.13327 111.52914,930.07529 111.67319,930.07528 C 111.80917,930.07529 111.9332,930.12729 112.04525,930.23129 C 112.15732,930.3353 112.21335,930.45932 112.21335,930.60336 L 112.21335,931.68331 C 112.21335,931.79538 112.16135,931.87741 112.05734,931.92941 C 111.95334,931.98141 111.82932,932.00742 111.68527,932.00741 C 111.54122,932.00742 111.42123,931.95139 111.32529,931.83932 C 111.22933,931.72726 111.18136,931.59128 111.18136,931.43136 L 111.18136,931.43136 z M 111.13339,937.22738 L 111.13339,932.83541 C 111.13339,932.64328 111.24533,932.54721 111.46921,932.54721 C 111.74117,932.54721 111.93515,932.58121 112.05112,932.64919 C 112.16708,932.71719 112.22507,932.81125 112.22507,932.93136 L 112.1174,937.67123 C 112.1174,937.76718 112.04538,937.81515 111.90134,937.81515 C 111.82126,937.81515 111.75118,937.80715 111.69112,937.79116 C 111.63107,937.77517 111.58505,937.76327 111.55307,937.75546 C 111.27329,937.66732 111.13339,937.4913 111.13339,937.22738 L 111.13339,937.22738 z M 113.68845,932.8713 L 113.79648,932.73947 L 114.0726,932.73947 C 114.13657,932.73947 114.21053,932.77743 114.29452,932.85336 C 114.37851,932.92929 114.44455,932.97531 114.49265,932.99142 C 114.60446,933.02341 114.69638,932.93741 114.76839,932.73342 C 114.84042,932.52945 114.92641,932.41342 115.0264,932.38534 C 115.12636,932.35727 115.19638,932.34323 115.23641,932.34323 C 115.38852,932.34323 115.52255,932.39725 115.63852,932.50527 C 115.75449,932.61331 115.81247,932.74326 115.81247,932.89511 L 115.80039,933.36312 C 115.80038,933.5953 115.77237,933.80539 115.71634,933.99337 C 115.6603,934.18136 115.57236,934.27536 115.45248,934.27535 C 115.26839,934.27536 115.14437,934.0953 115.08042,933.73519 C 115.04843,933.55917 114.9924,933.47116 114.91233,933.47116 C 114.84054,933.47116 114.78261,933.53116 114.73855,933.65115 C 114.69449,933.77115 114.67246,934.0112 114.67246,934.3713 C 114.67246,934.48337 114.67246,934.58139 114.67246,934.66537 C 114.67246,934.74936 114.67246,934.81527 114.67246,934.86312 L 114.67246,937.79135 C 114.58456,937.83138 114.48459,937.8514 114.37252,937.8514 C 114.30051,937.8514 114.2125,937.83938 114.10849,937.81533 C 114.00449,937.79128 113.90451,937.73324 113.80857,937.6412 C 113.71261,937.54916 113.66464,937.43124 113.66464,937.28744 L 113.68845,932.8713 z M 119.45774,933.78317 L 119.52951,935.22311 L 117.82552,935.49923 C 117.68173,935.53122 117.58579,935.56723 117.53769,935.60726 C 117.43368,935.68734 117.38168,935.7994 117.38169,935.94345 L 117.40548,936.49533 C 117.40548,936.6152 117.44351,936.72518 117.51955,936.82528 C 117.59561,936.92538 117.68563,936.9714 117.78964,936.96334 C 118.01376,936.93136 118.29379,936.75534 118.62973,936.43527 C 118.96567,936.1152 119.11764,935.91916 119.08567,935.84713 C 119.11764,935.83126 119.16965,935.82333 119.24167,935.82333 C 119.37765,935.82333 119.47763,935.86734 119.5416,935.95535 C 119.60556,936.04336 119.63754,936.13534 119.63755,936.23129 C 119.63754,936.27133 119.62557,936.32333 119.60165,936.38729 C 119.55356,936.57138 119.48556,936.73141 119.39767,936.8674 C 119.35763,936.93136 119.24556,937.07138 119.0615,937.28744 C 118.90964,937.46322 118.75571,937.59115 118.59969,937.67123 C 118.44369,937.75131 118.25765,937.79135 118.0416,937.79135 C 117.89755,937.79135 117.77956,937.77731 117.68766,937.74923 C 117.59573,937.72116 117.50168,937.6672 117.40548,937.58737 L 116.78182,937.0234 C 116.75765,937.00729 116.73361,936.97122 116.70969,936.91519 C 116.68576,936.85916 116.66574,936.81515 116.64963,936.78317 C 116.59372,936.63937 116.56576,936.48739 116.56576,936.32723 L 116.56576,935.3912 C 116.56576,934.78329 116.69974,934.1553 116.96768,933.50723 C 117.23562,932.85916 117.52158,932.53513 117.82552,932.53512 C 117.87363,932.53513 117.90964,932.53915 117.93357,932.54721 C 118.08566,932.59531 118.27969,932.63132 118.51566,932.65524 C 118.75162,932.67917 118.93563,932.76315 119.06771,932.90719 C 119.19979,933.05124 119.29782,933.18924 119.36178,933.32119 C 119.42575,933.45315 119.45773,933.60715 119.45774,933.78317 L 119.45774,933.78317 z M 118.58176,933.77145 C 118.58176,933.63547 118.55576,933.51547 118.50376,933.41146 C 118.45175,933.30746 118.36972,933.23935 118.25765,933.20712 C 118.13754,933.1832 118.0256,933.31125 117.92185,933.59127 C 117.84982,933.79123 117.80173,933.93124 117.77756,934.01132 C 117.71358,934.17929 117.66562,934.32327 117.63363,934.44326 C 117.60165,934.56326 117.58566,934.69125 117.58567,934.82723 L 118.58176,934.59945 L 118.58176,933.77145 z M 120.69589,937.04721 L 120.80356,929.73947 C 120.80356,929.64328 120.85361,929.5712 120.95371,929.52322 C 121.0538,929.47525 121.16379,929.46323 121.28366,929.48715 C 121.45163,929.51914 121.56564,929.57517 121.6257,929.65524 C 121.68576,929.73532 121.71579,929.85129 121.71579,930.00314 L 121.53562,937.61117 C 121.53561,937.6993 121.45969,937.74337 121.30783,937.74337 C 121.25168,937.74337 121.19162,937.73532 121.12766,937.7192 C 120.91965,937.62326 120.79568,937.5433 120.75576,937.47933 C 120.71585,937.41537 120.69589,937.27133 120.69589,937.04721 L 120.69589,937.04721 z M 125.71554,933.78317 L 125.78733,935.22311 L 124.08335,935.49923 C 123.93955,935.53122 123.84359,935.56723 123.7955,935.60726 C 123.6915,935.68734 123.6395,935.7994 123.6395,935.94345 L 123.6633,936.49533 C 123.6633,936.6152 123.70132,936.72518 123.77738,936.82528 C 123.85343,936.92538 123.94344,936.9714 124.04746,936.96334 C 124.27158,936.93136 124.5516,936.75534 124.88755,936.43527 C 125.22348,936.1152 125.37546,935.91916 125.34348,935.84713 C 125.37546,935.83126 125.42746,935.82333 125.49948,935.82333 C 125.63547,935.82333 125.73543,935.86734 125.79941,935.95535 C 125.86337,936.04336 125.89534,936.13534 125.89535,936.23129 C 125.89534,936.27133 125.88339,936.32333 125.85947,936.38729 C 125.81136,936.57138 125.74338,936.73141 125.65548,936.8674 C 125.61544,936.93136 125.50339,937.07138 125.31931,937.28744 C 125.16745,937.46322 125.01352,937.59115 124.85752,937.67123 C 124.7015,937.75131 124.51547,937.79135 124.29941,937.79135 C 124.15537,937.79135 124.03737,937.77731 123.94547,937.74923 C 123.85354,937.72116 123.75949,937.6672 123.6633,937.58737 L 123.03964,937.0234 C 123.01547,937.00729 122.99142,936.97122 122.9675,936.91519 C 122.94357,936.85916 122.92355,936.81515 122.90743,936.78317 C 122.85153,936.63937 122.82358,936.48739 122.82358,936.32723 L 122.82358,935.3912 C 122.82358,934.78329 122.95755,934.1553 123.22549,933.50723 C 123.49344,932.85916 123.77938,932.53513 124.08335,932.53512 C 124.13144,932.53513 124.16745,932.53915 124.19137,932.54721 C 124.34348,932.59531 124.53751,932.63132 124.77347,932.65524 C 125.00942,932.67917 125.19344,932.76315 125.32552,932.90719 C 125.45761,933.05124 125.55562,933.18924 125.6196,933.32119 C 125.68356,933.45315 125.71554,933.60715 125.71554,933.78317 L 125.71554,933.78317 z M 124.83956,933.77145 C 124.83956,933.63547 124.81357,933.51547 124.76157,933.41146 C 124.70955,933.30746 124.62752,933.23935 124.51547,933.20712 C 124.39535,933.1832 124.28341,933.31125 124.17965,933.59127 C 124.10764,933.79123 124.05954,933.93124 124.03537,934.01132 C 123.97141,934.17929 123.92343,934.32327 123.89145,934.44326 C 123.85947,934.56326 123.84348,934.69125 123.84348,934.82723 L 124.83956,934.59945 L 124.83956,933.77145 z M 126.83358,937.26327 C 126.73764,937.19931 126.68966,937.09933 126.68966,936.96334 C 126.68966,936.88327 126.71366,936.80722 126.76163,936.73519 C 126.8096,936.66317 126.86758,936.60916 126.93558,936.57315 C 127.00357,936.53714 127.06955,936.53524 127.13352,936.56747 C 127.34152,936.70346 127.49752,936.80343 127.60153,936.8674 C 127.80148,936.99533 127.94942,937.05929 128.04538,937.05929 C 128.14937,937.05929 128.2134,937.02127 128.23746,936.94522 C 128.2615,936.86917 128.27353,936.81918 128.27353,936.79525 C 128.27353,936.75521 128.27353,936.72726 128.27353,936.71139 C 128.27353,936.54342 128.18149,936.37936 127.99741,936.2192 C 127.82138,936.08322 127.64737,935.94522 127.47537,935.8052 C 127.30338,935.66519 127.17141,935.52719 127.0795,935.3912 C 126.98757,935.25522 126.92362,935.09921 126.88761,934.92318 C 126.85159,934.74716 126.83358,934.59518 126.83358,934.46725 C 126.83358,934.05929 126.94956,933.6893 127.18149,933.35726 C 127.41342,933.02524 127.70944,932.85922 128.06955,932.85922 C 128.17355,932.85922 128.23752,932.85922 128.26145,932.85922 C 128.3176,932.85922 128.35764,932.86728 128.38155,932.88339 L 128.95761,933.23129 C 129.08553,933.31137 129.17355,933.42343 129.22164,933.56747 C 129.24556,933.63144 129.2695,933.78732 129.29343,934.03512 C 129.29343,934.21921 129.28341,934.37527 129.2634,934.50332 C 129.24338,934.63138 129.20139,934.76742 129.13742,934.91146 L 128.5134,934.91146 C 128.48946,934.84726 128.4515,934.79318 128.39951,934.74923 C 128.3475,934.70529 128.32149,934.48337 128.32151,934.08346 C 128.32149,933.9714 128.32149,933.85531 128.32151,933.73519 C 128.29757,933.67123 128.26157,933.63925 128.21347,933.63925 C 128.13363,933.63925 128.03366,933.73526 127.91354,933.92727 C 127.79343,934.11929 127.72141,934.28732 127.69747,934.43136 C 127.67355,934.6152 127.71761,934.79123 127.82968,934.95944 C 127.90951,935.07931 128.05344,935.22323 128.26145,935.3912 C 128.46945,935.55917 128.68552,935.72714 128.90964,935.89511 C 129.16549,936.12728 129.29343,936.3674 129.29343,936.61544 C 129.29343,936.92745 129.15347,937.21145 128.87357,937.46743 C 128.59366,937.72341 128.32968,937.8514 128.08164,937.8514 C 127.89755,937.8514 127.72549,937.81539 127.56546,937.74337 C 127.40543,937.67135 127.16146,937.51132 126.83358,937.26327 L 126.83358,937.26327 z M 130.2789,937.26327 C 130.18295,937.19931 130.13497,937.09933 130.13497,936.96334 C 130.13497,936.88327 130.15896,936.80722 130.20694,936.73519 C 130.25492,936.66317 130.31289,936.60916 130.38089,936.57315 C 130.44888,936.53714 130.51486,936.53524 130.57882,936.56747 C 130.78683,936.70346 130.94284,936.80343 131.04685,936.8674 C 131.24679,936.99533 131.39475,937.05929 131.49069,937.05929 C 131.5947,937.05929 131.65872,937.02127 131.68277,936.94522 C 131.70682,936.86917 131.71884,936.81918 131.71884,936.79525 C 131.71884,936.75521 131.71884,936.72726 131.71884,936.71139 C 131.71884,936.54342 131.6268,936.37936 131.44271,936.2192 C 131.26668,936.08322 131.09268,935.94522 130.92068,935.8052 C 130.74868,935.66519 130.61673,935.52719 130.52481,935.3912 C 130.43288,935.25522 130.36893,935.09921 130.33292,934.92318 C 130.29691,934.74716 130.2789,934.59518 130.2789,934.46725 C 130.2789,934.05929 130.39487,933.6893 130.6268,933.35726 C 130.85873,933.02524 131.15475,932.85922 131.51486,932.85922 C 131.61886,932.85922 131.68283,932.85922 131.70676,932.85922 C 131.76291,932.85922 131.80294,932.86728 131.82688,932.88339 L 132.40293,933.23129 C 132.53084,933.31137 132.61886,933.42343 132.66696,933.56747 C 132.69089,933.63144 132.71481,933.78732 132.73873,934.03512 C 132.73873,934.21921 132.72872,934.37527 132.70871,934.50332 C 132.68868,934.63138 132.64669,934.76742 132.58274,934.91146 L 131.95871,934.91146 C 131.93478,934.84726 131.89682,934.79318 131.84482,934.74923 C 131.79282,934.70529 131.76682,934.48337 131.76682,934.08346 C 131.76682,933.9714 131.76682,933.85531 131.76682,933.73519 C 131.74289,933.67123 131.70687,933.63925 131.65878,933.63925 C 131.57895,933.63925 131.47897,933.73526 131.35886,933.92727 C 131.23873,934.11929 131.16672,934.28732 131.14279,934.43136 C 131.11886,934.6152 131.16293,934.79123 131.275,934.95944 C 131.35483,935.07931 131.49875,935.22323 131.70676,935.3912 C 131.91476,935.55917 132.13083,935.72714 132.35495,935.89511 C 132.6108,936.12728 132.73873,936.3674 132.73873,936.61544 C 132.73873,936.92745 132.59878,937.21145 132.31887,937.46743 C 132.03897,937.72341 131.77499,937.8514 131.52695,937.8514 C 131.34287,937.8514 131.17081,937.81539 131.01077,937.74337 C 130.85074,937.67135 130.60677,937.51132 130.2789,937.26327 L 130.2789,937.26327 z M 145.64951,931.63534 C 145.6014,932.07529 145.41745,932.48727 145.09762,932.8713 C 144.84152,933.17526 144.45345,933.50326 143.93344,933.85531 L 143.41745,934.20321 L 145.36167,937.13143 C 145.37752,937.21127 145.38546,937.2712 145.38547,937.31124 C 145.38546,937.51119 145.29745,937.6672 145.12143,937.77926 C 145.03354,937.82736 144.94955,937.8514 144.86947,937.8514 C 144.74959,937.8514 144.63961,937.82137 144.53951,937.76132 C 144.43941,937.70126 144.34945,937.6152 144.26962,937.50314 L 141.84567,934.22738 L 141.78562,937.58737 C 141.78561,937.70724 141.68955,937.76718 141.49741,937.76718 C 141.38558,937.76718 141.29757,937.75118 141.23336,937.7192 C 141.0815,937.67135 140.94956,937.62539 140.8375,937.58132 C 140.72543,937.53726 140.6694,937.41525 140.66941,937.2153 L 140.69358,930.0632 C 140.69357,929.91135 140.71554,929.7814 140.75949,929.67336 C 140.80344,929.56534 140.8895,929.48337 141.01766,929.42745 C 141.08164,929.40329 141.14963,929.39121 141.22164,929.3912 C 141.39767,929.39121 141.53769,929.45529 141.64169,929.58346 C 141.84969,929.47946 142.03769,929.41342 142.20566,929.38534 C 142.37363,929.35727 142.60556,929.34323 142.90145,929.34323 L 143.52548,929.34323 C 144.03744,929.34323 144.52144,929.56723 144.97751,930.01522 C 145.43356,930.46323 145.66159,930.95126 145.66159,931.47933 C 145.65353,931.55136 145.6495,931.60336 145.64951,931.63534 L 145.64951,931.63534 z M 141.86947,933.48324 C 142.29354,933.37924 142.61758,933.24124 142.84159,933.06924 C 143.06557,932.89725 143.24959,932.75522 143.39364,932.64315 C 143.70566,932.39535 143.95163,932.11942 144.13155,931.81533 C 144.31149,931.51126 144.40145,931.19919 144.40145,930.87911 C 144.40145,930.68723 144.30544,930.54129 144.11344,930.44131 C 143.92141,930.34134 143.68538,930.29135 143.40537,930.29135 C 143.07747,930.29135 142.76552,930.34336 142.46952,930.44735 C 142.17348,930.55136 141.99752,930.67929 141.94162,930.83114 L 141.86947,933.48324 z M 147.50216,937.62325 C 147.1423,937.47921 146.87833,937.25119 146.71024,936.93917 C 146.54215,936.62716 146.4581,936.25521 146.4581,935.82333 C 146.4581,935.71933 146.4581,935.63131 146.4581,935.55929 C 146.4581,935.48727 146.47409,935.36325 146.50608,935.18722 C 146.52219,934.89132 146.53817,934.65133 146.55405,934.46725 C 146.61826,933.83517 146.87436,933.17917 147.32236,932.49923 C 147.3702,932.41916 147.4421,932.37912 147.53806,932.37911 C 147.59421,932.37912 147.7103,932.41513 147.88631,932.48715 C 148.06234,932.55917 148.19833,932.59518 148.29427,932.59518 C 148.75034,932.59518 149.09432,932.85922 149.32627,933.38729 C 149.52621,933.84335 149.62618,934.42331 149.62618,935.12716 C 149.62618,935.84713 149.53414,936.43112 149.35007,936.87911 C 149.12618,937.4233 148.7822,937.75533 148.31808,937.87521 C 148.23825,937.89132 148.18625,937.89938 148.16208,937.89938 C 148.09811,937.89938 148.03817,937.88339 147.98227,937.8514 C 147.92636,937.81942 147.87033,937.78744 147.81418,937.75546 L 147.50216,937.62325 z M 148.03025,933.43527 C 147.91012,933.69919 147.79208,933.96518 147.67612,934.23324 C 147.56015,934.50131 147.50216,934.83127 147.50216,935.22311 C 147.50216,935.34323 147.49019,935.51931 147.46627,935.75137 C 147.44235,935.98343 147.43039,936.15145 147.43039,936.25546 C 147.43039,936.45541 147.46035,936.62539 147.5203,936.76541 C 147.58023,936.90542 147.69016,937.01937 147.85007,937.10726 C 147.97824,937.17929 148.11826,937.01132 148.27011,936.60336 C 148.38217,936.29135 148.47421,935.91134 148.54623,935.46334 C 148.57015,935.3193 148.58212,935.16732 148.58212,935.00741 C 148.58212,934.62338 148.53011,934.25137 148.42612,933.89138 C 148.3221,933.5314 148.22214,933.35141 148.12618,933.3514 C 148.08615,933.35141 148.05417,933.37936 148.03025,933.43527 L 148.03025,933.43527 z M 151.76157,937.77926 C 151.64145,937.77926 151.54147,937.75521 151.46164,937.70712 L 151.05331,937.45516 C 150.98154,937.41513 150.92362,937.36715 150.87955,937.31124 C 150.83548,937.25534 150.78146,937.16732 150.7175,937.04721 C 150.6135,936.84725 150.56149,936.64328 150.5615,936.43527 L 150.72959,933.02731 C 150.72959,932.94723 150.78159,932.9072 150.88558,932.90719 C 150.93344,932.9072 150.99741,932.91721 151.07748,932.93722 C 151.15756,932.95724 151.23959,932.97726 151.32358,932.99728 C 151.40756,933.0173 151.50949,933.07529 151.62937,933.17123 L 151.46164,935.79916 C 151.46164,936.23935 151.48557,936.56344 151.53341,936.77145 C 151.58956,936.84323 151.6456,936.91122 151.7015,936.97543 C 151.97348,936.78329 152.14945,936.61526 152.2294,936.47134 C 152.30936,936.32742 152.34932,936.17538 152.34933,936.01522 L 152.4094,932.67941 L 152.72141,932.64315 C 153.01755,932.64316 153.27353,932.74716 153.48935,932.95516 L 153.47763,937.83932 C 153.40561,937.8713 153.32149,937.88729 153.2253,937.88729 C 153.20138,937.88729 153.14742,937.87728 153.06345,937.85726 C 152.97946,937.83724 152.90347,937.80721 152.83548,937.76718 C 152.76748,937.72714 152.72946,937.6672 152.72141,937.58737 L 152.64963,937.08346 L 152.13363,937.63534 C 152.03744,937.73129 151.91342,937.77926 151.76157,937.77926 L 151.76157,937.77926 z M 155.04757,933.75936 L 154.85567,933.66342 L 154.66342,933.66342 L 154.48361,933.43527 L 154.45944,932.85922 L 155.07174,932.72738 L 155.13144,930.44735 C 155.2596,930.41538 155.38363,930.39939 155.50351,930.39938 C 155.57552,930.39939 155.66153,930.40738 155.7615,930.42336 C 155.86148,930.43936 155.99545,930.4874 156.16342,930.56747 L 156.13961,932.60726 L 156.6677,932.60726 L 156.83542,932.91928 L 156.8237,933.49533 L 156.0195,933.57919 L 156.0195,937.75546 C 155.93161,937.77938 155.85959,937.79135 155.80344,937.79135 C 155.74752,937.79135 155.68154,937.78134 155.60549,937.76132 C 155.52945,937.7413 155.45553,937.72323 155.38376,937.70712 C 155.10372,937.62728 154.96372,937.4714 154.96372,937.23947 L 155.04757,933.75936 z M 160.42649,933.78317 L 160.49826,935.22311 L 158.79427,935.49923 C 158.65048,935.53122 158.55454,935.56723 158.50644,935.60726 C 158.40243,935.68734 158.35043,935.7994 158.35044,935.94345 L 158.37423,936.49533 C 158.37423,936.6152 158.41226,936.72518 158.4883,936.82528 C 158.56436,936.92538 158.65438,936.9714 158.75839,936.96334 C 158.98251,936.93136 159.26254,936.75534 159.59848,936.43527 C 159.93442,936.1152 160.08639,935.91916 160.05442,935.84713 C 160.08639,935.83126 160.1384,935.82333 160.21042,935.82333 C 160.3464,935.82333 160.44638,935.86734 160.51035,935.95535 C 160.57431,936.04336 160.60629,936.13534 160.6063,936.23129 C 160.60629,936.27133 160.59432,936.32333 160.5704,936.38729 C 160.52231,936.57138 160.45431,936.73141 160.36642,936.8674 C 160.32638,936.93136 160.21431,937.07138 160.03025,937.28744 C 159.87839,937.46322 159.72446,937.59115 159.56844,937.67123 C 159.41244,937.75131 159.2264,937.79135 159.01035,937.79135 C 158.8663,937.79135 158.74831,937.77731 158.65641,937.74923 C 158.56448,937.72116 158.47043,937.6672 158.37423,937.58737 L 157.75057,937.0234 C 157.7264,937.00729 157.70236,936.97122 157.67844,936.91519 C 157.65451,936.85916 157.63449,936.81515 157.61838,936.78317 C 157.56247,936.63937 157.53451,936.48739 157.53451,936.32723 L 157.53451,935.3912 C 157.53451,934.78329 157.66849,934.1553 157.93643,933.50723 C 158.20437,932.85916 158.49033,932.53513 158.79427,932.53512 C 158.84238,932.53513 158.87839,932.53915 158.90232,932.54721 C 159.05441,932.59531 159.24844,932.63132 159.48441,932.65524 C 159.72037,932.67917 159.90438,932.76315 160.03646,932.90719 C 160.16854,933.05124 160.26657,933.18924 160.33053,933.32119 C 160.3945,933.45315 160.42648,933.60715 160.42649,933.78317 L 160.42649,933.78317 z M 159.55051,933.77145 C 159.55051,933.63547 159.52451,933.51547 159.47251,933.41146 C 159.4205,933.30746 159.33847,933.23935 159.2264,933.20712 C 159.10629,933.1832 158.99435,933.31125 158.8906,933.59127 C 158.81857,933.79123 158.77048,933.93124 158.74631,934.01132 C 158.68233,934.17929 158.63437,934.32327 158.60238,934.44326 C 158.5704,934.56326 158.55441,934.69125 158.55442,934.82723 L 159.55051,934.59945 L 159.55051,933.77145 z M 161.68845,932.8713 L 161.79648,932.73947 L 162.0726,932.73947 C 162.13657,932.73947 162.21053,932.77743 162.29452,932.85336 C 162.37851,932.92929 162.44455,932.97531 162.49265,932.99142 C 162.60446,933.02341 162.69638,932.93741 162.76839,932.73342 C 162.84042,932.52945 162.92641,932.41342 163.0264,932.38534 C 163.12636,932.35727 163.19638,932.34323 163.23641,932.34323 C 163.38852,932.34323 163.52255,932.39725 163.63852,932.50527 C 163.75449,932.61331 163.81247,932.74326 163.81247,932.89511 L 163.80039,933.36312 C 163.80038,933.5953 163.77237,933.80539 163.71634,933.99337 C 163.6603,934.18136 163.57236,934.27536 163.45248,934.27535 C 163.26839,934.27536 163.14437,934.0953 163.08042,933.73519 C 163.04843,933.55917 162.9924,933.47116 162.91233,933.47116 C 162.84054,933.47116 162.78261,933.53116 162.73855,933.65115 C 162.69449,933.77115 162.67246,934.0112 162.67246,934.3713 C 162.67246,934.48337 162.67246,934.58139 162.67246,934.66537 C 162.67246,934.74936 162.67246,934.81527 162.67246,934.86312 L 162.67246,937.79135 C 162.58456,937.83138 162.48459,937.8514 162.37252,937.8514 C 162.30051,937.8514 162.2125,937.83938 162.10849,937.81533 C 162.00449,937.79128 161.90451,937.73324 161.80857,937.6412 C 161.71261,937.54916 161.66464,937.43124 161.66464,937.28744 L 161.68845,932.8713 z"
+       id="text3253" />
+    <path
+       style="fill:#ececec;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3304)"
+       d="M 62.895745,955.18402 C 62.360025,954.46973 57.538595,951.43402 57.181455,948.04116 C 56.824315,944.6483 57.895745,913.75545 57.895745,913.75545 C 57.895745,913.75545 60.217165,909.11259 62.181455,908.57688 C 64.145745,908.04116 65.217165,907.50545 65.217165,907.50545 L 53.610025,904.82688 L 50.931455,901.43402 L 51.467165,913.93402 L 52.360025,944.6483 C 52.360025,944.6483 51.645745,947.68402 50.395745,948.04116 C 49.145745,948.3983 53.074315,953.57688 53.967165,953.93402 C 54.860025,954.29116 62.895745,955.54116 62.895745,955.18402 z"
+       id="path3282" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3368)"
+       d="M 47.895745,901.79116 C 47.895745,901.79116 51.288595,907.68402 54.502885,908.04116 C 57.717165,908.3983 64.681455,909.6483 68.967165,909.6483 C 73.252885,909.6483 73.252885,909.6483 73.252885,909.6483"
+       id="path3284" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#fdfdfd;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 76.467165,910.00545 L 264.32431,909.29116"
+       id="path3286" />
+    <path
+       style="fill:#2c2c2c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+       d="M 59.598995,868.26188 L 91.671345,744.01311 C 91.671345,744.01311 93.439105,737.19458 100.76272,733.65905 C 108.08633,730.12352 119.45054,729.61844 119.45054,729.61844 C 119.45054,729.61844 522.5014,731.13367 523.51155,731.13367 C 524.5217,731.13367 538.91638,733.91159 542.19938,737.19458 C 545.48237,740.47758 548.51283,747.54865 548.51283,747.54865 L 575.02933,869.52457 C 575.02933,869.52457 576.03949,876.3431 573.76664,877.35325 C 571.4938,878.3634 564.92782,878.3634 564.92782,878.3634 L 65.407375,874.82786 C 65.407375,874.82786 60.609145,873.56518 59.851535,872.30249 C 59.093915,871.0398 59.851535,868.51442 59.598995,868.26188 z"
+       id="path3752"
+       sodipodi:nodetypes="ccscssccsccsc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#868686;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;filter:url(#filter3982)"
+       d="M 59.346455,869.77711 C 59.346455,869.77711 59.598995,872.30249 61.366765,873.0601 C 63.134535,873.81772 67.175145,873.81772 67.175145,873.81772 L 570.23111,877.85833 C 570.23111,877.85833 573.00902,878.11086 574.52425,876.3431 C 576.03949,874.57533 575.53441,870.02964 575.53441,870.02964"
+       id="path3754" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3962)"
+       d="M 163.96716,740.00545 L 162.18145,748.13045 L 166.02075,748.13045 L 168.07432,740.18402 L 163.96716,740.00545 z"
+       id="path3810" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3958)"
+       d="M 168.69932,739.20188 C 168.25288,739.20188 163.7886,739.0233 163.7886,739.0233"
+       id="path3812" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3954)"
+       d="M 189.36894,739.91617 L 188.29753,748.13046 L 192.13682,748.13046 L 193.4761,740.09474 L 189.36894,739.91617 z"
+       id="path3814"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3950)"
+       d="M 193.92253,739.1126 C 193.4761,739.1126 189.01181,738.93402 189.01181,738.93402"
+       id="path3816" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3946)"
+       d="M 215.44037,739.46974 L 214.45824,747.59474 L 218.29752,747.59474 L 219.54752,739.64831 L 215.44037,739.46974 z"
+       id="path3818"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3942)"
+       d="M 220.17252,738.66617 C 219.7261,738.66617 215.26181,738.48759 215.26181,738.48759"
+       id="path3820" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3938)"
+       d="M 241.06539,740.00545 L 240.17254,748.13045 L 244.01183,748.13045 L 245.17254,740.18402 L 241.06539,740.00545 z"
+       id="path3822"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3934)"
+       d="M 245.79754,739.20188 C 245.3511,739.20188 240.88681,739.0233 240.88681,739.0233"
+       id="path3824" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3930)"
+       d="M 266.33324,739.64831 L 265.79752,748.39831 L 269.63681,748.39831 L 270.44038,739.82688 L 266.33324,739.64831 z"
+       id="path3826"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3926)"
+       d="M 271.06538,738.84474 C 270.61895,738.84474 266.15466,738.66616 266.15466,738.66616"
+       id="path3828" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3922)"
+       d="M 291.95824,740.36259 L 291.69039,748.75545 L 295.52968,748.75545 L 296.06539,740.54116 L 291.95824,740.36259 z"
+       id="path3830"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3918)"
+       d="M 296.69039,739.55902 C 296.24397,739.55902 291.77967,739.38044 291.77967,739.38044"
+       id="path3832" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3914)"
+       d="M 318.11895,740.63045 L 317.76181,749.20188 L 321.6011,749.20188 L 322.2261,740.80902 L 318.11895,740.63045 z"
+       id="path3834"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3910)"
+       d="M 322.8511,739.82688 C 322.40467,739.82688 317.94038,739.6483 317.94038,739.6483"
+       id="path3836" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3906)"
+       d="M 343.56537,740.63045 L 343.65466,748.84474 L 347.49395,748.84474 L 347.67252,740.80902 L 343.56537,740.63045 z"
+       id="path3838"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3902)"
+       d="M 348.29752,739.82688 C 347.85109,739.82688 343.3868,739.6483 343.3868,739.6483"
+       id="path3840" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3898)"
+       d="M 369.81537,740.36259 L 369.81537,748.93402 L 373.65466,748.93402 L 373.92252,740.54116 L 369.81537,740.36259 z"
+       id="path3842"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3894)"
+       d="M 374.54752,739.55902 C 374.10109,739.55902 369.6368,739.38044 369.6368,739.38044"
+       id="path3844" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3890)"
+       d="M 394.27966,740.98759 L 394.99395,749.38045 L 398.83324,749.38045 L 398.38681,741.16616 L 394.27966,740.98759 z"
+       id="path3846"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3886)"
+       d="M 399.01181,740.18402 C 398.56538,740.18402 394.10109,740.00544 394.10109,740.00544"
+       id="path3848" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3882)"
+       d="M 420.61895,740.63045 L 421.24395,749.02331 L 425.08324,749.02331 L 424.7261,740.80902 L 420.61895,740.63045 z"
+       id="path3850"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3878)"
+       d="M 425.3511,739.82688 C 424.90467,739.82688 420.44038,739.6483 420.44038,739.6483"
+       id="path3852" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3874)"
+       d="M 446.15466,741.34473 L 447.13681,749.6483 L 450.9761,749.6483 L 450.26181,741.5233 L 446.15466,741.34473 z"
+       id="path3854"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3870)"
+       d="M 450.88681,740.54116 C 450.44038,740.54116 445.97609,740.36258 445.97609,740.36258"
+       id="path3856" />
+    <path
+       style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3866)"
+       d="M 472.1368,741.07688 L 473.20823,749.55902 L 477.04752,749.55902 L 476.24396,741.25545 L 472.1368,741.07688 z"
+       id="path3858"
+       sodipodi:nodetypes="ccccc" />
+    <path
+       style="fill:none;fill-rule:evenodd;stroke:#494949;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3862)"
+       d="M 476.86896,740.27331 C 476.42252,740.27331 471.95823,740.09473 471.95823,740.09473"
+       id="path3860" />
+    <path
+       style="fill:#010101;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3996)"
+       d="M 457.00288,731.43402 L 458.43145,635.00545 L 457.71716,507.86259 C 457.71716,507.86259 460.10513,497.86259 471.28859,497.1483 C 478.45249,496.69074 482.71717,509.29116 482.71717,509.29116 L 482.71717,651.43402 L 484.14574,731.43402 L 457.00288,731.43402 z"
+       id="path3986"
+       sodipodi:nodetypes="cccscccc" />
+  </g>
+</svg>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg b/wpa_supplicant/wpa_gui-qt4/icons/laptop.svg
new file mode 100644 (file)
index 0000000..06235f0
--- /dev/null
@@ -0,0 +1,1568 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://web.resource.org/cc/"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="400"
+   height="400"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45"
+   version="1.0"
+   sodipodi:docbase="C:\Documents and Settings\Mete  Ä°slam\Desktop"
+   sodipodi:docname="MyLaptop.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape"
+   inkscape:export-filename="C:\Documents and Settings\Mete  Ä°slam\Desktop\MyLaptop.png"
+   inkscape:export-xdpi="98"
+   inkscape:export-ydpi="98"
+   sodipodi:modified="true">
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="1"
+     inkscape:cx="319.93339"
+     inkscape:cy="202.90098"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="400px"
+     height="400px"
+     inkscape:window-width="1277"
+     inkscape:window-height="751"
+     inkscape:window-x="0"
+     inkscape:window-y="22"
+     showguides="true"
+     inkscape:guide-bbox="true" />
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient3757">
+      <stop
+         style="stop-color:#70ffea;stop-opacity:1;"
+         offset="0"
+         id="stop3759" />
+      <stop
+         style="stop-color:#0055f6;stop-opacity:1;"
+         offset="1"
+         id="stop3761" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient3460">
+      <stop
+         style="stop-color:#f1ff00;stop-opacity:1;"
+         offset="0"
+         id="stop3462" />
+      <stop
+         style="stop-color:#8bff00;stop-opacity:0;"
+         offset="1"
+         id="stop3464" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient26774">
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.1122449;"
+         offset="0"
+         id="stop26776" />
+      <stop
+         style="stop-color:#ffffff;stop-opacity:0.89795917;"
+         offset="1"
+         id="stop26778" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient17245">
+      <stop
+         offset="0"
+         style="stop-color:#ffefef;stop-opacity:0.58163267;"
+         id="stop17247" />
+      <stop
+         offset="1"
+         style="stop-color:#ffefef;stop-opacity:0.14285715;"
+         id="stop17249" />
+    </linearGradient>
+    <pattern
+       patternTransform="matrix(0.9848362,0,0,0.9848362,-402.92422,36.839002)"
+       id="pattern13296"
+       xlink:href="#pattern13289"
+       inkscape:collect="always" />
+    <pattern
+       patternTransform="matrix(0.6565232,0,0,0.6651903,-8.1640579,-22.602821)"
+       id="pattern13287"
+       xlink:href="#pattern12311"
+       inkscape:collect="always" />
+    <pattern
+       patternTransform="translate(-88.774232,-72.100299)"
+       id="pattern12309"
+       xlink:href="#pattern11335"
+       inkscape:collect="always" />
+    <pattern
+       patternTransform="translate(-5.8654428,10.456268)"
+       id="pattern9368"
+       xlink:href="#pattern8394"
+       inkscape:collect="always" />
+    <linearGradient
+       id="linearGradient4451">
+      <stop
+         offset="0"
+         style="stop-color:#fffbfb;stop-opacity:0.82653064;"
+         id="stop4453" />
+      <stop
+         offset="1"
+         style="stop-color:#000000;stop-opacity:0;"
+         id="stop4455" />
+    </linearGradient>
+    <pattern
+       height="253"
+       id="pattern8394"
+       patternUnits="userSpaceOnUse"
+       patternTransform="translate(404.25649,166.01976)"
+       width="337">
+      <image
+         id="image4466"
+         width="337"
+         y="0"
+         xlink:href="data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QCURXhpZgAASUkqAAgAAAADADEBAgAcAAAAMgAAADIBAgAU AAAATgAAAGmHBAABAAAAYgAAAAAAAABBZG9iZSBQaG90b3Nob3AgQ1MyIFdpbmRvd3MAMjAwNjow NjoxMyAxMzozNDoyNAADAAGgAwABAAAAAQAAAAKgBAABAAAAAAQAAAOgBAABAAAAAAMAAAAAAAD/ 4gxYSUNDX1BST0ZJTEUAAQEAAAxITGlubwIQAABtbnRyUkdCIFhZWiAHzgACAAkABgAxAABhY3Nw TVNGVAAAAABJRUMgc1JHQgAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLUhQICAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFjcHJ0AAABUAAAADNkZXNjAAABhAAA AGx3dHB0AAAB8AAAABRia3B0AAACBAAAABRyWFlaAAACGAAAABRnWFlaAAACLAAAABRiWFlaAAAC QAAAABRkbW5kAAACVAAAAHBkbWRkAAACxAAAAIh2dWVkAAADTAAAAIZ2aWV3AAAD1AAAACRsdW1p AAAD+AAAABRtZWFzAAAEDAAAACR0ZWNoAAAEMAAAAAxyVFJDAAAEPAAACAxnVFJDAAAEPAAACAxi VFJDAAAEPAAACAx0ZXh0AAAAAENvcHlyaWdodCAoYykgMTk5OCBIZXdsZXR0LVBhY2thcmQgQ29t cGFueQAAZGVzYwAAAAAAAAASc1JHQiBJRUM2MTk2Ni0yLjEAAAAAAAAAAAAAABJzUkdCIElFQzYx OTY2LTIuMQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA WFlaIAAAAAAAAPNRAAEAAAABFsxYWVogAAAAAAAAAAAAAAAAAAAAAFhZWiAAAAAAAABvogAAOPUA AAOQWFlaIAAAAAAAAGKZAAC3hQAAGNpYWVogAAAAAAAAJKAAAA+EAAC2z2Rlc2MAAAAAAAAAFklF QyBodHRwOi8vd3d3LmllYy5jaAAAAAAAAAAAAAAAFklFQyBodHRwOi8vd3d3LmllYy5jaAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkZXNjAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAC5JRUMg NjE5NjYtMi4xIERlZmF1bHQgUkdCIGNvbG91ciBzcGFjZSAtIHNSR0IAAAAAAAAAAAAAAAAAAAAA AAAAAAAAZGVzYwAAAAAAAAAsUmVmZXJlbmNlIFZpZXdpbmcgQ29uZGl0aW9uIGluIElFQzYxOTY2 LTIuMQAAAAAAAAAAAAAALFJlZmVyZW5jZSBWaWV3aW5nIENvbmRpdGlvbiBpbiBJRUM2MTk2Ni0y LjEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHZpZXcAAAAAABOk/gAUXy4AEM8UAAPtzAAEEwsA A1yeAAAAAVhZWiAAAAAAAEwJVgBQAAAAVx/nbWVhcwAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAA Ao8AAAACc2lnIAAAAABDUlQgY3VydgAAAAAAAAQAAAAABQAKAA8AFAAZAB4AIwAoAC0AMgA3ADsA QABFAEoATwBUAFkAXgBjAGgAbQByAHcAfACBAIYAiwCQAJUAmgCfAKQAqQCuALIAtwC8AMEAxgDL ANAA1QDbAOAA5QDrAPAA9gD7AQEBBwENARMBGQEfASUBKwEyATgBPgFFAUwBUgFZAWABZwFuAXUB fAGDAYsBkgGaAaEBqQGxAbkBwQHJAdEB2QHhAekB8gH6AgMCDAIUAh0CJgIvAjgCQQJLAlQCXQJn AnECegKEAo4CmAKiAqwCtgLBAssC1QLgAusC9QMAAwsDFgMhAy0DOANDA08DWgNmA3IDfgOKA5YD ogOuA7oDxwPTA+AD7AP5BAYEEwQgBC0EOwRIBFUEYwRxBH4EjASaBKgEtgTEBNME4QTwBP4FDQUc BSsFOgVJBVgFZwV3BYYFlgWmBbUFxQXVBeUF9gYGBhYGJwY3BkgGWQZqBnsGjAadBq8GwAbRBuMG 9QcHBxkHKwc9B08HYQd0B4YHmQesB78H0gflB/gICwgfCDIIRghaCG4IggiWCKoIvgjSCOcI+wkQ CSUJOglPCWQJeQmPCaQJugnPCeUJ+woRCicKPQpUCmoKgQqYCq4KxQrcCvMLCwsiCzkLUQtpC4AL mAuwC8gL4Qv5DBIMKgxDDFwMdQyODKcMwAzZDPMNDQ0mDUANWg10DY4NqQ3DDd4N+A4TDi4OSQ5k Dn8Omw62DtIO7g8JDyUPQQ9eD3oPlg+zD88P7BAJECYQQxBhEH4QmxC5ENcQ9RETETERTxFtEYwR qhHJEegSBxImEkUSZBKEEqMSwxLjEwMTIxNDE2MTgxOkE8UT5RQGFCcUSRRqFIsUrRTOFPAVEhU0 FVYVeBWbFb0V4BYDFiYWSRZsFo8WshbWFvoXHRdBF2UXiReuF9IX9xgbGEAYZRiKGK8Y1Rj6GSAZ RRlrGZEZtxndGgQaKhpRGncanhrFGuwbFBs7G2MbihuyG9ocAhwqHFIcexyjHMwc9R0eHUcdcB2Z HcMd7B4WHkAeah6UHr4e6R8THz4faR+UH78f6iAVIEEgbCCYIMQg8CEcIUghdSGhIc4h+yInIlUi giKvIt0jCiM4I2YjlCPCI/AkHyRNJHwkqyTaJQklOCVoJZclxyX3JicmVyaHJrcm6CcYJ0kneier J9woDSg/KHEooijUKQYpOClrKZ0p0CoCKjUqaCqbKs8rAis2K2krnSvRLAUsOSxuLKIs1y0MLUEt di2rLeEuFi5MLoIuty7uLyQvWi+RL8cv/jA1MGwwpDDbMRIxSjGCMbox8jIqMmMymzLUMw0zRjN/ M7gz8TQrNGU0njTYNRM1TTWHNcI1/TY3NnI2rjbpNyQ3YDecN9c4FDhQOIw4yDkFOUI5fzm8Ofk6 Njp0OrI67zstO2s7qjvoPCc8ZTykPOM9Ij1hPaE94D4gPmA+oD7gPyE/YT+iP+JAI0BkQKZA50Ep QWpBrEHuQjBCckK1QvdDOkN9Q8BEA0RHRIpEzkUSRVVFmkXeRiJGZ0arRvBHNUd7R8BIBUhLSJFI 10kdSWNJqUnwSjdKfUrESwxLU0uaS+JMKkxyTLpNAk1KTZNN3E4lTm5Ot08AT0lPk0/dUCdQcVC7 UQZRUFGbUeZSMVJ8UsdTE1NfU6pT9lRCVI9U21UoVXVVwlYPVlxWqVb3V0RXklfgWC9YfVjLWRpZ aVm4WgdaVlqmWvVbRVuVW+VcNVyGXNZdJ114XcleGl5sXr1fD19hX7NgBWBXYKpg/GFPYaJh9WJJ Ypxi8GNDY5dj62RAZJRk6WU9ZZJl52Y9ZpJm6Gc9Z5Nn6Wg/aJZo7GlDaZpp8WpIap9q92tPa6dr /2xXbK9tCG1gbbluEm5rbsRvHm94b9FwK3CGcOBxOnGVcfByS3KmcwFzXXO4dBR0cHTMdSh1hXXh dj52m3b4d1Z3s3gReG54zHkqeYl553pGeqV7BHtje8J8IXyBfOF9QX2hfgF+Yn7CfyN/hH/lgEeA qIEKgWuBzYIwgpKC9INXg7qEHYSAhOOFR4Wrhg6GcobXhzuHn4gEiGmIzokziZmJ/opkisqLMIuW i/yMY4zKjTGNmI3/jmaOzo82j56QBpBukNaRP5GokhGSepLjk02TtpQglIqU9JVflcmWNJaflwqX dZfgmEyYuJkkmZCZ/JpomtWbQpuvnByciZz3nWSd0p5Anq6fHZ+Ln/qgaaDYoUehtqImopajBqN2 o+akVqTHpTilqaYapoum/adup+CoUqjEqTepqaocqo+rAqt1q+msXKzQrUStuK4trqGvFq+LsACw dbDqsWCx1rJLssKzOLOutCW0nLUTtYq2AbZ5tvC3aLfguFm40blKucK6O7q1uy67p7whvJu9Fb2P vgq+hL7/v3q/9cBwwOzBZ8Hjwl/C28NYw9TEUcTOxUvFyMZGxsPHQce/yD3IvMk6ybnKOMq3yzbL tsw1zLXNNc21zjbOts83z7jQOdC60TzRvtI/0sHTRNPG1EnUy9VO1dHWVdbY11zX4Nhk2OjZbNnx 2nba+9uA3AXcit0Q3ZbeHN6i3ynfr+A24L3hROHM4lPi2+Nj4+vkc+T85YTmDeaW5x/nqegy6Lzp RunQ6lvq5etw6/vshu0R7ZzuKO6070DvzPBY8OXxcvH/8ozzGfOn9DT0wvVQ9d72bfb794r4Gfio +Tj5x/pX+uf7d/wH/Jj9Kf26/kv+3P9t////2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB AQEBAQEBAQEBAQICAQECAQEBAgICAgICAgICAQICAgICAgICAgL/2wBDAQEBAQEBAQEBAQECAQEB AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgL/wAARCAD9 AVEDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIE AwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJico KSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZ mqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6 /8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAEC AxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNE RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmq srO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEA PwD+lfSrfVX862m8NeKtZEEiS215pNhfvD4eWORy8dvNMFbxJpwkbf8AZzsRSzGGU53H2vwz4x+I WonTtGs9Nlhg1SLUZZdVn02TTLzSrywmS21Pfp98piiSSS6t76FSSSXlhw2045PwD8L9X1+4XXJv ih4oeLT7vypWjS5WSSRkJKW89zq0oVlDDJaM7Tj5TXu40K38D282p6d4m1ODTVunudTtPEF5Pqtn cNctHGiW7yuHsn3sVhSNtm+cZU1/gH4PeH3HOCy6jxLWqY/hLhydnWrUcdlHt8TgYVYTdSLoV5p0 qdNYmlOWJrurhqcozwTlChRw0Px3LMHjIwVdynhqD3anTvKF09HFvRLmTcpXimnDZRXiHj+XUrC0 j0D4rxR+Jnuplk0jxbpVpPpGn2YuN6PY3l60Hk296wjRkIUp+8AmIUb15LXPBdnaeHbm/wBA8VQW 99qdzpSwNqd3HpV450CymsbjS7iZpVSC/b7bbSPl9rqilQyMGrubvx+/jW71HQ7m+kl0XU9RSTQm vfBtrqml2giaQJZandWuqlLqCUlQJWEbwvt3YXfVLQYYvHFhN8N38O20+pwTrHbaDbwav4ZuLgc2 zXpe/S9FvdWUn2cRF5PLSKTY22IBa+Yz/KOGuMc9zOnllf8A1kjnWHr4LJMVjJzebPH0o1KKwtTG 4LE4zG4+VXCYic8DiMVRr4p061KlSoVasamHOetSoYmtNU39Y9rFxpSnd1edaKLnGUpzvFvklJSl ZpJN3ivKNP8AH/xN8FwWNhqUt3b6dZvusJr+0TU9MZdwkSGSZNyXFnkDY0Ugkh3nYcZWt24+OAuL 25h8SeFxDp89uWGk214L7RrlvLMkBm0zVYJY/IkkPM1s0TgMGG8jns/FH7PPxB+Gojm0fX7+00y/ kjt1i1OOG90eSaZfltdQaxeeBGJLLma2VG2kq2K841Pwpq9jpU11410b/hF9NimFvDr/AIdNrrOn 30wcs1vb6MjTfZwEEzNLBJbRxlTvjJIQ/nOa5J9IHw7jieG8zq59w9/Y0I1J4XOaFHMcFRw9OlyU 6dTMJqVHDYapSdlUxNPK6EqdoTnXtSjT4atPOcC5UKjrUPZWbjVUZwUUtE5vSMWuslTVtLvS0vhq 5+EnjG+uxfeB9V8O3KwtKsuj6re3vhu3LNtWbUYtiS6bbbyMsHMYGeBXVaRo+h+HZYtd0TRrLxLC qTW0d/4RmbWdNtJJPNSVNbm1W6luIYnh3KypbRgq6/vSzYHmupWXim80HUbHwra6Vq3g55IL6/m8 NSpPqM6WCu8cuuROI7pZlB3OHgWNCg8tQBz5jpGt6noF3LqGjajfaRfQqojls7l4GLCSM+VPGw/0 iIgMWRxj+8CowfgZ8d4LhTF5A8/4KwdXMF7TELPMFk2X5XWnVVVtVcFCnh/qGY0KUYR5K0fqzq1K skpUuRVJ8n1uGGlR9thYSlrL2saUKbbvo4pR5JxSSs1y3b3Vrv6P0bV/D17418R+RrGkQWF/ZXOg SJrdvJqeo6lfajiKxt9MFw0wuNNi1QW52uoRUgIKBSDXG3T62dMuofE1lDBbxaouiS2yQ29ldWt0 qxWyzaYkSqtlbx3caIWwI9l6ysG3k1iab4h8K+NtQtLXxhZ/8Izr9zdRpb+NfDUKQRPdySKIX1zQ 1IjbMpXM9sY3BO5kIya9f8eajcfD2S18TWOn6T4kh1lRBca3fFNStD4ksTDa3c9rarI8Vi08ETze UcN5yEycoFr6/CPDcU8K51xFUzmjT4WyTE4mtia+BVeco0c0m3OnmGS1HOtT+p1Vh4UqdKSy+s8V iZLMKslO3RHlxGHq1nUSw9KUnJwu9Kj2nSbuuV8qSXuPmk+d6m1+zr4oFt4k8T+C7i8muRdRrqlj JdbN73umKmn6gkciSMsyvapburKeVtjwMGvr+vzA0fxrqPh3xf4V8QzXEsljYazcXKQ4gRIrK4uP suqwLHbooiZrZnYoCU/eqy4yRX6cpOkqLLEQ8cqLJE4IKvHIodHBB5BUgj2Nf2d9DXjeln/AWd8I TxE62K4GxzVL2q5an1DMlLGYZtOrVbjHEPGUoS53ejCk7Q5uSP1HDOJVfCVcNzXlhJaX0fJU96PV 315ktdktrpE1YniHw/pvibS7jSdTjkaCdWMcsEjQ3VpOY5I0urSdeYLhVkcBhnAcjBBrY3j0NHmD 0Nf1vj8Bgs0wWKy7McLDG4HGwlTq0qkVOFSnNWlGUXdNNP8AXc+jlTVSMqc0pxkrNPVNeZ8b6v4O uPhdrnhyx8PXc+oajrN+lp4fvr8Wq2/h2yQLHrGqyRGNY7rXjDczKkm1xDbRM+AdoGZ8UfiDFqNh FqdlGJZ4dSu7W21BzzYWd9Dp9xBNNZuBHdaobSGzmt2iOYAwm4cgp9BfEO5snuIdM1W106OS/s7q 28IXk98y3tzr99YX1pqNulrHHugsI9MZ/Om3HcbtEUBvmHxPrOoHT4dFtNSMzxa/qN5r99Cthcos FrcRwaTpM1nY3IwL9U0+SeMYZSJ4gMoStf5neNeCh4Y4bizhHhPMllXCmdVY8mFSny5bUofUaVO8 cQ1NV54rEqvHFXq1P7MeDlh6tOjhcK4/C5rH6gsRhsNU9nh6jVo9INciWkurlLm5tX7PkcWlGJ6H 8MbctomsQalqF082uWRtrSV79WNvazqbKG6NrcuzQsItVlcb1ZPKcPwGBrbt9Ol8O/EptPm0y/vd KawTRY7t4Z5YdPjsbDNvvklPlvePqVvHOtw+VViGVG5I8k1HV5/DWu6Zo80qTWqXFpLLJbu7Ld6L eG0torOUnBu7hNPt4HU4DQXBeMD5ePT/AIivJokmra9BLc3/APausJb2OlK8kTzTaTHbxu2q3cn7 25hF7HEY7RAHuUdWEgTIPwXDmPy3D8J06UcJOOP8JswwU8TUcoSqKk446tiI1aUoVHVpV8TJYaXI qtZSnTcIzqUqduOhUgsNpF8+XTg5N2vb327qzunL3dLvVWvZHs3hyznv7s3erQ6lKdXliFppc15b x3VlYRzGS/OrrFCIprM3TLNbK6eYHuZYI5FXbn0PUBe6n/amlXNvYTaZLohn01VLL9ovhI88MLWj zZDQCC1cMP73DLjFfNeh6t4o0211K+axm1jxVq01vqCaNPNtezs9YsBaxz3beZvgnSKDISJ1EUVv KiBWDCP2DxVr2p2GkeFhoS2F1rfkM0Viv2dtNE2nQiHVSt/Od08KiVwIgyGUIHZwocN/XXA3F2U/ 6o5vVxWEx9GGEhGtjKcqXPicZSxOIqYVUKlqUYV8ROpWqVXgaU5TwjlhsKqUKcrVPpMJiaf1aq5R mlFJyTXvSUny2eiu223yJ3jdRtbfx+S01zWNZGj6n4chj0LSxfyT6/Pp8sP9m2txGbWdhNazBbqF JzMPs7EhwvzhFUEe46/4ftPiL8M7LTdN1h7rNtp13petugnkuJ9Lfy/OuEVMSTyLFOskY2/vHKkj k1y3jQ3ev2sukW9na6laNcaZeT6ANR/se6nhawhluVEkaNm0hS4jljZCxyGVg2xlPVfDK8sLTw1a 6fpC38mk2FrqsVv9r08Wl59r0vVLpLuNbL5XKuZo/JXbkiPLNvc1zcDcMZVh+JuMeB82lLPOH+MM txdDF4vEc9HE1XGrQwtPC4dUY06csLhKEnFV/bVsbQrVKTqrC0qlBVlhKFNV8RhKt6tHE05KUpXU nrGPKrJLlinvdyi2r8qav4+99fa29pamDVtM/sxby01/TvEMcclpqJSzlktvEWrJagHS9QlW3mC3 ERMY2pA52ygnrLrR5r3TrpLTxFcCa90eHTV028gtNUhE7JG1xHPJKxjns7mVSGWX7s8bAEq7GvIf iBBq/gPxVfXllq0Njp2saha3+n6ZfR3M13q+j62q/a9Fu7W5iZZrKC5adWTexgMygxqGQjvdY1fT dY0vUdZ0IyQeXps1pqEJuYoowI7f7VFFp13Am+2m85jLbyvH8r2rrlcbW/MMkzbC08VxrknEdGrU 4kyKpKliKU8VKjUr4eEK2HhXwksJOD5I0aPtqkJUaanKu8dDD1J1FJefTqRUsVSrpuvRbTTlZtJO KlDla0Sjdqy351FtnlelWetL4vjW5tdJ0rxTZA/abe+GnnSdVsUt2xB9uRDJo92bYxi2klZSFHlC c4RR6X4YiOs+DviZ4V1q3vPDHicaRHqWrXeopdzvcW9gjJp2uLGsjfbHFraGKaRZDJKIomcMWY18 9+NPFl3Nc6N4shMx1K70mPT7jUJDCx1K1+yW4S31iyfdHPIUkk3uOJQAxBJG33T4K/EjxV4suRp1 14fs9Vvf3EOuamq2dgg8MsBbRvKikNJdK0jCEbWjdIZEAVitfCeFOfcI1OPK/BM8wx9eeaVMxp0l UwssZSx+CzbL3h1KrHCxp18NmUaVSPtcTguWhOhHFe+3UlXqceXVsNLGPCuc26ntErx5lOFSHLd8 tpRna13DRpS73fz9o+pfDLSb3R1SPxV4g1C31W2vFuraS30iCOa3uYglha2VqZWuo5QqvvyrswRQ AN1es+KtH8Z67JceHNN0DUbafUZZ7ltOgtLbTtOs7DT7+J5NRijJQ6ncXET2o891aUNZCNSxY45/ xv8ACDx94Vv9d1u30O38V2Imujpeo6e0cN9pVgWk+zXMlnp6xSvfojoG2pIqLEW6sCv2b4L1K/13 wv4d1fVLE2WpXGl2v2mFmjlZZjFGJ2SVDlQzxgshwysNrDK8/U+D/hRm+d4zjDw74wwmJ4Dq4aFG rTpYTK5YRYnBKpPCYmU8bV9vDFVKkOT2FR4rGxjGrOUX7SnBUN8sy6rWlicFiYywcopO0afLzRvy ybm+ZSbWz5pbtrVacd8FvG154t8N3tnqkCW97oWq6lo9seVa607Tp0htpvJZB5bwLJHA2erQZ4OR Xf2usWt/faxpQfbf6LNax3cbgpK0V3As0F55LKN1rIVkVHGVYwtzxivnDxx4n8Q6J441y38G2ip/ YNtaeIHgtrGWFNbuY7S8ur+xvblcG9tpZbmBPLhwfOky/wA6Zr17QvEMXxH8Mwa5ohHhjxh/Z0sI i1SzWW5065jcrLaX1s+06hpAuslTkY3h1CSZFf1Z4f8AG05Uo8B4nNKmc8U8GPEUK1SrQVJ5zQwU quFqfU6kqsaKxNGt7FONWopVIxXPJRqVMRT+gwOLdlg5VHVxGF5k21b2qg3F8rvbmTtu7vru5Lnp /iDq9xq3ifw9plk1l4r0O3fVdD0/WUMFp4g0W3vGi1CxBif92zR2jTQ3Csz7LgOF2xyLWjca/quu 6eupWenf2t4f1G5t7a3uNEmaLWvDi3No7Xt7qMMsmy6ht7lYCstvktDcLKqEA5+ZfGniTxn4X8d6 ZrNzpF/eG1Aub7R4knuYNO1S3uJo7c6Vqaqz21tMXYuN6RyW16BIpB49f1jRb+wh0DxLpmq+K/CG i3k0msXfhO0gtJ7+3uruOzE/hTSrdZ1WKMvFfXJkG6OFFd0bbhD8ZkniBnec1+LcHVnj8XPhvEun iqDp0sLUjhK1ZvCYtrFQWEcadOvVw+MpOdLEzWGwrhh6VehVqvko42rVliYyc5uhK0lZRfK2nGVp Ll0UnGSupe7FqKabMDxL4z0HwPceBYL+xvTofinTpJb/AMWaTZWkNpObjagg1i2FkoujLcvcXM6o YmDpHIRJ84PIeMfH2g6d4ps/FXgy0S+1fwrFLH9nhkSKLVNLezjebSooooUFm8unMXRZCxC2rvGk hRCMjxB8S/C3xJ0TUdN8VQ+HD4n8LahcJb2WuXuo+GbbXdMDliLC+KbbDUxGIVnS5iaIhAY2G5iv jniDwZZ31sl1beHPib4at7VhdLNoejWnjTQrOG6iWU28Gr6TrCm5jEUiNGWIKBpFIUMAPyXi3j3O q0cWuC8fgOIcjrTwWMwUqdOVHHZfWwlONOpCVWdKrg8LUo4zD1MTCOJxM8Q5K8nXw1RVY+Xi8bWa n9UnCtRfJOFladNxSTV2nCLUk5LmlzX3vF3PtuH4wab4n8BzeKPA8tsdaubGzW2sNbjmSHSr6/vH sM6oLfmSGH7NeynYQjx2e5nRWyPlzSPFupah8YoNM8Xyz6v4d+Jvhu58JXlxrkyabCwme8Rbexby V+yP5+yGKOIsWe8DRu421rW1xoGieErPwQvim+1zxSNKvdVn0zU/DWp28dzZ6+0GIpbTSvNuXv7X TJwk8CmVYkuZpcgxvtlt10eC60PWH8Y+DL3UINR0zSF125ttS+xaVJbzrZxf8I1Lb6WLa48UG+Fz vjBKIly4IU7p6+tz3ifiTi6pwbXzLOsNRx2QrL8RmFCjicG8HUrxm1j6FfDLGVaXtalKrVwlenz1 Yxi1Pnw9G6qbVsTXxf1SVStGM6Hs5TipQ5G18cZR52rtNxkrtLe8UtfmeDRbDwVf61c3sj6Xe6Vq E6XZhuhqQ8HFXmS10bSbiQBNY8eywBlSUjy9Ni33En78ARdp8PfHdiFHjH4hWdlb/D/wtcLpXw60 t9Nt9U1LT/E0hjljl0a7vV825itYit7qUkjsjzPEQokZFG58VfhL4+8c/GDWdG0bQprTwPBqt5qU OpaVaxT6VY2lyTc63qlxHZyu13r894l2BG/+kTSqkIVUAC+XeK10HUpreO5XVtM8L+G7R/Dvh7wx 5Fs9zY3TRtdLeapcPOsNrc3N8IZdRuXdpGe4eFECRqR+CVcv4g4CzjM5YLLpZblOQ43E4XBQxdJ4 fDZpiqFVOpiaykqUa2X4S1LE2go/W8SsHyUVRo08PgPCdOvgqtTkh7OlRnKMFNcsas4tXlLa8IaS stJy5bK0VGnV1u6jhM4GvWWi+IYNYub42M2lx6ZqmttNvYX82taRDKqX811ueLPk74liZwHaIt1v xJ1rxD8P/BXh34cLql1b+K/ESf8ACXfEXU/NZrlZ9QG3RfDst+gIjgjs3MkwBwzSgtw5z23w6+Hn h/XvFN98VPE8uj2PhDwqLC6tIIruO5TxFr1pYLJExuEijivLRLm1l2LboGuJoVTZgfN4Xq8958QW 8R+M76+tINT1G+vbuDSp5r1JtXl+1W0Mmj291dyRLbW0FjOjLGrSoGsyqKGLVeY5fnGScOZhjaM3 hs14vWJw+X+zxFX2ksowc4PG4+UKtS9OtjpeywdD2apyrUPrsaVKo8RF1CpTrUcPOafJVxXNGCUn d0oNOdRpvSU9IK1rrnsm5a4J+NXxQXTINFk8Z65LZ2moR321tRmked7aNYYIJrkPvls0jQhYw4jO 8kqTgjY07xJpnj7R08AP4c0jQ9ZvL281jQ9ZsJZbS2n8TeSVh057Bn8m0t7+zhhtXZMbrmG2lYcN nx/UZYfNkihgtYNjHiLzZGViMPEZXbkpkqSAVJjypIOTSgupLSSC5tnaO6t5RNFNkExyoQ8MsYPK yI4DAjowBHSvxWlxhn8MV7HOM3rZ9lsoLDV6WJm8RzYVyiqlKhPERnLDz5V7lWl7OcJJSTvv5Cxd fmtVqutBrlkpPmvHS6Tldxdlo1Zp6ln7NqH/AD4Xf/gO9Feuf8Lv1b/oAaB/4Ar/APFUVt/YnAf/ AEWNf/w21P8A5cV7HA/9Bkv/AAW/8/6ufqJoXxC0Vtd8Q+Jbm81+zt9IsLp9T8KT6jPfPYXklzFE l5a6akzQtBIqyJIN6izui4chXVl57wv8QNW+M3jXUfDFxYR2Xge2NlrstjdQqdUEejTWzWtrLcxs FMdzqRhklXa+EQoj85Pzr4ktfEPw98e6na2bvaX+navqcuk3bJmDUdPu3M/2OSGVTHdW01u4zGwK OZGQ5BXH0R8DvGfgHU9YuZotJPhjxbcabcRapbQyyf8ACPPbw3MMxuNOiuZWbTwSAZIgypEFPBXB H73wT4kcQcZ8bZH4c8T5/R4Qo4DOsW85y6tCVBZ5JudKphPrEYOk1JyVPEZbKOGw1aH+0UJTjKnh 8J9PhMdWxeLpYLEVVhYwqy9rBpr23Rxvt5Sh7sWvei2mox+qI7WCJPKitrSKLnEcdvFHGMnJ+RVA 6k/XvXyx421/Qr34mx3Pg25vtJ8a6TZXmn65ei3l0xWkj2nSrqFJVVrmW21OOyaaQAiS2nV18xYm x7LoXxa8CeIdd1bQbHxBpyXOmyCOKS6nFpDqG0bbhrGe5VY7lUl4yjsXHzKNvNfPnii30zxH+0f4 ZeXVvDkmlQ614Tsp0XVbQT3CQvBLMrqmRNIZJCoBYkqFUelfvXjfxRlme8JcGx4KzXLc7oZhxRl2 XuVCrCc6FWnWqx9thsXRq82CqYarStOrCnUlUw9SdODjCtzy9jNsRTq4bCrB1KdZVMRCGju002rx kn7ji1q0ndNpaPX9M7nw/F8SvhtaaJ44sGhfxHoGlvrdmoEc1pfvDb3MphJBMEyXK5GOV6e1fAH7 WCeIfhp4s8JjwdHfeHfCtj4cg07TpNPdjpTzI7iWyvLdlaKacrE0jecrtN5pYk4OP1EZTnjucDkd uPWvgv8Aab+IXguePxP4Z1rTNX1TT7O3stLvdY0o6ZLBa61MXnt4LX7Rdqy6haM0BkG3b85VmBOD /W/01uC+HJeB+ZYzG8TQ4X42p0svw+Gz+UfZ47FPKfrGPp4WviaFN4inhqtX2+IrSpOnTo1Z+2lz csaU/puK8LQeUzlPELD4tKEY1npOXs7zUXJK6i3du1km79En8Kjx7b61p9vpeoqnhO7hvptRTxD4 YtTbx3V7coscsms6fbur+UcFibR0VSxb7O9N16PxHYWFpqviSw0nxZoF/I1tYeJbWaKQ3LRgM0cW r2PlzwXCr1ju4y6k4KZryJ3QOwj3NGGbYzgK7Jk7C6qSFfbjIBIB6E1oWGt6jppP2K7liifcJbZi JrO4VwodLizmDRTowVQQynO0egr/AJ/IccYrH0q9DiKtXxGJdOFOniaEqfK/ZcsYLG4GrGeCzCnG mpJPlw+JdSSqVMVUS5JfjP1uU01WcnKySkmulrc8H7k0l/hlfVyex2Wm6foOp3tq+lauNMuhPDIm m+ImWGN3WRCqWms248p33Y2idYASBluTXp+m+Jv+EU8d+JfDvi2wWXwj4uvZrfUbDVo2a0iaSZv7 O1xQhwjBnRpJbdsiOYlW3RgDxNP7E1wFVaHw/q7AeVE7FtAv5DgCNZZGL6NKxzjeZLfPGYV5rqfE WsarpGtvpOpaWL6xvLLS7oaBre+UW9xfaZah5tOukZZLOU3LOVkgcJIANwda+s4Zzh8PYOnn2BjR wVbC5jhHHF4WFSvgcSquHx1GthcxwE06lKliKUqlDEUoqCqUKr9jgq0bTN6Fb2EFVgowlGcfeinK DvGacZw3SaumkleL0g9333iX4K3TnU9Q8MX1tFpejPnXNM1u9WC68PCaFLhZ4r9k8rWtHa3KPHPH iTZxIm9TX2j8KNRa+8CaFBPqOn6pfaPbLouoXWmXLXdqbjT1VIgJ2jUyObJrRmO3BL8E18PQ+N/+ EZvdP8Ia/Pq0F74aligi8R6dew3F1pGoXe06nZ3dpOrQ67okUEkdu1vKTxBIY2AkIr6g+Cup+E4t W8XaHoN/dLeTyWut6ho11pEuj2sV7tNrf6joUMszj+zZs2jNEGIiLKY2aJlx/aH0bcw4IyfxOrVO FMPTyGvxJTqZbmuArY6MJYfGU4VMVRpYfB1VRcq1KrhquGqRw6rUGpSr4R0adWrgMB9Tkc8LTxz+ rpUpV06dSEp25ZJcyUYu12nFxdrrVuNk3CH0NRTdx/unH05/EUbv9lvyr/RXlb/4dH2yi32+88n+ K3hzTNVh8Oa5qUk1rH4a1O5me+gIDWUOo2MtqtxN8p3WiXosmkGQMDJIGa+HPFtv9l8Q63b6xfX7 2Wi2Hh/TrOK5jkN/dRjTrSOGSC7c+XBMsZuCzkuCrsFV8Db+hfj6/u9O8H69qViubnT7RbtYi+xb iOGeJri1dyjbI5YPMRm2sVD7gCQBXzf4w1LwNezWFlrNxaaYm2AaBHomsu1/BJPapp73OoxXdqkE ttBO7CHypEaRRvC7WzX8MfSf8P8AJM7zPEVqeZYTJM2r/U8dVeKjUhRxc50MTllONWtGpGlJU4YS hOEKiVqtKlSl7WGLtQ+Sz7B0qlSTVSNGq+Wb5r2k3GVNJtO2iinr1STupaeUeEl1CTxjbxWmnaZF c3gt/wCxdWvY2u5mg+xiK70mzNzIIbnVZBEVWVgwhdJCpQPuX2i41PT9SivfC9xrOoHxFZzapBb6 pZWotb3TTb2tpepp8EMAP9qx/wBkywJ5kTiQ/Y8xsgZmbzHUrrxN4cc6Qrtq1v8AaDfafdQb11BN IsZYI9RSykkPmwTyXKWtvdwRujR+XKghWMFn6fXPHWk3ltbajoT2SXnhy1F1cwedb2d1f3Dxw6Qk r6jIytDNHKVSa1dw0sYhkUOvA/G+Ea2W8L5Rn+VY3MJ4fGQxHtcXhcXRnD2+HrtSxlPB0cPXhRq1 8PTprEUswjKvXWG+r06EY1o0WvMw0oYenWpSqNT5ryjJWunZyUVFpNxSupq75bJWdjat7bVLCz1X StHv9EuZIbbTV1TWZZrPTbDVNJh+3/aLKNSHlis4bGcyPISzyzRlDtVjmLx4tzq/hzwYdGNtqUmj Xc3h672odKuvsz2sXk3qW10AiF7iwu3W4yqRi2JG7kV5rrOoQaXo9tqHhvTb3UL/AMR60NsFxqMd 1bWeoxtPJc6JcaQ0YM3mNPJMEwIpo5FlaRh8q9Nc6roWsaP4v0TRNeW7uNX0m7gu4GaRNviGzjj1 XTtK8OyROEurU/YtTEoGA7OsakxjL+i86wGNynPuF/aew+sYGEadGOMk+a9armmX0Mtni6UFXqVJ 0cOsZN0sZUhTrrETftvbKroq0Jwq4e/LzQVlzb6+0gqbktW2kpaSaTu3e9+qg8Q23izwZr9q95ca td+HtXthc6lFDZxTvArC7si6tHC13p1u8d3F80kUkpjM++PzUFetfD+DXNTvdH8TtqD3Gj3ej61B NavdXEv2aW61CxudOTy7obzIqJc7slzGJBGXYDcfifwHdeJW1nStO0S7uXaS5N7rurXM1tPZWNpL ZTT6l9qGyRJ7aHSILyR0cvtlZdoD7c/Xfw48R2F3NoNr4ZgvbLwrd+HPENxDYJZhYr+5tNetrQax IyRg2moyLMztbhgsUUo2hlUEfceBnGdHi3N8lzTPXXw+Mw0MPhGozlTeKr4TMMNUw1SnOc5yxFDB wx0cNmTrVKk3OrTlCderWlGh1ZRiliatKpWvGUbR3a5nGcXFptvmUVPlndt6pptu0fCPEHjF/EHx B8X+E/FUrwabN4kll8I6jrUcUaeG9Z0+Q2tjEWuQ32fQr024huNmdpaO4GCrGvN5fEer+HptZ8Ja 8osra4t4NN8SwRQOt1HLCYRb3KOz7XljSNXVEYxSLBwWSRjXffGTSPD+va+moWsFzofiTxPpvnp9 unK6Brd7Z3EmlXWnx3bRD+y9aS9sVUiZRDcNMhMsTPluBs9d0bxLHZ+B/iGNX0TXrKWHSdI8VXVp AdR0bO61h0zxMZSkmp+H43dQhZPOt1clZDGMV/OvG39tx4t4hy3G8SU3nEswxv8AZuaVKs4YXMML j51K0MozGpU9l9VqToYhfVZYlqH1bETwtSp/Z0sLiqHiYp1ViasJ106jnL2dRtqM4zbapzbtytxl 7vNpyycW+TlkuN1uKUW9wq3FjeWAtI7uy1DT2kS2jSCVY4YHtZhm0mM1zLmMHg3IXO1MD6D/AGcf A+qX+qTfES01aTQ9IgnbR4dPhhW5l1lYobb+0RcmVwkNqZlUg4ZhISU27cn58gT/AIRW88X+HdbK yjTpJtNkKWhmc3sd0qZt47ggfYpkt3dmZdwVI5Y8OFNdX4c+Mfi34Y2Ftpeimyn0yW3tL+PTr511 CzS4vx9qvWjlhEb28hDMsiBv3b7Rzht3znhdmnBvCXiDk/FniBQxdDLskdWpPDYV1HiMFmeGr1KW HU506tKs8NTkq0uT2vNOcacKnt4xnFc2X1MLhsbSxOMUlTpXbjG/NGpGTUdU07LV2vq7J82x+nDO JAXU4bOCR2KkFTjrnGR7/lXyT8YPF8Xwx1a2j0KW80geJ72zk1qS2huZ7OxtZrtZdb1Ozt7m4+zD UpohEmFjTbuLZLOaj+FX7SFvr9z4pHjdk017e0Gq6XZ6VZSTW8Wm2Mbf2hFCQzTXN2qN57mRuURv LUbCD4j8ftV8bWPjiw1m1vpv7G1uyjvPDGoac7yaLqtjcSSNBF5FwGhnuFs5LRJ0kQ5zuxsYGv7b 8YPG3hriLwhw/GPAeLq4nFvGQpfWKNKMsdllOderhalerQjXpVsMsXGHs6TlVouarUrzp1ZUkfVZ pm1CvlccVg5OU3JK6Xv005OLbXMnHmtZap6rVNo+nPi7pPh2PwOddt7lLQ6NJYa+VNw5/tfTrua3 trmO9t0lX7QJYbpWUqBiYADG41oeHviH8OvCXhTwyJ9Z8PabHcW9tbxWuhrLPHFNLZ/aYorhIxJK tw8UYy0xLvJIMkls18Aa1451Xx9BZWXiYmTUNHtlstNudPSK0tl0y0Cy3NlLpdqUS+PmRJLhNrHy j5RyFQ5s+sa5p2g6dNZfZ0SHU9ctLJ9Pijk+zwXtjaie1Yyfv4gqPcGN5D5kUgKoVMZr8hq/SZwu D4uzji7g7hDD0MHiMswuHjOrSnOt7ajiYfWKlXDUp0FRbpVIU/Z08TOFT2NCcqjnUao+Y8/jDE1c VhcLGMJU4xu027prmbimraNKylZ2Tvd6fd/jv4aeHfjRD4b+JHg/xDd6RdFAl3faYslte67pNs8g OnRq8sQt9bjnSSGF5TsBkKy5VVI4Dxn8QtU8I6H4T0r4geFNQh8K6kr6BrEc9xdXOuaDfRkXmk6r ZaxcEjV9QTTJkF18wWV7aeIEAlRzvwF8Vaf420K8+DfjSUKlzC+p+ELmxuIrfUbefTJGmmLzWUoZ NVhuI47mAucyrG+4Mo+b2g6D4qSC50H4j3fw/wDGvg1tRubLw1ZeJ7zy9au5ILWOPQY/7SEBSPV5 r4TJKJA8sYJaN3yQP2LLJ0OPuH/9fuB8K+G8746w1Glm+LoUo4jL1nGHlReLo5xldVzq4XCVoQgo VsLUxMsXRx7qYihKuqNdelBrG0HjcHD2FbGRiqs0lKHtY8vMqtN3cYtJWcebmjNtq9mfF3xn8L+O NEuYbnWZYfGfh37LBdWPi/Tbe1e6Gk3Qb7BFq1zbxvJbqYFOxrver8hZXwQMb4Upq+m6rb+JtJ1b Xm0SyWc2dlBqsmgQ6hqce3daarIT5FxpVsJ45r2TJDR+XCgMkscZ+y7jwB4e1sJZXlr41+D8fgS1 vVsNaivdLGjXOhzTC7ls01i7eYa5o8bHz0W4VYoFumhCkbkHG6v4h+AMsum6NoXja00u6kuhcW+r aR4T0270y51nT4AJ5gJ7KKyGs3EzRMhMbRxytH5AjLhq/NMb4M08o4wfGdbiehklF1aVTBUcdmlK ni62Lg4QqUsNUzOWBxc6WHxP7mFPMsNaqo4eVSu1UqU4+bUypU8V9beJjRjdOCnUSk5JpNRdRwk1 GWiVSOvuty1aXmP/AAkFrpOoJr/iC00zXNWvQ4OqWMUmieKrizN2q/ab64sI1TRPD5jSVBFPC+pa x53liGOKUR1q6p4D8Q+IL/w1Lpnhfw9HpqeIrO78P+HNcE1nP4e0m7mSS4ay8NxTQ3FlKba0guJI LiKXy5Y5XEktu5ZLviHTP7c0uHWf2eNXg8R+NNOu7jS/EkPitoLPx7p9xJD5KXuh6LrS29tpV6Ns ytNDbicAgRTYVhWTpHwg+Lnhi2jm0+8sJvHutNa61408W6/4it724gsbW7iuE8FaDbG6kuNQv5TG TqEwVEkBWzjkCMxb36OS5tVxFXLq/DuN4tyFqninjsqhRxGGrObhJRp5l7PFf2njKmIio4nD1JQp YOeHWKq4qtVpOpPRUqrlKm8PPF0NJOdNKUXe2iqWl7SbkrSi7KDjzOTau6XxZ8f6hp+u3uh+F/E1 3JpNj4h1G+1hdLK3eoeMNXSW5lvNBlS0uEi8OaDZ2MstuGkKPO6y3TxyMgevLtE8A3HxQ102l5Lr Uuj6Rb2Wo2/jRr+xlTSvBU1quoRzeK49SLf2vdW0dy1ujxETM1vLHgxxIE1fit8MX0XxH40upfBf xIv0vr/UPEEGq6ZpkGl+GpBcXTan9na/s0u5LhkhvLqENKkTB12hR38z1n4neJbLwD4Wh8O+Z4X8 JanceJfD2q+FtLvbtbe/i0q70+9K3epTubwzPDrLRuyyIqhSqRqmUPwXFmZShxRxBU8SMJiI5blk 511l1F4iftKcMTHCU8OpYiCoUcN7SrQq4jEUKk8Vy8tamq2KqV8RPgxVW2JrvMIydKneXs05XaU1 FRXMlFRu4uUoty6rmk5Sfs3jWLw1F4b0vRPBepax4c8K+ALhr+0J0C5XxB4gvbq8hjvtU0nVLu5a 0nnluJLF0uBte3jcqwRAQ3zDqqXV/Ot1Z6lrdzBHbTX8sF7Bb7LVbBC264/s25CxsLmaYM/kq4Mh kzJuJrIh8Y3OnXVxaaN9qOk3l8JrWK6na61i3s5x5Mllb3TuRHIY22BtmS8Eco2sFIztc1K3jlur KWSLXL1rh5ZtcLSxzpI+d8SSIFN4+BH5sko5dCiqAC7fl/F/F2W8SpYunhIZesNGnRlRoVK1PDRd CKp4aGHj7SSVFYalGjThTw8YKEFKo4SaZ5mKxVOvaah7NxsmouSjppFR125VZWikkru1zEvYxFcS iJNsAlbyPmaRGiLsITFO0SGdNi8OVXd1IB6UwzE4HXOMAE565PWo/M3tl2baCATkuQueqhm5HJ4y PrT4FaeeGCGN5pZpo4YYl+V5pJZAkaAjO1mZlHtmvyGUFVqP2cLOpLSKSvdvZJL5JJeS7Hl6N6K1 xdzejfp/8TRXb/8ACEN/0HdA/wDA5v8A43RXq/6sZz/0B/8AlSn5f3/U1+r1v5PxX+Z+qkVn4h8X eC9J0Tx14YsYda0eE22pnxJq+maTe3elW6SEaxp80cjXFheW0aRGUyRhPnyySITjx7UPhnqOkapL rXhPx54Sv7bRlSa41RtWB1CziuFISHVbK1t5xcqbZ3R5kVoZkY7hjq/UdMtrPxDpT3Ny2h+L4Z7u 00LXYp5bTQvFsds721tZ63e6isyaDq5hxDLA7TDawhufKJVqy9E1u5F06+FPDOnaZrNpPP8Aa9Bu ra5vdVhltpXkuf7A1C7nVJ7MFZC2nt5bp96Hzc5r9k4lxOQZ7XyvB8TZPiMRnWVulh6OPWNlVzef 1PD0fq6TweAwUamNpynCWIw2Il7aqowxGW16kMXiMW/o686NWVOOIpSlWp2ip816r5FHl+CELzTa bT1ekqbalKZVXwH4e8Qo39n+O/A9nrXzTPpNvPqr6fLEgZp202aXTw28tylsN7L8yxsylQuH/wAI xomh38Ev/CytAttQ065hn2R6P4nM1rdW8iyqCDpgw6uq/wA67W3EXiR59N1r4c6XDqGs3djYaf4z 0rQNQtreDUtTuDHa2muaLDcRC3uHnVkkkgCSR/6zy3Qgn6N8Ufs66LqHw503w9olvZ23izR3/tCD UYzIE1PUbnyv7TsJrm4ZpDYy7Qtv5jMYTBEeBvzy5D4JY7jzL87zvgrg7LMxxfD+G+tc0q2eYWti cTGq1SwlPB/WaEaeNqUqdWtGvQr1sBUlCFHDy5nOcZoZVPGwq1sJhadSdCPM3etFyd9IqKkrTaTa abg7KMerXvHxK+OOpaZ4Q8KeGtG17T9K8eeM/A8HiCLV207VLpIrcWolurzT7RLYtHcyW0F68YkI dG27UZhkfmJ/wj3hrVL557/4saGZLy4ae5vLvSfE7zSTTuHmnk8yxw7licksMkcn09f/AGsr/UdE +KmgWdi91Zy+EPA/hDS7W5h3ILe4g04SSLHKBt3bpcMOQQSCCCRXyOZcsc4JzzgjqeegFeh9Lvxm zbi/xTzLhni/KKfElPw4xNXKqFLE4jM6WHjDDUcLSrVY0sNmNCP1rEYyli5YrESg6lSmsNT57UTT iXNKuJzGph8VTVdYGTppSlUUUoqKbtGaXNKSlzNq7XKr6Ho954R06OeaHTvH3g3UhHK8cTvc6hp3 nKGIR995p4jQkYP+sKj+8RzVF/AnizYZrPTBq8AyfP0K8s9YQgE87NOnkdRx3UdK4MyEck/pSxXU kDiSCWSGQHIlhdopByOjxsCvT261/JE8dwxias51+GamXwk24xwWOqRUU3omsZRx0pqK6e0g31kt z5tzoSbboOH+CbSX/gam396NO5tb6zkaK7sbu1lQEtHc208DrjqWWWMED8K2te8QQ6vZ+GI0F6Lr Q9CTSLme4nWRJXh1C9urdrML80MKW1zEgDEndGcAKBUdl8QvGenRyQWvibVvJljMUkNzcG+haNhh kMd+sgCkcEAdKxJtWW6wbuwspHBJMtvAthO+cZ3G0AjbGDjMZxuzzTlVynDYPF4XJ80xMVmsIwxF PFYSlGEY069OtT9nXo4ivOUlKmm5/V6LSvHWM2g5qcYyjTqSXtFaSlFJaNNWalJvVb8q7dSOW4aR 3kkO5pCWYuWZmJ6lmJyzEnJJ5JJJr6I+Avi68X4h+FklvZTKfM0O5hknYxahpVzavb2zhGbaby1l Ft0ALwRg8mL5vnO5l0owxPZvfLcl2We3ukt2gjTgo0N1E4aUnkENEgGM5PSk0rVptJ1LTtUt3aO4 02+tb6J1JV1ktZ0mQgr0OUA+hr1eBeJcXwJxpw9xDTrOvTyzG4PFVVSqaVIUa9Ov8SvaaSaakr2l OnOLhOcJaYTESwmKo107qnOMnZ7pSUvv+XdNWbR+3FMLgHA59f8A63r3rPsLyPUbCy1CE5hv7O1v YiCCDHdwRzpgj/ZkFXM46YPTqB+Pb1r/AKB6VSjVpU61Oaq0q0VKLWzjJJp/NNNH7OnCyknzKWxk eJRcy+HddSxm+zXraTf/AGS4IQiC5+zSeRMQ4K4WXY3Ix8vPFfnNFf6R4g8M3cd1f2Vx4n0k29xp ccjyva6ellcWGmapcahcxsftFtcC6WQMx2o9p5gOJMJ9+/Ea6a08AeNblTsMPhbXWDL8pU/2dcAE MOhyRivyH0u7+zWerSNE432kdslwjMJIrl5QVKOpG5Sy/OpJG0A8kCv8/wD6ZfE8Mm4m4Jy+WDjj aGPyrNPbQrTm6UVKVNUqtOnFOMcRSnTlOnVmpRjUVKpyqdGnOHxfFGJVLEYSChzRnTqXTei2s0u6 aum9nZ2ukz03VvFeq6rr8t3ql4dNt9Mv9llDbSKXL2aGaNlc7TfRGCIAyvkslygYyEqKxNP8T6nP fzx/2dHqN5qd+W1C0ijf7XqjTS73tY4IVKTXAumeVWaMlXQH5kUKOW0nW9TEtlYWKW0l6LhIrBf7 MtLqe4knlK/YZZJIWcwM0zgAZ/1hU/Ljb02oyjwtM2k6MyXHiXUUMUt/bGEpottdBopdG0q6j+W4 1LJlhurtTiPy2ggbmSQ/wxDM8wzRzz6pm2KjQhiHUxdScY1Jzq1opU8PRg5yp1q7SnRpxVGjCNJ8 1VewnPk+RVWdS9X2krKV5NpNtvaKV2pPRpaKy30vbtNalvdBmvtM0SefWtT1yP7VrepWZ82HS7W7 Ut/ZtpaWRYWt4qtKl3eJvUbWt4mMfm73eAdG1HTfEGma7frHHDZ6hZQaTBd3VtaRStPBdtBqCTtm KS2tbWGWecHaGHynG5iPKLC+lt1tooP3N1ZmRYdWC7fs6zTOhtpUkYCez8xtwbDMhduGU7R7J8MN C1fxTrEOqa1rljF4ZA1OLXb6FvtBciyYtaXVhLDsiMml6fcAEIiJbRSDcuQrfXcIuPFHGHD31HLs TUxdHFUp4PC061P6tg17WMpVa1aulObo15+3rV6vNSm4urVqWToR6cO/rGKoKNOTlzJwimuWOqbb bs3aTu5NWe7fReiaqH8IaHrOseELU3MPxBl1K10NdSiVIfC+g2iOniRZo8/ZLiN7yLybcKrK1tuk YkGuy+AWra7ea/b2N1f201np/hzWNUW10iMR6YRqr6Qtvb36gqg1CKeO5JEaYjM7oCFwK+b9T8bw eK7nx7b/AL/TvDV1baPNoVqqR48O6fpUy6Xpr21lhjJZfY7pBc+UUlkWUyfMylW734Sz33gPwxZ6 xFFGk3i/4laH4el1mzeO687w1YWcmqXi2LEbfLluAytu2t8jBlV1wP2vgnjHBLxR4bzDKalenwbk tPEVa8cNUlToQ9hjqjlKnhoxT9jmmYxo1qUsTU5KGHr0cC5OGB5D1cHi6f8AaFCVO/1WkpN8raiu WbvaKW1SpZxcnZRkoX9w07zVdE8f6P4j8Esr6nrPh64n1/wjpXnww6kvlSXH/CS+GdM1ZoGXWgkC Ry24KrK625QEPGrN51p+vS+I9DHhjWBpGqXFsby602+vtXg0rX7nSlijd9A/ted3lhurdlnaK2u1 lgkBdFZWSMt5Vr8txofiaW90zVJ5l+1x61oOtRNJBcT2txJ9qsL5TuzFcqQFlGSUlt5EJJU16Hd2 8nxStG8XeGLa2tfiLpw3+K/Dto0SXHidChz4r8Nac3+tvNqSC+tYgSsmJ4R8zAflC4qzLirHZjh3 hFU4syunVwWIwdFU5vO8ppVpVIxpNwr08RjsvppLDUZKpRq4KjQWDjCOGnh8X5rxNTETnHlviIJw lBWftaad7LSSlOmvhjrGUUuW3K4z627stK+IdtpdvpOoxR/EvR4INOh0XXIbXTrnxfoVtp+21juN QhuXtLvXbazVxFMHja4hIVkWVVz4bqmgeK9NtfKvdPvXsY1WRZ7YDU7BcgFUF7ZNLFGy+YwK7lKs zAjcTWPf+bo0ulNbw3Wm6nbwrdS3LSTwXkd8t1NtIjZVNpLEIUA2/NnLE5OB1+vyT+ItEufHnh4X ljPFLbWPxB0/T5ZIbaDVLhM2niJYrYqq6bqEsMzSIV2wXiOo+WSOvks2xeXcYUMwxGKyurg+Lcso +0xCwdeSjjcJGjB1MTVoV6M51cdhYwg8xlSrUadehGeMVGMqGKrV+WrKniY1JSpuGJpq75ZaSjZX k01dzjZc9mk1edvdk5cx4efXbTXtIudI0+4u9SjvYja2TWsrreYG6S1kRkxJBLbCVXBP+rZicDmv uDXtL0XWPhlf+BDa3Omp4at7DXNFsJbe6vNW0X7JqUp1rULZrlWOp6JE+oW8aLGxkkhEkeMqhr42 8OeNvHd1f6dBBrer39vpMbyraNeFALNZoZ5bUXUkiNEZpooIg/mBx5pVWCsyn7u+EHhHxpqvhnVb z4h+I5NQtPFNnciz0WFppdU0K2vZkaVE1+dvNQmBEj8lA6ARKwkLjNfvX0Z8lw+dQ4l4ZybA47OM PxJg8VQxFTFYTD0sFh6E8NGNZTqrFyqRVfFLDJSozq1J1MLhqv1SLoc9P2Mhpxre3w9OE6scRGUZ OUYqMU4pO75rrmly7XbcYvl0uvhHWotC0HVrjUJNasfE1zFNnS7eF7mwtzDFFHPBfakRaAzB28yL 7KPLk3KPOaP7lYPiDxDe69Hd6pJbWf2rVdZ1K5JsYFQQgWtlLNDDGBlUUupO1QOSQQCQf1Et/hJ8 OYFVZvDNjqhj2lZtbe61ifKII0DPfyvgBQABgDjPXNeQfHjwf4A8HfDbUvEGleDfC1pqWm3+nnTR 9heCJrm/uo7SdHjtJ4jODbMxKFtjeQu8Mq4r6biz6LnGGQcNcS53iOKMqy7JcBQr5hXwuHjjJy/c xdWcnWrQqVa1WNF1oUuebSclGFlKRvi+HcVQoYitLE06dGnGU3GPPf3dX7zTbdrpX8rdT4Z+G8Hi dfFuha34Zilt59M1aymGryeXb6bY7pljlN1eXLLEymFpVMRYtIGKBTnFfSvjdrnV9C1H48/Dia61 jUJ5jYeL9Os77Um0Xw9qumRQxz67p2jyRxvqtnuFvIPP/dwiQTGJ1ZtvyZfeOfEN7eaffG4gt5NA lF5p1jDHDDo1nIghjD2mikeRHJuCkhVO7JJHBr1/4AfEzVNCW58GaZrFtpep6prtjrGgRatg+HNc uvJfT9W8La25RjZJfWZh+z3GNkNzaJ5mFcmvyzwo4g4aoSqeH+Ix2Np5Xn9StXo47kpRrYTNqeHh TwGMwGGnVVKFaXNisHVpVK05YmM6EYVaNVUpUfKy2vh4v6jKclTrNtTsrxqqKVOUIt2Un70Gm3zX jZp2a7bwR8aJ7jXj4Y8X/b9d8JfE/UW0jUoNVuzKnhvWNVgj07UH0/z1JOmzNdxSrbMBEsc8bxHd Gc+OfE/4YTQvqOsfD3yvEXgvStQl06TTtOS8PiPwjOXKS6f4p0K5T7Rbzvd28xFwBJHIFB3KoFfU niPwd8PviF41huYL278G/Eu80yDUofAvinH9jzSaNLd6TYJYwWCL5eoW76V5pihd2kif5VUNvWDx LDaeDPEkln/bi6T8UviHZvrnh/U7/TGl0jw1rlsl7YWtjdavZzOt/fS/2hq9rZTSlvsiSx/afM2x Y/dc58P82zzh3G5XxnmuHz/I8rx1SGAzujVowx1KOIjzQwlq1WLhVr46VCX9iY5RqQniko4jB4fD RxC9etgqlehOni6sa1GnNqFaLippS2hZvRyna9GdmnL4oRipHzHB4k0/4a21lb+KfO1f4i3Nq1tN q2lPZnxF8M9Cu4RF9kTUJ1ePU/FjW7tiK4ydOjfy1mjmciPyjXNG08xjUtI8R6hc6JM0zDxBeW1x NKt00kzQWuupZXc0uiahtKKA0ZSbJlR5Ewa+htJ0Xxn8SR4hivNL8Hy6zbXLWniaPWPDWkImo6tD dxI14l9ZxWeoaZeCVV+028hO4Xbz27scxx+UhPDPh7WLmGS11PTNTtnube/tPAaaxKL22KMbiz1P SvHS+S9gtqszMyySBSc4bAavyjPcixOIweV+2jGjwnUcoZbUxlOphsVzwkqdZylCvNY7E1FShLFy rVKFXmhChhp0cFSo0X5FehOUafMlHCy0puScZ3Vk9m1OTt793FvSMXGCUX1eqeLPFHhvW/DPi/wt 4p1az1jxN4L8Nz2FtaaldXOi6ld22nz+ENTil05irXEx1PTY7gM8RRfNbcu4BzV1nx9canoPhy3+ J3w90Lx7cvqfiW2m1GKS58J69psltFpImU61pXlw3OpmNnaR7uGRi1sodmKNXftoHhbxL8NNB1Tw bbaBqM3h/X9WlsdV1YMq+GdM16FJPtGseHDqyJaRnxBZzouGntopplliiCSlU5zWvgZ8WfE2haPp F35c1tYaE2opeapDqdpaXF/d32p6ns0qa3gdL25/s6W1j8gK7BUyGGxQfvcXkfHUaOOWSfWeIMFn GEoY2nh8PGGYZVVxGJlg62KlUoYyFXCTlUrUsTGlVhQrUVClG0vbRkod06OOcZqjzV4VoRmlFKpS cpODleM04ttqVmoyVkvtJ2wrj4ZfCHXtItfEfw68U+Iobq4uW02bw9q154dufEOk66IRcrYWsF5N Yw6xLJEkphlhvD5gRhFG7qQvh2t+F9J0K/8A7O1g+OtLv5SfJi1fwla2Uk53kGWOGTXN1whA/gZj u4ye/Uad8OfHXh3UIrGBfDd+Naa7sbvw9qepW8UWqWdkGuFe403UPIllgZUaS1uLcGWKRCUeORWW vTbfRfF/gGxs5Y/FuiWei69b3L2PgfxjrMWr2V55iqkkPh3xTYCRNHvGjd/s955mmTebABmRlJPw uIyHD8RUFisXwI+Fq+CUY4yph6FT6pCclTSqRw88Rh6adZ1KfPGjivbRqVFToYOpFQiuCVFYhKUs C8M4fG4p8i+HVRcorW6vaXMm7Rg1ZHz7deGtNtBC51LXHgusLazv4QvrOOWfJEltuvL5AZkIGQhc HPBwM17p8Dvgxa/EXVln0a416XS7SK6tdc8R3+l2+l2GhzTxxKv9jFL2c6nrZt5LkQoxVYWdbiUB UCseGfhf4h8e+I10PwNruo28vmQ3Hi/w/wCNbr+2JfDVpdTlJtVtZ3gez8T2LQsoSVAl2ZSYmT/l qe7+M/xm0f4faLB8Hvg1eRWcOlW11o/inxHp8aQzSTtLL/aVhZOqgRXclzJcGeVN3leZ5EDLtYj0 eGODOGOH4YvjrjjL44XhHKZKGHowlOVfNsapJxweAlUxM3FU7SePqVaUKuDj+5l7LEqU6WmFwmHo RljcdTUcLSdlFX5qs+kIXm9tXUcknBaaSu17x/wpL9lr+/pn/hVt/wDHaK/KTf8A9NJP+/kv/wAV RXo/8TAcFf8ASP8Aw5/4Jo//ADGa/wBu4P8A6EWH+5f/ACB+tfijxHovia6sjqMUnhm78RwYm0bx SjHwtfaxbottq2k6gbiAt4W8W29yAIrwIFmimgkk3pIVrjvE3he4t9JvrvVtOvVvLCSwsLLVre5f SPNhlDRQ6P4vNrdiKW5MexdO1OHfbXShY5WjxhNnwX478OfFzw7beBfirc2ra5qU5j0HWFlW2uNR vrFXt4Lya7WELY6ud4jXdvjvEBVkD43ZWmat4m+Gt5rPgzVbOfx54dtI5ILjwhqoSDxLZ6dcCaL7 RojSho9a0mSI5AtjImN26C2kU42zZ5JxRh8PxLjcbHNeHeKqcqazKjh4OvgMxnh3UeHzfJqf1j2c 4yk8ZQq5e6k/ZOrVjQrRniMbW66vscQo4iclVw+IVvaKKvCo435atJc2qfvxdO7tdqLTlN+5/s4T a9rdrq17q97e6hpOlTWljYx61B5l/b6tbxDeiXksQkkW1tn8rDu7I0u4EZFfWlgnmX1mn9+6t1/7 6lUfyNeeeAdC07w54T0fTtKgvLS0e2W/W3v5DLfQtqAF0Le6lLEySQxyRwjczFVt1XJCg16NoeX1 jS1JPN9bD1/5arX+hfg3whi+FeDuDsizLHyzHMoxozxNWU6lRyq16iqOnCVVubp0VNUad7e5BPlj flX3GV4SeHwuFpVJ887Jyd29W7tK+tleyv0XQ+Kf2zfh34hk+Id94v0GW4vNP1yC1stT0uKaXzRf 6VaJt8iD7k6PaxBwg+ffC5QNnA+IvEmvR6xd2/k6Lp3h+LTbOHTVsLCBonJty5km1B3Ae5vnmeQu 7AHkKAAor9lPjB4dtfFkfirSLixs9RlaWS6023v/ADvsp1exAuNMeY28iOIxdIgbawJR2XOCRX46 zeIF1ia50vxRb2UGqQXdxFb6lcQNEbaRJ3R9K1SaE+cLNJAUimLPJbhQHEkedv8Anx9Njw0wfDPi hn2c5fjpYDB+JmMxeMdOpBVKEsbh8Q54il7eUFPCKrVxP1mMIVJ0qtSq1Up0Y0ac5fE8WYCGGzGt WhPkjmEpTs9VzqV5LmavG7lzWTabeqSSZynnDv8A174ppk56+/H+evJrsvEeheF9L8PWFzBquqWH jFLl4dZ8KaraedE9tLmWz1bQtXtYRFc6a8BQguxL5yjHHPnhmzxz/wDq/H3/AEr+Gs3yDFZHi4YP GTo1K06VOr+5rU6qgqkVJU6qhJyoV6bvCth60adejNONSEdL/JVKUqUlGTTbSejT3V7PqpLZxdmn ui+ZfQnNN8388+vc8/4VQM2ep9MA+mTz9aj87Geh7jv+HFeYqPkZmg0nPXH0z69/XpTPO7ZPOfTk c9M1Q873/QUxpRnrn6+nt6d6tUull+YH7F/BDWRrXwn8DXpcySJokOnzMWyfO0uSXTnBPri2X869 ULkjgY/Gvlz9krVftvwmW0yC2k+I9YtCOuEn+zX6Ac9N109fTZckEcc/X/Gv98vB3M1n3hV4d5tP 362JybL/AGju3erTw1OlVfr7SEr+Z+y5VVjWy3AVWm5SpQv6qKT/ABPMvjdfx6d8JvHdzOSUfQbi zADlCXvpIrNAGwcHfMOMc9K/JCAXup3Vvp9hBNcz3Mois7KFd7mSTA2xxpgKThSzYwAm5iACR+mv 7Tj3k/wuudGsI99zrmtaVaF3lSC2tbW0kk1O7vL65lIS2so4rPMjuQoyBnJAP5rXmt2OiWs2j+Gp /PluI2t9Y8SbHin1CNv9bp+kI4D6fopb7zHE911l2RYir+AfpsypZh4m5JSxlf6tlGS5PQjPlt7W vXrYrFVpYehFr3peydGc6kr0sPGcZzvUnSpVfieLJqpmFJTfJSpUle27k5SfLH5Wb6Rvd6tJ7c+o WnhSCew0i7hvPEU6Nb6rrdqd9vpkbhkn0vw/OvDylcpc3qkbwTFbHy98knM2d4jCK0u4jNbudtu5 BM1ozOSz22W2vGZGy8bAq3ONrHdXNCb3A9t2MYzx+dWIbqRSojkmMyOjWggblZ96jIUgndjgbcHd jOa/iuvmFXFV6PLSWEwOGThSw8VzU4wbi5pqTftKlSylUqzvOpUUXeKhTUPlXUba05YR2j06Xv3b tq+rtskren6b4Z1u/u4PJ1bTIIoAs0er3t5D9gt9NRJJHunRVklto4hG7NG8QZMjdtYqG9CtfGKW 3w78X23hm3uRbQxaf4ZOu6hOZtR1O41i4uLzVLmwtSBFoNo2n2V4rRpvmddSVXlONteYW8F0mkwQ yX91ZLe+Y+sfZYYLp9Ru7y/RLHSm2MDdX4htLmTypDthZSXdAWx9aTfsq/E/xV4S8Mz/AA1tdBn8 F3Wmrq9wl9rmmrqGta7dmWK6v5GhXyWVbWO2jt1xEI1Rhsy7lv6D8N+BeNc+o59/xDrhXMM4zOll tX61SwcHjcc45hSeGpfV6cFKeFo4WUvaYjEKMa8p0oYdTnG6qe3gcJjK6rLA4apVqKm+aMffnaou VWS1io3vKXxXSjdnxFY300F3FJbxCR3H2ZrbaxS7jlURSW0katl1kU4IBHJyMEA19xeG7Xwj4Z8B +H7KO6j1GXQpNS+KfimwRY4pItH1DTtY0awuLW4upW869s76C2tlETNvkkDtmNg1emaf/wAE9hf6 RHey/EXU9J1++tV+3Wd54dtWjsbqVg9zbxva6jh4wQIw8bFSmSuAcBvib9l/4o+EJb27tYNM8b+G I9D0XwbJYaHGkevN4HXyLXW4ktLyLdJqioiXMRikY74So7Bv3Lw7+i948+FlDNOIOIPC2tjsPmGH hGhWpTwuYTwcG/rk6s8LgsTWxEF7TCYaOLhPD1HKnKWHUP3lSS9fBcPZ1l0alatlzkppWacZuKvz NuMJSkvgjzJxenu21Z8HXGivq2gTzaJPNqumeGZJ7yG/NjNHMNG1B4ZLu2voow5truzu90jx5KyJ dzTQsyIxrg7DVbvRNUstV0m/MGoabdw3thdwM8c0Fxbzb4pFJUFTuQEg9VODnJFdDqtprXwt8d6t orXd5puoaDqr26TNFLbyXVvFcLJaXTRSp/q3tGSQhlZWV2jZSrMpxfEOHt7TVLS4RtM1ZZbuKzSP edKvY5Vh1DTpJzGNipI0JjJJLQzwk5bk/wAX5xgXSrV60cJVyTibhqo6OMoKbvQnhKyo0alCbqqp Sjh2qeGcJe0qwdOhNVKjnUnH5OqrNtRdLEUHaaT+Fxdk1rdKOkbatWi7u7a+pPG/hvSfibY6d4q+ Hkfh6x1bUdFk8R+MfCV7dSWN9rFwszvqup6LeXcn2a6WC5huRJ9n8mSNmJkJDba8i8Iyz+EL/wAQ jf5Ftf8Ahu8nutH1Zgt4lvZXFpdo0tg8ax6sfsiX/kPH5tvcRzHeqozV5po+u6uttbWmn301tf8A h+8m13QJoJSl5BO6wLqFpZuDk+YkEU3lDIdrVxtJkOfc7TxJ4S8U2nhjUZZxpcuqS33h3xNo2qPG vhaLVri1uYRc6Df+RJN4Qv7mzvvPiKg6eGjlgKRiIZ/YsFm2SccZzQ4owlGlwzxbCFGpWkpQo4TF TqRpYTFVcNSUYrDVlWrVKmMoRqTjXpVfrVDDKtOrBenGrRxdZYiKWHxNk3qlCTaUJOK0UXzNuUU2 nF80Y3bOfk0a2i1jQ9U+Ger29rZ+IdU01rfTdftrSWO31dLphp4hkuopIJoxJLJLBazuJUe1eMtc NEHP6WJqFj4T0a0j8S+J7czQRQxz6x4gv7CwmvriNQ085DNEgBfJCIu1FKqOma/KSy+2+ENe0+HT 7LVLbXdNvYp77wf4lt4kGsR6deQahZLpd7bAJqV2s4neBlWOT7pgMm4o3QeLtd1Hxt4cvtT8aXs/ iGGHUpdT0bxLbskV94ft9UnksLjRdT0jys+Vbaktpm1Yq4hnaSzlaNWFfqHhN4oYXw0wPGeIw3Dc nxHjownHBrEVqOUYX6tTlUxcnT/eSwrqTqNyisPD6tW5aFZ4SlUhUq9+WZlHL4YuUcPfESSahzNU o8qvN21cW76+77rtGXImnL718U/Hz4X+GNOXU5PEI1q3lle3jPhuB9XxcRkB4J7mHEEEg6hZJFZl +ZQy818E/Gj45eIPibcpYQ2kGl+D7aZ5NMtOJmvZwrxi/vL50XbeGCSRVhVV8oSFcFiWrzHSRqnh yddO1SQW/hrxUsVt/aihbrRZQsjLp+swSsjRuLa7bMisokETzwuiliBzWpPrPhm9vNPuIYbSeKRr WcW8iXWnXckLypLLGySvBcoUYr8oI2nI2MK+U8UPHzxB8Qsk/s3HpcMZDXkqeNwmDozjN1ElUpwr 16lWdSth8RDlq0o/7PTm4zVq6o88uXMc8x2Oo+zqf7PRdlOME730a5pN3cZLVL3Vo/isYk08RLtE jICz7UZvMVFz8o3MoLMBkZI5x0Fbfhov5mtujQxyx6HdQR3UrMsFkdQurLTXvGuI1YQKkF5NlyDh S235ttc5qd+l7M939nitnlYtNFABHbeYCAXghA/cIR/DkgHkYBwJ9Fee5kvrO0kZrq8064trOzG4 i+nuJIUa1jCuALgwiRowQS7wqigsVFfztlUI0s1oumvrFpT9m4x5XKThJQcYuzU+ZxcYq757Rhd2 Z4MNKqt729ul9NPn2XfRH2l4Z1/w8+l+DNV0DWNL8W/GDR5r7w7peseI0urKw1yy0m5huptJ8OXl 9cbLbxJ9j1aBbK5uVje6hjZEMcrAH2Px98PvGHji48QaE9nceEp4LfUNf8B+I9BvJrTSNTl1CO1h 1bQ/Glk1yY7bXftTRSRSrtG6CR1ychvzv8PW7654U8R+Foyses6Lejxrp9vteO+uodJspbTxBp9t MoI+2rYmK5SPAJGmSYJOAPrP4MfGHWviF4dXwr4i1G11LxJ4XttUews9at45LfxXpcWlXMtjaSXc ixrBrEFxHEN077JoLTzGw8ZkP9ueF/G+Q8TYWjwjxPgamCp8SYag8LTwdSNPD4zEYOEsHi6GKnWV bFxzOUVRxUcTDEPE1nhsDaDq2jjfrctxtDERWFxEHD28Y8qg7RlKC5JKTd5Ko1aXMpOT5YaX+M1P wl4n1TTNJNy3i7UfGWgCz0PxvpEN3fX+m63BpMbWlrruk3FlEYku3mtEEpLNLjT/AJ2ikuWjbzfU rjWPDt7Fd62NatPFlhpGmz32m6lqz3mgW+qSQLBLqOvT6Tqc9yIbi3LSSQLbrEGlUPKFcJX0lqOp SeKbSTVfDXiXWtF8b+GlMPibwZrd5Bpt7rtkz3FnDfWnkNDDHczrcWL217vS3lxA04Dsc+batdfF PwF4b8aX8/iVvGOraf8A2DdbvEOpWemXXhwyO0qbbC3uZLmEeTJaCRJLhLa6aIqkUiMoH2Wf8MZf QSzDCTzCeGpUZ13mFKnQxtBrDYerOFWvQjXp4r+0eahUp16cWo08TDFVa1Cn7enVOnEYemv3kHUc Yx5udKM1aMW05Lm5vae61JdJczkldMg+CeleN9R1fWo7rT4bvwfeaTeW2q6u8i/2fpupq9pqemaT ZXtltt5447W3kiF7aCWVbmYPMY5uDh+K0mTxKut+K9Sv9QS81OCLRfDNlrGq+I9EhXTooLPTZblL O5hm0HVd8UKvOkjiQznz7dVkG7y/TvH/AI2v73T/ABJonjCfRdXl1O1i1jStO8c6FceHrmxvLmKe dovD2p3ka2e6RLlRDGm1OA0gzmsX4n6xqFsbK+0LxBqciLdW9/NpusWmnOkV6lzcPJcWy3Clol+2 QONkvneckSMkzJHx8Ri+L8mw3BFKhh8Nj8yWTV3iFUxLounXhUknSdOjJ0alSFBOv7ahOUlGtXlJ uvTqKEOGWLpRwaSjOoqMua8uW0r2tZe62opyunf3m3qmkdV498Q3ngXUk1Dw3pEPhnPkXek3EtpB qK+H31F5fNvtNtBGlvo8s09sSwczy/uln8yOR2Q8nok3iz9oWSz8HPeSXXjO1u5Z4rwWSWnhweGC qG8+1Jp0Kw6bNDdt5od4P3rMEjdZWIfkPBl98QPHXiXT/D3hVf7Z1vxBLKbvz49lrbRs0p1CTVY1 BibREjk8x/tKypgDYobah+wNZisfgnoc3w88GXXh7w34n8SwGTxt8T9Xu7Tw5Y2kzQuDb6DZyF5t odnW0ghiaODmZyJGBHz+QYavxvLNeIMbjsZl/hjQm6eNwijyQxdeoozoYPAqlOnhXjG1TcsVKnTo 5fQUamIqypqFGWFGMsa61ec508ui7ThbSTdnGELNQ59ryslTjrJ2tE4f4hfEvw/8BfDdv8GfhfO+ rapGhHjvxWbqaG7a4mH+lafp13aybrC8AZwgiYLZK2ArTvIR8/NoGvfFg6fc6E2kXTWUN0viHW9S jttGn0m3soEmOreNNe+zpA0DQMEW4d5JZ57dyBvk8scvcHwB4cmmd7m7+JOs+a7tIftmi+ExMxLN LPLI/wDaGunzMsSPsaPnlnBrE1rx14l161GnXF8tjoibUh8OaPEukeH4UR1kRV0mxCxzuHVG8yYS yllDM5PNfn/FfG8M5xVSjxDUhDIMHThh8BkOV1ozo4HDUJRdKi8cufDqUlG9bERjjq1Sc6spRoyk mvPxONVaTjXaVCCUYUKTXLTjG1o8693prL95Jtu/Kzs/+FbeFv8Aosvw9/79+Jf/AJS0V49kf7H5 H/Civz/+3sg/6IjB/wDhVmf/AM2nD7aj/wBAcP8AwKr5f9PPL8T6Emu4p7xWgSW3tkkVLWCN/Oni iVsxjzAq+bcljuZgF3OxIVeAPrzwd4ks/iXe+EPAfxNuLuLxHpOsWM/g7xRbl/tF2bcRTS+Gtbub UgPf/ZljImUjErAOdwZn+XfGOkzeD9Zhn02Dy9JvoGutA1iG7GqWGpWVyhVZ7G+VNi3UQd45FDNJ DNET8p2mu0/ZwY3fxp8BxO7OsOoX1yodmKq8WlX0u5VPAYyIv1wM10+GOIzThzxEy3hHGUViaXFO a5fl2Y4OtB/UcTh6uMoRjKdG1NzlB1Pb4KtTVJ0JRhUoynCo0u/L51KGOp4WS5liakKdSLXuSi5x tdaXtfmg1bl0a0Z+wpZugJAzkAdB/kVr+H2P9uaSSSf9Pts5Of8Alqo71jc9v54/pUllqthp2ueH 4bu7gtp9R1aC00+GWVUlvbpFe6eC2TOZZBbQTO2PurGSeK/3EwVSlhsbga1eUaVOFeiryairyqwU Vd6XlJxjFbuTUVdtI/XoOCnTbaWq773Vt++xveK2YeI9XGSMXshABP8AskH3Nfjr+0l4dj8LfFvx FHBGIrTXPs/iK1UAhB/acZa8CgdP+JjHdn23V+x3jJAnibVV6Fplk/77jRsj8/1r4D/as+F2qeMd S8P6/oF5pR1Wz0m4099Eu763stQ1WGO8NxG+m+fIouZka4dfL7mVQDkgH+Z/pr8BY3jLgHOpZXl8 sxzjhvOFi6MKaUq0oSq1cNXhTjfmm3CtGfs4pym6UVGMpJI+f4twcsVg6vsqbqVsPV5klu024y/B 3stXZaXPhKPWxdWS6drDz3MNtA6aVdDM11pjKGeO1i81xu015MhosgIX8yLB3K+H5+AOe3vxnrnB q1qHhrxRpMbTan4d1yxhVnVprnTb1IUZGZXV5vJ2oQyOMEg/Ke3Nc/8AaM87s8nOCevfp0r/AB0z HC5lTnRo5phalDEUYqKdanKFVwVlBS50pSjBLlg3dxjaF3CMYx/LZ86aVRNSS6qzt0vfXTpfZabW NYzD2/U/57U0ze5HfHHvge/esoz+h/8AQs0eeCBnr+NecsPbpcg0TNjr3znk9/8AI/Kmmbqcg5z0 6/h6VoaN4Z8ReIg8mjaTc3cEW7zbw+XaafGQDkS6heyRwIwA+6ZM57Vat/C9xcXUViusaJJfTv5c dhp9zc61elwcECHR7KdTg9TvwAMk4Ga9nD8N5ziaWHr08tqqhinalUnF06dV3S5adSpywm7tK0JN 6q+6NI0aslGSg+WWz2T9G7J/I++f2Kb4zeEvGtiSSbbxFYXIBxgC60zyzjnPW1/SvtSviz9kPRbb w8nj3Tx4i0zWrzzNAnv7XS0uni0qTytTRbe5u5YxHNeHDB0iLiPZhm3EqPtDzE/vD/P1r/aL6NOH xWH8EeBMLjoqGKwdLF0pxjOnUUXTzDFxUXKlKcOaMUoyjzc0JJwmoyjKK/V+HpTjk+DjOPvQUk9U 9qkrK6bV7aNX0ej1TR8H/treKJ4YfBnhG2unjiuV1LXdSt45GRZ1ieGy09bhVI3oJBeMobI3LuAy AR+f/nZ789evtxXvP7U/ikeIPjH4ghikD2/h23sfDsO1soHtIDcXuP8Aa+23dwp94/avm8ze5655 wK/y7+kTxA+K/GbjvMI1XWw2DxjwNH3m4qngIQwj5OijOpRqVdNG6jet7v8AOs9xP1rNsbUveMZc i9IJQ08m03879TX8/nr19wf0rpNIeOxCalNc2kF2ZfJ02OdpWaCTpJqjQ26lysJwIhxukOeVQg8L 5/Xk8AnoP6Vt6JbW93r2laZqdw9jbXmo2Vpd3XlNO1rBNPGJJI4o8mRgr52rk/NgDPFfkuVUZrG4 d0qUalec4QpucuSnGrOSjCU5NpR5W7puUVFpTbtFp+bTvzxsrybSWtkm3pr+Turb9D60+D/wJ8bf GrxJb6Z4bcWHhbwzpEcWoeL76Hbawanqtm9xOUMMSNqGpLPeSCOEFjEtuA8gGWP6zfA/9nvSfgdp zWGieMfF2sLcMsl9aaldwf2NNcbAjy22lCFhZkgDBSTd8oJJ5z8++HP2vv2ZPg1pVt8NdAj8VfYv Co/s+8urXw6FW51CN1ivr24M92klxdPcBmclc/wj5VFfWHwx+Nvwy+MFk134C8UWerSxKGudLl3W WsWo7tNptziQxjoXQOgPG6v90Por+GX0b+DMfgY5bx7k/G/jXTVSVeeHzVSnhJuMadXA5Xho1oKr h8NTpxw863JWq1lSlUm6dOSpQ/YeHcvyHCVKfssbSxmb2bbjUu46JOFOKeqiko3tJu13ZOy9WzjG fUf/AKqU8EfRe+OwzzSUrdeueAPy4/pX+gx9xd2bTvqeNfF34CfDf40aY9t4u0WEaokTRWHiWwRL bXLB9uI2S6Uf6VAMLmKXchGQNp5r8evjL+zl44+C9xqeg6laJrngzxDdJdeFvF1okht7XWrQSmys NQ3P/wAS27ubV57Z0f5ZXlhdXYRDH72dgOxAJHHHYn/Pv0rJ1vRNJ8RaXe6Jrun2mq6TqMLQXthe RLNbzxt/eU/dcMAyMpDIwDIQwBH8seP30TvD3xtoYjOKeDpcL+IKpzjTzbD0oReKjOm4Sw+Z04r/ AGqjUhJwVdr63h3y1KNRqn7Kfzud8NYHN4yqqCw+OtZVYr4r6ONRL4k11+KO6eln/LSbl433oWhk STKlWZHikVjgIc7kZT75GPau38Oa3Y6ilz4e1d7eyg117aO51GSMeUbm2leSx1FsOv2PU4TLOomU hJ4riSGdSzLKv2d+0r+yvYeB/HE+sad8S/BHhfwv4pE8+m6d40vr231CxZ5FN5ZRyWthN9osUnYm KR8MRIUO4hs/MEnwP0XqPjz8F2JYcDXNaXjuSToY49q/xFzzwM8TvDXi/N+H8wyrDV8Tk9epQr06 mPy6lTxNCUeVVIRnjY1VSxeHneMuWE/Y1OWUUpSgfj9fJ8wwGKqUJ0oylRbUk500pR7q807Ti9Ho 7PzaOk1XS/GOi6deQ6jp9xqNvZm01fX/AA9czpJI2liP7LN4v8I3DALbadMz2tzFLa7JrO5JEoaL g7mi+IItHntJPGGp3c/hnX9FXTLrxFaeU97qokM+oaY/ibRbm3ZIdYtBeW2WYSS7beSR1vIJEdOh 8DfDtm0mPQoviv8AB7X9b0aa41DwVdr4rnmniLiFL7wvKt7bwNBoVxErybUZ0WdiDC6yurdbN8Df iFf6dqupeFZ/AOtS6zDpmsQaBZeKPD832uVyLK8sG23UUHiPQmtRdiBriKKUeV8rNIS5/Z8l4B4w lDC5zkGS4/HTVLnWGjOnmjh7KM5VqFWOElD6wqjaoOhRcJyjiI18NLD0sVi8KvUpYLFNQq0aM5tK /KmqtrXck+W3NfaytfmUo8qnKJ806gNS8MeJVMdlqmj6PdQlbaaC4h17SLqyaJDbLeQyvJpmo6fO 9w0yokkOVuWACOQDm/ZtK17QJtb0SGL+2bSGb/hKPD+pTz2OnasovlsrbU9Gsb+KXfMy3FrFIsNy s1vOEZRtckew6v8ABn4o3WlyaTa/DXxB4etryKJmsND1v+3fDUd9aNEGVY7CW5kjtXm82URTvJGv mA20sJDxy+Q+MdI8RaDrGmWfifwh4v8AB88FvHa60ms6JfzLNHNaf2XcbdStoo11C2lt0WVS0Alj acqsjjp8DnfCed5CsZPM8gxmGyfENqH1vBYnDRoVsRKN5YeriMHhaVB0VCM7VMLRlisPVq0nTUac nDhrYerRUnUoSVJ7c0JRUXK1+VyhFRta+sIuUW1aybPMdX0mwuMz+HLO9jijt0fVbTU72FrrR7kT NDcFpCkaz6WJFCpKQrL5ypKA+C/GTK1nK8M/mRTwsDvikSRMcNG0UsJOMnDK6MeBx1zXRTeKdV0+ W60t7xbW3t57mxurSILNFIjGe2nk2zw7pAVIxtZdrJ5gjDHIwVvrUTXAv4FuTDJCYtRtpC0UcSzA LHLaPlLqzKycqAsikABuor8VxuGy6vWhUw0vq2Jbaqp04U6MZRTdqcKblKHM01d8kIyUtKdNqMPF mqTfuvlez0SV12Sva/yV10W3pHhzxNdWnxA8GX15M1gbu60+01O5gumksru01s/Ybq/VgT9nL6ff TGRQcxzq77RllHS2V7f+BtQ8Q6RqWj2et6vo03izRrXVru2tL62F4unajHMLuaGLfM50yHyliMpj C3Uz/eXI8X8L20t34u0bTYIbW+87W7EKN5FmYBcCZ33eYAkYt/MY7jlQrKSCGr1STx9ba78VRO12 llFZ+MNW+wids2GuJe3VzpenC+ihi8uC5itrhAZX3RSxl92HOX/TeE8ffAUa2JzCeBxrzaNPDyko zi/bU6MMY5KcY0nVpKjha0JrlnzpqMlVqQUu/DVfcTlUcJuqlHqteVT30vHlhK+/ndo+hvhx8UV1 Txl4cthLFdzyaDb+GtN+y2q2t5De6DpOnGXQhffZGLWk5ikBt5A1tJGsNzG8M4lRtiaDwD8StNOo 6fr0/hbx7I1rp2o3cFnqHhnU9Au1vZpLCDXrW0vH/s+wluLXMk8Ylso7mJQY4zKqH5gg8XweHdQ8 UyWNkdM1rw7q0OpvBbCGyii1Oynt7Vr+0sREBK8Krf2t1AVaCSOUTpsJIr6Wi1XwJ4kup/EWmJq1 jNqHiKSF7XSY4dQ+16DrNk32XUdA/tC5WaSze4mkkeG2CmzvBLbz20q/Mf6H4S4mlnuXV8mzXG4L OJUa9adXD4lVqcpU637n2uFqurFwxOFqYSr7STcnChiFCjZ03Uj7uFxLrU3RqzhVak24y5ldSsrx d1aUXB3eukly7XPEfEXgW303Wdninxf4LmnmmsdQsrzWdLkuLDVzPPdwulx4t8OWOy0lZ0Lh5/I8 0lWkihcc89pPw81PXvGEnw1tNIn1XUJrgXcN3aLdPbLpeoRRTyX9pLcytb6XocaMo+2GSWQyoVAY SGOvWfEHwZvfGVzBd/CrWI9Vt9QtdOeZ9VnW1ttOtW1Gey8Q2uraadPSKGGPULaa5mgfaIWWWKGI bo0P0hdeMvhh8NvhxqGl+Arzw/aa75F1okb6dIFMuvR2a3V28cUssktrZMxubi3hcJG/lN5SMR83 Bl3hjl+a4/NMZxFTo8McNZW/rKxUsS8ZVzfDv2jqYXBVa05UsRUny0mq9NQWHU6F6FadeZFLLadS pVniEsNhqfvczlzyqxd7xhKTtJvT3lblvH3W5M+fPFOueG/2Z/CWoeA/hrNba98SdTs1k8Z+NFe3 M+l25dFSKC2Zi0MCPMBFbrnBIuJ8kgV8JXl7eald3F/qN1c319dSPNc3l3K9xc3EshLPJLNIxZ2L Zzk/0o1hr86pftqsk8upPdTS3k11J5txLPIxd5ZZc/vCwbO4cENkcYrPBx9f5da/mjj3jTEcWYrD YLC4FcP8NZFGVDL8qpSfsMHSUndtNKVTE1HeeKxFVzrVqrlKUrcsY/O47GSxUoQjT9hhqGlOkvhg r+msnvKTu3K7v0JwxPc9Mde3oOelFQ7uMe47DH+c4/Knbh23fkOPw/D9K/PnB/18jhJKKZvHof0/ xoqeWXYD6K8P/EfU9C8N6r4TkstN1nRNVuorw2mrQNcLZXA2pdy2BGDayz26qrPGyujxrKjBwc+w fs/XXg2X4veBbnRk8V2Oq/b7gPpk8Wn6ppYElheR3BGpRSQzQWSQuzb5IXYbQrE/er5s8R6n4XuJ rJPC+l3unWttZRxXlxf3z3dzqV+T5lxdeUQFsrcOzJFGu4+XGpcly1dX8NvEt54UbxF4ssGVLrRb TQtsu3dKkd14o0g3McL9YjLaW88THvHKy9GNfbcF59icq404U/tXF0c9yvhCvRxFOpGm6ro0Mvm8 xnDCVZwoV04uFSMVJujzWtGpSjC/o4Su6WLw3tJqrSwslJO1+WMH7RqL92WlnbXl8mkj9zxM+ex7 Yxxn169a8n0Q2Xjbx/pfjaKWG+0rweNUsfDksdzErw6nLcSaXq18sVtcSLqGm3MELCCVhHJC9lIm PmIrkfjl8WdL8I/CaTX7LVksb7xrp0Vj4Vu4oZLtw+sWQuXvoooWDYh06R2D9EkePPWue/ZI8Inw 58KbXV5Lh7q48XX0+txu8c8Rh09SbWxgRLjlVJiuJiR8rNdbgTnJ/wBZMz4tw2eeJ/Dfh9gcLTzX CYHB/wBvY+sq1Nxw0qFbDyym9K0nUc67jXipKHK3hsTTlzU01+mTxUa2Y4fAwiqsIQ9tUd17vLKL p6a3u7S8vdkndH278QMp4gadcYvLKzuAcdd0IU89+Vr8t/2xPHb2Pjfwv4fSKz1C0sfD732qaXeR 74Wl1G+k+zSRzRMs1hfLBabo54JI5E8wclcg/pf8T/EOj6D4U0bxrr99DpukWWhsNSv7htkUX2Jx Eqk9WkaR1RFGSzOABk1+Bfxa8Xa14v8AiD4k8R65A1pc6pe+daWpdZYoNIRBDpMdrOjFLi2FjHDt lQlJCWdTzX539O7jaGQ8NVOHcvrtZrxfjcNimo2fs8EksY51VZxUa9ZU6UIVFy14xxMUpKnUS4eN MYqFB0IStUxU4y06Qsp6+UnZJPSSUt7M9x0z4oax4mijitPE/iOO+ht4obZLS7WLxbpUdumFjjhj Mdr8QtIwMvHKi6kq5ZRJhi3M6z4kurpLOHxt4N0HxpYXc7W1h4u8JW3/AAj/AIgnuHKhoHutLtVj fVUwN1pf2RmDfKRjDV83LeMrK6uUdGV0dSUdHQ5V0YMCrA8gjkYzX6HfskXuma7PqGqX895rni2C BUvr5dD2Wel2Cu8dhDrWtTybdX1iZot8DxxG5iij2vOV3Afwx4c4rOvFjiDL+Dsdncsvx2OvzVq/ s8ZhasIWlL2mX4uUqeJrJK8IRjKrFXnCthsJhvZL43ATrZnXp4Odf2c5/alacWlq7056SemitddH GEbHmEP7LnjjxFY2et+EYbq10+/dwmmePI4/DevWChVYNKkZljvrY7vlkjEbt3hXnHX6T+yH4402 GW81C88G6hqqlVsdLur7UzpEOVJa71F4bANeurYEduu2NjlpXZR5bfohP1VdxLKoBz9wtyWAbP3s kZz61zPinxjoHgnRrvXvFeo2um6VZRGR5LqULPO4UsltYxZ33V05G1I0DEkjgDJr+yIfRQ8Gckp1 s2zVYmCwdLnrVquLjRwdJxh+8xCpTjKNKKd6ijUqVKdPaMVFRS+s/wBWcpop1arlaC95ufLBaayt 076tpH5QeMdKl0PWb7SfiR4rN9f6LcyWY8MeFyt0IRHnYkcjwxWOg2zIUZVWOWYKw3QBq4e+8Yzi 2l0zQLODwzpEyGO4t9Okkk1DUIzjI1bWpT598pPWJTFbjPEIrv8AxtYWfxf8Ua/4z+H2spqeqa3e y3914F1dYNI8V2o2qgj0hTcG28R26xRpgW8ouecNATyfBbyO7sLmayv7W5sby2cxXFreQSW11byL nMcsE6ho2HPBA6V/njxnRzDKc0zKpk9KUOHsbXxEMJmUKjxVTG4ZTap+0zFTqP2kqDh9YwtKdBQb 5K+HjNWPg8VzUqtV0v4E5SUKl+ZzitFepd6uNuaKattKKZ+i/wCwzua1+I0mPkM/h2MHtuCaoxHH sQfxr7o1rVrXQNH1XXL5xHZ6Pp17qdy56CGxtpLl/wASI8D3NfFf7C9qV8GeN9RK/wDH34nsrVTn gpZaWsjY45w15XfftgeNR4V+EV5pUMwjv/Gd/b6DCobDmwjIvtWcDPKfZ4I4m/6+wK/0p8Hs7h4f /RXyjibE2j/Y2WZljIRlop1Z43GVMNT161as6VOPdzR+gZVXjguG6WIl/wAuqdSWvVuc3FfNtL5n 5Ya1rlzrusatrd25e71fUb7U7hiQSZb25kuH5PQAyED6VjGbBPcfh+eazjP/AJ+nXvUDzEE89c+n AFf5MVfbYqvXxNebq18RKU5yfxSnJuUpN9W222+7Z+ZNuUnKTvJ3bfds2kuDGUlRhvR1ZQVDYK/M GIIwRuA4Oc/Trci1S7S7N2sri7d5ZVnVtkkck5YyTxYwI5iWJUjoeQOlcz5/+HA/lk+lOF03zHqW GCSAx6g5BJ4Pv71VONSm4uMpQ5WpKzfxLaX+JdH01sNNrZtHbavqVjP5jwee93eG2NxLIwMcKWsK wvEOD9qkkljWUy5BG4oVLAsYfD/irXvCmrWmu+GtX1DQ9YsJBNaahptzLa3MLoQflkicZXIGVOVI 4YEVyktyjRWwU/OkbpINpGD5sjLyWwcow6elQCYngAknpwM/hg12TxOMhj6WYYarLB4yhKFSnUoO VOdOomqiqQnBqUaim3LmTUoy+G1klTnNTVSMuWas01o097prZ3/E/Yb4H/8ABQueHSrSw+Nlkt8s bGBvFug2+y8ghiiJSXXNMO2OeaQiMIbYh33FihNfpH4G+Kvw7+JVil/4H8YaH4ghYEtBZ3sa38BA Tetzp0xWeB1LIDuQDLDBORn+W1dVUaJcaezlW+3W08EaoPmQwzi7kkkBG750s9oYNjadu3nPcfDb WrrTdZt1068u4b+6tr+SL7PM8YjItp4r+OZYcSSJJpRkZAjhlntImXtX+hngz9PbxP4WnkvDXGeG o+IuUOFKmsRiasqGa05c/s+R4uKqxxL0Tj9ZoVK9RyTlWhHb7rKON8xwzpYfGRWPpNJc0m41U72+ JJ83/b0W33sf1NbW9CPwNcD8Qvid4L+GOjT6z4w1uz09IoZJLXTzNE2q6nIiki306w3+ZcSMwAyB tUsNzAV+AKfGr4w+DfEV74auvih42m07TNT0wLfWmvaolrqFle39reQyWdncyOz21xpbh4F3qwiG GIy9eVfFXxjqviXxhc67dahfTarazXVhqMd3eXGox2lzYXVzp8SwPfD545bKKF2Ug5Z3BA28f0Bx f+0Uy/CcOZhPhvw8r4bijD1fq0qWYYylKjh5qVSFWoo4em3inRnDSkqlBVVzSVTkp1EvdxXHtOGH m6GBaxMXytTmrJ7N2ivfs1tdX1d7JnvvxX/aEu/i94z8S33jiK5tdJttVS20XwzKkUcOk6BGs8Cr JP5O+/Vp0s5LqKRG4kkuLcxyIK+W9W06wZxLobTs4gnuNQ0mfa82mGKVg/2O7Riuq2PlGNldP3gV sOvyljY1HWbO90C5kuE+0XmoT2PlyCaeW50oWNs6SfaJBCovYbq5kmWMSSM9uiADduyeJttTuba7 trmKYw3NtdR3MU+4I0U6sp8wDGF4RQcgg45GMiv8t+OOLMz4xzavmfFGOXEGY5rUqYmrjZJ/W+av WlOzndR92moKlRX+z06MvYqnSqU4qh+bY3F1MXUlUxM/b1arcnN/F70r+S0VrL4UtEote7q6Vqx0 u9tb+OSeOezvLe4h+zOsM26B/NU+aVO1fMWM9DnB5HFfVGj+NLXx5oGoatBZWui+MvDM+m3M+k21 +1jp2paXaQXCb/DlnsaNr5ZoJp5rKXctxI26ArgrXxrdXW+ZphIWeUtLLkbNsruxkUDoRk5yMD58 ADFbPhvxNeeH7pru0TzJornTLuMmWSJEOnXv20oxRgMSKHQ5PPmFRndiubgfiivw3i6uDqz9pkmO U/bQ5IykpqlNUcRSfLOUK9KTUoOL5XrCopRbtGCxUsPOUG70Z3urdUnyyXVNbr7metah458UQ3Gv eHLi7vtDlltzqeh3Fqb3SL2ym05ri/srdTDdAyWstlJexruaTY0yhXcIK9S+G37W/jzShbeF9e8T +INQ8PTWVopm1HVDqF1pVzpkbyyvZTX1rN/od1DAguIXV13tmJo8sa+ePGN7qesanL42tIZbvSLk JuSFlkk8NOJfOm02eKF99vbK73P2eRwsbRzMq4MZVfN2mgghvHhumS4+0wpapF8xms545pJ3klIz G0e22XAwWMzA/dxX1uF4+404Oz943h/iPF4SnRlUUZe1nTji8C5TqU4ThzRhVpzjKqov4kpU4xca lKmqfRHH4zCV+ehiJRSur3aUoatJ62aevnqlo0kvrrxJ+0qLi6uLLxR8LfhX48jLhpb7V/DR0vWl V8F1e80qK1LhkdXSVWcMsoYMcjHL3fi/9l/xDN/p3w38deCnEMMkmo+C/EkOoWtvLIqmYLoHiQSm WBJOMpdruHQAGvC7rXX8SaTYaFcNZG/0KK/n0/VJspfatFcst3Lpd1dMP3zxlJTbK3cmJTnYD580 5ODk5I56+uMe3FaZr4m8S4vEOrmP9n8V4PE8klLMcqwOJrQajerR+tVKMsZF05z5JVI4pTnGMakJ QjJIVXMsRKTlU9nioStZ1KUJNaXceZx59H15ruyadmj7S8GfDP4S614p07WPAPxj8O37W97b3aeF /Hmm6n4D1SMGdI0tYNRL3VhK7PMsYaScDLhgCRtPnXxG+BHxd8Ja/qXiK98C6lc+HpdZuNTttd8N yL4l0M2U9697byjVdGeZVT7OyEM+xiMNjNfPtrdOsM8EaxLLK8c4nY/vUS1iuS8SOTgK/mAkEElo k2kEc+1+GvE/xI0+z0LXfhn4l8X2Ouuw0m60/wAPXN7LPdXv2hhBFHZWHElt5EseUmjZFDxgMQ+B 34TOOC+JMr/szGcF1skxeGqvFqpkeJlKLnJwpTf1HM/rdXESS9nJUaWY4aDV1DlSsrhWweJpeynh HRnF896Mm1dtJ+5U5nJpWdlUiu2xHrEGr+JtR8WeMJENpqM0Da/c22oeWLi+s5zbxX0jL5QjurT+ 0oJIljURspYK27ccev8AwV8L+LPHltb2OkQ6RN4e8PX9jeTatqbXcGn+EbVb2bV7iLasEZvfEFtd RMFXzGWWG5InQJEjL7jda/YaR4Yj1P8Aax0PwL4lu7nR47Wx0/RbBPDvxHk+0nzr611zX/D7xwbR cF1MBhubhmQuTDnIwNU+JngT4l22i6N8GPiVpvwah0eVTZfDnx3oy6Ro2pugKpZnxXprzWtzbTK7 rMl3HA03mHz5m4x+w5bwLkGR51TzfM+K/reOxtKVWpkdWpQy7P8AGzr1ueSxFHF1/qOFUpOM3QpY 7FYzEKEPY0KdSUZw9angqFGsqtTFc05rmdFuNOvNykn70Zv2ce/KpynKytFOzOp8UftH+BvDN4fB fg+Sxuf7dW603U/HjiKWCPUJ2uUe48hGZ5LFb2U7SWMcPnfIWCMw/PnVidLudU0PVJ7m31N9Zzq0 Kw297YwvAiyW2qW90ZPMluDLNJuT51MMx+Zsitn4jfCn4i/D0eZ4n8H3Wm6Vc3Ml5put6Z5eq+Gb hbjHmJpuu6c8tvcWRKI0SiXcgyDk5I8qa5nu5Q0ha5mkWOJWYs8jMqpFFkjmRgiIo65AAr8g8UeP uL+Isxhl3F2USyfGZROccLhJUKuEjhcPiIRVWj7CcYVZ+0cIyjWm3UqRnU9rKpen7Pycyx+Lr1FT xdJ0p0X7sOVxUYySurNJ6taN6u7vfS16/v7y8ljN7cG6e2hSzjlKrua3txsgG8IGlUIRtLZbHB9B ViWSWRY4o5JpHwEiiRpJXIzkKiKSx/WtfTtNspJ7m11G6ityhEbX8dwjWtgYir3LzRqC10xUiOFV wJZTsVhy6sutVisUNloRngjV5RcaozGHUdSDYVATE3+h2OxQVgVicuTK7nAX8mngpSX1vG4jljNt Nc3PWlKLty8snfbX2knyJac0p+4/McHbnqS3+cm1pa367fPQ6Xw5oN3FJqV5qukwxwW+iai9omty RWUU2oSxrDYrFbXUqPeSmWQBFQFgzBhgqDWbrOgf2ZJZXOZE0zUESeMho7q4hhVnjvPLlhbyb0Rv FNsZZBvXYXVCxA5mG6kMd3mVROyxSieaYLMBbyq4jgkdtxlLMpwDuxHnoDXb+GNC8XyJC2jwpE+r K5C393ptrDJaxo8sjzWOrXSrc2rxI8jyNE6rHDuBwTXrYShh8xo0MvwmU18RON589Ne2qqUqij8F OnTlNTSjBQc1ytqXPpZXBRqKNONKUut17zvddEle+i1elzqNn7Pn/QV+Lv8A4KvCH/yfRUP2SD/o NfBj/wAB5v8A5W0V9hyR/wChDk//AITvy/6j/wCvy6/+4VH/AMBfl/f/AK+Rwf2jpg59QcD+ldto mr3Nr4L8c2SCA22py+FoblnhieYGDULu7hEUzDdEuYXyFOGxznAry4XIHRuB7n/D3rrdPmf/AIQv xNJkeWut+Fo2+8CGaHxA6Y454Rs59K/MMkpVqWMrzpOVOTwmPi3HflngcRCa9JQlJS7xbOCi2pu1 0+Sf3OEkzu08SeLvinP8NPh5PctdR6K0PhPw3CoYtHFq2qBmnnyx8ySOKSJA2OIbJF7HP7e2cWj+ CvDFtatNDp2heFdDige4kYR29ppmj2So88jfwoIYGZvU+pNfkX+xh4eTxF8arHUJo/MtvCejanrr FiSqXbImmWBzj7wmv2Ye8We1fWn7b/xFbw18PtL8G2Fw0N744v5BfbHw40DSDFPdRnHISa+kskPZ kikXnOK/u76PGYPgPwk8R/GniGU8yzGt7LB4d15ylOrRy2hSwuCw6qO7VOria9PDO1+WGHho+RI+ 2yGs8DlWPzivepN2hG+rapxUYRT7OTUfLlXY6fxb8fIfiz+zn4u8W6Zokd7oHw0+JlrpWueF75i6 +I/h54jtTaCe/HJsr9rzM0EiYNtKkeCSrZ/Pf4naDdaLovh6+8P358Q/CzWZrzUfBWsTQRSajozz lRqXhPWLpU32V7bz43W7N5cjL58Kje+fWv2KNUtfEutfE/4IalKosvjF8Ota0nTUkYCNfE+iwSap okig9Zi8UoXuSR3rl/h745X4V6XP8KPFN3c6UnxCuLi61bWVZWufh5dZk0vw5qtrayoyid7i3Nxf hgHWzeHYQ2TXw3Gec1vF3hjgbirjDNfqb4hy7GZVicyjCMVhM6ynHx9jhZ01KlTlgcwy/GZOpUJ1 IU8PWp/2hGdOFDF+24cZXeaYbBYrFVORV6c6Uqn8lalNWi0rLkqQnSvFtKLXtLpRnf5lM5X72V+o A+nUD3r7f/Zw+Meg+AvBVn4ZsLL+2/HHjD4irZw6dG7RxWmmPb6RbDWdTnjRmWyhV7ooijLtG+Si K7D5Z8UeLfij4O1/WPDOu+JNWTUdNu2tbpJZYbmC5QDfb3du88LLNZzQMksLjh45gRwa7H4W/ETx RDL4z8S3F9p5Twn4F1y+huJdB8Phn1PVBB4f0q1luYtOWV1kutV+4H+fyiGym4V+O+GWPXAnHVPE Zdj8XgM3oxxGFryr5ZQ58JSjrjZxjPMJRjWpYelWV5wlGDcm4SseTl1f6jjVKnOdOquaMnKnG8Er Obt7TRpKS1Tt2PYfG37Y/wASV8Ua3aeFrvw8nh2y1q+g06caMJLjU9Nt7qSKCS5mnunMfmxJu3Rh GAYMCDXmer+KJPirayS61rmr61Lame8hbVJ2v/Efg9pSGuWihhVV8TeDy4zIYIheWajeY9gbzPG7 nx7Z6k8bat4K8HTGOKKEvpNheeG5nWJWAdjo2oRxNKd2WYxHJAzX1F8Mv2cD8RPhvqPxL0dvEngv V4Z5LnwfZW13HrJ1SHS95vLq0EkNrOjSSrJFbfvyXeA/MVYZ+ly/NPEvxbznNMpo55X49wtanicZ PLatTFUo0cPTtObpwrL6rBRbp06VOWIbnUdJUpQxKo16fTSrZjmtarRjWljotSm6bclaKs3a/uK2 iSctXazUrSXy7rOk6r4bvYIr5DE0kUd9puoWkwlsr+1ZswajpWoQnbc25I4dGyrDawVwQO+j+KFv 4ltrXSfijpsvim3tIFs7HxTZyx2njrSIUyIgmpyDy9ftIweLe9ViQMLPGea6po4fF9hN4XXVbPxL BYGbUvM0iBtJMN+0Je+vW0PUIkm8K66kzSrdR4OlX7RNve1uSJW+dtd0+70LVrrSLh45bi2kCK0B P7wSANHuiYh7efDAPFIFkjYFHUEV+b5nleacHyni8kxEq/D2aSVKrSqqliMPVqQ1lQrwTqYXEqnJ TdKtG7VlVpSg3TqT8+rCrhffotvD1bJp2lFta8slrGVns9e6ezf7Q/sseHdL8N/CWwfR9W/tvTtf 1jVtbtNTNjPps09tLLHZQx3VlcEmC6jWyKSBWdNyko7qQa+JP21fHv8AwkHxNt/C1tPvsfBGmJaT KrZU61qnl3t/ntvjt/sMR9DEw9a/Qnwu+n/Cb4J6LJqZWO18FeAba91Bj8pa4tdL+3XSAY4kkvXd R6vIK/JPV/EHgf4vf2hqV5HH4K+K+q6pLctO928fgXxObh2ld7ua6MjeHtadiEBLCzkchmaHJA/t Hx8n/Yng14f+E+XYnC5Rm+YYPCV62DlOpTVWjgqVOVTC0J1HVSnUx04zoRxFZSrfVp041alZqM/r s8k6OU5flcJRo1qkIylC7V1BJuCbvq5tcqlK8uVpNvR+NCc8Z6fhzkUx58kd/wAvy61Hq1hqeg6j daTrNlcabqVlJ5VzZ3UflyxtjKsM8SRspDI6ko6sHRipBrKefkZI79wa/wA76mCrUK1ShXoyo1qM nGcJxcZRlF2lGUZJOMotNNOzT0aufBtOLaas1o09GvJrv5GqZ+Mjt+HBHU/570hnwOTj1Jx/n1rW 8FeDPFXxE1+18M+D9IuNX1a6OfLiAS3tIMhZLzULpzssrJNwLSOQOcAMxCn9Xfgt+yR4K+HKWmt+ LktfG3jJAkvmXcIk8PaPOADs0zT5lIvJlb/l4nUkkZSNK/X/AAs8C+NPFfFt5NhY5fkWHny4jMsT Fxw1N6OVOkl72IrpO/sqSajeLqzpRkpHq5Zk2MzWb9hHkox+KpK/KvJfzS8l5XaTufCPwv8A2a/i n8UVgv7PSR4d8OzFT/wkPiNZrK2ljP3pNPs9hn1HjoUQRn/noBzXj/jHRJfB/ivxH4WmuRdy+HtZ 1HR3u0iMIuTp9zJbm4WJmJiVwgbbk4DYzX9Bqv8AKAcYG0KoOAqrgKoA+6AMDAwBivwQ+PEo/wCF zfE8oflPjXXcAY6C8kz9eRX6v4/+AnCnhFwVwtiMpxWIzPPMwx06OKxVeSjGpGOHc+Wlh4fu6NNT jdJurU1tKtJaHp57kmGyrB4WVKcqtepNqcpaX929lFaJXV92+7OFN4TbGLK484SZOPMz5ZQheOU4 Geeqj0q5pWoJaX1jcNtdYZlllGWhbZgpKvnrkhBFuYYHBBwDXKfaOgOABn8c45P51YiukmuY3u5H aIFfNYBS3lxqcIMsOoUL171/KNCE4VqNSLXtKThytpWXLJNXbutHvdNPW66P5iLakn1TX4H0rp11 c+ItMhRtXjeXwxpmkvr6XVhNPM2lWYhlS9tZgC63NqQsauQoMVzGGOMIviut3hudQv7yJ5LpG1G7 33sskkstzunke3muUm+ZJTFwSxO4oSctuznaf4u1jRdcTX9NupLbU455ZU3F5IzBOphls7iOVj9o tXtdsbK2dyAAnIyMi7ukld54lWEXEjO1sJS4jY4kwGY5dMsducsMFTyMn6fO81o5tgMJTUJLG4ec 41XJzl7SnFJYepBznJQlZyjVpqMXzJ1FOXtqkafTWrxq04LXni2nre605Xrez3ut7631aVpryTMm HcB/vAHAI4OMLgFcgcY7D0qGS4BLYLfMql953Hd/ERjsT+PPOayWn+ZseuMjrz1yK3NS0xk0jT/E VhFL/Y91ImlXE0txBI1vr8Ft595ZlEIcRNCY5omZMbJtm9mU187QwFfEQr1acXUWHipzSTbUG0nP RWUYyklJ3VuZdL25lFyUmteVX+Xf5FEz9Md/pn8ff/Gt3UNDutP8M6D4ma+sJLLxDeazYQ2UFyza hbTaG9iLhr62ZAEhf7dbtEwZgec7SOeL88YPQEDkc/h9K9Z1K3lm+BXhjU47y2+zWHxH8TadcWk9 q0d817qOiaPdQPYXuwi509LWwczruXy5rqPKHIavZyTKaWNw+fudJzrYHAvEUrSS5ZQxOFjOTTlF TiqE6ycfeavzpNxNKNNTjXbV3ThzLXqpRT9dG9PmcNp2syWF0JN032adVttQgglw11bMSsi4f5TL tOY9wZVcA7SCQY/EAtYdSmNisa2zYdY4RIBCWADRTQyktaXCvuWSIlgjgqjum01zKzlWD7gpRtwY nIVl+ZSBjk5H+NdJ4V8JeMvHt+2m+EfDuteJb2WQGVdMs5blIpHbIkvLwKIrVdxJJlkUdTnijA4X G5jCllWEwdTMMVWqL2NOlTlVq8zTThThBSk+dtNxitWr73vMOeolRhB1JSfupJuXmkldu/ZGGLjZ IsikgowYFCVdWU5UqwPysCBjvkVoaVpOt+JtSXTtB0rVNd1O7lwlnptnPe3Ukkr5BaO2RvLyx5Y4 UZOSBX0lb/AHwV8PYrfUfj78SdP0S5YpInw88EvFr/i+6Jb5ba7nh3R6eWBCkqr4LcSAivoq5/aF 8H/B/wCGr23hb4ZzfD+/1q1ntPAehXXkReKb6wEM1s3jrxU89sz21sLzP2aOczyXckDn5Y13D9e4 f8HqUHiq/iJxPR4IwOXUfrFbDKP1zMVFW5Y1cNRk4YGdV2pUY46pSr1KsoQpYeo27erQypLmlmGK jgoU1zSjbnqW03jF2g3e0edqTeiizwfQ/wBmRPDWnReJvjt4z0r4baKQrDQYrmDUPFt7ESC0CQRu 0VnMwBTb/pEi5yYwRWhN+0Z4H+G9nd+HfgT4Ej0a3nt5rabxtrjvceI7yUo0cd9+9BkkQNtLRM0U TBdvkgdPj7W/EeueJNQl1bxDrGoa1qM7M0t7qd3LdzkscsEaVv3Ue7OEXaq9ABWSJAeMjrxggkD0 A/OvIfiHhOHYvD+G2QQ4XSTjLMsQ443OayatJ/WpwjRwSmt4YChQlqlKrO1zF5hCh7uXYdYa2ntJ WnWff3muWF+qpxX+JnY+IvEeoeJ9SbVdSu76+vJos3D39yblhcEGS5ktgiILa0acyOkSqFjDbeQM 1z4kHc56HGOh/OqAkA6Zzkg9sZ9x9aeJTjqB14Pb8SK/L8TVr4yvVxOJquviK8nKc5NuUpPeTbu2 3uzzZSlKTlJuUpO7b3bPXvAXxq+JPw3Mlv4X8S3cWk3Py33hzVBFrHhnUYyfmivtB1NJbadGXgny wwzwQea9Zt/GHwA+JsyN4w8NXXwT8Ys26Pxf8P1nv/BE99gmK61TwlLKZ9HAn2uz2EzKuOLcDivk oSH6j8qswDd5jsp8tIpCzFHkTcUYIjFFPlszDCk8A88DmvrMn444gy7DUMpxbpcR5BRfu4DMofWs NTW8nhpSlHEYCT3lVwFfC1XbWbWh10cdXpxjSlbEUI7U6i5orvy6qUPNwlF+Z9E/ET4LfEPwho9r 4h02Sx8eeBoriS+j+IHgm5j1rS3vrthcF9Ue3T7Vo06qExFexxMrlyOWNeDwLJfSSyzvKYoIxPeX GRJJHArpHkb2+aQvJGiA9WcA4GSOq8KfFHxl8PNefXPAPiHVvD8kixxzW6zxzWl7biJI3sdUsGQ2 +qWhClSssRV15Kgnj6FXxt8IPjJo7aL4w021+B3jbUjHd3PjbwppCSfD3xFeNNstF8VaFbo9xoMD TwK3mWTGBZWdzaYGa+gWTcHcXV60sizWfDOaxTcMtzKtGeDxE4rlpwwebTdGFFSaVqWZwoqnSVv7 QxFWSibqlg8W37Cq8NVSdqdSV4Sa0ShWbSSfary2X/LyTPDtP8SNoGnHXNLstE0+VJY9O0iwlsLD Vr+RjbvJPruqTalBKzsvyeUqiNDLMCsYiiw2HLrt5d3Gs39+93d6hJZNHLffallkjivBDFMkU6KR FHLLO5kwNpjZoFCByR0fxR+FXjv4bixk8S2kN9o+q3F1daL4w0KeHVvCniC1eK1SGfS9dscxSjZG B5LbJIQu140ORXn2hxadm/1LV/31pploZ4bAM0f9qahLIlvZWTyoQ0Vt5shlmZfmMVs6KQzhh89m tDiDLsxjw9mdCtk9bBLmlQxClRjTlKk5yxDo8rjFODUoOlHllSjBUudyUnhVVenU+r1IypOGrjLR J2vzctrbaqy2StfQqfbp/wDntP8A9/pf/jlFXP8AhKbj/oFeGv8AwQWlFfPewwn/AENJ/wDgqf8A 8n/XzOb3f+fj+70/vepTu7u3luriSzgNpayTSPbWrTm4a2gZiY4DcMoM2xSF3EAttyea6Ky8VQWv g7XfCzabDLcaxrWhavHqxkYTWkejW+qwvaKm7a6SHUQc442Nk8rjzj7WexHccnH5Un2ojqRz7k/1 4rPD1MTha1avQcadWvTrU5WpwtyV6cqVWKjy8sbwnJJxScLpwcWkKM5RblHRyTT0W0lZrstG9tuh +pH/AAT40tGX4meInXLg+H9DhkIOQp+36hOoPbJW3JHsK8Y/bg8Utq/xrk0dJN1v4V8O6RpiIGyq 3N8j6xdNg/xH7bCp7/uh1r6a/wCCf1qE+E/ie+CjfqHjm5Qtk5ZbLSNMjQHjqDM2PrX52/tF642r fHT4o3hfeF8XalZJuIIEemGPTogOvRLUAfSv6+49Usj+if4YZJQ/dyz/ABrr1bfbp+0x+Ls11tOe Hf8A26ux9bjpOhwrllGOn1ifM/NNznr83EsfBDxHfeGvi98Odd0/Uo9KuNJ8W6RfPqEwlkgtbS2u Vmv5bhYFLNALFLjeAMbc5IXJHvP7dPhS20T43XXjPRJvtvhH4t6JpPxB8L30YAtprHVLOKO4t4do wixXELKEGNqsoIr5R8JMbbRfG3iV5PKbStEi0fTyFB8zU/FNyNKZFfOUddFGtSBuxjHIOM/ZaTf8 L5/YkbYftfjz9mDXdxX5Xu7r4a+JH5x/FJDaXq+4VIe2a/M+CcBHiLwv4w8O50+bNq0JcWZT70nz 1MojUw2Ow0IJuPNXyz+0cS0o885YChG7Til5uCSxOWYzL2v3zTxVLzdJctSKV93T9pLa79mvI8in v7b4vfC20hgsjN8UfhPpp+13CyGS88W/Da3Z0Vo4+Wu9R0dpLfcpy/2Ml1JCsFoWME3hn9nnVtfT VLayuPiR40tvD0dksf2q71jQPC8LX17HvC40mOLVri2dyctOPLVcDOfD/A/jnVfAPizQ/GGjCKS/ 0O9W6jtriSVbW9iKNFcWN55TBpLSa3lljkXOGWQg5r6t/aY0u21n4dfB/wCJPgTQxpXw2vdJ1VZb CA7R4f8AE2v6xcajfWU9uANts1xFPFDN8wItVTIXygfPyaFLiHhriji6P77i3hrKHg69KnGcZ1sP WnhcvhmrlSiuaVDA1sRhsbeSc5Rw1es6qr4m+dGSr4fE4te9isNR5JJJ3cZOFNVbrrGEpRnrraMm 3zSPDfhJ4Evvin8QvDfgqzZ401S9D6pdRqD9g0a0H2jVL0kD5StojhM9ZJEXvX786Tpmm6Dpem6L pFullpmk2Ntp2n2sS4jtrSziWG3jUDHSNBk9Sck5JNfnZ/wT98BCHRPFnxNvYAbjVLseFtCkkByl hYeVd6xPESOkl7JaRkj/AJ82HHNfo3kjrj8z+gxX9r/RN8O6XC/h+uKcTQUc540l7bmatKngaUpQ wtNdlUftMS2nacatK93CJ9lwrglhcD9alH99jNfSC+FbddZabprsflD+2n8Iz4K8VwfE3w1C9roH jSaa21xLTdDHp3iWSJ2uSfLIEdrqFsskhXhTNHOCMOorxT4NW9p8U/iD4F8H+IY55dTTWdPex16C MT3FzpOkONQvNG19SQbyzFhZzLb3RJltziJ/MgKiL9wPGnwQj+LvgDxB4Z8TywaB4d1qyMcet6gF V7K+iIn0/UtPt3Ia4nhuo43UDAcAoSAxr5j/AGXfCv7OXgTxl4yg+G/hfxR468YfDwnw9rnxG8er /Zmn/wBuXct1a32neHvDEJ/cRCGymLySkvsdQCQ+T8zxl9GjEf8AEasizajjsDkHA3G+Jhi8RhcZ OXPiK1CTxOYUMFgaMKleo6lKnLE08Qo0cPh51JReIpqMIz58Zw5L+2aFaM4YfA42SnKM3rJx96pG EEnJ3SclKyjFt+8rI0/2pZtd1PwVp/gDw/4U8VeJJfHl9LZXx8LaVqGo3WkaZp0S3cWoPFZxETQD VTpgeFiBNCkyKd2K/N26/ZC/adtjJn4L+NrmJFDrPaaZ58M0LjcksQDbiGTB2sqyLna6q2Vr6l/a J/bR+KmqfE/xZ8L/AAR49m+H1joM1rpWg6j4cFrpVtfeIILdTquj6veCMtDazXkhgt7lWQW89uFl zDK7x/H9t+0/8fEefw9r3xa8f6VqFnfXZtdXm1/VLfUdJ1VnjjntdYKy7rrSmkh2ujqzWzv50Xyi SOT4LxzzjwU4v48zGtnmO4kzaWVzlluHrYGllWDwtKeElyVsM5YieLqVFUruriKNWpGhG1Wyfs3K dLgzvE5Ni8dUlWniKvsm6cZU1ShBOGjjeTm3eV5JvlWvbVUPFOl+NvDujDw38XfBfjHRLrSLWRfC mt6rot1aahpzJyuh3lxfRoNQ8Pu27YDIZLR/mgzGzxnlPhl8OfE3xb8W2HhLwvArXE/7+/v5VY2G jaajKLnU7+VR8sKAgKo+aV2WNMs3H0T4U/bf/aS0HVo/BfjabS/jBp97eWunXPg3x/pNh4hXVmvW jS2gtdRij8xzOk8Xkyo7q6zK6kqQa/X74b/Bj4c6Voeo6x8NfCOn+BfFuvx2ms+MPCNrdSX8SXot Y/PsdFvZwGbToLhpvLhUBNzswUFhny/DjwA4V8aM/oV+GOLMfisp4acIZrgc1y5YHN/ZRU3Sw+Hx eGr4vBY2bVKVFuVTDYuhRj7mGqRp04xyy7IsLnNdPDYupKlhmlVhVp8lbl1tGMoOUJvTlveM4x2i 0kjzb4TfCbwl8HfDUPh/wzaCS5lWOTW9duET+1NdvVX5p7uVRmO3DFvKgU+XEvABYsx9TEg5yAOv OT+FQskiMyOCjqxVlZSGRgcMpBPBBBphY9lJ/P8Awr/Q7Kcly7IMtwWTZNgKeW5Zl8FTo0KUVCFO EeiXdu7lJ3lOTlKbcm2/0CjThh6cKFGEadOmrKKVkkunT57tu7ZcRkLoMnJYcY9+e/pX88/xT1P+ 0fiX8Qb8MGS78Z+JJVYNkFDq92qHP+6BX76+IdZg0Dw/ruu3LiKDRtG1TVJWc4CpYWM90ST2/wBU APc1/OJd373tzdXsxzLeXNxdSNnJMtzK8zk8f3nNfxF9NrHw+p8AZOpXq1KuPxMl2jCOGpQf/bzq VEn15XbY+M40qpxwFLq3Ul8kopffdlkyg459s5Hr79etR+Z1+Y9D7f5PNZ5lPUEduBTPOIB/xz9c enGa/gFUX93yPgzSM5PLMc+pwSfxPsP8Kct2VjkTCkSbMkgErtYt8p7E/wAuKyTNnPP5nH+Hqaja Yr3B98jrz19atUuys3/wwami04yeeD0OB1Pb/PrXYaF4ssLLw/4i8K6vptrdWHiCbTbu21fc8d94 b1fTHmS21S3KBvtNm1rd3cVzblcyRyhkYSRrnP8Ah94g8IeH/FVjq3jjws/jTw7bw3YufD6Xz2H2 q4kt3S0kknjdS0UcxVim5d2O+MH1a4+PfgzTLlp/A37P/wALtDZHL2914hj1Txhdxn5gjeXqN2sC sAegjI4A7Zr7bhvKcrhhv7WxvGGEyaq5VsPLCTwmMxWJqUalJQqSUIYZ4Vwqwqzpx58VSqKUXKPJ JRmdeHhSS9rPFwou7i4uE5ScWrN2UHCzTa1mmrN6aMw4/gN8Y7y/Wy0jwD4g1uC4cjT9Z0uxlk0L VLUgPBqOn6tNsiksJYWR45GK5VxwG4r6B0H9nPxLp/w38UaN8XfFfhT4b6Va6poninSX1LXINTv9 AvfMbSNVuL3RtNlO2G7064ghVTKC1xBD/d58t1/9rX40eM/Csnhtde0/w2LBXuWm8L2UWgXl/pUY WP8AsuFoJCII7eNtyJB5bPFGwYnYM0P2ebHRtd1P4k+Kfii15qfw20bwPdN46urjULz+0ry4utQs Z/DunadcifzJtXm1ewi8pd+CI3zjNfq3DuA8K4cSYTLeGcDmfEss6w+Jpyq5piKeX5bQhPC1XWeK pYSlXxc8PhVF1a1ZYqjKjCn7eClKEJy9PDwyxYmFLDQq4n28ZK9WSp0opxbfMoRlNxha8nzRcUuZ XsmdG+p/sqfDgJ/ZuneLPjn4jgYFZ9YZvDHgt5wSBiwhAnvLfdjCsJA/r2rqYfF37S3xQ0G1bwXp +lfCj4YXd3cabayaNLpXgLwzaQ28SC4N5rV5LFcXkCI5BkTdvcuqAsCo53RfHXguw0/xP4z+B/wR 0DQ18BadYz3Pjf4o+JJPEV7Zz3s4trMaRo12VsLjxLM3mmOJfNkKxNIiAJXzb4++KPjf4n3tnqPj fXJdZuNPtDZWQ+zWlja21qZ5bnyorKwhihXEk8nzbN20BSxCitsz4iwHDuXwowzJYbCZlTbo4Phb C1MpwmJowrexlLEZ5mFGWa4yClSrU5UpUMTSnKHu4mDUkipiadCCSq8kKi0hhYOjCaUrPmr1I+1m laSa5ZK60mj1KDX/AIf/AAuubvUdDvm+JvxNtLz/AEHxNqFiy+A/D15FJIJtU0y01CQ3Pi3VUkAa 3nukjtVcCVYpcKT5H4l8XeJfGWqS634r1/VPEOrTgiS+1a8lu5wjO8ghjaRsQwh5JCsaBUXd8oFc cJR1GSOmRnA9Dx+H50eZzjJ+uSB9DX43muf47MsPDLqVOOWZPTl7SODw7nGi6j/5fVXOc6mIxDXu +3xFSpVULQjKNOMYLyKmInUiqaSpUVqoRvy3/md23KX96TbtorKyNLzOeTjrn0HHvTxJx0z9DxWY JAO459Pb1z04/GnCT1wfUDqPXjua+ddP1RgaYkOOSR9D/L0p4k4znjpznrgnv9f0rNWTnrg+/I+m aeJMnk547H+lQ6b7JgaYkx1z+HH9fYV9Q/sop4e8S/EO9+FnioW6aN8W/DupeCoLydUJ0rxJOovf CuqRyP8A6p49ctbVSwIJSZl5DYr5RWU+wA7E1r6JrN7oGr6VrmnSPBf6TqFnqdlOrMrJc2NxHcQu jcdJI16cV9Bwhm9HhvijI87xODjj8Fl+JpyxOHmrwxGFk+TFYaf93EYedWjLspu3Q6cHXWGxVCvK CqQpyTlF7ShtOL8pRbXzNjxX4d1Xwb4l1/wprkElrq3h3Vr/AEfUbd1KvHc2FxJbyAhuQpaPI9Qw PesDzSAVyyq4G4AkB8MSNwB+bnkZr7x/bs8N2mtap8Mv2idBtlTQ/jp4K0vV9UaBcQW/jPTLSC11 2BivCyuRG57llfjrXwB5mMDdjjnOPzHrzXp+I/By4G42z/hqlWeKwODqqpgq/wD0E5fiqcMTl+JX RrEYOtRq6aJzcd0aZjhPqONr4ZPmhB3hL+anJKVOX/b0HF/M9t+Gvxy8Y/DeG50SI2XinwJqrY17 4e+KoDqvhXVo2AEjizlbOm6htH7u6tminjIBV+MV63d/CvwN8YNC1bxD+z5Le22t20Eeo+IfgxrN 0bzxNpcdvJLcXl54LusD/hMNHjgaUrAgF9EsWZI5ADJXyVptmlys97ePNDpViI2vJ4UV5meVtlva WyyEK11I/AycIqtIwIXB6LSfHmreHbyxuPCN7eeDpLS6F0L7Srhv7QE8RcWtzJqMSrPJMsbyBgrp H852RqOK9Th7imEMBh8l40oPPuF4wnDD05NLMMEpXi6mV4qf+7U1Nt1MPWlPA1nzt4d1kq1LShik qcaOMj7fC2aiv+XkL6XpTfwq+8W+R6+7zWa6z/hX7f8AQv8Ajv8A8Ed7/wDIFFdv/wANpftPf9Fe 8Sflp/8A8h0V7P8Axpj/AKGWef8Ahiy7/wCiE3vkv/Pyv/4Ip+X/AFEep8o+f7jAz379+3NJ5/Oc 8dxk55649Pyrr/D3w81LxF8OvHvxGt9RtIdO8A3nh6zv9Okjna9vX8Q3DwQS27qdkcUews+45PAU HOR5v559f/Qvevz/ABeS43A0cvr4vDSo0c1o/WMPJtWqUVWq4dzVm2kq1CrCzs7wvazTflSpzhGn KUeWNVc0X3XM43/8Ci1rZ6H7gfsDMF+BZdRky+N/EJY9yVi0xAD68KPyr8kvihqDXfxK+INyxIM3 jbxTIck551u9/oK/V7/gn9crJ8CXQHJh8d+IVbHJBeHSpefbDCvyA+I0zL8QfHanIK+MvFAKncCM a3fDGM9a/qTxmp83gZ4CU4fwvq1VtL+aOHwy/BuXzufUZy/+ELIEusHf1UI/8ExxcHBBY4OMjLAH GeSO/t/9evrX9jH4s6Z8OPjNYaZ4pdX8AfEuwu/hz44tpz/osmj+JF+xQ3cyNwBb3rwSbuSoDV8Y mc+v6n+lKty6MGVyrowZHUkFWUhlZSOhBH5iv5x4Rz3H8HcT5FxRlaTxuR4mliIQnrCooSTnRqL7 VKvTcqNWOqnTnOLVmfO4PE1MHiqGKpfHQkpJPZ2esX3UldNdU2e7fHr4Zan8Ffiz41+HWpq+3QdX nGl3JDBb/Q7pjdaNfwsR+8jl0+SFsjjOR2rtPjH4y8Y6DovgbwAPEVxBol38KfAN7rfhm0MH9mx3 r2N1PamTYrB7iSwntJpGRl3NKu/LoCPe/izbP+1L+zJ8PfjfpKxTfEz4Qzab8KPiyMkXF5ok7xW/ hDxXfFVJ8gF1illYEhpH/hSvPbH9nbxv8eP2i9a+H/hGJYtC8DReGNC8W+LdR3QeH/DWleGdB0nS tQubu7cBSWe0uRBEDumYgqNuSP2jOfD3M8Jm2dYLw5wmLzDK/Earlc8iWGnNVK2XZpDH4l4Wag1z ywcsNWwGN52qVOeGxDqrltKPs1sBVhVrQy2M6lLMnReH5W7yp1VOXK7buDi6c7uycZX7n6vfs2eB L/QvhL8NfCOmWLy348Naff3kaJg/btaQ6xeyztwEAmvWBJ6CMZ6V9RvH4c8CgG78jxJ4oUZFsDv0 nS5ev78/8vEwOPl6A9h1Obe+ItJ8JaXH4T8DfJDb20Flfa+Ri7vRbRLB5duw/wBTb7VwMenHcnzd pS7FjuZmOSWOWYk8kknk5r/VjJsuy3gvJspyPLYU8bjMowtDDRq2UqGHWHpQpRjQi7xq1IqCvWkn BP8AhxbtM/VKUKWCpUaNJKdSjCMb6OMeVJWS6tW3ei6Lqb2seI9W165NzqVy05B/dQ/ct4E7Rwwr hY1A44GfWvItXbw18KvD3xJ8eW1rFZfaV1Txx4hkyFW91Wz0e3tY2AA+XzBYWyhe8k7sOXNd9vHo f0/xr40/bv8AE1/oPwEvLOxDhPFHiTRdB1CZWK+TYZuNUkRgOqyy6dFGR6Oa+F8RM7/sLhfiLjDF w+uZhkGExWKoVKi9pUjiPYTp02pO8lzymqc2mv3cpJ+6cOY4j2GFxGMmlOph4SnFvV83K0u+97el z8Z9V1q71vVdT1jUZTNfatqF5qV5IzMTJc39zJdTsSeoMkrV2klz/wAJxo0kxO7xl4bsN9wxb974 o8M2MKqblsnM+u6dbqPMPL3NjHvOZLV2k8k+0H1HQdj2r1f4E6zp+lfGP4bXurRW8+nL4s0u2uo7 pBJbBL+U2CyzLJwUSS5R+ePk5BHFf475DR/tDOMPl2OrpYXP69KjiKlRtqDrVVFYlvdVKEpuqpLV rnpybp1Jxl+P0P3laNOcvcryUZN9Lu3N6xve/qno2n9wfsP+G5fiD4gk8U+LdAsdRtfhfZ6Xa+Dv EU9vJa38F7Ot6tppT+Uqw61Y29pJPNFJMjz20iwCOXYdg/WfT9TvdOvbe9spWiubeQSROnBBXkg4 6oRkMOhBOa5OwFnpdhFFHDb2sYBKW9rDFAgUHaixQxIqqoVQOgAAr5Y+Mnhz44fFXxLc/Dzw7rk3 gf4d6jpNnqcXjHQ4pRIZYLkW2t+G/FU4vUmLTQSrLZrZgBzGFn/dmVh/qjwjkUvBngXA5bl2GxPG XEDq3csLSp0cRi8VUv7CVacpyVClSpwpYZYivVqezjCF3y2jH9SwlF5NgKdOnTljq7d7wSUpzfw3 d7RSSjHmk3ZJdND718Q6/wCG/Ftnb+LPClzbaw/9qtoHjC30Ca21GDw7rsVq908mrvbzH7DG8aKD nJMk8akbmOOXWVWJ/hA7sQKzP2Zvht4J+D2hy/DHw/bsukeJIWTWNUvXEuo6z4gaMCLWNQlzjzy6 hURQEiXaqjgk6mo2UumX15YXGElsp5YJSTtUeUxBck9E2jOemOa/a4vNMwyfLM/zjC0MDm2YprG4 fDTdWjh8XFJyjCq4U3UVSEo1JT5IRlVdVQXIont+/Uo0sRXiqdaqvfjF3jGatdJtK90072SbvZWs fKf7ZnjuHwb8ENds4p1TUvGlzbeFbFFI81oblvtWrSKc52rptvMhI6G5Ud6/EDzeOMAY/Hp0+vSv p/8AbD+NcHxS+JR0nQrwXXg/wKt1o2lTxNm21LVJJF/tvVoyDiSJp4Y4YW/iitQw4evk5ZeM54HA 5yOfp9K/yQ+kjxvQ468SsfVy+v8AWMm4epxy/DTi7wqexlOWIqwaupRniKlRQnFtTpQpy6n5PxFj o47Mqjpy5qOHSpxfR2+KXzk2l3SRfMgz1Jx6fn+dMaT3PHGScn1/PFUzL75xggY61E0vvg9Tk564 9vr+dfgipX+yeEaMbxPNEs0pgheWNJZljaVoYmcCSURBgZSqFm2ggtjAIzx1c3hmG9uLW28Ka9Ze J5brcgsfKm0XVlmjXc4+w6m6pLBjJR0mYsFO5EbivPzLkk/l259vQcVE8mVAYAjHBx6dM16OF+rU 4yp4nBLERm4++pzp1YJPWNN3lS95bupRqW0asVFxWkocyfm016Pb74s6/wAUeF/E3grUX0vxRo95 o94oQqtzse3mWRA6PbXkDvDdIVYYMcjDtwQQOWaYLyDx17YyT2wOnNdp4W+KvjLwbpmoaJpF9Y3G havPDPqGi67o2leIdKuJIFdI3+y6xaTC3bY7BjEULcZPygiPW/Gvh3xRIs2seCdF0K7FssJ1DwEj aDFJMh+W4utBuJJrOQ+WNrCEWzN98tmvbq5bw9iKXtsuzKrhK8rv6tjKXMo3k0oQxlDmjWajytzq YXBptyXKlFOWso0GuanUcZfyzXnspxvzO3Vwgt/nyljFfahf2VjpltLe6jeXdva2FpbxNcXF1eTy LHb28UKqTMzyso2gHOcHjNfpFq3h3wDonh/WvgRP4P1ix8NeHtFsPiX8ffHmkaxJLL4M8XrokdzY aLo3mweRrSpJIIYrCXJY3h8v5o3lGf8AA/wv8LvgF8O9L+N/xB1qzXxV48jZPhlFreiXcOoaCIo7 tJLiGCL7T5Ek6tGzaj5TxW8M0TqreYVb5T+JafFzxBBpt7daW1h4G8XeKJrbw5aeGtcg1vw/rviL UpjM1xeX9pfSza9r8xkDNcXwExxsjjhQCJf3HJeH34bcLvMcfg6ee8RcTUIVKuX0aVHFzwWVVYWp Qxk+Sr9ThmrrUXVnFwrPCKNCi1XxMq2D9qjR/s3C+1qQVfE4mKcqcUpuFFrRTdpcircyu1Z8lox9 6TlT9m0XwQP2htL8NfCL9nHUzGNMuLvV9R8B+J7Sbw/rGtXaQwRt4u1zxFbz3Gn65exKbhEBNqLa FwkUbHJr9LPgN/wSp8AeGray1v446tP4618rHM/hjRp5tP8ACtjIdreRcXSBbnVyCCrEGGM7eAwO T9Y/scfsw+H/ANmz4X6Zp5sreb4heIrK11Hxzr7Rq15JfTRrMuiW8xG6LTLTf5aopAeRHlbJIx9d 5PrX+ivg19E7hLD5fkvGfivw7h8642r4eg/7Pl7SWV5dTjCCo0Fgqk6lKpiKUElXUr4SFVyjRw8e X2tT9IyXhLCRpUMbm2HjXx0ox/d6+yppJcseRtpyS+K/uJ35Yq13+On/AAU5+G3w2+G/7PHgmw8D eCvDPhNV+IVnBB/Yuk2dlcvCNI1FpY5buOITXCnbGW8x2yVBPPNfguJsdDj6Hp9OeOa/cz/gsT4u hg8N/BzwQkw+0Xmr6/4luIQw3i3s7a3062kK9gZri4APqp9DX4Q+cB6fnnn3/Ov4O+mfDK4+POfZ flGEo4LCZPgssw3ssPTp0qUJLB0qrjGFOMYR5Y1YxskrW8j4HjX2Sz/EU6MVCFGFKNopJJ8idklZ aXNUS/7XXnp35GOOlP8ANH+zn6/0PespZSTycc+vU+w9KlWUcY6j1PP8ulfye6Xl+h8oaglyck44 Pf8ApjFSrL6fQn2z9PasoS99x+hyalWT16Hnjv3GQetYyortYDVEo9cnoD2+hHr1/Kvrv4l+CtPk /Zb/AGffidptmkV0+seOvAviG4iRVM9xYan/AGtpclwyj5n+z3dwgJydseOg4+NhIeOh6dOvsfav 1GsvDx1//glxc6ls3y+EPjRc6xE2MmO2nltNNuf90f8AEwXP0zX6x4VcOU+IsF4r5dOgq1XD8J47 H0m4qUqdTLcbluOcoPeMnSo1aba3hOUdmz1sqw6xMM1puN3HCVKi8nSqUp6fKLXo2dh8KdEP7Qf/ AAT18feCkX7Z4t+BXiW68T+HVBD3S6Y0D6tLaxZJIieyfWgAMZaBB2r8oomRpMTM4jQM0m1SWwqn CY/hLOFXJ6b89a/a7/gk5pulS6T8Q7u3u2uW1UjQPFehXLK8KhY/teg6jDFj/UTWb63bygkgtCmM biK/ML9p/wCGV38Ffjn8RvASLNb6dbaxc3Wjt8yLdeHdXddS0zgAb4vIljUjkboPUV+n+MPB+NzL wS8BPFqrRU6k8BPh7H1INTjJYCrXllFSUl9t4KNTC1btOEsIqe8Vf1M3wk6mS5Dm0ldum8PUa1T9 m26TfnyXi/8ADY8da7ibTmjLwpLLJJKY1aUhVge3S2gWJYyI22vdNvLHcB8x3AZxi+Mk5A+vfn2q qXHJz07A9MdAKYZD0/LnI9jX8rVOaryOSs4RS0Xb8Lttt2W7ufKt3t5Fvf8A7X/j3/16KtfYYf8A n+h/75P/AMVRWn1Kr/Kv/A4//JD5Z9vxPQPDlrrmu/CXwt8PfD9vENV+Jvxjufs++RbQagmg6Hpe nWKX1y33tPhv9ZvHy2UjZJGALV4Tfxvp99e6e8sEz2N3c2Uk1rJ51rNJbTyQPLbSgDzbdmQlWwNy kHvX1j8MxrGmr+yj4uuzY3WhQar8RdI0qxluLaS7fU7bVdavbp20+RSTYj7RYgyNwSdg5xXxzqer S6pqWoanPFawTaheXN5NDZWsNlZxSXErSvFaWlsix2tuGYhERQqgYAAr9b4syqjQyfh7EVHVWNlS oUYQnHlhHCrLcuxilFJ/bxGPryTu+eNpPlbd+7F01GjhpNvnajFJqy5fZUp3XrKpK3da+v7U/wDB ObURc/CHxTZhgzaf4/uWZQTlReaNpMqk59TG/wCVfld8aoTp/wAYPijZOApg8f8AitdvOADrN24A A9mFfoR/wTO1YS6F8WNHL/Nb6x4Z1RY+flW6stRtHYDtlrVAT7CvhX9q2zbSf2ivi1bFdgl8VT6i gxgmPVbS11FGXAxg/aT7+tfs/iNS/tD6Ong5mHxLBYjE4V+TvioJeWmF/A9vMv3nDeTVEtKcpQb/ APA1/wC2nifnjPIPufzBpfP+v4Z+prH8/IHPp3J/ECt/wn4d17xv4l0Pwh4Y0+41XxB4j1O00jSN OtUZ5rq9vZlhhjVQeF3Plj0VQWPANfzBQwFbFVqOGw1GVfE4icYU6cIylOc5yUYwhFXcpSk0oxSu 20kj5aMZTlGEU5Sk0klq23okl3Z+hf8AwTU1TxhL8cNQ8LafoY8Q/DfxV4Y1Gx+LVpfukOhaZ4Xg ie4h1/ULi4PlW89repGYSxDMZGRCMkj7G/a01HW/h9rafCvwrqXhn4IfBqTxDZeOvFvirVNcguvG /wAcdYvL+21WZtK0rTZGvNX0VZpI7aNCY4QYjvYRxBB8b/tC+OdD/Zc+GP8AwyJ8JdShn8Y6nDbX /wC0d8QNMkUXOq6+8QkHgLTbyM7k0ayDBJwG+dwVOC0oPv8A40sNL/bG+C3hLwBFbQWn7Q3ww+Dv g74ifC+4aQGf4geDZNFjTxL4Wiml+e51C2v9NmlhTLNuxgYLtX93cI1Vkfh7xD4NZbj55r4h5BFY tqFWFOlPEYlzqZhwnhMVSpSxPLGFHnxEaGKoLHZnLEZdRn7KtKWJ++wc/YZdiclpVHVzLDrndmkn KTbqYSE0uayUby5ZxVSq5Uou0nzfocHEgEinKuA6n1DDcCMdRgivmT4+fFHUdF8RfDD4QeD72S28 a/FDxTpkV3dWjD7ZoPgmxvUn17UoyAfJuJ7e2uYInPRI7hwQUBr1n4deJV8Q/Dfwf4onWaN77wpp d5qEHlyPdQXtvYJFqlo0AG9rqO+t7qMpjcXj24zxXyj8IPhl4v8AFX7Rfjb48fEm80ux1PSkuNE8 IeBLbU7LV9V8NaNeWws9JuNd+w3Dpot4NKN1i0cec019PK4QKN39CcY5nmuY5fwzlPDdOq6vGWIw 0auJp3hHCZY1HEY2v7X3VCtUw3NRw8U1VlOpKdJN0W19Fja1WrTwlHC03fHSgnNaKFLSU5X6ScPd it222tmfeRwSSOBnge1fB/8AwUN1GGz+B2l2bn99qXjvRVgAGT/olhq1xK3soXaPq4r7nr8nf20P jFYa38XNB+F1lKNS0Xwd4d8VP4us4WSSC81zWvD11PFpsu6NwJbO3tLGQttYxTTcYeM48fx3zPCZ b4aZ/hMViI4etxD7LLqF1fmq4qpGMrR3ap0VVqytqo03a7snjn9aFLLMRCVoSxFqUdnrNpPTyV5P yT6n5l+eSOvX6nP5HpT472W3kjuIZGjmgkjlhkVirxyxMJI5VJ6FZEU57ECsMT4HJ42jJ5549a/S T9jj9kf/AITcaf8AFf4o6cw8HxyLc+E/C92jIfFEsT5TV9UibBGgLIv7qPj7Wy5P7gfvP80OBuAc /wCPuIMLw/w/h+fE1ffqVZcypYaimuevWmruEIXSVk5Tm4wgpTkov8wwGBxGY4iGGw0bzlq29ox6 yk+iX3t6K7aP0s+C/ia4+JXw08FeNL22u7GbWtCspr2C6t5baZr6BDa3skKToC1nJcwySQyAbXjl VkJBBr2VFWNQiAKg6KOg/wDr186fHH9pP4b/ALP+mQRa9MdS8RXFsn9ieCdDa3XUZbdFEcM10MiP RtJUKFEkgG4JthjcjA+O9b/4KN21tqVhd6N4Vi1zQNU0OzubrSTeXOia/wCFfEETSRalptzevaTW +t6e+IZreeII2yRldQRtH+meZeJ/AXh/DDZFxPxdRxGfZfRoQxcqdOc6rm4Ri61SlS9q6bqTSnKj B1KtNVIzlH2V6i/UKma5dl6jh8Xi1LEU1FTaTb2S5mo3tzPVxV2k02ran6mXOr2nh6Fta1HULXSL PTgt7NqF/cxWdraxwurefNPM6rHGH2jcTjLAZyRXxj/wUb/aA17R9I8E2fw6P2Pw98Z/CD6xf+Mo Gliup7a3mbTdR0XTEeNWsXkKA3ExxIY5gkYUMzV8SfHX4vw+Jvg9F4rXwzN4O8S/HfXGW509/Emr a8t78PfBN0Gh1FIL9lh0f7b4ndVZbaGNJ00osc853/ijKfF//BPb9nnxNMzS3vgH4m+NfAjTyMXl FjqEX9qW8BdiSIxsXA6D2wK/KPEDxpxfFPC3iTwXwxU/s36vkGHzqGLo1KvtalP+0MJTlRSrUKFS iq+T476xUtCNSDm6SnKMfaT8jH53LFYXM8FhX7Plw8a6mm+Zr2kNNYxa5qM+Z6XV7Xsrv4LSXGAC MDn1/A/l/SrCSgYJ6fU8noM/jmsVJffOO/ORx/8AWq2spz17DA55/wDrc/rX+b9Si9bo/OjTaQ8Z yBz0zz/kH9KiaQ8479x6fT/PTioPMO3qc989D+R7cV6D4B8CR+Mf7e1LV/EVh4P8L+GdLl1DVvEO pxvLFJdssg0vQNLt0ZTf65eXCMkMIYYWOSVyEQ56MuyvF5pi6WBwNJVcTW5rJyjCKUYuc5zqVJRh TpwhGU51JyjCEYuUmkm1dOnOrONOCvKW2qW2rbbskktW20ktWefmT0JPsSR+pqN5MZ7cdCT364/z 2qu0nGcHGc5zzjJxkHoenrzXXeBPAniP4ka+PDvhuK0a6WyvdUvLzUbuPT9M0vS9Nha4v9R1LUJj 5dpbRQKxLMQGIwuScVWBy3F5ji8PgMDhp4vGYucYUqcIuU5zk0oxjFatt7f5ChCdScYQi5zm7JLV tvojlJg8Wzeu0SRJKnIO+NyQrggnqVORnIxzX1J+zj8FfD3jKHWPir8VdUt/D/wc8BXKDWrm5maG TxDqqJHPDoNqVG94j5kHm+WDJIZ0gjG9yyLN+zpb+NfiX4X8GfCPxIfFXg+58K2Wq658RbiBoNE0 pLG/1Gw8UahJJLFGqWcV/Z3CWiE7pgEO4qWYYP7QvxI8JXFroPwX+EpeP4V/Dqa5zqKufM8ceL5Q IdV8WXrA4uIy6uluTxhmdAqGMD9ZyThTD8HVMdxXxhgKGYZfk1R0MBhJVo1KObZkownFQnRbjiMu wkZqvi61KXsqq9lho1G8Rp6tHCxwbqYvGU41KdF8tODknGtV0ejjfmpQT5ptPlfuxu+Y6n9o740/ Cz4x+PIZrTS/Eln4Z0DSNP0Lwxr2jz+RLHpsFuJpobjwbq6LBCEvZZIw0FxA7R26Fi+Fx6F+wd4W g1X9pf4a2fhvx5Yar4eOtNqviDwtqkM2jX1/b6ZYXN6hk8P6l5tpqzxXEUREltNJPEQJVAVSR8F6 /rNpq9xZS2ehaZoEdppOnabJbaW140d9PY26wT6vdte3EjNqNzIvmTbCse4/Iijr9LfsMeJj4W/a q+EGsGKaW2h164h1EwoXMGm3em3kF/eyDPyww2zySSMSAqREngV7vBfFn9u+NXCWecQ0cPipY/P8 vnWxGGhVwtqc8ZQTjCMHDmoxhaHLiKVSpKinCU3fmW2Cxv1jPMHiMSozdTEUnKUU4ac8dkrXSWnv JtpWbP68TjJ3Ag89PX6GobiaC1gmurieKC2t4pbi4uJ3WKG3ghQySzTSyECOJY1ZmYkABSScCvz2 8U/8FQ/2RvDcV39m8Xa/4mvLZpo1stB8Mai7TTRMyFVub8QRBCynDbsYOeRX5SftY/8ABTTxz8dN G1HwD8ONKufhz8P9RVrfVpnuxP4q8RWh4Nre3duFTTtPcffghJLg4kkdeK/1l8QvpT+DvAuUYzF0 OKsLxZnEYS+r4DLK0cVUrVUvdjUr0efD4enzW9pUqzUlG7hTqSSg/wBdzHi3JcBRnOGMhjKyT5ad KSm2+icleMVfdt3tsnsePft6fH20+Pv7QPiDWdDuvtXg/wAKwx+EfCkysTFdWOmSy/a9TjH925v5 LiVT/c2CvjES84JB7dOPqOOlZYkI6ng9cZGP/r5/lXaeHPh58QPFyQz+FvBXirxFb3NwbSG50jQt Sv7SW6Gf9HS5t7dkaU4xt3ZzxjPFf4s8S5xn/iJxbnvEuNozzDO+I8VWxVWFGnOo1KrO6hThFSkq dNONOmteWEYo/EcViMRmOLr4mcHUr4iUptJN7u9klrZbLsrIxoPPuZY4YY5JpZXCRRRI0kssjHCp FGuSzlsAADJJwKlk82CR4JopIJo22SRTRvFLG46o8bgFGHIwQMVHb3eueFNcguY/t+ha/oOoQ3EJ liktNQ03UbKdZYpPLlUNBcRzopGQMEA4r7B8X6lpH7Tfw41n4jQabZab8fPh7CNT+JEWmWws7T4i +ClW0sYvF1pp9svlx+JLKdlOpbFQTxXAucZSU1hk/DFLO8Hm9KhjXQ4hy6Eq9LBVKdo4uhRjKWKj Sq891jKEI+1jh50lGtShW5Kqrwp0a00MPGvCso1OXE0lzKDWk4xTcknf44pX5WveSdndJP5EWTBH cnr+OT/Mf561KsgOPXk56dKyo3LlVQMxY4VQCxY8DAAGScj61YLFWKurIykhlYFWVgeVYYBByO/P HtXyEqLSulp3OU1Vl5H8+cY+uOea/af9m+2TxZ/wTJ+PuhIpmm0nU/FF6qKN7q9tDoWsI2O2Ft3P 0U1+JKynjJwOwGee9fuN/wAEvZovGHwE/aW+Gsh8ya8iklgtyQTs17wzqWnBgp7faLWH8cV/SP0U MNDH+KGY8OTt/wAZhw7xDliT1vKvltapFa7tujoj6fhKKqZpUwz/AOYzD4ikvWVNv9D54/4Jk/Ee ++H/AO0la+DNQS4ttP8AiRo11oNzaTxyRsmqWsJ1bRbloZACrFoJEDY+7dEg4NfS/wDwV0+E4Q/D 740adbEZE3gnxJNGvGV8zUNCuJyB1Km/iBPaNR6V5lB4PXwZ+3D+yNrsdoLNPHPg/wCHWqXSKuxH 1W10Wbw/qTY/56GaxUt3y/vX6+fta/C2P4w/s+fEnwYsPnai+g3GtaFhQzpregqdTsPKBH33a3kh 45IuSO+K/rDwy8Msz4p+jH42+D2Pn/aGO4MzbE1cpbjaSqU8Jg82wqhFt8ixU5TTSbssXUV2mfW5 ZltXFcMZ3k9R+0qYKrJ0dNbqEKsLLpzNtf8AbzP5E2fjg5x69eeuPyp4KYySwweCCB0PB78Z7d6q SBo5JIpU2yxO8ciN95JEba6EH7pDK2R6jFQGQjjJHoP69frX+WUabTs1Zp7H5Ze3Q6L/AISDVv8A oJP/AN+rf/4zRXM7z6D9f8aK6/rOL/6Can/gUvLz/q4+ef8AM/vZ9KaN9on8R/sVeH9AdYZx4ag1 xs3VtZxnVNa8deIZ9Xm869xH9oaOwCbfmaQxpGgLsqn431N5YdT1GKXKzRahexTDGCJIrqWOQEdv mU8deK9p+K3jXUovh3+zR4etLgW48L/D2XxLbTxWsdvdx6rqvjHXp7eYXap5kqRQ2Nt5ZD7Msz7Q zGsn9o2zt4fiO3iTS9Hg0nw9498OeF/GuhvZweTp+pLrWhWEmtX1miEqjHxCuqLOikiOdZF46V+5 8YYWjjcvrVMNNzlkssr9pG1oqGMyfB0rQgnJRjhauBVGdSU7VHWopQg1aXpYtKdJuLu6Hsb+lShT Wi1soOnyt315louv2L/wTR13yfiX8QtAaUAav4Ktb+OMnG+XRtZgUkA9SItSf8DXl/8AwUA0ptJ/ aN1q7KbY/EHhnwvq8ZwcOUsG0qZgc8nzdNbP1rlv2DvEo0P9pbwdA8uyPxFp/iLw42M4eS80qa7t 0PPObmwhx719A/8ABTvQmtvF3wu8VquF1Pw7rWgzPjGZdG1GG+hBYdW8nV3/AAXpxX6fSo/239Fr EU0uafCOeKTW7jGrOCT9G8xl93qesv3/AAlNb/UsR+bWv/lU/NHz/fnnsf8APr+dfpV+zbb2P7Mv wE8WftfeJLW3fx54oe/+HX7O+mXsas6avcW7w+IfHUcUnJhsrd2jhkAK+YrAHLA18FfBr4ca38Zf ij4H+GPh+NpNS8YeIdP0lXVWK2lrLKpv76X+7DBZJPI7HgLGSa+mf29PipoviX4p6f8ACXwFKq/C v9n7Rbf4Z+Dba3INte3ekqsXiPXiI/llurvV0mJccusS8nNfnvh9h48J5JnnifWgnj8qnHLsiUle +cYmnKcsbFO6f9k4NSxMH9jG1sBJ6Np+bl1sHQxGay/iUX7LD/8AX+Su5/8AcGF5rtOVM+P9S1m/ 1jUL7VtUu577UtTu7i+v7y5d5bi6u7qV5p55pWJLyNK7EnPO6vuvwTB41m8N/s7/ABu8C+MrLwTZ /CPSdb0Lxd4+8Q3yR2fhjUNF8V3mqWWjvY27m41v7ZpOsxR2lhDG7zpI0bbVDMPiLxz4E8ZfDbVr bRPG+gXugajfaVp2t2UN2vy3emanbx3NrdW8yEpKAsmyRQd0UqNHIFdSK9jt5Lu6/ZE1hI4okttH +PmkT3kiSy+dP/avgm7jtTcQmTb5aSW2EIAyZCTkivM4NjjMnzXiSGZYXE08wwmDqYlw56uGxMMX gcRh8fSlKoo+2puM6F6jXs6vs3N06tOo4zWOCc6NbEqrCUalODna8oTU6coVFd7rWPvbO17NPU/X b47/ABh/4XL+yjr/AMUf2Tk1OPUoPEAtvi5puiQtpfiPwZbTyXN/rfiXTNFgnkm0/SdRu4xLvjY+ RFeTksHD7Lv7EXge68J/AvSNd1Z7ifxF8SL+78c6xeXkss99dRahtt9HN3cT5kmc6Zbwy5dmO68Y 55r8h/2QfjH8QvhR8b/B7+A5Ir5PGesaZ4O8TeFr9Wn0LxV4f1y8isr/AE3V7InbNELeaV1bG6Nk 3Keor+njXfBmkNo0HiD4fwRp4btoYre40G1jRZPDawxrGltHbp93T1RQEwMKqgDjp/cfgxjf+IwZ nV8UMTKdLiLhjALK8Vl0Y8uCjUqSVR5nl0U3yutQUoYyg1KdGpOU4zdCpCFL77I6rzqr/a0m44rC U/Yzp2/dpuz9rSXTmimpx1cW278rVvmf45eO5vhn8IfiD44tJlg1DQvDd9JpMrKrhNZu1Ww0h9jg h9uo3Ns2DwduCCOK/nX8DeIbu4+Iuk6tq1zcapd6zqt5FrE11LPJc6s3iGG7sdSjuJ4z5hluRfzI XU7gZtwOQK/XT/go94wOh/A/SfDcc2yfxn4y062kjVsM+n6Hbz6vc7gPvR/bE03Pua/Jb9n/AME+ JfiN8YPAnh3wtAJNQj1/TNauruRWNrpelaJe2+o6hqd620hYI4YQAD9+SRIxy4r8S+kdmWYZ34q8 I8L5dz4mWUQwnJQhed8Zi6/PpTvZzdFYda620btqeFxPWqV83wWEptz9ioWiv55yvttdxUd/yPo7 9jb9mib40+KpPFPi6xmi+G3hC9RNRhmSSL/hJtbhKyR+HIWJB+yR/K96w6IVg+9Kdv6U/tSftOaH +zz4Yt9C8PRWF58Q9XsBF4Y8PoqfYfD+mohtotc1S1iwIrCIJstbcbfPeLAxEjmur+J3xB8Afsof B651CysLW3trFru28KeGo3SK58R+J9UmuL5xI6jLl7qae5vZ8HZErYwfLWv58PiD468U/EDxnrnj HxtNI3iXX7r7dfLLBNaJChRVtrazs5zutbCK3WNYUHyhFGOtenxLmOA+jtwTDg3hatDFeInEMI1c fjoRu8NCakoyTa91xXNTwVOVrfvMVOMZSip6YqvT4awKwODmp5niknUqW1gn1+66px6azer10tQv /GPxE8QajruoSav4q8Ra1fLLqF+6S3l5eX12zeTAuP4yEcQwR4ASIrGgRDt7zwD8HPFHjPxn4P8A Cs6w6PbeKbd9bk1SeaKW3sfCNhFNd654gke2LqIra1tb1GQkSLc2/kSIrnFGpfCHUtA8AeANdg1m 7u/iD8Q/7Q8T6N4C0YeZdaf4C0vTryYeKNVmimDWN3J9nupYVIwbRJHDbgyj3v4BeEdZ0D4QT+Ld M1efwx4m+NfjXTvhR4a8TwJLNqeieEUvYG8Sz6BbQsHbUdQ1eS1s1cGOOOOznkeVADu/nfh/gzEZ jxDQo8RYHE4iTpLMcXP28G50mqFdUqkmm418V9YoUW6tanKjUxdKrWcY3PnMNg5VMTCOJpTnde1m +Zax92Vn2lPnjF80k4uacrGL8bPC3jn4hyaP468NeFNZj8AWsA8EfDDw3Bo1+2oW3w98KqljpviW 7EVsI4IdR1OS9c+YUmkuJJCqtGA1e+eNdH1Pwb/wTZ8E6R4htnsNW1r9obVdQtrOWWGRxawaCQ0g aCV1+6ybgGJVjtcK4ZR8hfG/4i6h428aReBPCOp69eeCfB9xZeDPCGn3N9dXN1rl3pAGjv4l1IZz d61f3iyyZbPlpMscYUAg/U37clxb/DH4e/s1/su2k0Z1L4Z+Bj4s8eQxNk2/jHxuU1CazuAD/wAf ENuxznnEwr7rD1cpo4Xxu4ooOtiFSymGUSxM6kPq+JzLM8bhKU6eFpRppxpexwuNr4aKq2p4XDJe zSty+lCVGMM9xcOaSVFUedtcs6lWcItRVtFywqSir6Qjt2/PVJQeM4Axzzkn/OatLL3z7ZGD/ntW MknT8hx+HOTVpJcflyTjB6Yr+Yp079P8z5k2VkyCO3X8jnI9RW1capqg8M2Gkm7kGjnWNR1SKwzi FtRW1sbOS9kUf61/s4WNSc7AHC43tnr/AIe/BX4pfFCK0n8C+DtX1uyuNSl0x9Vji+z6PbTxxwyu 11qVyVihhVZQWYMcbcYLcH730z9hHwH4Y0ODXfjF8VYLCz8MaZJqPjCx0FrSzgtJbi4eZEfVbtnm SDyFihjxbLLPIp8kcqK/Q+DvCjj3iyhi8Zk2T1MNlapJzxuJqRwWDdNyjOT9vXlThVhGEZVKkaft OWMHJrRJ+ng8qzDGRnOhRcaNtZyahC2jfvSsmktXa9kr9r/Dngr4O+I/ib8QNB8GeE7doB4m0u01 2LUZ4Z7jTNF0y5s5Zbi81KW2VjDZxX9vc24JO4uqoAXOK/R/Xv2c/Cfwr+G+geHNZ+LWi/Cvwdf2 Tp8Y9cggNt4t+J93A6m30zTbi6meS00ICW7j+zwRsZEaN5I3YkV4nqP7b/g/4a2ln4O/Z9+F+n2X hzRok0uLxD4okmOqanpsVzcXJMcEJ84bri6upka6nkIe4LmFcla+AfGvjjxT4+1678Q+L/EGqeJN TuJZGW71S5edoYmclILaE4SzgCYASJEUAfdFfo1DOfCrw2yvH0crpR8RuMs05qVavTnisHl2DpNU pVMPQrR9lXxVGpOM6VSpRVD61hpzoSqQozlCr6Kr5TltKoqUVmWNq3UpJzhSgvdvGMlaUk2mm48v PFuLai7P7B+P37R6WOhJ8F/gXFpHhj4PQ6YlpFrPh+5FzqvjHTpgxukv7mSNZ9KikumuRcwSqtzK 5dpXMbgN8HswxgDjp8ufzIx0prP1+u4dj7fjgfpUJfPXtzxnPt+tfkHFXFWb8Y5o8zzar7tKKpYb DU7Qw2Dw8f4eGwlGKjCjQprSEIq9tZynUcpy8XF4yvjavta0r20jFaRhFbQhFaRitkl823dt7P17 /mD+JP4V9MfB6QeBPhd8Xvi9JiLU306D4WeCZSxSVNb8ZRTHXr62PXfb+F7a+QsPutqKHqRXy67j B7jHXnt7Yr6V+M8g8IfCv4H/AAwhdVuH0C++J/iWELtk/tfxtcBNKiugPvNH4c07TmTPQXZxwTXo 8Hx+o/25xHL3ZZBg5ug9v9sxco4PDOL6VKPtquMp21UsLfpcvBfu/b4rrhoNx/xzahH5xu5r/AfN bSH1z3JPf1phk7Ejrn/6x56VWLFjhQSxwAgDEsTjCqoGSxOMAc5omSaCR4Z4pbeZCA8c0bxSRkjc BJHIoKHac8gHpXycaTtzKPup7+fr8jj6Mn34zjpjsR7ZHJ+n51+13/BIH46Xlj4z8V/AfXtZd9F1 7S38TeDdMu5UNvba/psqPq0FiJPuSz6eRKVXq1iGAyK/EUufpz/+r+Y/Kuj8I+MfEngPxLovjDwj q95ofiTw/fwalpGq2MhiubO8t33xyIwHzLwQytlWVirAg4r9L8IvEDGeFviFw5xrhozrUcrrWxVC nPleIwdWLp4mjvytypycqan7qqwpyfwpr0smzKeU5lhcdC8lSl78U7c0HpKPzTbV9LpM/Zz/AILD fBzRfDnif4efGPQ7C20+fxguoeGvFTWsSQpfarpMUN3p2ozrGoBunsZpI3bGXFqpYk5NfmN+zd8S 7T4W/GPwb4n1aM3Phya+l0DxZYmSRVvfCviOCTRddgdYzmT/AIl95KwU5BaMZGK+ubj9rvxD+174 ebwB8ffD1h4x1bwjHN4q8C6R4YuT4Km8WatZ2Mlvqem3l5bwz7tRbTy89vFCkYmeB4VG5464r4s6 p8K/hBcfD7wx4L/Zt8MT+PPEPgbQ/EPiK28Xa/4i8b6jpep688t9pOn2+lW1xbrb6iNPaxlZSrsV uEBUfNn9l8TJcN8WeJGZeN3AOd4fIOH54nAY2FPFYTGfWf7Sg6NOtSdDDYbEYZ1q+IpzxEqMsSpV KU51pLkmm/czSWGxmZ1c9y6vHD4ZypzSnCfN7VcqkuWEJRvKS5mnPVNyejV/VdE/ZC1H4Z/tBz+M pr3RdW+EHw48U6x4v1WW01a0udY8OeGdJsZ/E/hKfxTpV3Fvt7PUbQWCwyiKRJfNKDEnyV8i/DSD wt8QPih4u8X/ABDsp7nwVYW3izxlr9hYT3FpPdy3P2k6JpNlPaxlknuNdvNOiTAGQ5zwDX3r8Jv2 6PCUur6rpv7UvgDwrpviLxPoFh4DuPGPhXw5HNqEPhW3ureIeHviF4WkuxDdaRHbwxICoW/jjTCs pUY3fHnwEi/ZzsY/Fnwz0ZfGnwz1u91r4zyfEvSIv7W0FNE8N2wufh/4Gb5XEBi8R3nmzE4+0R+U smDC6r6mZcA8H57gMs4g8O8RSxnCeQ5jjMyzfAV6Uq2MwFTESoUcBTxmXyjGf1CMaCddOdTDQhLE /wC1yU4U6e08Bg68KOIy2UZ4OhVnVrU5K86blyxpqdNpNU/d97eKTl77ukvx4kkXz5ykbQL50myB 92+FA5KxvuGdyjg554r9f/8Agj34rsLD4ufEjwndXMi3XibwVb3enWojDW9y+i6kkt0XctlJEtbl yowQwLelfj/q91qVzql7f6tBNBf6pcSapOs9s9q0jai7XYnWF1XET+duQgbSrArkYr6//wCCf/js eBf2sPhPfSTeRaa1q8/hS9Zmwhh8RWc2moGOeQLmS3PPdRX4j4CZ7DhLxv8ADrOZvlw9HN6GHqSm uS1HGSeCqylFS91xp15StdqLWt0mn4nD+IWDz3LazdoqtGLb092b5G7ekrn68fta+HoPCf7Qn7Cn iGCJYIrDx5deETjgLbx6zZzW0YPZR9qlx2+fiv1mZVZWVgGVgVZSAQykYIIPUEGvkX9rXw58LtRs PhDrvxOk1fS7fw58XPDUnhzxRpVxDCnhzxFqMjrp02srOhWTQZ7q1ghucEOgdXQ8GvrpSGAIIIIB BU5BBGcg9xX+1PAPDq4e4+8XPZ1qEsJneIyXGU6VOf72lbKKOBn7alZOCnLBc1Ka5oVI8yUuenUj D9uy/DLD5hm/LKPJXlQmknqv3KpvmXS7hdPVPWzuml/IJ+2D8N/+FS/tG/FLwhFA1vpqeIrjWtFR lKqdH14DVbEx8f6tY7opn/pmR2r5mLsTgdfzOPp+FftV/wAFY/hPaXXxb+EXjWTU9O8NWHjbSLzw lrHiPVhcjS7G/wBCm+02dxqD2cMkix/Yr5EyqMQIs4wDX5CeO/h54n+Hl3Y2+v29u9jq9qNQ0HXd OuotQ0HxBp5Yp9s0jVLcmO6jDfLIoIeJwUkRXBA/xW8d/DjG8EeKXiPl2EwEo5JlmZ1J05QScaOH xyhi8JGajrTh7LE06UJyUYTnGUIScoyS/E8/y2eBzXMaUabVClVbTWyjO04X7K0kk3ZNppO6OL3r 6/of8KKrbz6D/voUV+K8i7s8M6z9o+y1LRvEngDSrqzvLPS7D4NfDG28PG5jdIbrTv8AhGre6u7m ydkXz4DrF1qIdwBmRHB5BrT+IOk6x4p/Z++D3xN+02FxZeDV1f4UaxbQ6hZSXth5Ws3+ueFpp7GK XzYHuLO91FMOmdunLKTtkFdT+2Rpeu+L/wBo7x7YeELTVfFGn+CfC+hJcafo+m3Utl4J0LQdCsBq FgoG5ItMspJlMsqlUEl0wPzZz51+zNJo3ivxPr3wc8US3v8AwjnxZ0G80y1a0eLfpnjbQrefWvBu uxRTHElxHe209tgFTJFqjxHKtiv6WzHLIrjvi7hycZywuf1sRluGqymoU/rVDE0vqslJRlCVL6zQ p0JK6dKjVfM1KLT9mrTSzHG4Sz5cTKVKDvZc8Zrk6NOPPFRa+zF6u6OO+DHi0+Dfi78NfE+/Ymje NvDl1O2dqi1bU7eC8LnuPsk0wPsTmv2F/wCClHhk6t8END8TQx+ZJ4Q8b6fLJKASU07X7S60yUgj ohvBp344Pavwbkklt5HQs0c9tKyjjY8c0LkZK5yriRM47EYr+jjx5Anx3/Yxv7i3Q3d54o+EGneI rJEw0ra7o+lWusxooHPnf2npskfrliK/QPBPDPPvD/xf4JcfaVsTgo4zDw6utSp1VorX/jU8Ku+q PTyBfWctzzAWvKdNVIr+8k//AG6MD4J/Yijg+Dvwi/aL/a71OJEvvBXhdvhn8LpZBgyePvHERs5b u0LfemtdNcscchZj718vfsteCT8W/wBoTwLomqhr6ybW5vFfiNpSZPtNhoYk1q7WcsDuWa7igibP X7SQetfSf7Vsx+Df7If7KH7PVsTa6v4t0q/+PXj+3B2SS3/iVvs3hqK6QHOY9ND4VhxtBxVH/gmF p8N58Y/G+qSKrS6T8PZY7dm+YxnUtd0yGUr6ExQsCfQ+9clLIqFbjnwf8LqsL4Ph2nhMVmFN7VMw zFU81x6qK2soYZYTL5J6pYSxCoRlmOR5PJLkwqhOqukqlW1eon5qHJS/7cP0+/ab/Z/0b9oH4fXW iPFbWnjLRUuNQ8Ea66hGsdT2AtplzKq5Oj3YRYpk6IxSZRuj5/FT4UQ6tP4d+PnwA123m03Wr7QJ /FOmaZdgRTWvjj4UXM+p3enkMuWln0JNZiGDtPkIRkHNf0bFl9R+HNfk1+1l4e0v4OftT/Bv45pb pD4a8dakuheO1VE8iS4SBNB1u4mXGM3PhfVVZz/E2nu3UnP7v448BYJ4rKvEHDwjhalGpDLs4klZ Vctx6eAnWnbeph41+RSevs5RbdqEEfRcQZfTc6OZxSg01SrtfapVV7NyfnFStfs1/Kj5K/YO0BPE 37THgiSRN8Ph2z1/xSwxkCXTdKmitHPbi9vLcj3Ar+j3w54n1Xwxfi+02bAYbLm1l+e2vIf4obiL o6kZwcZGcg1+Jn7Avgg+FP2nfjjo0qof+EH0fWtBtpFJePyLjxbawWksUn8SvYWiMpPJV8+tfseW AHUe3fn8K9b6MuUYjh/gGrVV8Pj62aYycpJ2lGVB08La/wDdlQlp5tdTbhOjPDZa38NSVao36x5Y fnFnyr+2/wDA/wAOftW6r4U0j4V+L9H0/wCKvgXTLvxPqvwKv9StdI1XxZomuXMMd5qHgzUb79w+ rbNImSKF1ZQDudVBAbmf2RP2dB+zx4M8QeJ/HWmXPhvxr4me7vtWj8RCKHUfCXg3TpZp9N0jUJgq pDciCM3V8yYQyFF6RCvm2TwPqXx0/wCChninVRc39l4V+Cx8MS6tqFhcXNpI9zomnWj6ZokN5bur RPc63NdNIFYZt7Scdwa/Rv8AaO/ah+Ffhj4ea34U/aD0S88XeE9fsoPDGq3HhyWODxjGviJZlj0+ yaCVZGvG0y21C73PsjENpl3JkUHuyfCcIZ3xTxv4vZ7l9HhrOMkxeOwGDxlatP8As7HSwcPqixk6 SpVKuCqKNJ4aVTDurh5L2tSOGpVIOU9KEcDXxeYZ3iKUcLWw86lOE5SfsqjguT2jVm6bSjyNxvF+ 81CLTv8AhJ+1T+1Br/xz8a6zY6VqEsHwv0u7Nj4Z0f7PAov4LGUY12+kaIym6uriMzKm8LFE0ceM 789j4E0Tx/4z8a/B/wAO3eoSfEPwz4z8N+HPH/jW38V6boeuXGgeGotel0bxO0OsahbyXFtpUVvp YO23kD20VztZEdXNemSfsg/srfGxo7r9mL9qPR/C+pT2H21vht+0BE/hbW495MyCz14Qx29zC0Ms QTasu7buEjBhj6H+Gv7C/wC058OfgZ8X9N0vwx4e8UfEDWdIh0L4deJPBPxH0W4kk8H6/fQ3XiO1 02ZZM28Ruo2uQGEZnW6miWVCcn+dss8PvEviLirNeIM6w1bizAY6NTFzxuS4qOZYarHCc2KWCprB SxChGvBTwlDDYilB03UjD2ak0fMUctzbE4yticRGWMp1E5uph5qrB8nvqC9m5WUleEYTirc1uVM+ WvDkf/C0/wBpPxV8PNY8NfDuG81DSvF2j2PjXS38TQ6LYeB7fQojoc1iNP1OKKHSYfB0KR21wIY2 f7WPNfDMT7/qPxa0z4fWF54ni8N5+DXgN/C0Pw80BLsQ39mbKa8t/hlqltpD582bUZl8SeIL6S9k Q3Vr/ZjIVZkz9E/A/wDYb+JHh34V6vqXjPw/ZeGPjD41+Glz8PtSvvEfiHRF0zQdC00XFhpjaith qImlt7u1Glx3jxO9yY7NU3RnC1U8f3f7IHwB8MHwN4n8b+HPij438C6fFrOsfBvT/Ebaf4N8ReNN D0K0TSbfxR4gu7SSbXLOxTSoYbDTDKdpZI7j95tWv0fL/D/irhzIK2fZ5icNwXiM1liMc8ZmUaeG qwddOWCwFWhOkq+Mq0Iqpi8Xh6NKrWrScIeznVoR5fWo5djMNh3iMROOAnXcqnPVSg1za06bi4qU 5R1nOMU5SelnKKPkz9nfwNY+ArPXf28f2krG1tdFt9X1HWvhL4HuLSHTbj4o/Em8nmu7O70/RwB5 Hhm0vHEzSBfLygxuC/N8CfEb4i+I/ip478U/EPxbete6/wCK9Xu9Y1GZidkb3MhaK2hB/wBXbQwi OKNRwqQqBgCvU/2o/jl8Qfjh4j8GeJvHGo6YIJvBdlf+H/DHhzNt4Y8JaRfalqYsdF0zTEPl2ctv ZW9tDLhQ5aH52Y816B+x58B9M8fatqXxW+JBt9P+EPw236nq1zqZ8mw13U7GI3a6e7PgSadbqqS3 eM7iY7cAmUgfzXndKrxlm2TeFvA1Or/YWErzxdfF4u1Otj8bVpRljM6zKzlGhRoULxw9DnqLC4dS XPUxGIrSqfM1k8bWoZTl6f1eEnOU56OpOUU516u/LGMdIxu+SN9XKUm/T/gB+wnqPxC8LWvjv4oe JL3wJoGpwx3mh6VZW9o2tX2myAPHqd/LqDeXpNtInMSMjyujLIQqkA/Vvh/4BfspfDr7Uy+DdV8e y2DC4ufE/jW5MHhSwmhhaWG1bXtZksNKDSNj5I1uSWZd3Ar4w+NX7eXxI8Wa9qGl/CnUz4G8AWjG y0eS102yTxBqlrCoiF9eXN1HJ/ZyttzDDAEMMZUMxfOPjzxH478aeNZRP4w8WeI/FEiv5qf25rF9 qUUchBG6GC4maO34JHyKoxxgCvoq/HPgzwFCllvCHBceMM1y29OrmmYUaU4YqrG6nWw6ryxEKdOU r8lsHH93ZJ3/AHj63j8jy9KlgsCsdWp6OtUSam1vKPM5JJva0Fp95+q3j/8AaysNGCeGtL8ceF/A HhmzjEUfh34I6TF4x8VC2UErYr4wvoLXQtCkZQA72cV08RbhmYc/AfxQ+MeqePBPoekQT+HPAiXv 2630B7x9R1TWb5SwGv8AjXXpQJfE/iJ2Z3Mkp8m3Mhjtoo0GT4TG2OBgDJ9uemB+Nd94v8L2fhW2 8JqutRapquu+GLHxLq1jb27JBoS6u0lxpNh9rZz9qun0c2k8w2J5L3Ij+frX5RxZ4lcacb4bHTxO J+r5Zh1FVIQqyVqdSSp06EVOajGEtHPD4KlQo1VT9rVoNUVKHmYvNMdj4VHOfLSja6Tez0UVd7Pr GCjF2u46XT9D0Cwv/B/j3xDfC+WXw7F4ah0iS2eJbZtV1vVzbG3v1dSXhbS7bUXTYVKvbjqCRXBM eOfXPJxnGOmPoK9u8F6e0fwa+NGv6lpcl1pLy+BNC0i+PnRRW3iyTXJr2GaKRMJPJDokeqiSNs4G oRnHII8LJ/IfXA/PtXxOaYKGEwPDdVUlTqY7BTqyspKU39fxtKM5XSTvCnBRabXLGOvRcFWHLDDO 1nUg297v95NXenZK3kj6A/Zb+G3g/wCMXx5+Hvw08dahqemeHPF2qS6Xc3WlTQW+oC4azuJLGGCe 4idIjJeRxISVY4c4Ga/Yz4kf8Ebvh7eaRcy/Cr4leJtF16KFms7LxfFZ6vo95MqkrFcXVjBDNZKx AG9UlxnO0ivwP8IeKtX8D+LPDvjHQbhrbWPDGs6frmmzbmAW7025juYQxU52F4wD6gmv7Ef2Y/2g vD37S/wk0P4maDaz6dNNJLpHiLSZ9pbS/EdhFAdStYnDHzrQvOjwucFo5VyAQa/tr6H3CPg54k5T xZwNx3w3hsz4rjU+uYStOdalip4GVKnSqxw9ajUpuLwtaKqON7tYhS5ZRjPl+54MweS5pRxmAzDD Rq4xPnhJuSm6dkmoyTXwSV/+3tmkz+S/xR8CviF8PfjNY/Bbxzokuk+K5fEek6OIP9ZbX8Gp3sMN rqOnXKjbeafLFJvjkXgjrggin/tNeI4Ne+N3j17V0Ol6HqaeEtHEZAjj0nwlaweH7BYsHGzyLAHj jLGv6c/2p/2arT4t/ED4DfEbSrS3j8UeAvGL295elAHudCm0+/v7KO6YDMkVtrVrA6DqBdPjrVf4 Q/8ABPP9m74ZWgvdd8E6b8SPGl5PJqOseKfGsA1U3Gp3Er3FwdO0mcm3sLPznOxTG74ALuSa97Hf Qu4vqZrxLwhw1mVHC8LTzOlioZjj5S5nhKOEvhKKpUKcpVq8KmOxVKrJRp0nLDKo5Q9pCBvU4Hxz rYrBYWrGOEdWM1VqN35FD3I2im3JOpNPZPlvdXSP5tvA2meGvgemi/Ez4kOmpeMNR0Ua98LfAGnN ZX1/ZXl3DL/Yfjbxzb3i+Tp2lQy+VcWdlIHnvGRJDGsSkt88eIvEes+LNd1bxN4ivX1PXddvrjU9 W1CRY45Lu9unMk8zRwIiICxOFRVVRgAADFf2K/GH4D/snz+G9f8AF3xf+GvwztND0+we51nxHqWl WWkz21tbW6xR7NTsxFN56wxRxwRxuXJVI41PC1/IF8TLrwTc/EHxlP8ADiyu9P8AAcniPVT4Qsr+ Z7i8t9A+1yDTUuJpPmkk+zCMktzzzkivx/6QvgzmXg3g+G8nlxJl+PyfHVK1SjhMPKtDHTqRhCNT H42lUjySU7KlSnGfJSV6NKH8WpPw+JMjq5HDC0JYqnUoVHJxhHmVRtJKVSomrO+yd7R+FLdvjS31 BzzjJJ9j9MfrQG9cnjuTjr3qDdyck9++fX/P+eE3ZweR68/TJHP/ANav5gUH1PlD0L4Y6fqus/EX wNpGh3FzZatqPivQbPTruzZlu7W5n1K3jjngcHiRC24Hp8vPFfoZ+0f8Q/CniDxJ8R/iH8CNFuoP iX4Y1lPD/jnxZPqEo8T6TpXh+AaB/wAJT4Q0e2/cWulXwgjW8uE8ya0lAKGGKZSPmr9iqDSbb4xX njTWohcWvwv+H/jr4i29s0rQtcar4e0C5fR1SYA+W41Oa2cHqDHwc4r550Tx54i8PeLT400a+ltt Ze/u72WR8TxXi38kj3tnfwy5W+s50lkSaOQFJUkYMCCa/XMhzl8LcB0KdVp0uMsyruc4QhPFYKGW 4enSoY3Byqe7Sr+1zDER5oOFScKMoRq0XKFWHs4ev9Uy+Kl8ONqyu0k5U1SjFRnBvaV6ktmm0rXj oypp8Oo+Jtds7NXuL/VNd1SC3EsjNPdXd9qN2se93c5llaabJJOSWyTmv0I0j9qTxh+yf8XvEfw1 0S4k8e/B3RLKDwD4r+HGuXZk0TxDBb6ctp4guLIK8i6bqT6hcagVlhypD7XVlJFeNeBdP8La14hX 40fDuaHw/rHgux1HxT4g+HKW/wDaFxp2u2FnNNYX/he1lDnUfC02oNGZFIaXTwxEm6ILKPB/BXiT QD4gv4vHVsbjSPE8pi1TWoUL6z4eupLr7RHrulSHJM0VwcyxEHzoS8fysVdebJcbmvAjy/MMjzv+ zc9zbHKrhs0pVL0Hg8PSnHkmnzc9HGV8TyYuhiqSjTWG5MRSlepGM0KlXLvZVKGI9liK1S8aqfu8 kVs11jOUrTjNactpR3R9ofHH9n3RfiD8PZf2nP2eb/xTr/w+tltLHxt4A8Ui5uvGfwsnRUt7Oyiu GTGu+Eo4xHFbXEX+qjRUcAKSPiHwrr134V8UeHvEVsXhvPD+uaZq8HJSSObTr6G6UEYyrb4sEfga /XX9iP8Aazj+Gvibw98A/jdq0Unga6ku7T4e/EJGB0S+0fXpiG0bXvPKw33hq5kbzIriUGfT7pSj ELvQfGP7TX7Lvir4Z/tOX3wr8OQf8JJH411ZfEXgMWciyS6j4f128mubRXeQhGniVbiN9rEHyNwP zCvsvETgfLcz4e4a8U+Am5Zji8XRwGe5fQounLLs8ko1KNSjhlWr1KVDH1FU+rxpyqUHUp82GlTh WjhMN6GY4GlVw2FzbLnepOcaeIpxjZ0sRo04xvJqNR35Um43V4tKXJH+h79qu3i+Lv7GfinxHpKf aJbjwV4d+JWkbB8yzaS+meJsrx8rrbR3IP0Ir6l+H2tx+JfAfgvxDE4dNc8KeH9WV1OQ32/SbS6J Bz/elNeJfA2O58afAq+8C+ItHm0gaZZa/wDDSSyuo4Y2m0mzsW0W3ulgjc+VC9u7BAwU4i3YwRW5 +y5pnijQPgZ4E8L+MtLvdI8Q+ErG78LXdrfpsnktdD1G7sdKvFGTugm0mKykjYHBWQGv9d+GZ1sV xhlXEvsZuHF/DWHhiKns5whHFZVi3NQnde5Oos2xHs4yd5RoTtfkZ+xYVyljaOJ5XbGYWKk7NJSp TvZ9m/bSte11F9tPlf8A4Kn+BrTxT+zOfEFxFM//AAgfjLQNbnktlRrmLSr+Z9G1Uwb+PM8u8gIz 8pMYzxX5afBT4QW/xI8M33gKbX7bxX+z3r07P4e+Jdzpwh8Q/B34jagtounaVqNtO4NpdX0qR2s1 rDJJa3ImW5R1MbMv9CP7QXw9X4q/BX4k/D7MSyeKPC2oafbSTYEUN4FW4s53LcKqXUMTE9gpNfz3 aH8aPF2nfGbwZ8AP2eT4T8M+C/AHiCz0S58RvDZSDxedIuYo/E3i/X7jUpWtp5rlkvRGEQO8LxQo WOyv5I+k1wvw/lHjDkfGHEOEnicp4twGEy36vhoSqYvMcbHEV6NTBul7WnSng5YOdF4uVRwqQlDD fVa9Cu4VI/IcUYXD0c5w+MxMOejjKcKXLFNzqTUmnC11FwcGue9mrR5ZRlZnm/8Awxtcf8+3jn/v 1oP/AMsKK/Tbz9U/6Cenf+C8f40V+VrwF8O9P+E2t/4FDy/6eeh5X9gZb/z6l+H+f9fn+VX7Vf7X y23i/wAR/C/RPAGi33gm80pj4pOq79N1fxTN4n0vTNe06e11rQVt7vStPt5JrMvCZJPthtcTMVIA +UfDnxY+A2mah4C1mL4S+LvA3iHwJrum63H4g8H+OI9abXmsdYh1JodasPEmnL0jWRIpYZUdYwsT B/vDjvj1qV54qtvhJ8TtSvpr/V/H/wAMtMXX7qaHymm8QeCdQv8AwTqF00igLM88WjWczFcfNMxI Ga+e/O75HfPPJr8V4w424hxXFWY47EVqGPpznGphfb4PCTdLC1fZ4rCxoydOdSgowdKbVKqn7Rzl KUpVKjl8jjsxxVTGVKk5RqJtShzU4Nxg7TgotpuGln7slrfW7d/tLxt8CtQ8W/F/4palpeteGvA3 wyi1tfFNp498c3raF4WbRfGT2uv6Ra6XcCBzrGqnT9WLpZ2okfbasGKcV+yf7Aur+EvGvwi0LwT4 Q1TWPEOj+DfFuq+A21PXrWzs7vVLd7xdSS7jsLWRxaaVLbaq/wBmjdmlEKASnduA/Cj40+JdW8Tf DX9nG+n1q51LRdM+HeqeFYbB5ZDaaVrvhnxNqFvqEYjbAF4+k3WgMzkFjGIwGKgAfpR/wRS8XLJ8 WfH3gC6kJt5NIsviBYxlsotxoPn6ZqLKOzmLUdOPv5HPQV+ueCmZ5Vl/jPlWUYLL1QpcXKUa1ec2 21jqVPMKNClTjL2dOjTlyUot+0q1GlKUqak6Ufc4fr0Kef0KEKdo47SUm9f3kVVjFJaRinaPVvdt bL5c/wCCnHi6TX/2xPiTo8cJtNL+H8Hh7wBodkoxFaaX4e0SyjhSBOiws8zuuOMSVq/8EzfFdvpP x71bQLlwh8XeBNWsrQsQoe90m7sdZSIA/ec2tveEd/kNbP8AwVT8EvpXx1034m28LLZfEvSp1vpg uEOv+G7g2k5dxx5j6TcaWeeT5TEdDX58fDL4hat8LviB4S+IOiEHUvCmuWWrRwlyiXkET7L2wmYf 8sriye4hb2mJxXwnFGZYrg/6QOc5/madRYPPauLm0rOWDxNZ1Y8i/wCwOslFLRaJW6edjK8sDxNX xNbVU8S5v/r3KV9P+4ctD+nXUfiJcyfHnwx8KdLkTyLb4feIvH3i8hVZ0hk1HTtB8K2Zc8w77qTV Z2xgsLeMcrkH4w/4Kg6tpdj8LPh3ZXVta3mo3njm9nsIJ5Jo3S3tvDmoQXd1E1vIrjy572wPXaW2 hwVJB2v2PvHtt8Yvip+0v+0RLHcWPh++n8LeEfDov1xPpnhrw9plxqc8MoVmCOIvss0oU4LyE8k1 +ev7cHxbt/izrvgvxFdyapYTyR61LoHhWa4tAnh74ey3Vsvh3VdU0tR59v4o1tYp9UPmlVXT57GJ VO3ef3zxH41o4zwn4ixvtliXxficRHARl8KwOGxtDCRqpSTXLOFGNaMPilUxDny8sarj9JmuYRnk uJqKXM8bOapJ7ezjUjBS7aqKkl3lfVJtfoj+xXqnh/xh4y8X/EPSrC7gv/FHwl+FK6tfu9tFbanq ukLqPh3Xp5rGKMtHq39u+Hr5ppTMVmimgcRI5dm/RUEAgnoCCfp3/SvzY/4J1WN74b0D4q+CbprX U7Hwr4l0K78N+JI7ZreXVvD/AIz0KLxFabVfJWzaMwXCJuZUlvpgOeT+kJc+gx781+p+EsasuA8m r14KGKxUsVUrpRUUsRLF1vrCXLdNKsp2nd86tK7vc9nJFzZbh5zX7ybm5WX2+eXNto/eT1677ni3 hHTNB+D3gXxp498WJbaTe6nf+J/iJ8QdWPmT3Ege9v72zgnkUM8/2XR2tLaGJAQrApEvzAV+Y/xP 8K+LPjj8Q/CngyfwbqPi3xcvh3xD8ZvFENr4in8IeDdXOvvE+iaLFqN5pTXL3Vr4UsfCmkow+zeT NPIkiwu8kh+tP2r/AImadrfivwF+zTYWy6rqfjbXvCev+NLNIpbgR+DdP106pNpZSBgVvbtNGkYK T80ShSMTZHwza/EbUfCnivxXqknii4+KOoeL5/H1l4yuLnXjH4V+HOm+ItC1QWfgqHTNLv3uNI8T yXFvZ2VzqcDS6ZAsUVtaSTyIXH5P4l5nk1arg+E3K/DeUVaOHxUqapS5sQpwxGKhOE6dWM6yTwrl Vhh6rp+3xjrqMHK/j5tVoOdPBX/2WjKMZtKOsrqU0007te421F25qjlZHz54Q8P6VBoXxOn+KnhG 6h0/4c6honiLTNNtkdme/m8QPoVz4Ai11Lp3j0e7a6tQ8gnlMMWkSSxN5zAt+hv7Efw31TX30H9o rxN4q1S01DVI/EVv4P8ABmka/faRpV3d6RdT6Sq2+hpdJCmg6fodl5UMBLxt9qMsx/coX+Lfhj8C W8Y6L8VtS+HniCK7+H2pfDOZpIPE7XGna/oviIyab4r8OaVNbLbeV4juS+j33kXVh5nmxRSNLFDJ mOv1d034L3Vxp3w6j8ReKGs/hN4I+H+jtZeDNBVNBn13xTdL5zT6lqpkj3wy2U9raxWiuvmXLtIW VmXP5/4UcJY6WYYDOquSfW8JlVJToJVaahXxX1+r7PGVcVFRVShgsLCUabjSqclS8FRb5qT8zJMF UdSniJ0OeFGKcfejaU/au03NWvGnBNK0XZ6KO8Tovil8fPBXw18Ia58U9Y046lpWt6xoGneFvEL6 xHrc3jTVbkv5kkNpHP5lv4d0iSymaSEyBZlsZTBGcxu/4ueOfBWmfEfxJ4y8V/D2407Ttdm1LVNa 1z4U6hqclx4h+1TMNRu774fXRgI8a6DcfaTc20Y8u+iikKNDKiCU/anizwh8Y/2jfh14x8LeKvh9 pfgvwzo2qN4j/Z71C317wpDpfhpNFtodHtvhz4kaw1R40OpaNOhtLhiT9sLbj5RUj5OsPhv4M0gP Y/EfX9d1j4g/Ce2lk8SeBfhtbw2mupo9vPFHY6ZJ4r1mWCOfVtM1G5X7RLpsF7JDYzgoXS0Lji8W cbnPF+Ky2ljcqS4XlSdWhVxNL+zav1pzq/W5QnUpxq83J+/w9JQxHt8HCNSrTq4rmnTnOqmIx06S nQ/2RxbjKa9lLnu+dq6Ur296MbT5qau1Keq9P0X9kTxz8V9K+Glz4bvdKtNDg8GeC7ObVb23lsr7 VZ9f1TVdb8TzWdow3TRaFDqP2a5kfAM0EcAG58L9KftIaroPhf4Xaz+zL4L8MeJdG8B+B9C0yTWP iWAtrog8VWLx6vHoesx3EaSa1BfTPAb64tTK0N1fwjy2ijcD2/xV4q8G/srfAPwrruupqEXjK48M w+DdBvkey1vxktzrktxrt1uuNQkhh1R7Ca8kurlyI45poFB2+agH5B+KNC8R/ErULzVtD+K3/C07 a2e6n0628Y+Ijo/j1Vug1zLax+FdevSLjUZJIioi0ya6jldU2YLKo5uL8Hk3hxk1fI8gy/65xlxZ gKEMzhCvSnVo4KpQaWDhTqReIpwqvWv7CnKq6WHo89eFSrGsGNhQyuhLD4enz47G04qqlJNxpuPw JO80pfa5U3yxjeSlLmPG4C0jIqK7yOyqsaKWd2dsIiKoJZycAAcknA5rSeK4tbia0uopbe5tpZLe 4t5ozFPBNC5jlglicAxyq6sGUjIKkEAivavB3wf8VaBZaB8TfEtpFpui2q+Ltfs9HuWnj8RzTfD+ xmv5JbzSJIFex0460mmwNJIQT9rUqpDA14U11NdTTXE8jS3FxNLcTzOxZ5J5XMssjk/ednYsfdq/ kvM8jxeV4XC1cww9TCYjGvmp06keV+xdOlUhVafvctVVouGiTim+qPk6lGdKEHUi4SqapPT3bRad t7Pm09DYs42uri3tUdUa6uIbZXk3BI2nlWJXfYrEIC4JIBOBwCeK9K+KmqQ6l4816K1gt7ew0KS0 8KadDbg+Wun+FLODw/bSGRlDzySDT2ld3+djMc4GAMr4Q21hqPxQ+H+n6tZRajpmoeLdDsb+yldk SS0vb+G3nYyR/NGyRyNIGH3TEG7Vl+Lr4X/i7xVfrerqK3niTW7lb5YBbJerPqV1ItylsHYQxyBt yoCdoYDJrB0pUuGqtZTjy47HQg4/b/2ahN31+y/raty3TavPlap8ztbCt3/iVErdfci/w9/z87aX +qPAmgWM/wCx18YtX1y+1S1sk+I/hS50e3s7GO7Fzqmm2EtnENzyqLWzkuNYjjuJyMItptXfIQtf GbN6dR+XfI59/wCVfdK2N4vwV+O3wx037Rrmj/CzQvh3rF1Ppt2LO1bxTda9fX/inW5Y51Ml1p8C X/2Y2+QHGjxyjaw5+EiQ3KkEcjcpyDjGenvmvp+PcNDDYTgTDQocjwuUOjUn7z58RRzPMaeJXO26 c4U60Z04Ok7OKTfxI6swioQwEVGzjR5W+rlGrUU+rTSldK2liNmPv9c479vX/wCvXtnwZ/aS+NH7 P2oyaj8LPHGpeHormZJtQ0Vil9oGqMmADqGjXYaGdsYG/aHAGAwrxBj1zkevJI9cgdun619rfDj9 hP4x+Kf+EM8W+K9Ki8N/CbxJolp4vk8bpewahaz+HjLE02nWi6e0jQeIpLYzeXBKqbdjOx+XB5eA sq46zDPKOJ4ApY+Oc5a4T+s4B1qc8JGc1TVWriKTj9Xo80lGdWpONNJ2k7OxGApY+pXjLLlUVelZ 81PmTgm7c0pL4Y3dm20u5+nnwJ/4KAfEz42fCD4z618T9L0nwBo3w78HvexfFLwXHdDWX8RNJCiW mi6DeySRT6strKXyjiONrmPzFCsCPmm5/wCCyXxg0/VriHQfAHhTVPC9vBFaaUfFkt/L4luFt1Mf 9oate6TcQxNdzAK7xpGVQkgO5ya8a8Jap4u8f+Bf2jPDfw+8DeINI+FPhD4a3mk+APDOn6feXtu0 j+MNCn1bXNR1cwAa54nu7C1muLiYuxEKrHEoiQA/nl4b8K+JvGer2+geEfD2seJdbvDtttL0TTrn Ur6Y9CVt7WNmAHckADHJFf0txd49eMmXZZwHhMh4zzDFY/GYavCrmCoL2mZ16OPxFCMaUJUuWpQp K0aMpUViq8HTq4q0nTpUfpcbxDndOll8MPj6s6k4yTqKOtWUako2ScdYrZe6pyTTnrZR+lf2nP20 /jP+1Pc6dF45v7LRfDGkZfT/AAZ4ZF3Z6CtyxLNf30c87vqV9ghVklZgirhAozn5k13wv4j8MjST 4h0XUtGGvaVba7ox1G1ktv7T0a8Li01Sz8wDz7KQxuEkHyttOOld546+BHxl+GNvYX3xG+GfjLwX p2o3UVnZ6j4h0S9sbGW4lOVgW5kj2ecUyQmdxCkgcGvq79trwRJpugfCfxhf+KBqF3b6Np/wt0zw 6kJt7fStC8C+FfDsn9oWkcoEhjudW1a/YkhV4BXJY4/GM4yvjLi6jxzxfxvicwxPE2RrAVK/15ez quniqkqSlUhiOSpGnCMIRo06MLe/G0VCLa8OtSxuNjmGNx8qk8Vh/ZuXtNJWk2veUrNJJJRUV12s fnruA6k5AyeP58cdRTQ3GSfwxz+fSm9++PTPoOnPUemfSkyc9TxyOf51+Wcr26/1/W55B9ffs9Wp 8PeEP2ifE8rJqFmnwGvbEz6ZIsiadf8AirxFo2lWdnqUskQNtd8TFo1JLKMbiCcea/Av9nb4t/tF +J08LfC3wvd6zNCYjqusz/6HoGhQOyr9p1fVZV8u2Xk7U5kcjCIxr7G/4Jw/COw/aF/4Xp8Dta1T U9C0LxV4a8Ja1qusaPbxTXyQeHfE8Ex0+KS4/dw+eJz8zZx5OVViMH+lv4PfBr4e/AnwRpngD4ba DbaHoWmxr5jKqPqGq3m0LNqesX2wPf6hIwJaR+mdqBUAUf3F4K/Rrr+M+RcE55mmZPKeBMro4yOI 9i4vGYjGf2livaYehzKSpU/YxoTniKsZWc+SlTm+aUPvci4XlnmHwFerU9jl1KM+bla55T9rK8Y3 vZcqi3J97JPdfmD+z5/wSJ8F/D270bxZ8TviN4j1/wAX6fJHdpY+Cp28N6JZXAAJh+3yRvdajFyy uCIUdSVKlSRX2Xc/sAfsgXt5d6he/A/wtdXl9M1xdTSS6snmTycyyrDb6ikcJZssQiKuWJCjpX2L TXdI0aSR1REBZ3dgqKoGSzMxwoA7mv8ARLh3wK8IeFsqp5RlvAOW1sHTfO3jMPDHVJVOXldWdTGK vLnlFWk04q2iSikl+l4bh/JsJRVGll1KUFr78VUbdrXbnzO9v6sfHet/sHfs16n4YtfC+meBk8L2 +lXc+oeH7/RLqR9R8PX1yQ1xPpU2ri6EMcjqjPEytEWjDBFf5q534nfscaP4x8O/C7UtS17WfFfx I+CL3Fz4W8aXyWcOueINLs0uLuw8O6+kKrFekXItkilUJsaIMVAd8fVd/wDFX4YaVK0Gp/EfwJp8 ysVaG98XaBazKw4KtFNqCsDnsRXS6R4h0DxBaR3+g65o+t2MzFIbzSdSstRtZXXO5I7izmdHcbTk A5GDnpXVW8OvCvNI47LsNkWW0p4ulRpVaWEVKkuXDVadbDTlh6DjT9phqtKnKhVlTdSjy8lOcYSl F3LLcpqqpThQpRc1GLULL4WpRfLFpXhJJxdrx2TSbR+Ln/BLLxbqfhHx98Y/hH8SNd1vSviPr+r3 HiH/AIV/4o0zU7bUreTSDIL7V7bUbxilwJoLtg8QCkC0WRS6k7f06+FyeJdA+KPxr8L+IfEVnqum ajruleOPA2nS6zDd6zpOg61psVtq1lLpjN51jpUWt2kgt2I8s+cwU8Yrr/Fnwg8FeKvGPhT4jT6T bWfxB8FXTTaD4rtI1t9T+xzQyW19ompTxAHUdGntZpUaGTdsLCSIqwO7yjx94Z1jRP2nvg18TtIt idH1zwv4u+GXji4WOcxxW7iHX/Cs108MbBD/AGtFcRRvJhQZNpZdwr4zg3grO/CrhXhrIsVinxBg uE86jTwmJoyrU6lbLM3qSw1SWPozlWUquExOOnXrRhJYf2dGlXp+y5ZUo8WCwNfKcJhcPKf1iGEr pQlFyTdKs+VupFuWsJVHJpPltFSVrcq+oL21ivrO7spl3Q3ltPayqSQGiuInikUkdMq5r8O/Cn/B L3Qpfii2v6trt7pulad43S7HhzQdRXVo20+Cf7dFHqMlwlvfaak8hQgmN1iRGzIysklfubXyB+0R 8KviJqOreFvir8F/EUGh/EXwC2pajq+ltpMV5b/ELw28ERudBlgLqj60YbCK3tpHYNi4Kb0U17Hj P4ecMcY4PIs74g4RlxjU4Qr+2p4WlOMK8qVWpQWIdLmlD20qcKarfVnUpxxCpOnzc7gntneXYXGQ w9fEYN414OXMoppSs3HmtdrmaS5uW65uW172Pgj/AIRHwL/0M9n/AODBv/jtFfMn/DTXxp/6Nx1X /wAJyX/5Eor+Iv8AXvgH/oEr/wDhmzPy8v6ufEf2jl/8kv8AwRW8v6/4Y/HSyuNT8dfA3UtMMs9/ efBbXE13Trc/ObLwH43mjsddWMdRZ2viyDS5iOQh16RuhOPDDN9PXjv7da9o/Z1kub3x7J4SZZ10 f4m+H/EHwy1K5Fs0lrFc+KNPf+wHlkxsSRPEtrosiBmUnZx1rxW6hjslNvO9xFqtre3NnqNlNEFS I28hjJikHIYOjo6thgwyvAr+Wsyw7xeW5Pmck/ack8LUlJ6zlhfZqm1fdRw1ahSiu1GVvhZ+dVVK dHD1nvZwk315OXlt6QlGKX90+g9ElTxF+zV4404wrPf/AA5+JfhfxXbTFAZrPQvGmmX3hvWBG/UW zatpehFxggM6twSM/pD/AMEbPBOt2fxa+IHxS1O0fTfDVj8H/Fdpo+pXkkdsusXU2p6bY3Z02F23 3trbzKqTTKvlRyssZcyHaPgT9j2/0DWPibrvwx8SaTYar4Z+LnhTVvDS6FqmpzadbXviPSpI/FHg ixk1eErLAJNf0m2tmZCrut6wHJFftD8IfDei+El/aXvrrxL4N0PXPBv7GNl4W8RWngTUbLUF0nVt DvZ7+6u9G8MWLGPwtp8V60VjFBczLc3c8RuZIxl5H/f/AAV4ep4ziDgzjiVRVo8Ne2ozp39m3icH SxWJpSnUlHl5I4V0FTV4uThLmlBQhCr9Rw7hvaYnL8xc1L6nzRa2vOmpzjdtWsoONle7s7tJJSr/ APBQf4YP8Rv2dvEGqWVsbjXfhzeQ+OdPEab5nsbNJLXxHAmBnadHuJ5iB1NgvHFfzfed7g8enX3+ tf1rfD/xNpPxX+F3hTxS0Md5pPjzwZp17e2kyq8ckWs6Wseq2E6EYysst3C6kcFGBr+Xn4+/C+++ C3xe8cfDm7WUW+hazM+izyAj7b4b1D/TtBvFY8Pu02aBXI482N16rgdP0lOGqVfE8Pcd5fH2uCzi jDD1pxWjmo+1w1Rvq6tCUorsqEV1K4uwqlPC5lQV6deKjJ20btzQk/8AFFtf9uo/T39lXWp/BP7A nxd8V6dK8Go3fjTUbaOeGJZZUlvbjwj4ejZYWUiYiK7fCkENnGOcV+XXxj1HVL74sfEm61u6N3qk vjbxKt3OZo7kFodVuoIYY5YSUMUVvHFGip8iJCEUBVAH6ofsQanpV9+xt8QrHWoIb3SNC+LOnT6x a3DTLG+mS6l4I1Gcg2o82OVVSV42QqweEfMBk1+aPjj4b+NPE3xI+K2o6D4X1C28P6f428cXUmsa vImkaFb29vrOsTpbjXtceC3uryRLWZYYUkeaaQBERmNfI8fYHF43gTwwp4HnxVGWW0EsPTU5ONSn PGe1qyhFOPvOXKpaNckk7p3XBmcKs8ryVUrzTox9xJvVOpzSsl+O+jvofsb/AMEwdZ1LXfgv4qn1 WZbubSvGFv4csbx1/wBLOj6VodlNp1hcTdZ4bYX88cG75kiYRZ2IgX9HdR1Cy0jT77VdTuI7TTtM s7m/v7qU7Y7aztIXnuZ3bsqwxuT9K/NL/glgoPwD8VzBkJm+JmqEgMpddmg6Cq+Yg5QkZIz1HI4r 3P8Abx1+88O/srfFG6sLyWwub+20LRBcQO0cph1fxFpdpdQI6kEeZaNPG2OqyMvev6k4CzWWR+DG U53VTxEsqyqvinGT1n7JVqqi3v73Ko387n2uWYn6tw9h8RL33RoSm135VKVr/h1Pl3xl4R8Q+Pfi H8X/AIzaadN8P+Ibr4SajYaBHeTS2qabe6bqXjX4dPq91dCORoZjo8ENzCkLPI08kWFA24+M/hj8 FZvC3jK++JHhnx74UvvDfwtZNav7bUdQ0b+2dfsdOhig1y01HTLu9j0/RLW7u3vLe2tNRvPtNxH8 0dvP5cjD6ltLjTpf2XdN1P4n+L/Cngnw58RPC/h3RbrRvE1lc2lx4gsfD+s6nq0s+kaLBci7Pilt cuXju9Qjtii2rLcJHNI0deVeGPCvwt+LOuaVbfBDQ5te8DeHNama/tvHfm2Phb4X2EUhfT54vBFn cwP8Qtc1GKG9uI9U1W+Yxqrpc28EVvtP4Jn+V4LM8wyDGRw0KubYpf2hGk8W6eMqYvGVJ1fbww9G M5KnGKoV6tRwSajTTVPDU61dfNYmlSrVsLNRUq8/3qi6nLUlUqScuZQim+X4ZSdv5doKUj7E/Zn8 XeBrvwxFe+Eray1nSbn4hapb6VHrulafolj8G5NN8Pz3d3Z6xrEE8MWu25XWLxrE2cIhhjvLmECJ CzL1X7V/xOsvAfwh8K6j4oEbeLvFaWPhyw8XL4auNY8LaN4i0PU7XW7zVP8AhENQnjDaebvShPay bZLgRwQPGk6ptP5t6x8YNDs/Avis/EBJbnXviV8TviLeXa+B7LQZjf8AhEW+h6S+n6R4iWcWGjaW dW062E0tpbXMl8uj+QXFu8vmeo/tOaxJ44/Yp/Zp8ZWR1GawtPEur6XfS6nqLarqMM4ttWsLVNVv /KiW+vj/AGa4klESLu4REU4r1Fx7KPBHFWU5c6U8flWUrExUVKPuVMThaM5Sw6c5UZKjWVSLeInO hzyhQdKjSpHRHM/+E/G0KVnVoUOe2q0c6cXeP2Xyyuvfbje0bRSPhLxj8SfHHji+WTxR4tv9bjsJ Zk0+O326XotspuZJzPpeiafb29vYeZMxkytvHISwL/MOPt74IavoP7QXiXwX4k+IfheD/hJvg9f2 niX4nfFJ5PsWgax8KvDWkz/Y/wDhNreOVP7Q8W/2lZWFnbTxEPcxI/2hZQjA/EXwj+GfiX4xeP8A w98PvCkDS6prl4EmuTGz22k6ZERJqWs3xUfu7O3tt7sT95tqL8zqK/RH9tjxd4O+Dngvwx+yx8K7 SysPJ0jRLz4latZQQw6nqttYCS50TS9ZvIR5lzcXF9Nc6jcJIxCCaFAAkhFfhfBtHMaeWcQ8e8QY x1+HcsqUIzo4le3/ALVzCEva4TC05VudxlSaU6+IjerRws6sYO1WR4GXqoqWKzLFT58LScbxn73t qqd4QTlezi9ZS3jByS3Z4D+1b8cYvj94k0vxpp/iCVNAtZ9X0LRPh7dI8d74Xs9Plt/J8QS7F8m4 XWIZElLA+dA9o9s4McUcj/LUbcqw+8CGVlGGVwQQVb+HBHGPzrJjbPTjPIH49fbitSyj8+4t4AQG uJ4YFZ87QZZUiBbH8OWyfbpX5PxJnWP4nzvG53mUva5jmc1KrJOVpVOWMbxUpPkT5bxhFqnTVoU4 wpxjFeViMRUxdeeIqvmq1XeT7uyWl27LyWi2SSSS/RHX/Gtn4e/Zs8D2/jE3nia+1rwvbaTp9pPd S22u3Fxq+tS+Jbyx1HxCytcJ4Ij8N6Z4NDW9qyyzO/kvJGGLj4z8U+LdQ8Y6rHqt/ZaNp32fTtO0 ix07QNLg0nSrHTdKtUtLO2gtoSWdhEoLyyvJNK7FpJGJr6Q/a5Wyg8T/AA98A+FluLzT/C3g68a3 srKJrlIQuq3+lubVIYzI9rFpvhm2BZwzJHBukc8tXyFGwGMHOBz9D2Fe/wCJ+Y4/+13kEq3tMJkl DCYRzWvtqmGoQvKdV3lUcJ1ZxinLlStJRjKTv25pVqe3+rOV4YeNOF19pwir3lu7NtLWy3sm2e6/ Aa20+b4gR6jq1teXWm+GvDHjLxXcLYzfZ3ifw/4Y1O+tZZJfJcpD9tS2QlQG3SqVOQAcb4V+Hp/F nxA8JaNBErQy61Y3WovLg2tno9hcJfapeX00uEhsYrCCYySSFUVRlyBVX4ayPE3jq7SYRJbfDTxa sincDOt+lnpQhUL94+Zfox3cARk5yBX2T8GfgD4v1H4TWviv4c6Nc6p41+L+j3ngezvtQUwaL4W0 CXV9Vj8ca5qLXlsUhs5dDs9GsrWVBI0z6lcNbgspxycJ8N43iZZDgMFl88bHLJ4zMcRCjDnr1qSq YKgqNOMYuUp1atOnQpKV4qpWcpOMLtGCw08V7CnCm6ipOdWSirylG9OPKkk3dtKMel5anWeCdT8Z Xngz43/GfwVoPiGw8UfFvWvB/gvwhf3K6Bf6brviXU/FrLrel6Ho9vYLANNhjthAGuVkUwsfN3Mz tXzDP8WLK41C40zxx8Cvhz4u8aJqFxot9qVpbar4c1K6VVk02ewTSfB93DZPrSXCjyLyC33o0YPl ysQ1fb/w6m/Zz/Z28L+N/hX8SvjtceMdV1oiy1fRPDena5JYeCb5La6tL4+HJraCX+z9f33tyJLp HSVfKUbFYNn481n43+Cvh8b7S/2bfCFz4WeeSVLn4o+M2tfEHxFvoMgIuimeA2/hC3PLBoEN2xOW lRsiv0biuCynKOFqmacZ4PAYvD0MQsyy2NTA5zXhjZ4zEYmrOOX0qdTBxxNWeIb9viMRh3RoxjRn VnWhL2np4z91Qwjq46nCcYyVWknTryVRzlOVqSTp87cn70pRcYpRbct/S9c+En7PPgXTLXxd4+i8 Q+DfEMGh6tr8PwA8ReL4dT8TeIZJYtPPha3vdV0bR0l8MWE1xLemS3utt5JbIkgKYcV+1v8AwTg+ Mtj8ef2d73QNV0Dw/o//AAgWu3PhUeGNCt5rbSdO8OTwpe6BDbJNcPKWWN7lDM0nmO8JckMTX8tW qapqWt6hd6trOo3urapfzNcX2pajcz3t7dzt96W5url2eV8ADLE4AAHAAr7a/YV/bIuv2UPHWoR6 xph1n4c+OJdNtvGFtAManphs3kW01vSxuCyXEK3M3mQtxMhIBVgpH0XgJ435BwZ4r5ZiMVl2G4W4 HzWjXwOPnChCVSr7WEVQxWNdKnGKjCvSoudLDUqWFownWnCg5yqTn1cPZ9hsDm9Kc6UcJgKsZU6j UVd8ySjOpZJaSSbjBKCTk1G92fur+2H+zzoCfs+fG/Xfh7ba7pnii48CzPPZWOv6w+najbaRf2Gq MRpUt40NvdRWljMEeBI2ZGZH37uLv/BPr9mHw/8As/8AwQ8Navc6Tbf8LK8f6TZeJPF2tzQRtqNv FqcK3enaBb3DLut7KCzlhLopUPM7s4O1cfX3hrxT4M+Lfge38QeE9Z03xR4Q8WaVMlvqFlIlza3F re25imt7iPOYZ1WUrLDIFdGyrqDVjxb4z8E/DDwxP4i8aeItG8I+GNGtUSbUtXvIbG0iht4gkcMA c5nm8tAEiiV5GxhVJ4r/AE/peHvBFLjbDeLNOOEp08DlDw+GqR9jHB0YVa9TFVswpzVqMJ1aVTke Ii0nTlUk5Pnk3+qxyzL44+GcRUIxp0HGLVlCKcnN1E9k3F25uzbvqfkD/wAFi/iPbQaF8FPhDHOG uPEHi4eMNYtgw3rpulyJpWnlh/AHub2+wT/zxJr4l/4KCaZfazo82r299aalaeAPirf+FNShtnil OgWmu+CfC114UsTcBt1zvsdJvA4AxDLbMjHJBPmX7UXxlsf2t/2zdF1HwjLc6l4QuPEfg/wN4MSd GtJLvTLfUbaGW6SGYg24ub6a7lG7B2yLkBs16Hr1kPH3xF/bl+BEmq2c3iPWNYuPGPgSxs7abUBr PiL4Z311cXWkaS5XdDfSeGnv13YDObbYMg4P+eniJxlT8TuJvGCeBmsblPE2YYTKMrr06i5Ks8my vMq+EjSbajVWOxlGl7KN3zfWrRXPOm1+bZnjY5ris7dN89HF1IUKUk9JOhSqygo9Je0nFWS359NW mflhz/kdcelHYknHQgY5x3PTpxSyKYmaOVSjozI8bAh0dG2sjKRwwYEEex9KidxtOPTt+Hr3r+KU ntbU+CP6qP8Agk98E7H4cfs22Xj+5s418T/Fy9n1+6u2QfaE8PWE81hoVirsMrD+6u5yBwxugTna K/USvnH9kGSwm/Zf+BEmmFDZt8M/C4QxgBfNXT41uun8X2pZt3+1mvdNRSz1mPU/D/26eCV7JEvz p1yba/tbXUVuIY2iuIzvs5nWKcpIuHXy9yEHBH+//hRkmX8K+F3AmTZXTgqGHyrBuKi1FVq1bDxx Fapf+atWnUqyer95vWx/RmT0KeEynL6NFLljRp26c0pRUm/WUm2/U+M/2i/2ydG+HkereCvg9d/D /wCIHxis7j+z7jw3rXjrQ/D1l4cuZIt6y6ob68iN/cJnm2hkDKy4leMjafyQ/aT8E/8ABRb45arZ 615PibWvBuqeE9G1iPQ/AHiqxHheyuJdLhfVrdbHR9T/AH0xukuSA3mllOEZhXY/tO/8EvLDRfjN 4Ik+HV/4muPAvxc8SL4fu7u/uZ9YvvA/iu7mN9Jd6vfNC82o6HdWMN/5ckhWSO4AEspUAv8Aqp4k 8H/Bj4c/By9+HHhfW9O0rVfhh4W0PQrK++0yz+INOlvbq20rSrnUEgmSS7F1qUsaPtbAaXaCm1cf zBxBkXiR4wY/xAyfxOrYrgTh/hiUFhsPlmcUaOFxko06uJhCn7TAOeLVTBv21atWq+zU1CMaOHcK sKXymJw+aZ1VzKjm0p5dhsJbljSrRUJ2TlZXp3neHvNydr292Nml/KZ4f+FOvarq89p4zm1fwfbw eJbPwpdavq2g63f2w8Q3kskaaWZ44tovcRuwRnBdVLDKgkftFr/hpf2HvgJ8L9IPxX0zw7qdh4xl 8SeJdIvtB1nV5vGviWW3iuLyCxn0v95pUGk2I0yGCaKQRm6u7gSswHln9PfBXwt8Ua1pnhDSPizo HgTxBpuh6Nomu3d7NZ3dzrafEu0uFmlvbVb5XWe0htEtYhcTvJOxjeP5kJY5n7RX7K/gj422R1XV r7xRp/iHS9Ev9M0m60HU/KtYLm+uvtcd7PoFzFJaanKLx23IyrujkZVO8IR4nB30Xsx4E4b4nzvh nELMuMMfhqVLA1sbHFYGvh4urGviozhQxnJOf7ulCk6dSjCqoVYV5So1Z0zHA8J1MBhcVXwkvaY2 pGKpuop05RXMpTuoz1eiSs4p2kpNxbRwH7LH7dfww/aOsLXT7XXLfQ/HNpFs1Lwtq7QW11e7AV+0 2J3KJULDIkiymGxLFEcY+7Li3juo4t6KwSRJgrBX6A5UEZ5IJGQefXFfmR+zp+wdoPw+1G58S/EY /D/xXqGh6lb6j4Q8XaB4Sm8MeJZY7WFvt0msrFeIkN1DeBgDHG29VDFuStff/g7x/wCFvFV3f6V4 a1aHXDozmDUbqzmF7BbXJJMcE95Eoj+0FEkLRqWZCuHwa/qHwnzXjurwxluG8VI4TDcQ4tyhRVOc Y1sTGno5VcPGdWlGraPPJ4atVo1IWqrlUrH1eT1cweEpRzdQjiZ/DZpSkl1cU2r6XfLJxa106+hV +fP7UX7f3wf/AGafGcvgvxBoni3xJ46sNJ07VrbTtKtIYNKjt9VJZd+pXNyqm4a1ViT5ThcBQQc1 9/QrMktx5s6ypJIJLdNqq8EXlojREr/rF81XYMef3mCcAV/PR/wV/wDAnhlvjN8KfEVlqkcHifxh pUfh/X7KeVVjtbCxvYYNI1dkxuS3IvLuN2+6fsfHzA14P0leNeMPD/wrzDingqrhsPm+BxeEpzli qcKtqNeo6HNRpzlySrqtOhOHOppQU5ODcdMOJ8djcuympi8DKMK1OcE3NJ+7J8uiejldxa30u7aH mv8Aw8m0/wD6EDU//A61/wAKK+Zf+GXLH/ofPDH/AIN//tNFf5nLjr6RWn/ChT/8F4Dy/u+p+X/2 jxJ/z9j/AOA0/wDLzPyi0rW9R0S+sdQ02/urGfT9RsdUtpYJpYxFe6dcx3VrdbI3GZY5YkZTjIKj GK+jP2vfB6eEfjdr19ZW8MGgePtN0D4i+HZrS3NtY3dh4u0i01S6ltY8kADVpNQDDqGHIFeMWvh7 wPr8NnHo3jX+wNZu7i7SXR/G1mbLR7SFZJnsyvjHT2lhk3WqxK5ntLUecxAITBr7K/aI8HeMPH3w 8/Yk07Q3tPHvizXPhzrXgy1uPC9x/atndSaN4ggt7C1l1BF2L9ls50juZX2xxfZnZ2CLk8OV5LXx vC/EuHVL61VwssvxWHdGcKzcnX+pzoqMJSlGVT6/Tm48qlzUoxkk7I8KjQlUweMilzyg6U4crUrv m9m42TbTftU7WveKTseE/s4Xnh7wn4l174yeKNKvddsPgxpmmeL9I0SFmgstb8Y3Ou2GleFdM1W+ QF7Oz+13E11uRSWbSwjYVjn9Y/2dfDun2nif/goBrXhVJj4Q+MP7KWpfF7wNfZMkV1Y67J/a+o2s dwq4e4stefUbWZM7onttj4yM/kH8RfFOgeD/AAg3wM8Etb6jFa6/BrXxN8cxlifGHjHSYbmxt9H0 Tp5fgrSXuLxLVmAe8uJJbxgqGJR+k/8AwSK+INz4s134v/s6axGNRTxN8FPid/wr6aV83GlXusaf AniLQIGkYY0q8MVld+XnbHc2DOuDM9fpPhNicDR4t4c4NqSg61SddqtCKf8AwpYnCYzCTw7qpOU6 c6FenQvC9P61Qg6d6c51ZexkVSnHH4TL205ScveS/wCX06dSm4827ThKMdLrnguX3W5P60/4J8/F HQviB8BNP0rS1kg1fwVqF5p/iayMbJZWep61eXutqmkKUVYtKZbh3ihTcluHMIZgleB/8FRPgc/i HwjoXxz0GyMmp+CxH4f8Z+QmZJ/Ct9cltL1OUAZcWWqztG5/hi1TcSFj48D/AOCbfxGuvhX8b/HX wE8Z2E+hX/jC5uLS2s9Qs7mHU7Pxn4TN2f7JvRK4W1ik0kajt+Tc80cY3Mrrj9x9f0LSPFGh6x4a 1+xh1LQ9f0280jV9PnUNDeafqED211A4PTMUjYI5VgGGCAa/c+FcFhvFDwchkGYSjTzDDUpYGp7v K8NjMFJfVpSj8UWoKhKolbmjOcUkpWX0eCUc64fjhatlVjF0m7WcKlN+42t1Zcjl3Ta0Pxj/AOCX 19aeKvC/x8+FGoeXLbalD4X8Rx2s7uqTKzXWm3eQhyEEltpu4ryNw718YeOPG/jb42fFDxd8NfH/ AIw8UalJf/EjW4/A9ndSJc6N4b18a1c6d5Z0q5mhFlpT6RF5BWDY0bxRTFWCSB/rX9nPwLrf7I37 e0Xwv1uSaTw14+0fX9G8I6zOGSDXtDv1Os+G7nePle/ivdI+x3CZylxuH3XUn5c+N2meOfhz+118 XvBnw505NX8T+K/Gd5baBay+HtN13VJG8YS23iGyOiR6lbTCw1FJdQUJdxBJIliL70G41+E5xSx+ G8O+E8rzGnXo1OHc3xuT5jh6d5Tq3csVh6TpxnD2nu1aqoSu0nL2kHdq/wA1iPawynA0aylF4TEV MPWgr3lf34Rtdc2kmodm7rWx+qP/AATG8L+K/CfwU8Y2vinw/q3h+W7+JOoz2EWr2c1lJeQW+j6T Y3NxbRzqDNai8tpoxKBsdom2EgE19SftN6v430b4P63qXw68JeHPGvjGDUvD66Ro/iewTVdOt5p9 Xtrca5FpU3yX97YNKlzGrfKggaZsrEQeo+CPgjUPhv8ACP4eeCNYvZtR1rw94W0uy1u+nnNzLcay 0AuNTZrhuZ1S8mljRySWSBTk15N+1r8Qbv4W+FPhr45tIby5Gh/GbwfNqNrZxNN9q0GbTPElv4lj uFVgRAnh+XU5s8gPaoWG0Ej+osLldPhPwtw+W4jE1sHDAZcoVaq5Pb4d1VepOPuuEqmHdWXKrWk4 JJ3dz7SnTjgsljh5zlT9nSSctOaPMtXtbmjd201sj8yf22fC3xH1bx38G/gzDZTeNfGmneG7XxD4 h8dnTEga/wDF3jXU2GqqdTKLBofhK2vLMypCWit7dLjeQqIu2hb/ABG8B/D22WC51zRPEGmwX+s/ C3RP7AS4n8K20msaDqF14+8fa7cRpbt4+1mfxLc6DJMyMltp9lqgsbW4njEqj6N/bS8cXdldaf8A BDwwl14f1v40eEde1aXxHHf3F3camlj4i1fU/Dvhf+1FjM13BrKm/tjbIEW3Or21sC8Jevyn+Llx ceHj4c+EnkWsdt8MdPli1CX+zIrPU7jxj4ngsdZ8YC+ueZJ4ra++zWECM2xI9HDqoMrV/LHiDiaf C/EvEeY5dVljMTKeHpOdVSnClVjCn7HAe9KNSq4YRfWK2IqpqdaFJKCrw9qfGZpUWCxmMrUX7Sbc Itu7SklHlpatOVqfvynLeSjpzLmK/wARYhaN4ORoWtbi98G2Wv3ttC3laXb3fiHVNX1NhomnRAQ6 TpJtJbMxQwKEx8/LOa/Vz4v+BZdC/wCCcvwm8KWti97rt7f/AA81O105I2e/n1rxhqF/fC3s7Xbv lunOrNGigAnaeeMV+cXiXwTLq/xO0Hww1xBc3NjovwctjapcxzyX2haj4e8KwXgtdrFWubf7cHkt wPMETzOVxE2P6UPFOjeAtEg07xh4xn0zS9F+Hy2uq219rFxFa6Rocml2N7pOn37+cRFA0EGq3ixE glZblGX5kSu/wm4Medw8UvbVIYGFfBwyp1allGjTqymsRXf2b0lhIzcW4qTd3OMXzHRkeB+sLOOZ qmpU1Q5n9mMm1KV9rx5E7Oyb6pan55eH4PB//BPL4AW3iHXNO0/Vf2g/iNZMiWEkouJFvNomj0wy Lhrbw1pkc1s16UI+1XeUVjuQr+f/AIi+PPhf4ytf3nx38ERzeL7gxzQfFD4Y22n6B4rna3UQW2m+ ItEv5Tp2t6atqQiyKtvdRi3jHmuM4439pj4veJvjN8YPFHiXxE0UMGnX1z4f8OaVZ3kF/p+j6Bpd 1NHZwWl3au0V40pLTyzoSs0lyWUlAgHhsR4xzyAfX69T9K/J+OvEKdXFw4Z4ZpQwvAvD8XhcNg61 GE4YlwvGpjcVCcbyxVefPUVVclWkpe5KE5VJS8nMMzcprCYSKp5dhVyQpyimpW3qTTXxyd3zaSjf Szbv614i8K+CoLCXWfBPxEs9dsIkt/N0PxJpd14Z8aQzS7Vkij02M3NnqVvGzD99b3pyoYtGmMGX 4Y+HrnxF8QfAejeU8cWseJ9IjWaWCRoGtINRik1CdeP38cdvFOX25C7CDivMIznv6enTJyPpmvsn 9liwuNeutfEesaSb/wADaP4o8UaFpetNPCunT6j4T1jRm8QafeQl2lgTWJ9DhubCK3me4kvLecDM DGvz3I8HhuIOJ8qwsMFDBQrVablSpSm4SVOaqVbe2qynG9GM7JTm+ZJJJP3eDDQjicXRgoKmpSjd K7TSacvibesU+r12NX9oX4z399q1n4d8E2MPg7w9e+F9K1O7uLAt/wAJPrtr4ttJfEM2ma94gIEt 3pSHWrgRW8Qhh23DLIsnGPnTwd4R8TePPEGneFfB2iX3iDX9Ucx2WmafF5k0m0ZklkZmCW9uiZLy yMqIOWYV9uaz8LdN+Pfxa8X+CPAvgzxfqXiHS7Hwv4eXxbrWp2+ieAfhZpGjadpltJnTLS1efWJ/ JjuY44Jp42mmndkgRV3D2nxd8R/hh+wnpY8DfCLwjJ4t+KOtW4fXvHPi60u4rGWCAGKWS2vI44xf W63ySL9hsnWCIxt9omeUYP6BmnBGIz7N824q4y4khlnA2W4idGeLp06jlJwqyprA5bhVTVOdaSpp t0ebD0VNTrTdWM6S9epgJYmtWxmNxSpZfSm4uaTu2m17OlC1nLTdXjG95PmvEu/D79l74cfs+6Rf /EL4268mr6/N4B1e5m+DcF3pt1PeReVF/a1ikltL5mvoZUtlQokcMbTlZGk2ZPj37Vf7S3xEHxBH gjwH4j1X4e+FPCWj6HZt4e8KXsWkpDrU9jFqF9bXz6UQUuLI3cVk9vv2RS6fL8gZjR8GLjxt4x+M vjr9oD4xWcU/h3RPhxqHijxJqcFhNe6DZLq2i2UvhPQ7KwtHcC6WN7G5+wK+8Im+42+YzH41+IXi fTPF/i3Vda0bSn0nTpnSK3huWWTUr1ogwudY1udc+frl7dtPc3RBKrLcmNCURTV8acVYfJ+AsPl3 BeEnwVlua46pClTU6kc0xuFwbnGeMxddJSVLE15xXsIShQo1sPVpUoVP3koXjcZGjl8aeBg8BSrV HZXaq1IQunUnLR2lJr3U1FOLUU9Weu23xm0vx9AuhfHjSn8QW5UNa/EjwxpekWfxP0m4gheO3N7e FIofGOmtnE1vfYmOBJHcqy85d98GV160bVPg34oT4q2sMLXGpeHbPSbvRviDoVuoy9xqPhO4eQ39 ihKq9zYTXMasw3hFINfsn8FP+CWvwA+Jnwf+FXj6/wDEXji31XxL4Ii1fxGlpqduLK41jVNOLW0l rDNY7rGKzvWy0ZLiYRbWZc7q/HrRND8Z/Bn4lWOr/Yda0vw3eeKfEPgHT/GNzaXul6dq1hJdS+H9 WutI1gPEFuoba4S4EkMymJ40beAK5eMvC/jbhLAcJZl4nZXQzPKuL4RnQzPCYiVbMKNBwwdT2mIq R/dzjD63Tp3xtKtJvnpU61NeykzG5TjsHTwVXNaUatHGpONWEnKrGNoO8ns0udL94pPdKSXKzwW1 sb3Ur610yytLi81G+uobG0sbeIvdXN5cTLBBbRRAZadp3VQvXJwa6Pxn4a0/w5q1j4e07UG1rWra wto/Exs3t7vTrbxJO7vcaNpE1oCbsWsbwW87ksGvIZxEWiCE/S2qWmhWXha2+IGiW2qyftCWU3ih NQsrmCwgstV0nStV1HTbv4u6Tonllp9cSOOaN4o9yLPbzX6xOLZ3Pl3ws8DX2h6p4d+KnjtJfC3g LQ2fxbp95qGo2mh6p42u9ELXum6F4NivFeXUb261G3hj85YXgjQSM7hgoP5o+D54erhcsgvrlfMv ZV54uKbo4LAy9nJzrWfLSrxc/wDaoym40VCEIzn7a68l4NxcKS991bSc18NOm7O8uikr++m7Rskm +Y9d/ZE+MnxQ+AHiqXxxFr3irRPhxoN9YQ+IfDM93cWOg+JtX1m9h0qz0qXTr0hLuWGO5uL6cQIZ o4tNy5VWBri/2wfiN8XvG3xs8Z6H8R/Guv8Ai1NG8RXlt4bsbiVo9Kh0e8lFzoj6To9sFghSWxuL VlKJubzOpzX9Vdv4G+D37Q/wv8H634u+HfhDxNofi3w3pfiS3t9R0ixuHtZde021ubmS2vYYUkt7 vLBWliZHJiHPAA+MvEX7BXhSX9sz4QfEu3003vw60LwVcT6jpmoP9tT/AISfwOlpY+Ere7kuCTdQ /YriwYeZkuNHO/OST/c3Ev0XfEPDcBZBwhw5x5PiThLHZll1SNpYmjGnSx0pUsRiauD9pUpSwtGN TB1acI1Z+z9lXqqMZV5s+9xXCeZwy7DYPDZg8VgqlWk18cbKp7spOF3HkScGld2tJ6OTPkb9g3/g mn40ttV8KfG/4y3MHhi0t3s9b8N+A5dOhvtfuAksN5p+parLcHboDkxxvGqBrkL94R7q9M/bUm/Z d/Zs/aO+DPjPUvBHizR/GuoT3njB/EngHUdOt1u7648TMmqXPjCw1KNpNbFxFLdxllmjZIpCFBCh a/bYYJ5/z2A4r+ev/gr/APAn4veJ/G2k/GjRtNXXPAPhfwrBolzaaSs9xrXhy0srp7q/8Q61aiPF to8uoXyxRygnJhywAwa/V/ErwuyTwT8C8TS8POGFnub5VjsBjKmKxWHjjsR7SjVp1K+PkpprDqEK SivqqpqipKS2lUPYzbKaGQ8PSjlmE+s16M6c3OcVUleMk5VGn8KSj9i3Lf1Z4JrH7C+meIPGfxo0 zwZqE/xV+I1lZ694z0L4b+Eb2DTLLwj4b1yc3vhbVvFviTUB5V/qctpe2zwaRZ5mkY7ZpYwGU/mJ 458FeLvhx4n1XwX450HUPDXijRJlg1bRNTiEV7YyyRJNGsqKxAzFNGykEghgQSK/oj/Yd8WfE4+I /C3jnwp4A8L6l4J+Knwg8GX/AMSfiJ4l1D+w7nQdf8Bz3fgu6srDVEt3GoSzRadbXBsH2ZYmYyoM k/C3/BRzwr8FviX8d/Clr+z/AOLLr4l/G74j+JtQ0z4gabZ3s17bW+ryPZaZ4f0q03W8dvpvk+VN EYo3k2pEHkfHX+XPEzwj4XxHhrl/H/C8P7IzirmEqay+pelHHYXGVubCzy9V4SxWZYyVPEYSdVYe rLDU4/WIU6VF0lTfyGa5NhJ5VSzHCL2Fd1WvZv3faQnK8HT5lzVZtSg3ytwS5klG1j9Hv+CQv7Qm m/ED4GT/AAb1S/jHi74TXM4srSWTE174N1W5e6sbmBW5kW2vprqCTGdokh/vV6x4Z+LHiDwl+1Tq +t618Ovi5YeA/jbo1jo0mvX+jS3HhTwxr/gK81PSbW+vCB5mh6fcafEZC75jeO/jmA618bf8E+PA Nj+xTL8TfE37SPhq8+GerfadH8Ian458QLDc+G9Pl1K5gu9GsNK1exWRBY3lvL5k825ow9ogJjAO f1P+LGt/EH4k/D2w139l74yfDHQru2v4tR1HxL4gtbTxX4T1DQI4pPtVq93aPItg+/YTIOihgxXg j+y/CutxHjPCLgGhneY1cLxp4fr239l0KNGeZVMNhalXBUKWIwmNrYSUW8D9YwtR+0pRd5SVR1aT g/uMoliqmTZdHEVXDHZb73sYxi6rjBuEVKFSUGn7Pmg9Und63VjR8H+PPGF74+8UfD/xpocjWOnG HxF4Q+IFu9nb6NrkN/qF3caVotraxZJmttNS0V7hmIneRkIViFr5u+BfwVi+GHxL+L3iDxzf3XxH 8T+O/HGgafpEU1zL4ivWtTF/b8l7r1pc2yxaNollq0weGRYxHF9hWNJZXCg/Uvw8uPEOo6F4d1LV fFfhb4hIbLThe3PhHS7Y6JJqUP206rqWhX6HElk13HaiOPPH2RiCXbaPSbG80fUdUN5BALW5lj+y w3jQLbz6vBayTExCcoGntY5VnKxk9dzbcFSf2Knw3hs9nw1muYYupicZk2IxGIwbxSpzlGli6M6T hNUqk6NeVGlVUKVacqk9udzlOSn7kcLDEfVa1WTnUoSlOHPZtKaas7Nxk4ppKTbfe97PoZJ3t7dp 5Y5pmVFzDawmWR3AIbyo+pJPYngLzjmua8aaJp3iLw5e2mratqehaW0KXN3eabqD6PeQRwyJO5e9 DAwDam09NucgggEOt77w5Hqeo3kNz5+pxmG01AKZZrm15C29pNZRfNag4BTfGN+7cjNkmptT8UeG 7Z7OxvdQsXudRvv7OtNLlkQ39/dCIzSW9rpzDzLyRIss6qhChWzgjFfeYirhMRhMRSxVWl7CopU+ WU04y5m4Lmd4t8zaTgtneKlfU9GUqcoSU5RcXpq7rsr7b9vxPKLTx6s3jK5gsNUtvEPh6zGkaMdC 0nTprfxB4RuL3T57m41bV9TvL9Ydc0CSyijcG3jlkR23qzKrmux8K6HpFnqt1qPhtdPsdPuGzLpm j2ul2WjX0k80s8mui3sYkkXXGLeXcNLkssa4xk1keKr3RIZ7y+vYLSxd7vT9Os57q4stMv5rpoLu 2tmsJdRCILhoJbuKGNGMrLjajM22tDwH4UuNEaW/h1fxBf2Wr3K3i2fiJbfz9LgjtRHb2dtGkCva x5b5lLvlwcgc183goYj+06dGry5h7KtVq+0g5RlQjOTjDlVSc6nJLlinCM5QXKuSMKSVNctPm9qo ytVtJu605b7btu2ysnbTRKOh6c9vG9xDckuJYUljXa5CMk3ll1kQcPzGhGehHHU18X/ti/sWeDf2 stD0t7zU5PDHjnw4kkXh7xNBBHMotbieJ7mw1OEJvurMIJ3iVXXZLJu6FgftevCv2h/C/hbxv8J/ EWm+K9a8caFoUBjv7vUPh41/H4tVtNd5Ps2nQ2FrLNdCRlKvEIysiHDEJk1v4hZBkfEfB3EOUZ/k lDiDLcTh51J4TEVvq1OrKilODeJtJ4aUJQjKOIim6TSmth5jh6GKwWJo4ihHEUpxbcJS5U+XVe9r y2aVpLZ6n89n/Duub/oqcf8A4DQf/JlFeW/bvAv/ADx+Pn/hP2v/AMh0V/kV9W8Jf+jfUP8Aw+4r y/6df1c/HFDJ9P8AhOX/AIUT8vLy/E/EsOeMY79sYwcY6dP8a+7Ph78TfFemfsO/FvQvD2qtYXnh 74q+F7WW9tQqa1pvg34gafcR63Y6ZqEf7/TNPvNZ0K0S5MbIsolMTH58H4K3H1PQD8q+kv2er2Of Sfj34RvrYXeleJ/gn4iu5VMgRrPVvCGo6V4i0HU4cxsHkhvLZ12/KSlwwDL38fhDFVcJmtelQryw 8syweNwvNFtPmrYaoqV3HWyrKm32SurWPncBUlTryjGbh7anUhdX+1B22/vWPnfzD+PfPOc9yc1+ gn/BLjxkng/9uP4IyTy+RaeJNV1XwbdlmKo8fibRr7TY42PGAbiWH8cV+eIY4HOMgf5/z6V6R8Hf FWpeCfiz8NfF+ktjU/Dnjnwvq9nligM1nrNnKqFwCVVgCCQDgN0PSseD8z/sHizhnO1d/wBk4/CY hpbtUa9Ocl81Fr5k5fX+rY/B4jf2FWnP/wABkm/wR+ifx5S/uPjx8TvijoFnBoPxt/ZS+IlvL448 N2K3kzfEPwJ4W8SwabpfxAsW3s66xbaabW21xGLJJbyRXSkAyKP3d8OeIdN8V+H9C8U6LMlzpHiP R9N1zTJ0OVksdWs4b61bIPXyZ0B9CuO1fhv/AMFDPFF98Df+Ckfizx94PVRLrNr4M8Ta9otztbTd csfF/hjTv+Ep8N6pGYyt1pd9aSTxS70PMokC71XH6b/Cfx3oHhH4eeFdB8OeE72y0C30xbzRtMuf ExvjounatLJq1voVtdSaGrzafZrfG3tt43LBbxoSdua/sXw8zDD5Vxr4kZTiMQqU8Fj69PEe5O1W rRxVWnh8XFQi4qeIw79nil7r9ph6dW05V6jh+hZRWhh8xzihOVpQqyU9H70ozahUVla8oaT2d4KW rk7dp8dPgppvxc07wxqlrJBpPxC+G/iTTvGXw68TNHltP1jTLy3u5tIv3Qb5NAv4rcQXSDJTck6A vGA3mnjf4K6Y37R3gP4q6FpcsXi7x0INJ8ba08qPB4Z8I+B9Hl1O9h0NY4cw6vrN4dE0u5ui29bG CWODZ58hr1lfjFExx/wjUg4z/wAhtf8A5TUf8Ldty4Y+GGLqpCudZjLKrkbwrHRsqCUTODztGelf omY4PhTMa9TFVJxhiqtfCV5zVKpd1MJJqFS3s7Kq6E6mHdVfvFSlFKSdOm4+xVp4GvJzdlUlKnNu z+Knez2+Lkbhzb8rVtVG3tPnRCRIS6iWRXkjjLKHeOMqJHRM5KKZIwcDjeoPUV5h8aPBOk+P/h9q +ha3Fby6ZHJa6lfrcD5X0yxmD63bI/nRiKS40F9VtRIzBY/txc524r8zP20/2svif8KvH3w78RfD V9N0KWPwp4h0m8stZtYvElnejW9UsLiW6MMsVusM8a6DbIh2swEr/MAdtfV/wU+P2s/F34C+F9V8 Y6Yy6/4y8ManYa5q/h/UYtFInup9S0iXUNKtP7KmXTpxCqvGN0ipIueRxXl/6/cMZ7m3EfBdSNSW KwVCTnz0m6FalOFJaSV5qXPW5WpU4pKLlzbHL/aeDxWKxeXcsnOnFt3Xuyi1HZ7p3lbVLa9zwX4R zeDfiX8KPhz43+KS6hqmufsu+Ndf0LUdJtNZ06Sz8SeItIigh+HcUmqSEN4g1d1Ok2lkYJxbyXkU 8kpZAxb8ePG/jaf4lfFbXvGmpWs08PiXxnc6imk6jcl5IdNv9aee20W4u7cKSqWkywPIvPBZT0r9 R/D+peCtfms/grovhHVvDHgK11rxT8IYrKDxjPqN8NS+Hn2f4k+H/iV9tuNFT/isBrNxqETts8s2 180S7VVRXyH4y8BfDbw58a/h58L/AA54UubSxuhpmp+J9e1TXZtY13XLnT9W1yWeKxc2cNv4etLg aTCsiwW7ybWwZWAIb+WfEHDYvNsm4dpYfF4apQwVWhQxdblqqpjMwjSw2HpX56EZThSwzpR9pV5I ylKvU9nGUuR/H5pCdfD4RRqQcYOMJytK9SraEI7wTajFxV5WT952TdjvNC+Et/44/a3h8PaZPJaf EIfFGz8V6jpGjKB4W8BfDvQpY9Smg1PVcF7nXF0yLS7eO3hUwxmdYpZpJZGSP9Nv247zSvEHw41v wnrVxDpnh3T4LbWbrUdQ8ZWnhPSdX8UefGug+E70Jpt1eXax2txNqr+VGkBbT7eGRy8qhfnz/gnv baH4Q8F+Kfijc6TJrfjD4j6/fpd6hLeJatpmlWN4839l2rPZzvKst/PJNNIXUuY4l2AR5b4s/as/ aT+MXjb4k/E/wNfeM9Vs/h/a+Jr7SbLwbZPbwaVFpumyGC3t7l4LZJL53ILzNI2JZCCUAVAvtPN8 l4K8Ls1zDFQqTx/iRWqzVClGNSMKMoVZYeOInUdOEvaRl7TETpJ39p7KNGMIWXXGth8vyWtWqJut m8pPliuZKLTcFNyaTve83Fa35eVJWXzyNB8B6Sw/tLxxNr7LsBtPBeg3mxvkVpEOreJls44wHJTe lvNyu4KV6781z8GLi2sEs9I+Jul3Q1KU6lcSa34Y1qI6P5cQgSztv7Is9uoiTzixd/LwFAPORxug wWbeFPGlzPaRT3kCeG4bG5kAMlh9o1hzdSQZHEkkNusROR8jsO9UtGtoLu8ht7gTeXL5uTBIkbjZ FKy4aSFx/rApPHIBHBIYfyxWxXsY0I0cBhYQx0b8rpyqNWqygk51pTnF/u7t05JOMrPW9vkea3Ko 04JVFfZv7TVryba26NbkhMIkk8nzPI8x/JM21ZRCHPlmXZ8vmlNu7acZzjiv0v8A2Fv2cvFvii61 T4l69AuifD3UfDWtaFa6xM5j1KW5i1LQb6a4s7CdF36a9lbXKC7JeH5ZAPmUV3Xwg/Zo+D/wz8MR fFHxloV/8V9WtdDXXbHRNcv7XSfDFtPHbJdBX0uDTLg30gYgK1xJJGuM+STXqfwb/aV8Q/HDTDc6 7o0Ph3TtM8Qa2NH0LwlqMmk6Vb6NpOkPDZ6Nfw/Y3OpRbtRLO+YVY26BYUAGP2rw/wDDfK8gz/I8 w42x6eZ4yM6+CyygqklJRcE6mLxSg6UKaVSyo0XUqTvdzpOOv0eV5VSw2Kw9TMKn76onKnSjd3tb Wc0uVLX4Ytt33jYwviR+2N4X8LaP8RvCP7OEFjp9r4a0s6ne/EqdbWdNY8S6pr2lafFHo8F9Azaw 62t1qKrc3AJKWKiBRFEHbw34L/Abxl8c9N1D4lftCa3rGm/DiydfEcvjfxTqFzba7c6TC0kurPpO oXySJH4dlWLZIjrEqtIk9huYOr2fhN8Nfh74N8Y3Ol3/AIcfxjBr3xk1bwtokHiK9jm03QbLwJb3 99bXd/pkNkqeJtQlN24H2gx20TRpIbaZlArP/bH+Pvjb4g+IPGvw4sJk8JfD3wCdEim8M6ayzDxP f3D25jvtWvUhgKRQCVBBbJH5CLEMqzYYdOdZjUx2Xvi/xFxf9o0crlUwuC4bwSnTy+WIarYmCrtq FKGGpYWNKpNwdXE1Vy3q+1i6S1rVnOl9ezSftY0rwp4Wmmqbk+aS5toqChyt2vN6a82h6b4w/bx8 GeErqD4efB34Y6Dq3wg061/sjUxrZvdLvPE0EcENilxpT2p8zTlW2t4xHdXKzXM7KsjrHtUV5Brv jv8AZA+JFmg1Pw78V/h74jmngul1XRNP8N+Jrhp4rOOxTSJrgXVvJrsUjxQOLi5iW5aQMZJGd2Y/ BiuSQD7jPt1xWhY6hd6dcLdWcnlTok0aS7EdkWeF4JCm9SEfy5G2sBuQ4ZSGAI/Gsy8WOJ87q1aW fUMBmuV1HanhKuBoOhhaWiVLCez9lXo04RVoRjXT5vfcvaOU34tXOcXiHJYiNOtRe0HTjywXaFuW UUlt7/ne+p/UZ+xv+0rdfEKWT4Ix+O72+k+GHhzTzruseO9B8M+CdeTSLSEWv9gR6Pa3kxurizt4 oo7y4AX7PwHctzXzN/wUB/as/Zo8YQ2vwQ174deM9Ti8Aa/a6tb6h4buNE8OW7u8Lu9loMs/mH+w ryFj593HCj/Iixruckfn3+zLb6N4LvPhn8Vm0+51zX5fF13FrdpqGpytpPiDQpJ0srnQNTsZoJUn tZkmZ5XcOzOinGBg+i/8FOvEOh+LvivoGv6R4SsPCU8GjNoN7Dp9wk0epLYwWN/a3kwisbdY5lTV HiwqHKwqSxPT+os88YuK8x+jxmMMTiMH/aGHxGDw9Whi6VXMli8oxkVPC0oSxUJ06M8PyU/bOpUr VK0qPOpxbgl9dXzvGVeGqsZzh7WMqcZRmnV56NRJwSc01Fxsr3cm2r32P1h/Zx/Z2/Zj/aL+E3hv 432HgySPxlrF5c3emeINZ1aXWNf8HXmi3L6fp+kx29vdi0jtrWGANFBJE+4T+dL5juxPxb/wU/8A 2QrCDxv4N8W/B+K61LxX4p0yW11T4dpqtk9zLZaNGsA1zwrp2oX0cjoXaFLiysozGjt5yRoGKj3D 9gv9tFl+BNl4Qb4UaBZD4d2sWkpe+H9ZOixa8sECv/aGoWJ0OfZqkpJM8wlfzXJbavSv0N+EqeC/ jrNofx+1/wADaXB4z0Yan4d8LzXlw2tS+HNIkNrLcRWM89tFGLqS5EjmdbeOUCUoGC9f3rLuEvDH xr8J+HuGMqo5fS4k4iwuX4rGYzD4CrgKsK+FlTjj8ZD/AGZU54m9SvSUZuUa/tFGU/ZRjOH0NPA5 Vn2T4bCUI01i8VGnOc403TalBxVSovds56tK9+a9m7JNfOn/AATa+Luqar8F/D/wY+Iuia34R+JH w2tpdKttO8QaddWQ17wwk80umXmnXcimK4ubdGkgngDiaMQI5Ta2R+g/inVbjQNB1PXbXSLjXLnR 7C9v4dJtHSK81BobaSRbO0llG2O4ldURS3y5kAbAr+T3/godbar8E/2vviXp/wAO/FvjHw/Hr/8A ZfinU30/xDeadv1TxLax6tqC28WleQkFkLmT93HtYqF5djXk3hf/AIKBftieEYbG20747eMbyy05 UjgtNclstcjaCN9/kzSapaSyTKckZdmYKcA4AA+LyP6VuW+GMMX4XcZ5Hj8ZjuBJ1cohmOEnha86 kMFN4ajUqUazw8HUhShBykpNVHG0oXcpy8/D8ZUspjUyjHYepUqZc3RVWDhJtU3yxcoy5E2kk21v tbqf1oWt94m+Nvwi0bVNDvvFvwS1Xxhp9tc3i3ulWUnjHw7aTswubO3S+jaG11FogvlXPlyBVkDq u7GPw/1+f9q/4h/B7432vhHXvEXxNv8A4UfGPxX4H8eNqF2z2PxN+GS2b3uoXN/LNJEjwWd1oeTH ZyKYhfuiDDYP6FfsgfGT4ufGbxx4ys/FnjKFvBlp4B+Gvi/T/DsGhWUWraff+O9DS+vrK38U28kc jaZDdpK0Ub2zuEkEfmhV+b9ALXwl4b0rw5d+FtM0XTrDQrmzv4LjTbS1itrW5XUI5Fv3uI7cJ50s /mSGZyd8hkJZsnNf0FjeGKXjTkWT5zhOIMxyXBrC5hgMRUU/YVcdUjbDwrQw9KvVwmHgsVSqV3J0 faVKbeHnSVKpJL6WphFnuHoV44mrQgoVacmnyuo/gUlGMnCK505X5bte60otn4c/szfHbxT8KP2V PD8Ph7wf4T17xbN4n/4WLd/CK8tNSh+wfBnxJ4gl8O3mtabLqF5LJdGHWrSO7kch0S3YSt8u5x8b +A/iF8EfhD/wUHvPjF8TTqL/AA51rXNY8WeCPFXhu1lj0bTNc1CWSD+0o7a2R/7a0C11AanaM0BY FoxMoZV2nxjW/jF43+Gn7RXxg+ITXtt4l13wP4kk+HOk2eoWq23h1PBc2q3eg3XhuPQ4XaO30VvD 9rJZpArERJcM6kyANX2XoVt8N/DPxa1f4L+I/h3B408F+B9H8PfHb4Qi91n7Fq/w61HxHpVp4q1D wg1/LpV0vijwobu6K/Z7qJFJjEhUlnV/4zwvEuK4lo8GZZQzGjgqvhtmeCo4NZhhpYjDLE4R4qnh qtWlh1KShmEMHjFXo0qlSGFr4bAewlTp1q9Wj8HDFzxccBSjWjCWVVYRh7WDlFShzKMnGGtqqhPm im1CUKfK0pSlH9xLiD4F/ti/DUWbG1+I3wuvtU0zUp2RdR0/T9T1HRrlbu2s7iOeGCeaJJFiaVcK rKyruYEgesH4feBIPBsvgNfCugWngX+zm02fwxb6fa2Ogf2WI9slvJZW6pH5Hlr8+R83JYk5NeJD W/Fd7ZabrfhPV9L8F6fd+ELO7t9BtPDllf2VvM97pnzzMZ4FuGS2a6ii2RQqguclW2KK/JvT/wBs v4t/tQXvx3+FOt3aeCdK0zxDbaf4c1XwhcXFlquj6ZDJfWElrNMmw6pLLc20NxJJIyjcpjWNYzgf 3HxD4icM8JVMtee5RHMuLeKsPUw1GrRwlKlSzFYahUxPsHUqVcRWw+F99qFLFTqKMq92pfvZR/Qc TmeEwLpfWKCq43FxcIuMElV5YuXLdylKMNbJTbtzerX7naPp1noph8I+EdC03QPCukeHozp66ZbW 9jpSXF080Vpp9lbWcYEMUUEbTOVXn7RHjuTzvi74bX2vH4ftonirU/C83gfxjp3iSZdNYi18RaVH 58WqeG9UhZi0lpPDIjBixIkhViOTXnvhbx5qHgj4LQ+IdQW48TzeEPAkZnW6vRZXGtTeHmgsRcz3 a2swtp7iGePzT5cgDQAgckV5h+yv+0N4x+O3jXX5Nft7HTNDi+HfhLxhpWhWa+cNNv8AxFrXiO2k jOovGkl2senWVtFllVXZWk8tC20fWVOI+G62J4c4bxsakcw4mgp4fD04yhGhSpKNZL2tJwhThSdG MYqnJylKC93kbb7ZYrCuWFwtRP2uMV4xStyxVpLVWSSsrWbba7HqH7RXif4ffAnQtS/aa8Q+F9X1 nXvBWjr4cQeHbn7NqGpaf4i1axsobW6t5ZkgvvKupVaFp1cxea4TG/j03w1qdl8QfBWk+L7TwZf6 FqeoaN9p0zT/ABnpcGka/pZ1CFZ3SWSIyvZFmcMWjcBjhuO35gftd/G7U/i/8afEf7D2q6Dp+l+E dY8FX/iP/hNbK7vJPEFp4g0TTz4n0S8isWKwSWkNzpwjkgZv3y3DN5kZVQPjT9mf9rj9oaH446Z4 c8ffEjVfHvg/4daOuhW3hfybTw1YazBe61p3ha2udTksIZXlurZJ0njdzIxMAj3AEtX43nHjrw/k HiNismeFq4rhjM8R/Zbnh8LQh7HPcNVvmmIxUqnJiakKODqYF0p0aeIjVdKcYpzSv4lbiDD4fNJ0 ORzwlaXsrxhFcuIi/wB9KblaTSg6dnFS5mnbXf8ASXxHpKWXwu+H/gz43a2/hf4iXfxG8QDwBp/i PxNe63apr8z30eh6oviPR9Ld7x7W3vRd6atyCsMjRpI7+Wor76+HmiNoXhnSbBruW/FppWm2o1Ca e4nk1KSG0jF1qUjTkZlnuNzs21dzEsPlIqvpuh6R4ltrC51XTLC6t7GeHUtKtZrcTNp07SC4CxXM rFmiEqxNtwq7olOAoCj0EAKAqgKqgBVAAAAGAAB0GK/b+EuE4ZRjK2YutHE05YfC4ehUlFrEzp0K Sg5YqopKnVm2rwlTo0FGChH2ceV83u4PBqhOVXmU04wjFte+1FJe+9m7rS0Y2VlZWKF5qEVlPZQy qcXkxhEhZVSIkBYy5YgEtK8aAZyS/AOMVz2ok2Fve2EMWqX06xz61Yj7TJG1xJDcCZ9PhvI33jaz rtQhf3eFDHBI4v4m/D/SPiQV0DXLnUbSF20+TT7/AEa9n0/UtKvLG+ttVtr62lEjRvcLe2sDAtEf lTZnBJPo1xbTLHpMj3IluYXisbmd4QPtkM+2G5LRo48p3MYcbSQrHoQMV9FKtjcRicxpzwyp4Sgo KjWU4ylJyvGvCVJwShyJU6kJOVRT52ny8nLLqvOc6icLQjZRd0276SVmtLaNPW9+lrP8Z/8AhNfE X/QBH/gf/wDdFFe2/wDCh/g1/wBATxp/4Xb/APyhor+Mv9UuNf8AoY4T/wAKJ+X/AFLPU+J+qY3/ AJ+x/wDAn/8AKj//2Q== "
+         x="0"
+         height="253" />
+    </pattern>
+    <filter
+       id="filter16257"
+       inkscape:collect="always">
+      <feGaussianBlur
+         stdDeviation="0.41431294"
+         id="feGaussianBlur16259"
+         inkscape:collect="always" />
+    </filter>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17245"
+       id="linearGradient27074"
+       gradientUnits="userSpaceOnUse"
+       x1="136.5"
+       y1="161.5"
+       x2="313.74622"
+       y2="285.25275" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4451"
+       id="linearGradient27076"
+       gradientUnits="userSpaceOnUse"
+       x1="155.34465"
+       y1="112.46042"
+       x2="136.51547"
+       y2="2.1517708" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient26774"
+       id="linearGradient27078"
+       gradientUnits="userSpaceOnUse"
+       x1="280.27875"
+       y1="261.40704"
+       x2="322.26389"
+       y2="275.19568" />
+    <filter
+       inkscape:collect="always"
+       x="-0.086395349"
+       width="1.1727907"
+       y="-0.11145"
+       height="1.2229"
+       id="filter3497">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="11.145"
+         id="feGaussianBlur3499" />
+    </filter>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3460"
+       id="radialGradient3466"
+       cx="167.48819"
+       cy="192.38739"
+       fx="167.48819"
+       fy="192.38739"
+       r="105.62836"
+       gradientTransform="matrix(0.8393229,-0.4383343,0.2966517,0.5680291,-30.16055,165.27307)"
+       gradientUnits="userSpaceOnUse" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient26774"
+       id="linearGradient2490"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+       x1="280.27875"
+       y1="261.40704"
+       x2="322.26389"
+       y2="275.19568" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient4451"
+       id="linearGradient2500"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+       x1="159.38791"
+       y1="126.94874"
+       x2="138.87404"
+       y2="12.596838" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient17245"
+       id="linearGradient2777"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+       x1="136.5"
+       y1="161.5"
+       x2="313.74622"
+       y2="285.25275" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient3757"
+       id="radialGradient3763"
+       cx="170.31175"
+       cy="209.16652"
+       fx="170.31175"
+       fy="209.16652"
+       r="104.54334"
+       gradientTransform="matrix(1.0743517,-0.8811517,0.8948667,1.0910737,-193.89727,134.77199)"
+       gradientUnits="userSpaceOnUse" />
+  </defs>
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     id="layer1"
+     inkscape:groupmode="layer">
+    <g
+       id="g2491">
+      <path
+         transform="matrix(0.9747328,0,0,0.9747328,8.1232897,4.8258519)"
+         inkscape:export-ydpi="98"
+         inkscape:export-xdpi="98"
+         sodipodi:nodetypes="cccccc"
+         id="path2486"
+         d="M 183,323 L 53,296 L 0.99999999,150 L 196,96 L 255,237 L 183,323 z "
+         style="fill:#1a1a1a;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter3497)" />
+      <path
+         id="path5140"
+         sodipodi:nodetypes="ccccc"
+         d="M 103.94986,273.26347 L 112.82679,278.13224 L 130.15293,266.67465 L 121.4575,262.08933 L 103.94986,273.26347 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5128"
+         sodipodi:nodetypes="ccccc"
+         d="M 156.01098,239.64164 L 164.60219,243.86025 L 180.14702,233.7166 L 171.54021,229.64474 L 156.01098,239.64164 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5132"
+         sodipodi:nodetypes="ccccc"
+         d="M 171.60139,229.6086 L 180.08668,233.62387 L 195.07741,223.67266 L 186.68482,219.84353 L 171.60139,229.6086 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5130"
+         sodipodi:nodetypes="ccccc"
+         d="M 186.53932,219.8472 L 195.14618,223.77328 L 207.96039,215.29699 L 199.53214,211.5709 L 186.53932,219.8472 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5134"
+         sodipodi:nodetypes="ccccc"
+         d="M 199.53942,211.55443 L 208.00833,215.29699 L 219.61158,207.48873 L 211.43624,203.97858 L 199.53942,211.55443 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5136"
+         sodipodi:nodetypes="ccccc"
+         d="M 211.48298,203.99346 L 219.48498,207.43045 L 231.12889,200.02493 L 223.45622,196.26063 L 211.48298,203.99346 z "
+         style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2172"
+         sodipodi:nodetypes="ccccc"
+         d="M 68.307573,278.59889 L 233.3461,375.01483 L 393.09703,219.55258 L 250.10479,166.11978 L 68.307573,278.59889 z "
+         style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="cccccc"
+         id="path16273"
+         d="M 277.69986,176.50516 C 177.30035,235.42715 239.60255,221.94517 101.71421,297.5792 L 189.47945,349.38523 C 273.26264,323.79193 366.41421,274.97725 355.07543,205.22753 L 295.56691,183.1214 L 277.69986,176.50516 z "
+         style="fill:url(#linearGradient2777);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path2174"
+         sodipodi:nodetypes="ccccc"
+         d="M 68.171626,278.57262 L 68.303363,291.78988 L 232.57786,391.18452 C 235.83055,389.43142 237.23238,379.0975 233.63174,375.00319 L 68.171626,278.57262 z "
+         style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path2176"
+         sodipodi:nodetypes="ccccc"
+         d="M 393.07972,219.62716 C 394.063,222.66073 395.10956,229.26471 392.24335,231.97585 L 232.84133,390.92104 C 237.0608,386.09241 235.76133,380.47536 233.63174,374.73973 L 393.07972,219.62716 z "
+         style="fill:#000000;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 34.974214,77.099241 C 34.974214,77.099241 63.429139,279.82741 65.53691,277.66987 C 67.644683,275.51232 249.44002,164.63325 249.44002,164.63325 L 249.96698,8.789858 L 34.974214,77.099241 z "
+         sodipodi:nodetypes="csccc"
+         id="path2178"
+         style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         inkscape:export-ydpi="98"
+         inkscape:export-xdpi="98"
+         d="M 243.62988,7.3609944 C 248.64901,5.9522606 248.53121,7.8674281 249.93941,8.795189 L 34.974214,76.572297 C 33.041161,74.848362 32.232239,75.552395 27.898382,75.685479 L 243.62988,7.3609944 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2182"
+         style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path3155"
+         sodipodi:nodetypes="ccccc"
+         d="M 41.127572,89.515883 L 247.04644,21.857141 L 245.56959,155.03645 L 66.892268,262.94003 L 41.127572,89.515883 z "
+         style="fill:url(#radialGradient3763);fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1" />
+      <path
+         id="path4130"
+         sodipodi:nodetypes="ccccc"
+         d="M 270.30146,173.89938 L 92.059548,291.88393 L 83.064049,286.84829 L 262.53913,170.82327 L 270.30146,173.89938 z "
+         style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path5126"
+         sodipodi:nodetypes="ccc"
+         d="M 113.88343,277.34844 L 133.41939,264.57042 L 113.88343,277.34844 z "
+         style="opacity:0.3;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 260.55944,282.16245 C 260.55944,282.16245 299.34305,249.72696 299.34305,249.72696 C 301.56567,247.86815 306.24993,247.63883 309.88553,249.20565 C 309.88553,249.20565 329.40032,257.90584 329.40032,257.90584 C 333.27561,259.57597 334.75677,262.55505 332.67995,264.59388 C 332.67995,264.59388 296.81833,299.27831 296.81833,299.27831 C 294.33747,301.71377 288.97351,302.06916 284.843,300.06406 C 284.843,300.06406 262.71041,289.52115 262.71041,289.52115 C 258.86144,287.6527 257.92393,284.36659 260.55944,282.16245 C 260.55944,282.16245 260.55944,282.16245 260.55944,282.16245"
+         sodipodi:nodetypes="cccccccccc"
+         id="rect2179"
+         style="opacity:0.88999999;fill:#999999;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.97445869;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 156.31199,239.61556 C 158.84932,237.6711 165.25317,241.39833 164.88374,243.74344 L 156.31199,239.61556 z "
+         sodipodi:nodetypes="ccc"
+         id="path5322"
+         style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 112.5495,278.27384 L 130.07045,266.61299 C 128.6036,263.60624 125.86497,261.45071 121.46704,261.8451 L 104.04747,273.28768 C 107.08494,271.21271 112.98604,275.81472 112.5495,278.27384 z "
+         sodipodi:nodetypes="ccccc"
+         id="path6299"
+         style="fill:#808080;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 104.03121,273.29774 C 107.49791,271.05672 113.43311,276.6129 112.52259,278.36122 L 104.03121,273.29774 z "
+         sodipodi:nodetypes="ccc"
+         id="path4349"
+         style="opacity:0.68999999;fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path8239"
+         sodipodi:nodetypes="ccccc"
+         d="M 164.84019,243.7543 L 179.63884,233.97378 C 178.89388,231.15559 176.668,229.33065 171.42447,229.78669 L 156.27423,239.63114 C 158.95009,237.51097 165.57347,241.76179 164.84019,243.7543 z "
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 179.54294,233.98976 L 194.50141,224.24118 C 193.46879,221.32711 190.97123,219.43825 185.96742,220.29382 L 170.84915,230.10631 C 173.88125,228.78365 179.05461,230.45474 179.54294,233.98976 z "
+         sodipodi:nodetypes="ccccc"
+         id="path8241"
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path8243"
+         sodipodi:nodetypes="ccccc"
+         d="M 194.43749,224.22521 L 207.70194,215.40355 C 205.96614,212.71322 203.80418,211.59145 199.45559,211.72786 L 185.90349,220.3098 C 189.09541,219.56246 193.62952,220.65822 194.43749,224.22521 z "
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         d="M 207.74987,215.38757 L 219.33629,207.66862 C 217.34479,205.17006 215.05499,204.11222 211.28174,204.12078 L 199.40765,211.74385 C 202.7434,211.44397 205.80725,212.3959 207.74987,215.38757 z "
+         sodipodi:nodetypes="ccccc"
+         id="path8245"
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path8247"
+         sodipodi:nodetypes="ccccc"
+         d="M 219.30433,207.70059 L 231.03457,199.96565 C 229.55448,196.50822 226.62543,195.89785 223.29965,196.19408 L 211.04202,204.2646 C 214.01019,203.93277 216.67451,204.42126 219.30433,207.70059 z "
+         style="fill:#4d4d4d;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="rect2202"
+         sodipodi:nodetypes="cccccccccc"
+         d="M 221.68659,286.68216 C 221.68659,286.68216 271.7827,246.38199 271.7827,246.38199 C 272.38533,245.90267 273.62888,245.89275 274.57389,246.35926 C 274.57389,246.35926 282.22513,250.1218 282.22513,250.1218 C 283.18438,250.59535 283.48058,251.37156 282.88624,251.86264 C 282.88624,251.86264 233.55382,293.3224 233.55382,293.3224 C 232.81433,293.93337 231.39356,293.96431 230.37209,293.3911 C 230.37209,293.3911 222.1464,288.7742 222.1464,288.7742 C 221.14187,288.21048 220.9385,287.27719 221.68659,286.68216 C 221.68659,286.68216 221.68659,286.68216 221.68659,286.68216"
+         style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path2449"
+         d="M 219.03426,288.29491 L 230.39646,294.74997 L 222.03124,301.79784 L 210.47874,295.2849 L 219.03426,288.29491 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2451"
+         d="M 209.4152,296.07932 L 221.07617,302.55531 L 212.38161,309.86664 L 201.01939,302.85172 L 209.4152,296.07932 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2453"
+         d="M 200.12349,303.5516 L 211.48907,310.55261 L 202.5404,317.99996 L 190.60096,311.18361 L 200.12349,303.5516 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2455"
+         d="M 189.70952,311.95745 L 201.6931,318.78476 L 190.71106,328.04619 L 178.53214,321.0788 L 189.70952,311.95745 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2457"
+         sodipodi:nodetypes="ccccc"
+         d="M 273.68393,244.42789 L 284.19406,249.63047 L 292.16408,243.02723 L 281.60172,238.10854 L 273.68393,244.42789 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2459"
+         sodipodi:nodetypes="ccccc"
+         d="M 282.58552,237.35878 L 290.13787,231.26634 L 300.94177,235.89096 L 293.0886,242.26102 L 282.58552,237.35878 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2461"
+         sodipodi:nodetypes="ccccc"
+         d="M 291.16226,230.48888 L 301.87328,235.11565 L 309.36795,228.87453 L 299.02555,224.08989 L 291.16226,230.48888 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2463"
+         sodipodi:nodetypes="ccccc"
+         d="M 289.93538,230.01549 L 280.93975,225.55672 L 289.03737,219.25932 L 297.95995,223.58772 L 289.93538,230.01549 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2465"
+         d="M 177.42321,320.41889 L 166.48786,314.26225 L 188.72682,297.2375 L 199.03429,302.90648 L 177.42321,320.41889 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2467"
+         sodipodi:nodetypes="ccccc"
+         d="M 290.08046,218.43641 L 298.85735,222.73969 L 319.87746,205.75759 L 310.78391,202.19572 L 290.08046,218.43641 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2469"
+         sodipodi:nodetypes="ccccccc"
+         d="M 298.08535,210.7646 L 309.62549,201.69751 L 288.03973,193.31937 L 273.6384,203.95987 L 283.46625,207.8962 L 286.18927,205.81393 L 298.08535,210.7646 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2471"
+         sodipodi:nodetypes="ccccc"
+         d="M 286.62375,192.75442 L 266.91147,207.38925 L 257.59069,203.41102 L 277.74109,189.32694 L 286.62375,192.75442 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2473"
+         d="M 165.36066,313.63832 L 184.46965,298.98505 L 172.33203,292.59392 L 153.19109,306.83736 L 165.36066,313.63832 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2475"
+         d="M 151.95557,306.23713 L 167.0892,294.93513 L 156.09726,289.01503 L 140.88977,299.91382 L 151.95557,306.23713 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2477"
+         d="M 139.74507,299.3323 L 150.85746,291.335 L 140.35153,285.53863 L 129.36288,293.43149 L 139.74507,299.3323 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2497"
+         d="M 141.44916,284.84419 L 150.48582,278.42628 L 160.93153,283.91261 L 151.79607,290.59399 L 141.44916,284.84419 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2499"
+         d="M 200.06639,302.0818 L 208.31119,295.49502 L 197.64102,290.05561 L 189.67659,296.43646 L 200.06639,302.0818 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2501"
+         d="M 185.44374,298.19559 L 193.86354,291.59284 L 181.96116,285.25056 L 173.47309,291.62812 L 185.44374,298.19559 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2503"
+         d="M 168.25222,294.17765 L 176.70261,287.76919 L 165.64522,281.91859 L 157.20372,288.20299 L 168.25222,294.17765 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 151.55607,277.71812 L 160.35985,271.62622 L 170.55671,277.01143 L 161.94955,283.18847 L 151.55607,277.71812 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2505"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2507"
+         d="M 177.75365,287.00502 L 186.23698,280.4648 L 175.54186,274.93789 L 166.63928,281.22229 L 177.75365,287.00502 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2509"
+         d="M 161.45339,270.89479 L 170.04758,264.82618 L 180.19785,270.07166 L 171.68385,276.24869 L 161.45339,270.89479 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2511"
+         d="M 194.72496,290.89785 L 203.34236,284.16335 L 191.50585,278.08454 L 183.01779,284.4621 L 194.72496,290.89785 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2513"
+         d="M 209.44739,294.73981 L 218.00506,287.74135 L 207.50073,282.4405 L 198.6953,289.16034 L 209.44739,294.73981 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2515"
+         d="M 219.03117,287.00034 L 227.95111,279.8372 L 217.57851,274.66809 L 208.54255,281.61848 L 219.03117,287.00034 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2517"
+         d="M 229.01016,279.06327 L 236.63632,273.08946 L 226.18986,268.0248 L 218.58741,273.879 L 229.01016,279.06327 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2519"
+         d="M 237.61157,272.25159 L 246.12267,265.48601 L 235.81592,260.50649 L 227.24338,267.18776 L 237.61157,272.25159 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 246.92622,264.70405 L 255.31922,258.03494 L 245.2101,253.19516 L 236.79987,259.71739 L 246.92622,264.70405 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2521"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2523"
+         sodipodi:nodetypes="ccccc"
+         d="M 256.24944,257.30562 L 264.00138,251.14415 L 253.91623,246.49536 L 246.24255,252.47136 L 256.24944,257.30562 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 264.94343,250.38346 L 271.79281,244.57197 L 262.14241,240.07284 L 254.8806,245.75341 L 264.94343,250.38346 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2525"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2527"
+         sodipodi:nodetypes="ccccc"
+         d="M 272.6829,243.79667 L 280.45445,237.62292 L 270.90284,233.32138 L 263.17995,239.29836 L 272.6829,243.79667 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 281.41039,236.84761 L 288.91847,230.83852 L 279.86087,226.40525 L 272.03919,232.4481 L 281.41039,236.84761 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2529"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2531"
+         d="M 171.18769,264.04817 L 180.52708,257.51381 L 190.25819,262.57298 L 181.32501,269.3555 L 171.18769,264.04817 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2533"
+         sodipodi:nodetypes="ccccc"
+         d="M 181.56334,256.75316 L 190.15752,250.68454 L 199.60916,255.83687 L 191.23489,261.73446 L 181.56334,256.75316 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 191.13838,249.97224 L 200.12848,243.67074 L 209.58148,248.76519 L 200.57843,255.12855 L 191.13838,249.97224 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2535"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2537"
+         sodipodi:nodetypes="ccccc"
+         d="M 201.14148,242.94942 L 208.96784,237.50889 L 218.76812,242.01382 L 210.62107,248.07043 L 201.14148,242.94942 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 209.93861,236.70829 L 218.48624,230.77939 L 228.17074,235.18651 L 219.79647,241.31698 L 209.93861,236.70829 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2539"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2541"
+         sodipodi:nodetypes="ccccc"
+         d="M 219.48896,230.08656 L 227.94108,224.18895 L 237.62559,228.38647 L 229.19014,234.46609 L 219.48896,230.08656 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 229.04981,223.34939 L 237.64399,217.28078 L 247.18879,221.5016 L 238.67479,227.67863 L 229.04981,223.34939 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2543"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2545"
+         sodipodi:nodetypes="ccccc"
+         d="M 238.75966,216.51376 L 246.46891,211.02733 L 256.03698,215.155 L 248.29149,220.74984 L 238.75966,216.51376 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 247.52515,210.2999 L 256.52687,204.13813 L 265.95523,208.14936 L 257.24328,214.48941 L 247.52515,210.2999 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2547"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2242"
+         d="M 192.42707,277.38276 L 201.04243,270.93376 L 212.81727,276.9437 L 204.25518,283.46405 L 192.42707,277.38276 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2244"
+         d="M 202.01582,270.12726 L 210.21341,264.10219 L 221.92044,269.93134 L 213.74806,276.20857 L 202.01582,270.12726 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2246"
+         d="M 211.22102,263.35122 L 219.52192,257.1363 L 231.06172,262.8037 L 222.85738,269.20877 L 211.22102,263.35122 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2248"
+         sodipodi:nodetypes="ccccc"
+         d="M 220.48694,256.41147 L 231.99345,262.05286 L 239.51447,256.25393 L 228.07804,250.72214 C 228.07804,250.72214 220.51891,256.44343 220.48694,256.41147 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 228.98488,249.90241 L 240.4914,255.5438 L 248.01241,249.74486 L 236.57599,244.21308 C 236.57599,244.21308 229.01684,249.93436 228.98488,249.90241 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2250"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2252"
+         sodipodi:nodetypes="ccccc"
+         d="M 237.56868,243.44725 L 248.97931,248.99274 L 257.07404,242.57877 L 246.02277,237.37437 L 237.56868,243.44725 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 247.06561,236.56786 L 258.0749,241.84765 L 265.97947,235.69713 L 254.71266,230.82259 L 247.06561,236.56786 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2254"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2256"
+         sodipodi:nodetypes="ccccc"
+         d="M 255.89713,229.9869 L 266.95163,234.90506 L 274.38224,229.19653 L 263.30742,224.43317 L 255.89713,229.9869 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 264.32207,223.68092 L 275.3101,228.44997 L 282.89504,222.58711 L 271.91318,217.99159 L 264.32207,223.68092 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2258"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2260"
+         sodipodi:nodetypes="ccccc"
+         d="M 272.9276,217.22814 L 283.86907,221.82988 L 290.30525,216.88979 L 279.45645,212.28463 L 272.9276,217.22814 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 280.51495,211.48086 L 291.20783,216.17301 L 297.14678,211.54933 L 286.38838,207.05719 L 280.51495,211.48086 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2262"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2264"
+         d="M 187.21456,279.65363 L 195.69788,273.11343 L 185.00277,267.58651 L 176.10019,273.87091 L 187.21456,279.65363 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2266"
+         d="M 196.93117,272.23833 L 205.68571,265.78852 L 194.71938,260.1712 L 185.8168,266.4556 L 196.93117,272.23833 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2268"
+         sodipodi:nodetypes="ccccc"
+         d="M 206.76011,265.04128 L 215.60505,258.63667 L 204.90993,253.42617 L 196.23337,259.52977 L 206.76011,265.04128 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 216.55615,257.79127 L 225.72667,251.12321 L 215.30323,245.97983 L 205.95318,252.6478 L 216.55615,257.79127 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2270"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2272"
+         sodipodi:nodetypes="ccccc"
+         d="M 226.62217,250.28446 L 234.42747,244.46748 L 224.14692,239.49074 L 216.41185,245.13456 L 226.62217,250.28446 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 235.3202,243.66108 L 242.92208,238.20571 L 232.8824,233.22122 L 225.20027,238.78239 L 235.3202,243.66108 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2274"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2276"
+         sodipodi:nodetypes="ccccc"
+         d="M 244.04269,237.40717 L 251.47219,231.70252 L 241.73406,226.77098 L 234.01317,232.34768 L 244.04269,237.40717 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 252.6522,231.11226 L 260.3045,225.27523 L 250.58643,220.64138 L 242.89843,226.09476 L 252.6522,231.11226 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2278"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2280"
+         sodipodi:nodetypes="ccccc"
+         d="M 261.23976,224.42239 L 268.45876,219.21044 L 258.80517,214.83417 L 251.72352,219.79033 L 261.23976,224.42239 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 269.52519,218.43638 L 275.83883,213.61385 L 266.36416,209.30737 L 259.8121,214.10012 L 269.52519,218.43638 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2282"
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path2284"
+         sodipodi:nodetypes="ccccc"
+         d="M 276.80269,212.87655 L 282.2575,208.64164 L 272.64721,204.83239 L 267.406,208.63069 L 276.80269,212.87655 z "
+         style="fill:#ff00ff;fill-rule:evenodd;stroke:#000000;stroke-width:1.02280068px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="rect3291"
+         d="M 302.87759,234.27933 C 302.87759,234.27933 308.38212,229.69548 308.38212,229.69548 C 308.92862,229.24038 308.77424,228.59985 308.03355,228.25718 C 308.03355,228.25718 300.43832,224.74347 300.43832,224.74347 C 299.65719,224.38211 298.56475,224.46487 297.99153,224.93134 C 297.99153,224.93134 292.2163,229.63113 292.2163,229.63113 C 291.63275,230.10601 291.81664,230.77156 292.62585,231.12111 C 292.62585,231.12111 300.49174,234.51889 300.49174,234.51889 C 301.25859,234.85015 302.32155,234.74235 302.87759,234.27933 C 302.87759,234.27933 302.87759,234.27933 302.87759,234.27933"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3293"
+         d="M 235.3731,272.47703 C 235.3731,272.47703 227.42329,268.6228 227.42329,268.6228 C 226.73948,268.29128 225.78761,268.33455 225.2875,268.71966 C 225.2875,268.71966 219.50192,273.17479 219.50192,273.17479 C 218.99549,273.56477 219.13572,274.15174 219.81793,274.49107 C 219.81793,274.49107 227.7497,278.43631 227.7497,278.43631 C 228.44749,278.78339 229.41954,278.74258 229.9276,278.34461 C 229.9276,278.34461 235.73123,273.79845 235.73123,273.79845 C 236.23285,273.4055 236.07241,272.81607 235.3731,272.47703 C 235.3731,272.47703 235.3731,272.47703 235.3731,272.47703"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3299"
+         d="M 247.43674,211.44485 C 247.43674,211.44485 255.04475,214.72695 255.04475,214.72695 C 255.59412,214.96395 255.68572,215.40873 255.24904,215.72416 C 255.24904,215.72416 249.09015,220.17295 249.09015,220.17295 C 248.64784,220.49244 247.85024,220.55375 247.30291,220.31051 C 247.30291,220.31051 239.72373,216.94221 239.72373,216.94221 C 239.18929,216.7047 239.11431,216.26136 239.55452,215.94809 C 239.55452,215.94809 245.68459,211.58552 245.68459,211.58552 C 246.11924,211.27618 246.90023,211.21341 247.43674,211.44485 C 247.43674,211.44485 247.43674,211.44485 247.43674,211.44485"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3301"
+         d="M 211.38791,264.687 C 211.38791,264.687 220.69642,269.32187 220.69642,269.32187 C 221.37384,269.65917 221.55369,270.21304 221.09728,270.56361 C 221.09728,270.56361 214.59913,275.55485 214.59913,275.55485 C 214.12804,275.9167 213.20005,275.92451 212.52093,275.57249 C 212.52093,275.57249 203.19239,270.73714 203.19239,270.73714 C 202.53988,270.39891 202.39682,269.84723 202.86919,269.50005 C 202.86919,269.50005 209.38739,264.7093 209.38739,264.7093 C 209.84537,264.37269 210.73656,264.36268 211.38791,264.687 C 211.38791,264.687 211.38791,264.687 211.38791,264.687"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3305"
+         d="M 190.80124,251.03544 C 190.80124,251.03544 198.99762,255.5035 198.99762,255.5035 C 199.33669,255.68835 199.3609,256.01171 199.05255,256.22886 C 199.05255,256.22886 191.79012,261.34344 191.79012,261.34344 C 191.48252,261.56007 190.95608,261.59085 190.60912,261.41215 C 190.60912,261.41215 182.222,257.0924 182.222,257.0924 C 181.85741,256.90462 181.81745,256.57372 182.13313,256.35082 C 182.13313,256.35082 189.58629,251.08792 189.58629,251.08792 C 189.90275,250.86444 190.44492,250.8412 190.80124,251.03544 C 190.80124,251.03544 190.80124,251.03544 190.80124,251.03544"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3309"
+         d="M 200.86922,319.47957 C 200.86922,319.47957 191.5626,327.32806 191.5626,327.32806 C 191.09111,327.72566 190.29604,327.80875 189.78059,327.51386 C 189.78059,327.51386 179.45944,321.6093 179.45944,321.6093 C 178.94568,321.31539 178.91894,320.76315 179.3988,320.37157 C 179.3988,320.37157 188.87097,312.64175 188.87097,312.64175 C 189.33581,312.26242 190.11645,312.18928 190.62199,312.47729 C 190.62199,312.47729 200.77757,318.26317 200.77757,318.26317 C 201.28475,318.55212 201.32593,319.09442 200.86922,319.47957 C 200.86922,319.47957 200.86922,319.47957 200.86922,319.47957"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3311"
+         d="M 227.03328,279.37981 C 227.03328,279.37981 218.4746,275.11465 218.4746,275.11465 C 217.9779,274.86712 217.23368,274.93335 216.80449,275.26346 C 216.80449,275.26346 209.3489,280.99824 209.3489,280.99824 C 208.90258,281.34154 208.94628,281.82562 209.44841,282.08328 C 209.44841,282.08328 218.10282,286.52398 218.10282,286.52398 C 218.61686,286.78775 219.38667,286.71486 219.82736,286.36095 C 219.82736,286.36095 227.18725,280.45063 227.18725,280.45063 C 227.61081,280.1105 227.5415,279.63307 227.03328,279.37981 C 227.03328,279.37981 227.03328,279.37981 227.03328,279.37981"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3313"
+         d="M 200.87727,244.07429 C 200.87727,244.07429 208.8242,248.35707 208.8242,248.35707 C 209.24365,248.58313 209.26299,248.99029 208.86725,249.27001 C 208.86725,249.27001 201.29858,254.61953 201.29858,254.61953 C 200.89969,254.90148 200.24107,254.94426 199.82216,254.71545 C 199.82216,254.71545 191.88613,250.38067 191.88613,250.38067 C 191.47181,250.15435 191.45917,249.7474 191.8575,249.46819 C 191.8575,249.46819 199.41526,244.17067 199.41526,244.17067 C 199.81044,243.89367 200.46234,243.85067 200.87727,244.07429 C 200.87727,244.07429 200.87727,244.07429 200.87727,244.07429"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3317"
+         d="M 168.98052,312.35404 C 168.98052,312.35404 186.52228,298.92516 186.52228,298.92516 C 187.74742,297.98727 189.20942,297.50291 189.80916,297.83277 C 189.80916,297.83277 197.94313,302.30636 197.94313,302.30636 C 198.54751,302.63875 198.08283,303.67748 196.89261,304.64197 C 196.89261,304.64197 179.8462,318.45547 179.8462,318.45547 C 178.50836,319.53957 176.90663,320.12806 176.26526,319.76696 C 176.26526,319.76696 167.6358,314.90855 167.6358,314.90855 C 166.99971,314.55042 167.60418,313.40767 168.98052,312.35404 C 168.98052,312.35404 168.98052,312.35404 168.98052,312.35404"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3319"
+         d="M 192.5101,290.87165 C 192.5101,290.87165 183.27464,285.95046 183.27464,285.95046 C 182.54633,285.56237 181.54412,285.5639 181.0251,285.95388 C 181.0251,285.95388 174.43893,290.90244 174.43893,290.90244 C 173.90432,291.3041 174.06146,291.95091 174.79375,292.35267 C 174.79375,292.35267 184.08217,297.44861 184.08217,297.44861 C 184.83581,297.86207 185.87162,297.86005 186.40206,297.44408 C 186.40206,297.44408 192.93525,292.32079 192.93525,292.32079 C 193.44997,291.91714 193.25925,291.27084 192.5101,290.87165 C 192.5101,290.87165 192.5101,290.87165 192.5101,290.87165"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3321"
+         d="M 185.1382,279.89699 C 185.1382,279.89699 176.63387,275.50221 176.63387,275.50221 C 176.02877,275.18952 175.15088,275.21388 174.66322,275.55813 C 174.66322,275.55813 167.58535,280.55447 167.58535,280.55447 C 167.06223,280.92372 167.14522,281.48553 167.77396,281.81266 C 167.77396,281.81266 176.61165,286.41084 176.61165,286.41084 C 177.2442,286.73996 178.15674,286.69425 178.65527,286.3099 C 178.65527,286.3099 185.39983,281.11021 185.39983,281.11021 C 185.86446,280.752 185.74682,280.2115 185.1382,279.89699 C 185.1382,279.89699 185.1382,279.89699 185.1382,279.89699"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3323"
+         d="M 161.12258,272.02905 C 161.12258,272.02905 169.7964,276.60989 169.7964,276.60989 C 170.21763,276.83236 170.27487,277.2137 169.92432,277.46528 C 169.92432,277.46528 162.60291,282.7196 162.60291,282.7196 C 162.24117,282.9792 161.60395,283.00656 161.17461,282.7806 C 161.17461,282.7806 152.33353,278.12732 152.33353,278.12732 C 151.90283,277.90063 151.85435,277.51172 152.22438,277.25569 C 152.22438,277.25569 159.71303,272.0738 159.71303,272.0738 C 160.07158,271.82571 160.70003,271.80589 161.12258,272.02905 C 161.12258,272.02905 161.12258,272.02905 161.12258,272.02905"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3325"
+         d="M 220.2904,303.21609 C 220.2904,303.21609 213.19436,309.1832 213.19436,309.1832 C 212.74444,309.56153 211.88835,309.56212 211.27912,309.18598 C 211.27912,309.18598 202.00839,303.46232 202.00839,303.46232 C 201.45919,303.12325 201.36941,302.5694 201.80347,302.21926 C 201.80347,302.21926 208.65568,296.69198 208.65568,296.69198 C 209.07675,296.35232 209.86703,296.33025 210.43121,296.64355 C 210.43121,296.64355 219.94579,301.92755 219.94579,301.92755 C 220.57046,302.27448 220.72603,302.84974 220.2904,303.21609 C 220.2904,303.21609 220.2904,303.21609 220.2904,303.21609"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3327"
+         d="M 250.18155,231.04893 C 250.18155,231.04893 243.02922,227.42687 243.02922,227.42687 C 242.31181,227.06355 241.28875,227.09263 240.73358,227.49361 C 240.73358,227.49361 235.06335,231.58914 235.06335,231.58914 C 234.48252,232.00866 234.60822,232.64785 235.34714,233.02061 C 235.34714,233.02061 242.71349,236.73664 242.71349,236.73664 C 243.44994,237.10815 244.49431,237.0604 245.05318,236.63127 C 245.05318,236.63127 250.50942,232.44177 250.50942,232.44177 C 251.04367,232.03155 250.89663,231.41106 250.18155,231.04893 C 250.18155,231.04893 250.18155,231.04893 250.18155,231.04893"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3329"
+         d="M 241.73896,237.6183 C 241.73896,237.6183 234.09875,233.82511 234.09875,233.82511 C 233.42547,233.49084 232.47146,233.51871 231.96125,233.88804 C 231.96125,233.88804 226.11501,238.1202 226.11501,238.1202 C 225.60814,238.48713 225.74764,239.04628 226.42628,239.37343 C 226.42628,239.37343 234.12755,243.08611 234.12755,243.08611 C 234.78882,243.40491 235.72382,243.37142 236.22543,243.01146 C 236.22543,243.01146 242.01062,238.85982 242.01062,238.85982 C 242.51547,238.49751 242.39495,237.94399 241.73896,237.6183 C 241.73896,237.6183 241.73896,237.6183 241.73896,237.6183"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3331"
+         d="M 291.06923,231.66501 C 291.06923,231.66501 299.93537,235.46017 299.93537,235.46017 C 300.49195,235.69842 300.62867,236.14494 300.23967,236.46046 C 300.23967,236.46046 293.79405,241.68879 293.79405,241.68879 C 293.40327,242.00578 292.65126,242.0569 292.11015,241.80432 C 292.11015,241.80432 283.49087,237.78134 283.49087,237.78134 C 282.98847,237.54685 282.88811,237.11468 283.26389,236.81155 C 283.26389,236.81155 289.4626,231.81108 289.4626,231.81108 C 289.83674,231.50927 290.5524,231.44378 291.06923,231.66501 C 291.06923,231.66501 291.06923,231.66501 291.06923,231.66501"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3333"
+         d="M 214.6618,258.17714 C 214.6618,258.17714 205.79323,253.8565 205.79323,253.8565 C 205.30323,253.61778 204.58399,253.65545 204.17864,253.94061 C 204.17864,253.94061 196.98327,259.00223 196.98327,259.00223 C 196.56804,259.29434 196.62028,259.73235 197.10235,259.98475 C 197.10235,259.98475 205.83129,264.55497 205.83129,264.55497 C 206.34512,264.82401 207.10144,264.79412 207.5249,264.48748 C 207.5249,264.48748 214.85991,259.17623 214.85991,259.17623 C 215.27294,258.87715 215.18363,258.43136 214.6618,258.17714 C 214.6618,258.17714 214.6618,258.17714 214.6618,258.17714"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3335"
+         d="M 238.61522,217.71027 C 238.61522,217.71027 246.20482,221.06648 246.20482,221.06648 C 246.74975,221.30745 246.80537,221.77975 246.32841,222.12581 C 246.32841,222.12581 239.55856,227.03746 239.55856,227.03746 C 239.06928,227.39242 238.23201,227.47949 237.68245,227.2323 C 237.68245,227.2323 230.0291,223.78987 230.0291,223.78987 C 229.48641,223.54578 229.44798,223.06823 229.94181,222.71952 C 229.94181,222.71952 236.77542,217.89409 236.77542,217.89409 C 237.25693,217.55409 238.07699,217.47225 238.61522,217.71027 C 238.61522,217.71027 238.61522,217.71027 238.61522,217.71027"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3337"
+         d="M 228.91723,224.61205 C 228.91723,224.61205 236.61775,227.94965 236.61775,227.94965 C 237.17563,228.19144 237.24665,228.65958 236.77513,228.99941 C 236.77513,228.99941 230.06777,233.83356 230.06777,233.83356 C 229.58195,234.18369 228.73926,234.26255 228.18027,234.01019 C 228.18027,234.01019 220.46652,230.52787 220.46652,230.52787 C 219.92451,230.28318 219.88144,229.8127 220.36809,229.47313 C 220.36809,229.47313 227.0887,224.78372 227.0887,224.78372 C 227.56129,224.45396 228.37602,224.37747 228.91723,224.61205 C 228.91723,224.61205 228.91723,224.61205 228.91723,224.61205"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3339"
+         d="M 257.14967,204.40311 C 257.14967,204.40311 265.32623,207.88176 265.32623,207.88176 C 265.67464,208.02998 265.706,208.33073 265.3957,208.55655 C 265.3957,208.55655 257.84099,214.05443 257.84099,214.05443 C 257.51021,214.29515 256.95402,214.3647 256.59485,214.20987 C 256.59485,214.20987 248.16698,210.57659 248.16698,210.57659 C 247.81135,210.42328 247.8009,210.11114 248.14264,209.87723 C 248.14264,209.87723 255.94863,204.53395 255.94863,204.53395 C 256.2693,204.31446 256.80459,204.25629 257.14967,204.40311 C 257.14967,204.40311 257.14967,204.40311 257.14967,204.40311"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3341"
+         d="M 201.8511,271.34651 C 201.8511,271.34651 212.00249,276.52784 212.00249,276.52784 C 212.45381,276.7582 212.55592,277.14272 212.23112,277.39008 C 212.23112,277.39008 204.84954,283.01143 204.84954,283.01143 C 204.52035,283.26212 203.89008,283.27634 203.43671,283.04324 C 203.43671,283.04324 193.23937,277.8004 193.23937,277.8004 C 192.78929,277.56899 192.69387,277.18305 193.0251,276.9351 C 193.0251,276.9351 200.45262,271.37525 200.45262,271.37525 C 200.77944,271.1306 201.40305,271.11781 201.8511,271.34651 C 201.8511,271.34651 201.8511,271.34651 201.8511,271.34651"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3343"
+         d="M 219.21667,231.1118 C 219.21667,231.1118 227.42383,234.84661 227.42383,234.84661 C 227.83744,235.03484 227.89363,235.38938 227.54865,235.64192 C 227.54865,235.64192 220.45221,240.83693 220.45221,240.83693 C 220.08929,241.10262 219.45708,241.15831 219.03594,240.96141 C 219.03594,240.96141 210.6819,237.05577 210.6819,237.05577 C 210.26995,236.86319 210.23739,236.50104 210.60771,236.24418 C 210.60771,236.24418 217.85107,231.21996 217.85107,231.21996 C 218.20329,230.97566 218.81185,230.92758 219.21667,231.1118 C 219.21667,231.1118 219.21667,231.1118 219.21667,231.1118"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3345"
+         d="M 166.11203,294.40885 C 166.11203,294.40885 157.04241,289.52408 157.04241,289.52408 C 156.51843,289.24186 155.52217,289.42717 154.80588,289.94053 C 154.80588,289.94053 142.25863,298.93279 142.25863,298.93279 C 141.50124,299.47559 141.31352,300.15596 141.84075,300.45723 C 141.84075,300.45723 150.97129,305.67469 150.97129,305.67469 C 151.51617,305.98604 152.56442,305.78244 153.31853,305.21926 C 153.31853,305.21926 165.80481,295.89435 165.80481,295.89435 C 166.51725,295.36228 166.65299,294.70021 166.11203,294.40885 C 166.11203,294.40885 166.11203,294.40885 166.11203,294.40885"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3349"
+         d="M 151.01455,278.70397 C 151.01455,278.70397 160.37455,283.62006 160.37455,283.62006 C 160.68292,283.78202 160.7229,284.06518 160.46357,284.25485 C 160.46357,284.25485 152.27741,290.24195 152.27741,290.24195 C 152.01084,290.43692 151.5497,290.45709 151.24414,290.28729 C 151.24414,290.28729 141.97268,285.13512 141.97268,285.13512 C 141.68247,284.97384 141.66152,284.69337 141.92511,284.50616 C 141.92511,284.50616 150.02275,278.75515 150.02275,278.75515 C 150.27937,278.57289 150.72146,278.55003 151.01455,278.70397 C 151.01455,278.70397 151.01455,278.70397 151.01455,278.70397"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3351"
+         d="M 175.52443,287.1458 C 175.52443,287.1458 166.79876,282.52893 166.79876,282.52893 C 166.15936,282.19062 165.25137,282.21179 164.76171,282.57631 C 164.76171,282.57631 158.10028,287.53553 158.10028,287.53553 C 157.60379,287.90516 157.7174,288.48077 158.35625,288.82623 C 158.35625,288.82623 167.07489,293.541 167.07489,293.541 C 167.72676,293.8935 168.65274,293.87392 169.1498,293.49696 C 169.1498,293.49696 175.81827,288.43985 175.81827,288.43985 C 176.30838,288.06815 176.17677,287.49096 175.52443,287.1458 C 175.52443,287.1458 175.52443,287.1458 175.52443,287.1458"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3353"
+         d="M 201.97097,283.45905 C 201.97097,283.45905 192.78768,278.74285 192.78768,278.74285 C 192.07622,278.37747 191.08839,278.3982 190.5689,278.78852 C 190.5689,278.78852 183.98269,283.7371 183.98269,283.7371 C 183.44857,284.1384 183.58158,284.77203 184.28486,285.15865 C 184.28486,285.15865 193.36778,290.15177 193.36778,290.15177 C 194.11817,290.56428 195.16258,290.55584 195.70514,290.13183 C 195.70514,290.13183 202.39168,284.90631 202.39168,284.90631 C 202.9188,284.49436 202.72924,283.84847 201.97097,283.45905 C 201.97097,283.45905 201.97097,283.45905 201.97097,283.45905"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3355"
+         d="M 183.209,298.32124 C 183.209,298.32124 173.55788,293.23939 173.55788,293.23939 C 172.87828,292.88156 171.48018,293.22782 170.41912,294.01738 C 170.41912,294.01738 155.20003,305.34244 155.20003,305.34244 C 154.08849,306.16958 153.73855,307.14332 154.41967,307.52396 C 154.41967,307.52396 164.0962,312.93167 164.0962,312.93167 C 164.79619,313.32286 166.25692,312.95103 167.36704,312.09976 C 167.36704,312.09976 182.56068,300.44889 182.56068,300.44889 C 183.61957,299.63691 183.90689,298.68872 183.209,298.32124 C 183.209,298.32124 183.209,298.32124 183.209,298.32124"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3357"
+         d="M 181.25173,257.89055 C 181.25173,257.89055 189.52937,262.19408 189.52937,262.19408 C 189.93309,262.40396 189.973,262.78951 189.61763,263.05932 C 189.61763,263.05932 182.01974,268.82803 182.01974,268.82803 C 181.63543,269.11981 180.98628,269.17817 180.56566,268.95795 C 180.56566,268.95795 171.94248,264.44335 171.94248,264.44335 C 171.5243,264.2244 171.5122,263.82114 171.91393,263.54006 C 171.91393,263.54006 179.85732,257.98241 179.85732,257.98241 C 180.2289,257.72244 180.85025,257.68182 181.25173,257.89055 C 181.25173,257.89055 181.25173,257.89055 181.25173,257.89055"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3359"
+         d="M 207.30074,294.9799 C 207.30074,294.9799 198.59287,290.54083 198.59287,290.54083 C 198.06485,290.27167 197.31412,290.31751 196.90812,290.6428 C 196.90812,290.6428 190.40778,295.85065 190.40778,295.85065 C 190.00268,296.17521 190.08929,296.66071 190.60345,296.94008 C 190.60345,296.94008 199.08253,301.54721 199.08253,301.54721 C 199.62684,301.84297 200.40395,301.81213 200.82331,301.47711 C 200.82331,301.47711 207.55245,296.10117 207.55245,296.10117 C 207.97277,295.76537 207.85976,295.26488 207.30074,294.9799 C 207.30074,294.9799 207.30074,294.9799 207.30074,294.9799"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3361"
+         d="M 210.31834,311.52693 C 210.31834,311.52693 203.74605,316.99659 203.74605,316.99659 C 203.0788,317.55189 201.86,317.61152 201.01035,317.12644 C 201.01035,317.12644 192.24289,312.121 192.24289,312.121 C 191.3355,311.60296 191.17426,310.72413 191.88471,310.15474 C 191.88471,310.15474 198.87844,304.54948 198.87844,304.54948 C 199.56891,303.99608 200.82226,303.98204 201.68556,304.51382 C 201.68556,304.51382 210.03168,309.65489 210.03168,309.65489 C 210.84096,310.15338 210.96757,310.98662 210.31834,311.52693 C 210.31834,311.52693 210.31834,311.52693 210.31834,311.52693"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3363"
+         d="M 229.40869,295.58217 C 229.40869,295.58217 223.04271,300.94565 223.04271,300.94565 C 222.48275,301.41742 221.41805,301.45214 220.65599,301.0225 C 220.65599,301.0225 211.86431,296.06605 211.86431,296.06605 C 211.09687,295.63339 210.94055,294.9076 211.51326,294.43967 C 211.51326,294.43967 218.02408,289.12024 218.02408,289.12024 C 218.58411,288.66268 219.64217,288.64028 220.39694,289.06907 C 220.39694,289.06907 229.04379,293.98149 229.04379,293.98149 C 229.79334,294.40733 229.95631,295.12081 229.40869,295.58217 C 229.40869,295.58217 229.40869,295.58217 229.40869,295.58217"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3365"
+         d="M 217.05231,287.26056 C 217.05231,287.26056 208.43084,282.90987 208.43084,282.90987 C 207.9153,282.6497 207.16011,282.70044 206.73573,283.02431 C 206.73573,283.02431 199.50923,288.5392 199.50923,288.5392 C 199.05894,288.88283 199.11946,289.38044 199.64696,289.65417 C 199.64696,289.65417 208.47177,294.23353 208.47177,294.23353 C 209.01197,294.51386 209.80095,294.45066 210.23875,294.09263 C 210.23875,294.09263 217.26191,288.3491 217.26191,288.3491 C 217.67418,288.01194 217.57986,287.52677 217.05231,287.26056 C 217.05231,287.26056 217.05231,287.26056 217.05231,287.26056"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3367"
+         d="M 170.90895,265.27133 C 170.90895,265.27133 179.32655,269.62137 179.32655,269.62137 C 179.80913,269.87078 179.87784,270.30382 179.47992,270.59253 C 179.47992,270.59253 172.41934,275.71508 172.41934,275.71508 C 172.01209,276.01056 171.29204,276.04365 170.8056,275.78907 C 170.8056,275.78907 162.3215,271.34911 162.3215,271.34911 C 161.84045,271.09734 161.7847,270.66085 162.19576,270.37059 C 162.19576,270.37059 169.32283,265.33795 169.32283,265.33795 C 169.72453,265.05428 170.43163,265.02465 170.90895,265.27133 C 170.90895,265.27133 170.90895,265.27133 170.90895,265.27133"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3369"
+         d="M 204.55438,265.20903 C 204.55438,265.20903 195.83444,260.74237 195.83444,260.74237 C 195.21646,260.42583 194.32033,260.45289 193.82368,260.80347 C 193.82368,260.80347 186.74496,265.80041 186.74496,265.80041 C 186.23123,266.16305 186.32053,266.71769 186.94675,267.04351 C 186.94675,267.04351 195.78441,271.64167 195.78441,271.64167 C 196.41947,271.97209 197.33877,271.93803 197.84403,271.56578 C 197.84403,271.56578 204.80505,266.43735 204.80505,266.43735 C 205.29337,266.07757 205.18091,265.52994 204.55438,265.20903 C 204.55438,265.20903 204.55438,265.20903 204.55438,265.20903"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3371"
+         d="M 194.5991,272.5456 C 194.5991,272.5456 186.09478,268.15083 186.09478,268.15083 C 185.48971,267.83814 184.61179,267.8625 184.12412,268.20675 C 184.12412,268.20675 177.04625,273.20308 177.04625,273.20308 C 176.52314,273.57235 176.60616,274.13416 177.23487,274.46127 C 177.23487,274.46127 186.07255,279.05946 186.07255,279.05946 C 186.70512,279.38857 187.61765,279.34288 188.11618,278.95853 C 188.11618,278.95853 194.86074,273.75884 194.86074,273.75884 C 195.32537,273.40062 195.20773,272.86013 194.5991,272.5456 C 194.5991,272.5456 194.5991,272.5456 194.5991,272.5456"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3373"
+         d="M 284.73208,249.18471 C 284.73208,249.18471 291.64382,243.45826 291.64382,243.45826 C 291.9322,243.21935 291.84218,242.87732 291.44316,242.6915 C 291.44316,242.6915 282.28362,238.42609 282.28362,238.42609 C 281.90552,238.25002 281.37123,238.2925 281.08461,238.52125 C 281.08461,238.52125 274.21817,244.0015 274.21817,244.0015 C 273.92236,244.23759 273.98607,244.57745 274.36212,244.76359 C 274.36212,244.76359 283.47634,249.2752 283.47634,249.2752 C 283.87359,249.47184 284.43418,249.43153 284.73208,249.18471 C 284.73208,249.18471 284.73208,249.18471 284.73208,249.18471"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3375"
+         d="M 266.25849,207.11055 C 266.25849,207.11055 258.22296,203.68087 258.22296,203.68087 C 257.8725,203.5313 258.26258,202.94141 259.09217,202.36158 C 259.09217,202.36158 276.45653,190.22478 276.45653,190.22478 C 277.16993,189.72614 278.01001,189.4307 278.34442,189.55973 C 278.34442,189.55973 286.00228,192.51461 286.00228,192.51461 C 286.34641,192.64741 286.06579,193.16868 285.36877,193.68615 C 285.36877,193.68615 268.38219,206.29734 268.38219,206.29734 C 267.56963,206.9006 266.62006,207.26486 266.25849,207.11055 C 266.25849,207.11055 266.25849,207.11055 266.25849,207.11055"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3377"
+         d="M 150.04703,290.88787 C 150.04703,290.88787 141.14376,285.97573 141.14376,285.97573 C 140.70469,285.73348 139.97814,285.80683 139.51431,286.13998 C 139.51431,286.13998 130.20189,292.82886 130.20189,292.82886 C 129.7371,293.1627 129.71188,293.62984 130.14579,293.87645 C 130.14579,293.87645 138.94417,298.87711 138.94417,298.87711 C 139.38768,299.12918 140.1235,299.05996 140.59351,298.72169 C 140.59351,298.72169 150.01083,291.94429 150.01083,291.94429 C 150.47987,291.60674 150.49581,291.13546 150.04703,290.88787 C 150.04703,290.88787 150.04703,290.88787 150.04703,290.88787"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3379"
+         sodipodi:nodetypes="cccccssssccccc"
+         d="M 308.02082,201.07469 C 308.02082,201.07469 290.25388,194.17877 290.25388,194.17877 C 289.03643,193.70622 287.49217,193.72395 286.81047,194.22762 C 286.81047,194.22762 274.92805,203.00701 274.92805,203.00701 C 274.21432,203.53434 274.06866,203.98769 275.45256,204.3729 L 282.20799,207.13372 C 283.30081,207.58033 283.29864,207.60345 284.24763,206.91193 L 285.51228,205.99038 C 285.78882,205.78886 286.61757,205.71073 287.50613,206.09359 L 296.65875,210.03701 C 297.66812,210.31796 298.54496,210.40349 299.11475,209.9558 C 299.11475,209.9558 308.63657,202.4745 308.63657,202.4745 C 309.1849,202.04368 308.91548,201.42193 308.02082,201.07469 C 308.02082,201.07469 308.02082,201.07469 308.02082,201.07469"
+         style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3381"
+         d="M 263.00018,250.94679 C 263.00018,250.94679 254.5221,247.0388 254.5221,247.0388 C 254.08593,246.83775 253.46532,246.88652 253.12956,247.148 C 253.12956,247.148 246.6785,252.17186 246.6785,252.17186 C 246.33694,252.43788 246.40946,252.82015 246.84217,253.0292 C 246.84217,253.0292 255.25442,257.09309 255.25442,257.09309 C 255.70512,257.31082 256.34668,257.26542 256.6918,256.99111 C 256.6918,256.99111 263.20867,251.81133 263.20867,251.81133 C 263.54778,251.54179 263.45431,251.15613 263.00018,250.94679 C 263.00018,250.94679 263.00018,250.94679 263.00018,250.94679"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3385"
+         d="M 224.63535,250.5847 C 224.63535,250.5847 216.40989,246.52591 216.40989,246.52591 C 215.79702,246.2235 214.86783,246.29035 214.3264,246.67645 C 214.3264,246.67645 206.94803,251.93833 206.94803,251.93833 C 206.39716,252.33118 206.45553,252.89148 207.07898,253.19391 C 207.07898,253.19391 215.44611,257.2528 215.44611,257.2528 C 216.0613,257.55121 216.99156,257.47468 217.53182,257.08184 C 217.53182,257.08184 224.76854,251.81989 224.76854,251.81989 C 225.29959,251.43374 225.24017,250.88315 224.63535,250.5847 C 224.63535,250.5847 224.63535,250.5847 224.63535,250.5847"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3387"
+         d="M 220.79919,257.76358 C 220.79919,257.76358 229.7534,262.16116 229.7534,262.16116 C 230.47771,262.51688 230.65884,263.11821 230.15739,263.5097 C 230.15739,263.5097 223.79138,268.4796 223.79138,268.4796 C 223.27442,268.8832 222.26833,268.91225 221.53782,268.54453 C 221.53782,268.54453 212.50868,263.9994 212.50868,263.9994 C 211.79484,263.64005 211.64287,263.03537 212.16581,262.64384 C 212.16581,262.64384 218.60675,257.8215 218.60675,257.8215 C 219.11421,257.44156 220.09111,257.41585 220.79919,257.76358 C 220.79919,257.76358 220.79919,257.76358 220.79919,257.76358"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3389"
+         d="M 209.90305,237.93879 C 209.90305,237.93879 217.69144,241.5189 217.69144,241.5189 C 218.2857,241.79206 218.40792,242.28161 217.95894,242.61539 C 217.95894,242.61539 211.48141,247.43084 211.48141,247.43084 C 211.00555,247.78462 210.15328,247.81772 209.57718,247.50651 C 209.57718,247.50651 202.04394,243.43694 202.04394,243.43694 C 201.54218,243.16588 201.50989,242.69331 201.96601,242.37623 C 201.96601,242.37623 208.18868,238.05054 208.18868,238.05054 C 208.62095,237.75004 209.38314,237.69979 209.90305,237.93879 C 209.90305,237.93879 209.90305,237.93879 209.90305,237.93879"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="rect3391"
+         d="M 300.49388,221.41753 C 300.49388,221.41753 318.37188,206.97394 318.37188,206.97394 C 319.2072,206.29909 319.56463,205.63506 319.17704,205.48324 C 319.17704,205.48324 311.44233,202.45361 311.44233,202.45361 C 311.07717,202.31058 310.12278,202.71434 309.29892,203.3606 C 309.29892,203.3606 291.6901,217.17374 291.6901,217.17374 C 290.79969,217.87223 290.3631,218.57498 290.71507,218.74754 C 290.71507,218.74754 298.1804,222.40777 298.1804,222.40777 C 298.55498,222.59143 299.58864,222.1489 300.49388,221.41753 C 300.49388,221.41753 300.49388,221.41753 300.49388,221.41753"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 258.97104,224.63939 C 258.97104,224.63939 251.83463,221.23656 251.83463,221.23656 C 251.14159,220.90611 250.14534,220.95426 249.59505,221.34461 C 249.59505,221.34461 243.94919,225.34942 243.94919,225.34942 C 243.36824,225.76151 243.45494,226.38105 244.14978,226.73848 C 244.14978,226.73848 251.31231,230.42301 251.31231,230.42301 C 252.05284,230.80395 253.12041,230.75512 253.69927,230.31357 C 253.69927,230.31357 259.31886,226.02706 259.31886,226.02706 C 259.86601,225.60971 259.70806,224.99085 258.97104,224.63939 C 258.97104,224.63939 258.97104,224.63939 258.97104,224.63939"
+         id="path13125"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 245.03115,264.95865 C 245.03115,264.95865 236.89775,261.02915 236.89775,261.02915 C 236.29828,260.73954 235.41666,260.81767 234.92021,261.2046 C 234.92021,261.2046 228.15537,266.47697 228.15537,266.47697 C 227.65036,266.87057 227.72859,267.42473 228.33161,267.71924 C 228.33161,267.71924 236.5135,271.71529 236.5135,271.71529 C 237.12168,272.01232 238.01565,271.93037 238.51705,271.53179 C 238.51705,271.53179 245.23339,266.19289 245.23339,266.19289 C 245.7263,265.80109 245.63571,265.25074 245.03115,264.95865 C 245.03115,264.95865 245.03115,264.95865 245.03115,264.95865"
+         id="path13127"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 241.3425,254.88757 C 241.3425,254.88757 247.17841,250.38791 247.17841,250.38791 C 247.6407,250.03146 247.43669,249.46639 246.72219,249.12079 C 246.72219,249.12079 237.84809,244.82838 237.84809,244.82838 C 237.14306,244.48737 236.20078,244.49428 235.73414,244.84402 C 235.73414,244.84402 229.84385,249.25863 229.84385,249.25863 C 229.36826,249.61507 229.55538,250.18212 230.26467,250.52985 C 230.26467,250.52985 239.19316,254.90731 239.19316,254.90731 C 239.9121,255.25978 240.87126,255.2509 241.3425,254.88757 C 241.3425,254.88757 241.3425,254.88757 241.3425,254.88757"
+         id="path13129"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path13131"
+         d="M 250.09625,248.10772 C 250.09625,248.10772 256.04021,243.39795 256.04021,243.39795 C 256.61443,242.94295 256.40961,242.26587 255.58606,241.87803 C 255.58606,241.87803 247.4695,238.05567 247.4695,238.05567 C 246.66725,237.67787 245.54243,237.71942 244.94236,238.15046 C 244.94236,238.15046 238.73449,242.60981 238.73449,242.60981 C 238.09034,243.07251 238.23366,243.77042 239.06151,244.17275 C 239.06151,244.17275 247.44196,248.2456 247.44196,248.2456 C 248.29278,248.65909 249.47914,248.5967 250.09625,248.10772 C 250.09625,248.10772 250.09625,248.10772 250.09625,248.10772"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path13133"
+         d="M 270.78129,244.1004 C 270.78129,244.1004 263.16585,240.54998 263.16585,240.54998 C 262.59905,240.28573 261.81284,240.33065 261.40189,240.65211 C 261.40189,240.65211 255.67209,245.13428 255.67209,245.13428 C 255.23437,245.47666 255.35686,245.97255 255.94801,246.24454 C 255.94801,246.24454 263.88891,249.89826 263.88891,249.89826 C 264.4733,250.16714 265.27704,250.1004 265.6898,249.75019 C 265.6898,249.75019 271.09419,245.16472 271.09419,245.16472 C 271.48189,244.83578 271.34185,244.36173 270.78129,244.1004 C 270.78129,244.1004 270.78129,244.1004 270.78129,244.1004"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path13135"
+         d="M 279.36283,237.13131 C 279.36283,237.13131 271.95158,233.79368 271.95158,233.79368 C 271.36993,233.53174 270.52181,233.61628 270.04782,233.98311 C 270.04782,233.98311 264.05529,238.62092 264.05529,238.62092 C 263.57067,238.99597 263.64455,239.51827 264.22306,239.79213 C 264.22306,239.79213 271.59655,243.28243 271.59655,243.28243 C 272.19767,243.56699 273.0762,243.48424 273.56398,243.09674 C 273.56398,243.09674 279.59427,238.30625 279.59427,238.30625 C 280.07112,237.92743 279.96688,237.40335 279.36283,237.13131 C 279.36283,237.13131 279.36283,237.13131 279.36283,237.13131"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 259.13878,241.01984 C 259.13878,241.01984 264.94427,236.50262 264.94427,236.50262 C 265.5183,236.05597 265.27718,235.39329 264.41129,235.01866 C 264.41129,235.01866 256.13903,231.43971 256.13903,231.43971 C 255.34632,231.09675 254.26614,231.15807 253.71044,231.57558 C 253.71044,231.57558 248.09408,235.79517 248.09408,235.79517 C 247.52482,236.22286 247.6843,236.86458 248.45835,237.23578 C 248.45835,237.23578 256.54144,241.11225 256.54144,241.11225 C 257.38813,241.51828 258.54995,241.47801 259.13878,241.01984 C 259.13878,241.01984 259.13878,241.01984 259.13878,241.01984"
+         id="path14377"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 232.79455,261.43519 C 232.79455,261.43519 238.72958,256.85909 238.72958,256.85909 C 239.16465,256.52365 238.9726,255.99183 238.3001,255.66653 C 238.3001,255.66653 229.27525,251.30123 229.27525,251.30123 C 228.61176,250.98029 227.72493,250.98679 227.28579,251.31591 C 227.28579,251.31591 221.29544,255.80552 221.29544,255.80552 C 220.84778,256.14104 221.02387,256.67471 221.69138,257.00198 C 221.69138,257.00198 230.77154,261.45377 230.77154,261.45377 C 231.44822,261.78553 232.35098,261.77719 232.79455,261.43519 C 232.79455,261.43519 232.79455,261.43519 232.79455,261.43519"
+         id="path14379"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 254.17375,257.48652 C 254.17375,257.48652 246.32963,253.73115 246.32963,253.73115 C 245.70901,253.43402 244.79483,253.51721 244.27828,253.91779 C 244.27828,253.91779 237.75243,258.97869 237.75243,258.97869 C 237.22504,259.38768 237.29958,259.96347 237.92117,260.26957 C 237.92117,260.26957 245.77862,264.13893 245.77862,264.13893 C 246.41397,264.45181 247.35058,264.36687 247.87695,263.94859 C 247.87695,263.94859 254.38946,258.77374 254.38946,258.77374 C 254.90487,258.36419 254.80792,257.79015 254.17375,257.48652 C 254.17375,257.48652 254.17375,257.48652 254.17375,257.48652"
+         id="path14381"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path14383"
+         d="M 233.03678,243.79425 C 233.03678,243.79425 225.48646,240.13918 225.48646,240.13918 C 224.74342,239.77949 223.69299,239.82194 223.12841,240.23388 C 223.12841,240.23388 217.44731,244.37905 217.44731,244.37905 C 216.874,244.79736 217.00415,245.4333 217.742,245.80545 C 217.742,245.80545 225.24071,249.58767 225.24071,249.58767 C 226.00503,249.97318 227.0886,249.93684 227.66722,249.50562 C 227.66722,249.50562 233.39989,245.23329 233.39989,245.23329 C 233.96951,244.80878 233.80621,244.16674 233.03678,243.79425 C 233.03678,243.79425 233.03678,243.79425 233.03678,243.79425"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 282.03015,224.70872 C 282.03015,224.70872 287.9774,220.08364 287.9774,220.08364 C 288.56519,219.62653 289.56121,219.51344 290.21268,219.82947 C 290.21268,219.82947 296.76597,223.00851 296.76597,223.00851 C 297.42707,223.32921 297.49209,223.96249 296.90969,224.429 C 296.90969,224.429 291.01609,229.14982 291.01609,229.14982 C 290.41797,229.62893 289.39806,229.74916 288.73146,229.41875 C 288.73146,229.41875 282.12452,226.14398 282.12452,226.14398 C 281.4678,225.81845 281.42665,225.17805 282.03015,224.70872 C 282.03015,224.70872 282.03015,224.70872 282.03015,224.70872"
+         id="path14385"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20314"
+         d="M 274.59176,213.04701 C 274.59176,213.04701 267.63301,209.8841 267.63301,209.8841 C 266.9305,209.56479 265.98277,209.58635 265.5079,209.93372 C 265.5079,209.93372 260.69581,213.4537 260.69581,213.4537 C 260.20677,213.81142 260.39277,214.35936 261.11308,214.68093 C 261.11308,214.68093 268.24694,217.86572 268.24694,217.86572 C 268.95551,218.18206 269.90543,218.14595 270.37662,217.78604 C 270.37662,217.78604 275.0136,214.24417 275.0136,214.24417 C 275.47126,213.89461 275.28303,213.36122 274.59176,213.04701 C 274.59176,213.04701 274.59176,213.04701 274.59176,213.04701"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 292.00448,215.55279 C 292.00448,215.55279 296.3664,212.1569 296.3664,212.1569 C 296.79902,211.82008 296.4902,211.27517 295.67813,210.9361 C 295.67813,210.9361 287.77738,207.63715 287.77738,207.63715 C 287.00644,207.31524 286.04433,207.31631 285.61635,207.63866 C 285.61635,207.63866 281.30257,210.88766 281.30257,210.88766 C 280.86653,211.21606 281.12901,211.75032 281.89503,212.08646 C 281.89503,212.08646 289.74764,215.53227 289.74764,215.53227 C 290.55499,215.88655 291.56345,215.89614 292.00448,215.55279 C 292.00448,215.55279 292.00448,215.55279 292.00448,215.55279"
+         id="path20316"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20318"
+         d="M 284.72744,221.17105 C 284.72744,221.17105 289.45459,217.54272 289.45459,217.54272 C 289.926,217.18089 289.66619,216.61851 288.87179,216.28131 C 288.87179,216.28131 280.90373,212.899 280.90373,212.899 C 280.10221,212.55875 279.07175,212.57593 278.59356,212.93801 C 278.59356,212.93801 273.79834,216.56885 273.79834,216.56885 C 273.31611,216.93397 273.57888,217.50206 274.38726,217.84205 C 274.38726,217.84205 282.42339,221.22188 282.42339,221.22188 C 283.22457,221.55883 284.25207,221.53592 284.72744,221.17105 C 284.72744,221.17105 284.72744,221.17105 284.72744,221.17105"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 280.70619,208.18882 C 280.70619,208.18882 273.65334,205.39325 273.65334,205.39325 C 272.99267,205.13139 272.16292,205.14273 271.78475,205.41678 C 271.78475,205.41678 267.9355,208.20633 267.9355,208.20633 C 267.54254,208.4911 267.73876,208.95315 268.38364,209.24454 C 268.38364,209.24454 275.27945,212.36039 275.27945,212.36039 C 276.01926,212.69466 276.95281,212.71002 277.36247,212.39198 C 277.36247,212.39198 281.36853,209.28183 281.36853,209.28183 C 281.76145,208.97678 281.46158,208.48822 280.70619,208.18882 C 280.70619,208.18882 280.70619,208.18882 280.70619,208.18882"
+         id="path20320"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20322"
+         d="M 267.13462,218.61017 C 267.13462,218.61017 260.04553,215.39646 260.04553,215.39646 C 259.35688,215.08427 258.39121,215.12387 257.87608,215.4844 C 257.87608,215.4844 252.67496,219.12445 252.67496,219.12445 C 252.14829,219.49305 252.26711,220.05491 252.94569,220.38521 C 252.94569,220.38521 259.93387,223.78674 259.93387,223.78674 C 260.65566,224.13807 261.67299,224.1096 262.21011,223.72182 C 262.21011,223.72182 267.5121,219.8939 267.5121,219.8939 C 268.03699,219.51494 267.86652,218.94196 267.13462,218.61017 C 267.13462,218.61017 267.13462,218.61017 267.13462,218.61017"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         d="M 287.7354,230.25946 C 287.7354,230.25946 281.08316,227.00351 281.08316,227.00351 C 280.40678,226.67245 279.40568,226.75692 278.83891,227.19481 C 278.83891,227.19481 273.09436,231.63291 273.09436,231.63291 C 272.51043,232.08403 272.60418,232.71336 273.30418,233.04198 C 273.30418,233.04198 280.18673,236.27314 280.18673,236.27314 C 280.86539,236.59174 281.86259,236.48569 282.42295,236.03722 C 282.42295,236.03722 287.93718,231.62391 287.93718,231.62391 C 288.48138,231.18834 288.39153,230.58062 287.7354,230.25946 C 287.7354,230.25946 287.7354,230.25946 287.7354,230.25946"
+         id="path20324"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path20326"
+         d="M 267.94729,234.14015 C 267.94729,234.14015 273.40476,229.94747 273.40476,229.94747 C 273.94662,229.5312 273.71505,228.90956 272.88832,228.55398 C 272.88832,228.55398 264.75456,225.0556 264.75456,225.0556 C 263.95198,224.71041 262.87291,224.75882 262.33246,225.16387 C 262.33246,225.16387 256.88991,229.24286 256.88991,229.24286 C 256.34026,229.6548 256.54039,230.27309 257.34135,230.62945 C 257.34135,230.62945 265.46021,234.24152 265.46021,234.24152 C 266.28553,234.60871 267.39604,234.56364 267.94729,234.14015 C 267.94729,234.14015 267.94729,234.14015 267.94729,234.14015"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <path
+         id="path21282"
+         d="M 276.32923,227.66222 C 276.32923,227.66222 281.90001,223.35623 281.90001,223.35623 C 282.4517,222.9298 282.23314,222.31013 281.41303,221.96694 C 281.41303,221.96694 273.34754,218.59182 273.34754,218.59182 C 272.55203,218.25892 271.46937,218.32422 270.91711,218.73813 C 270.91711,218.73813 265.34181,222.91665 265.34181,222.91665 C 264.77733,223.33971 264.96112,223.95828 265.75691,224.30368 C 265.75691,224.30368 273.82693,227.80624 273.82693,227.80624 C 274.64767,228.16246 275.7651,228.09826 276.32923,227.66222 C 276.32923,227.66222 276.32923,227.66222 276.32923,227.66222"
+         style="opacity:0.99720004;fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      <g
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         id="g5349">
+        <path
+           id="path3281"
+           d="M 200.53013,313.0503 C 200.53013,313.0503 193.03458,319.37147 193.03458,319.37147 C 192.65485,319.6917 192.0145,319.75862 191.59935,319.52112 C 191.59935,319.52112 183.2867,314.76558 183.2867,314.76558 C 182.87292,314.52886 182.85139,314.08409 183.23786,313.76871 C 183.23786,313.76871 190.86675,307.54312 190.86675,307.54312 C 191.24112,307.23761 191.86986,307.1787 192.27701,307.41066 C 192.27701,307.41066 200.45632,312.07061 200.45632,312.07061 C 200.86479,312.30333 200.89796,312.7401 200.53013,313.0503 C 200.53013,313.0503 200.53013,313.0503 200.53013,313.0503"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3283"
+           d="M 172.41739,306.03622 C 172.41739,306.03622 188.0277,294.08595 188.0277,294.08595 C 189.11795,293.25133 190.41897,292.82031 190.95268,293.11384 C 190.95268,293.11384 198.19106,297.09485 198.19106,297.09485 C 198.72888,297.39065 198.31537,298.31501 197.2562,299.17331 C 197.2562,299.17331 182.08669,311.46584 182.08669,311.46584 C 180.89616,312.43058 179.47079,312.95427 178.90005,312.63295 C 178.90005,312.63295 171.22074,308.30947 171.22074,308.30947 C 170.65468,307.99077 171.1926,306.97384 172.41739,306.03622 C 172.41739,306.03622 172.41739,306.03622 172.41739,306.03622"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3285"
+           d="M 183.71685,293.09242 C 183.71685,293.09242 175.21974,288.61823 175.21974,288.61823 C 174.62141,288.30317 173.39049,288.60803 172.45631,289.30319 C 172.45631,289.30319 159.05702,299.27405 159.05702,299.27405 C 158.07838,300.0023 157.77029,300.8596 158.36997,301.19473 C 158.36997,301.19473 166.88944,305.95582 166.88944,305.95582 C 167.50572,306.30023 168.79179,305.97286 169.76917,305.22339 C 169.76917,305.22339 183.14605,294.96565 183.14605,294.96565 C 184.07833,294.25076 184.33129,293.41596 183.71685,293.09242 C 183.71685,293.09242 183.71685,293.09242 183.71685,293.09242"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3287"
+           d="M 166.83475,289.12754 C 166.83475,289.12754 159.13844,284.98242 159.13844,284.98242 C 158.69381,284.74294 157.84841,284.90019 157.24056,285.33581 C 157.24056,285.33581 146.5932,292.96648 146.5932,292.96648 C 145.95049,293.4271 145.7912,294.00445 146.23859,294.2601 C 146.23859,294.2601 153.9866,298.68753 153.9866,298.68753 C 154.44898,298.95174 155.33851,298.77896 155.97843,298.30106 C 155.97843,298.30106 166.57405,290.38811 166.57405,290.38811 C 167.17862,289.93661 167.2938,289.37478 166.83475,289.12754 C 166.83475,289.12754 166.83475,289.12754 166.83475,289.12754"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3289"
+           d="M 151.36095,285.65566 C 151.36095,285.65566 143.84889,281.51108 143.84889,281.51108 C 143.47842,281.30668 142.86541,281.36856 142.47405,281.64966 C 142.47405,281.64966 134.61675,287.29335 134.61675,287.29335 C 134.22459,287.57503 134.20331,287.96918 134.56941,288.17726 C 134.56941,288.17726 141.993,292.39653 141.993,292.39653 C 142.36721,292.60921 142.98804,292.55081 143.38462,292.2654 C 143.38462,292.2654 151.33041,286.54701 151.33041,286.54701 C 151.72616,286.2622 151.73961,285.86457 151.36095,285.65566 C 151.36095,285.65566 151.36095,285.65566 151.36095,285.65566"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3291"
+           d="M 210.70247,305.77557 C 210.70247,305.77557 204.93036,310.57929 204.93036,310.57929 C 204.34435,311.06699 203.27394,311.11934 202.52773,310.69333 C 202.52773,310.69333 194.82772,306.29731 194.82772,306.29731 C 194.03081,305.84234 193.88921,305.07052 194.51315,304.57044 C 194.51315,304.57044 200.65538,299.64763 200.65538,299.64763 C 201.26179,299.16161 202.36255,299.14928 203.12074,299.61631 C 203.12074,299.61631 210.45071,304.13144 210.45071,304.13144 C 211.16146,304.56926 211.27265,305.30104 210.70247,305.77557 C 210.70247,305.77557 210.70247,305.77557 210.70247,305.77557"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3293"
+           d="M 220.36679,297.64972 C 220.36679,297.64972 214.19821,302.83693 214.19821,302.83693 C 213.8071,303.16581 213.0629,303.16632 212.53329,302.83934 C 212.53329,302.83934 204.47426,297.86377 204.47426,297.86377 C 203.99682,297.56901 203.91878,297.08754 204.2961,296.78318 C 204.2961,296.78318 210.25274,291.97831 210.25274,291.97831 C 210.61877,291.68306 211.30575,291.66386 211.7962,291.93623 C 211.7962,291.93623 220.06723,296.5296 220.06723,296.5296 C 220.61026,296.83118 220.74549,297.33126 220.36679,297.64972 C 220.36679,297.64972 220.36679,297.64972 220.36679,297.64972"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3295"
+           d="M 229.27683,290.15464 C 229.27683,290.15464 223.76946,294.79471 223.76946,294.79471 C 223.28503,295.20286 222.36392,295.23289 221.70465,294.86121 C 221.70465,294.86121 214.09874,290.57325 214.09874,290.57325 C 213.43481,290.19895 213.29956,289.57104 213.79504,289.16623 C 213.79504,289.16623 219.4277,284.56425 219.4277,284.56425 C 219.91221,284.1684 220.82757,284.14902 221.48054,284.51999 C 221.48054,284.51999 228.96116,288.76985 228.96116,288.76985 C 229.60961,289.13825 229.75059,289.75549 229.27683,290.15464 C 229.27683,290.15464 229.27683,290.15464 229.27683,290.15464"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3297"
+           d="M 207.6366,289.76045 C 207.6366,289.76045 200.20453,285.97175 200.20453,285.97175 C 199.75388,285.74202 199.11315,285.78115 198.76662,286.05878 C 198.76662,286.05878 193.21867,290.50362 193.21867,290.50362 C 192.87292,290.78062 192.94685,291.19499 193.38567,291.43343 C 193.38567,291.43343 200.62246,295.36556 200.62246,295.36556 C 201.08703,295.61798 201.75028,295.59166 202.10819,295.30573 C 202.10819,295.30573 207.85144,290.71744 207.85144,290.71744 C 208.21018,290.43084 208.11372,290.00367 207.6366,289.76045 C 207.6366,289.76045 207.6366,289.76045 207.6366,289.76045"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3299"
+           d="M 193.30538,285.7839 C 193.30538,285.7839 185.22052,281.47582 185.22052,281.47582 C 184.58295,281.13609 183.70561,281.13742 183.25124,281.47882 C 183.25124,281.47882 177.48561,285.81085 177.48561,285.81085 C 177.01761,286.16248 177.15516,286.7287 177.79623,287.08041 C 177.79623,287.08041 185.92745,291.54147 185.92745,291.54147 C 186.5872,291.90343 187.49396,291.90165 187.95831,291.53752 C 187.95831,291.53752 193.67756,287.05251 193.67756,287.05251 C 194.12816,286.69915 193.9612,286.13336 193.30538,285.7839 C 193.30538,285.7839 193.30538,285.7839 193.30538,285.7839"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3301"
+           d="M 153.65825,274.40909 C 153.65825,274.40909 161.75797,278.66326 161.75797,278.66326 C 162.02482,278.80341 162.05943,279.04844 161.83501,279.21257 C 161.83501,279.21257 154.75107,284.39353 154.75107,284.39353 C 154.52039,284.56224 154.12135,284.5797 153.85694,284.43276 C 153.85694,284.43276 145.83383,279.97431 145.83383,279.97431 C 145.58269,279.83475 145.56457,279.59204 145.79266,279.43005 C 145.79266,279.43005 152.79999,274.45338 152.79999,274.45338 C 153.02206,274.29566 153.40462,274.27588 153.65825,274.40909 C 153.65825,274.40909 153.65825,274.40909 153.65825,274.40909"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3303"
+           d="M 176.68036,282.15173 C 176.68036,282.15173 169.08774,278.13438 169.08774,278.13438 C 168.53136,277.83999 167.74128,277.85841 167.31522,278.1756 C 167.31522,278.1756 161.51878,282.49085 161.51878,282.49085 C 161.08675,282.81248 161.18562,283.31335 161.7415,283.61396 C 161.7415,283.61396 169.32801,287.71649 169.32801,287.71649 C 169.89523,288.02323 170.70097,288.00619 171.1335,287.67818 C 171.1335,287.67818 176.93604,283.27774 176.93604,283.27774 C 177.36252,282.95432 177.24799,282.45207 176.68036,282.15173 C 176.68036,282.15173 176.68036,282.15173 176.68036,282.15173"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3305"
+           d="M 186.05575,275.08382 C 186.05575,275.08382 178.66837,271.26624 178.66837,271.26624 C 178.14275,270.99462 177.38016,271.01579 176.95654,271.31482 C 176.95654,271.31482 170.80827,275.65494 170.80827,275.65494 C 170.35386,275.97571 170.42595,276.46372 170.9721,276.74789 C 170.9721,276.74789 178.64907,280.74215 178.64907,280.74215 C 179.19855,281.02804 179.99123,280.98835 180.42429,280.65448 C 180.42429,280.65448 186.28302,276.1377 186.28302,276.1377 C 186.68663,275.82653 186.58444,275.35703 186.05575,275.08382 C 186.05575,275.08382 186.05575,275.08382 186.05575,275.08382"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3307"
+           d="M 163.51597,267.87954 C 163.51597,267.87954 171.1646,271.91896 171.1646,271.91896 C 171.53604,272.11513 171.58651,272.4514 171.2774,272.67324 C 171.2774,272.67324 164.82134,277.30652 164.82134,277.30652 C 164.50235,277.53544 163.94045,277.55958 163.56185,277.36032 C 163.56185,277.36032 155.76574,273.25703 155.76574,273.25703 C 155.38595,273.05714 155.34321,272.7142 155.66949,272.48842 C 155.66949,272.48842 162.27303,267.91901 162.27303,267.91901 C 162.5892,267.70024 163.14337,267.68276 163.51597,267.87954 C 163.51597,267.87954 163.51597,267.87954 163.51597,267.87954"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3309"
+           d="M 173.08132,261.26965 C 173.08132,261.26965 180.42033,265.06231 180.42033,265.06231 C 180.84108,265.27975 180.901,265.65731 180.55405,265.90902 C 180.55405,265.90902 174.39819,270.3752 174.39819,270.3752 C 174.04312,270.63281 173.41533,270.66166 172.99122,270.43971 C 172.99122,270.43971 165.59423,266.56865 165.59423,266.56865 C 165.17482,266.34916 165.12621,265.96859 165.4846,265.71552 C 165.4846,265.71552 171.69844,261.32774 171.69844,261.32774 C 172.04868,261.08043 172.66517,261.05459 173.08132,261.26965 C 173.08132,261.26965 173.08132,261.26965 173.08132,261.26965"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3311"
+           d="M 183.15808,254.05968 C 183.15808,254.05968 190.5411,257.89809 190.5411,257.89809 C 190.90119,258.0853 190.9368,258.42917 190.61983,258.66983 C 190.61983,258.66983 183.84308,263.81508 183.84308,263.81508 C 183.50031,264.07534 182.92132,264.12739 182.54615,263.93097 C 182.54615,263.93097 174.85492,259.90428 174.85492,259.90428 C 174.48192,259.709 174.47113,259.34932 174.82945,259.09862 C 174.82945,259.09862 181.91436,254.14161 181.91436,254.14161 C 182.24579,253.90973 182.79998,253.8735 183.15808,254.05968 C 183.15808,254.05968 183.15808,254.05968 183.15808,254.05968"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3313"
+           d="M 195.50846,268.00414 C 195.50846,268.00414 187.92237,264.08387 187.92237,264.08387 C 187.38263,263.80495 186.5995,263.82667 186.16449,264.13375 C 186.16449,264.13375 179.85083,268.59062 179.85083,268.59062 C 179.38419,268.92002 179.45825,269.42118 180.01907,269.71297 C 180.01907,269.71297 187.90254,273.81468 187.90254,273.81468 C 188.4668,274.10826 189.28081,274.0675 189.72552,273.72465 C 189.72552,273.72465 195.74185,269.08637 195.74185,269.08637 C 196.15632,268.76683 196.05137,268.2847 195.50846,268.00414 C 195.50846,268.00414 195.50846,268.00414 195.50846,268.00414"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3315"
+           d="M 202.55006,278.55525 C 202.55006,278.55525 194.52053,274.43157 194.52053,274.43157 C 193.89847,274.1121 193.03474,274.13023 192.58051,274.47152 C 192.58051,274.47152 186.82178,278.79836 186.82178,278.79836 C 186.35477,279.14926 186.47106,279.70327 187.086,280.04131 C 187.086,280.04131 195.02775,284.40711 195.02775,284.40711 C 195.68386,284.76779 196.59706,284.76041 197.07144,284.38968 C 197.07144,284.38968 202.9179,279.82067 202.9179,279.82067 C 203.3788,279.46048 203.21306,278.89574 202.55006,278.55525 C 202.55006,278.55525 202.55006,278.55525 202.55006,278.55525"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path3317"
+           d="M 217.28268,282.30723 C 217.28268,282.30723 209.76798,278.51506 209.76798,278.51506 C 209.31861,278.28829 208.66038,278.33252 208.29049,278.6148 C 208.29049,278.6148 201.99168,283.42172 201.99168,283.42172 C 201.5992,283.72125 201.65194,284.15497 202.11173,284.39356 C 202.11173,284.39356 209.80365,288.38505 209.80365,288.38505 C 210.27451,288.62939 210.96221,288.57431 211.3438,288.26224 C 211.3438,288.26224 217.46536,283.25603 217.46536,283.25603 C 217.82471,282.96216 217.7425,282.53927 217.28268,282.30723 C 217.28268,282.30723 217.28268,282.30723 217.28268,282.30723"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5259"
+           sodipodi:nodetypes="cccccccccc"
+           d="M 223.6315,280.9685 C 223.6315,280.9685 271.20382,242.80623 271.20382,242.80623 C 271.77183,242.35446 272.94396,242.34509 273.8347,242.78481 C 273.8347,242.78481 280.16259,245.8893 280.16259,245.8893 C 281.06675,246.33566 281.34594,247.06729 280.78574,247.53016 C 280.78574,247.53016 233.93323,286.78539 233.93323,286.78539 C 233.23622,287.36129 231.89705,287.39044 230.93425,286.85016 C 230.93425,286.85016 224.06489,282.94036 224.06489,282.94036 C 223.11807,282.40904 222.92637,281.52934 223.6315,280.9685 C 223.6315,280.9685 223.6315,280.9685 223.6315,280.9685"
+           style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.69999999;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5261"
+           d="M 227.03565,274.61509 C 227.03565,274.61509 219.57159,270.89543 219.57159,270.89543 C 219.13842,270.67957 218.48938,270.73732 218.1151,271.02521 C 218.1151,271.02521 211.61304,276.02653 211.61304,276.02653 C 211.22381,276.32593 211.26193,276.7481 211.69984,276.97279 C 211.69984,276.97279 219.24737,280.84554 219.24737,280.84554 C 219.69565,281.07556 220.36702,281.012 220.75135,280.70336 C 220.75135,280.70336 227.16991,275.54895 227.16991,275.54895 C 227.53929,275.25232 227.47886,274.83596 227.03565,274.61509 C 227.03565,274.61509 227.03565,274.61509 227.03565,274.61509"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5263"
+           d="M 192.51964,247.35677 C 192.51964,247.35677 199.78325,251.31635 199.78325,251.31635 C 200.08373,251.48015 200.10518,251.76671 199.83193,251.95915 C 199.83193,251.95915 193.39598,256.49168 193.39598,256.49168 C 193.12338,256.68366 192.65687,256.71094 192.34938,256.55257 C 192.34938,256.55257 184.91676,252.72442 184.91676,252.72442 C 184.59365,252.55801 184.55824,252.26477 184.83799,252.06723 C 184.83799,252.06723 191.44296,247.40326 191.44296,247.40326 C 191.7234,247.20524 192.20387,247.18464 192.51964,247.35677 C 192.51964,247.35677 192.51964,247.35677 192.51964,247.35677"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5265"
+           d="M 204.95758,260.67876 C 204.95758,260.67876 197.45088,256.83357 197.45088,256.83357 C 196.91888,256.56107 196.14744,256.58436 195.71989,256.88617 C 195.71989,256.88617 189.62606,261.18786 189.62606,261.18786 C 189.18381,261.50005 189.26067,261.97752 189.79977,262.25801 C 189.79977,262.25801 197.4078,266.21641 197.4078,266.21641 C 197.95451,266.50085 198.7459,266.47153 199.18087,266.15108 C 199.18087,266.15108 205.17336,261.73618 205.17336,261.73618 C 205.59375,261.42646 205.49693,260.95503 204.95758,260.67876 C 204.95758,260.67876 204.95758,260.67876 204.95758,260.67876"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5267"
+           d="M 203.42735,267.20908 C 203.42735,267.20908 212.2333,271.70369 212.2333,271.70369 C 212.6248,271.90351 212.71338,272.23708 212.43163,272.45164 C 212.43163,272.45164 206.02839,277.32796 206.02839,277.32796 C 205.74282,277.54543 205.19609,277.55776 204.8028,277.35556 C 204.8028,277.35556 195.957,272.80759 195.957,272.80759 C 195.56658,272.60686 195.48379,272.27205 195.77113,272.05697 C 195.77113,272.05697 202.21422,267.23402 202.21422,267.23402 C 202.49773,267.02179 203.03868,267.0107 203.42735,267.20908 C 203.42735,267.20908 203.42735,267.20908 203.42735,267.20908"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5269"
+           d="M 212.72962,260.6903 C 212.72962,260.6903 220.81856,264.71792 220.81856,264.71792 C 221.40722,265.01103 221.56351,265.49233 221.1669,265.79697 C 221.1669,265.79697 215.52012,270.13427 215.52012,270.13427 C 215.11075,270.44871 214.30434,270.45549 213.7142,270.1496 C 213.7142,270.1496 205.60787,265.94776 205.60787,265.94776 C 205.04085,265.65385 204.91653,265.17445 205.32701,264.87276 C 205.32701,264.87276 210.99121,260.70967 210.99121,260.70967 C 211.38919,260.41716 212.16362,260.40847 212.72962,260.6903 C 212.72962,260.6903 212.72962,260.6903 212.72962,260.6903"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5271"
+           d="M 235.38208,267.94652 C 235.38208,267.94652 228.36195,264.54302 228.36195,264.54302 C 227.7581,264.25027 226.91755,264.28848 226.47592,264.62855 C 226.47592,264.62855 221.36693,268.56268 221.36693,268.56268 C 220.91972,268.90705 221.04356,269.42537 221.64599,269.72502 C 221.64599,269.72502 228.65018,273.2089 228.65018,273.2089 C 229.26637,273.51538 230.12476,273.47935 230.5734,273.12791 C 230.5734,273.12791 235.69833,269.1134 235.69833,269.1134 C 236.14129,268.76642 235.99961,268.24591 235.38208,267.94652 C 235.38208,267.94652 235.38208,267.94652 235.38208,267.94652"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5273"
+           d="M 202.3439,240.55011 C 202.3439,240.55011 209.40685,244.3565 209.40685,244.3565 C 209.77966,244.55741 209.79685,244.91929 209.44513,245.16789 C 209.44513,245.16789 202.71836,249.92236 202.71836,249.92236 C 202.36384,250.17294 201.77847,250.21096 201.40617,250.00761 C 201.40617,250.00761 194.3529,246.15501 194.3529,246.15501 C 193.98466,245.95386 193.97344,245.59217 194.32745,245.34403 C 194.32745,245.34403 201.04453,240.63577 201.04453,240.63577 C 201.39575,240.38958 201.97514,240.35138 202.3439,240.55011 C 202.3439,240.55011 202.3439,240.55011 202.3439,240.55011"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5275"
+           d="M 214.95641,253.88523 C 214.95641,253.88523 207.20865,250.11064 207.20865,250.11064 C 206.78058,249.90209 206.15223,249.93501 205.79811,250.18412 C 205.79811,250.18412 199.5121,254.60606 199.5121,254.60606 C 199.14935,254.86124 199.19499,255.24389 199.61614,255.46439 C 199.61614,255.46439 207.24189,259.45703 207.24189,259.45703 C 207.69079,259.69206 208.35153,259.66596 208.72148,259.39808 C 208.72148,259.39808 215.12948,254.75806 215.12948,254.75806 C 215.49031,254.49678 215.41229,254.10733 214.95641,253.88523 C 214.95641,253.88523 214.95641,253.88523 214.95641,253.88523"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5277"
+           d="M 221.9033,253.92205 C 221.9033,253.92205 229.74607,257.77376 229.74607,257.77376 C 230.38047,258.08533 230.53912,258.61203 230.09991,258.95492 C 230.09991,258.95492 224.52409,263.30793 224.52409,263.30793 C 224.07129,263.66142 223.1901,263.68688 222.55025,263.36479 C 222.55025,263.36479 214.64187,259.38384 214.64187,259.38384 C 214.01663,259.0691 213.88352,258.53947 214.34155,258.19654 C 214.34155,258.19654 219.983,253.97277 219.983,253.97277 C 220.42747,253.64 221.28312,253.61747 221.9033,253.92205 C 221.9033,253.92205 221.9033,253.92205 221.9033,253.92205"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 244.75785,260.58731 C 244.75785,260.58731 237.5872,257.12295 237.5872,257.12295 C 237.05869,256.86761 236.28144,256.9365 235.84374,257.27763 C 235.84374,257.27763 229.87966,261.92591 229.87966,261.92591 C 229.43444,262.2729 229.5034,262.76148 230.03504,263.02113 C 230.03504,263.02113 237.24843,266.54415 237.24843,266.54415 C 237.78463,266.80603 238.57278,266.73378 239.01483,266.38238 C 239.01483,266.38238 244.93616,261.67544 244.93616,261.67544 C 245.3707,261.33002 245.29084,260.84482 244.75785,260.58731 C 244.75785,260.58731 244.75785,260.58731 244.75785,260.58731"
+           id="path5279"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 233.2943,256.416 C 233.2943,256.416 238.45564,252.43646 238.45564,252.43646 C 238.83398,252.14474 238.66697,251.68224 238.08213,251.39936 C 238.08213,251.39936 230.23378,247.60312 230.23378,247.60312 C 229.65678,247.32402 228.88557,247.32967 228.50367,247.6159 C 228.50367,247.6159 223.29424,251.52023 223.29424,251.52023 C 222.90493,251.812 223.05807,252.27611 223.63856,252.56071 C 223.63856,252.56071 231.535,256.43217 231.535,256.43217 C 232.12348,256.72068 232.90856,256.71342 233.2943,256.416 C 233.2943,256.416 233.2943,256.416 233.2943,256.416"
+           id="path5281"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5283"
+           d="M 224.84883,246.55597 C 224.84883,246.55597 217.52734,242.94323 217.52734,242.94323 C 216.98183,242.67405 216.15475,242.73355 215.67283,243.07723 C 215.67283,243.07723 209.10533,247.76083 209.10533,247.76083 C 208.61499,248.11051 208.66694,248.60923 209.22188,248.87843 C 209.22188,248.87843 216.66948,252.49124 216.66948,252.49124 C 217.21707,252.75687 218.04508,252.68874 218.52598,252.33907 C 218.52598,252.33907 224.96738,247.65541 224.96738,247.65541 C 225.44008,247.3117 225.38717,246.82161 224.84883,246.55597 C 224.84883,246.55597 224.84883,246.55597 224.84883,246.55597"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5285"
+           d="M 211.2176,234.55068 C 211.2176,234.55068 218.13924,237.73238 218.13924,237.73238 C 218.66737,237.97515 218.77598,238.41021 218.37697,238.70684 C 218.37697,238.70684 212.62031,242.9864 212.62031,242.9864 C 212.1974,243.3008 211.43997,243.33022 210.928,243.05365 C 210.928,243.05365 204.23311,239.43697 204.23311,239.43697 C 203.78717,239.19607 203.75848,238.77609 204.16385,238.4943 C 204.16385,238.4943 209.694,234.65 209.694,234.65 C 210.07817,234.38294 210.75554,234.33829 211.2176,234.55068 C 211.2176,234.55068 211.2176,234.55068 211.2176,234.55068"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5287"
+           d="M 233.15615,239.94299 C 233.15615,239.94299 226.44421,236.69379 226.44421,236.69379 C 225.78369,236.37403 224.84989,236.41177 224.348,236.77797 C 224.348,236.77797 219.29773,240.46286 219.29773,240.46286 C 218.78807,240.83472 218.90378,241.40005 219.55968,241.73088 C 219.55968,241.73088 226.22576,245.09312 226.22576,245.09312 C 226.90521,245.43582 227.86846,245.40352 228.38283,245.02018 C 228.38283,245.02018 233.47896,241.22224 233.47896,241.22224 C 233.98531,240.84488 233.84015,240.27412 233.15615,239.94299 C 233.15615,239.94299 233.15615,239.94299 233.15615,239.94299"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5289"
+           d="M 220.30432,227.87754 C 220.30432,227.87754 227.46486,231.13608 227.46486,231.13608 C 227.82573,231.3003 227.87475,231.60963 227.57378,231.82996 C 227.57378,231.82996 221.38229,236.3625 221.38229,236.3625 C 221.06565,236.59429 220.51407,236.64288 220.14663,236.4711 C 220.14663,236.4711 212.85793,233.06353 212.85793,233.06353 C 212.49851,232.8955 212.47011,232.57953 212.7932,232.35543 C 212.7932,232.35543 219.11286,227.97192 219.11286,227.97192 C 219.42016,227.75877 219.95112,227.71681 220.30432,227.87754 C 220.30432,227.87754 220.30432,227.87754 220.30432,227.87754"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 253.8328,253.35343 C 253.8328,253.35343 246.80705,249.98982 246.80705,249.98982 C 246.25118,249.7237 245.43236,249.7982 244.96971,250.15699 C 244.96971,250.15699 239.12467,254.6899 239.12467,254.6899 C 238.65231,255.05622 238.71907,255.57195 239.27581,255.84611 C 239.27581,255.84611 246.31353,259.3118 246.31353,259.3118 C 246.88258,259.59203 247.72148,259.51595 248.19294,259.14132 C 248.19294,259.14132 254.02601,254.50633 254.02601,254.50633 C 254.48765,254.13951 254.40082,253.62536 253.8328,253.35343 C 253.8328,253.35343 253.8328,253.35343 253.8328,253.35343"
+           id="path5291"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 241.69245,250.18836 C 241.69245,250.18836 246.86241,246.20217 246.86241,246.20217 C 247.27195,245.8864 247.09122,245.38581 246.45826,245.07965 C 246.45826,245.07965 238.59679,241.27706 238.59679,241.27706 C 237.97222,240.97496 237.13747,240.98108 236.72408,241.29091 C 236.72408,241.29091 231.50595,245.20176 231.50595,245.20176 C 231.08462,245.51753 231.25039,246.01986 231.87874,246.32792 C 231.87874,246.32792 239.78839,250.20585 239.78839,250.20585 C 240.42527,250.5181 241.27499,250.51024 241.69245,250.18836 C 241.69245,250.18836 241.69245,250.18836 241.69245,250.18836"
+           id="path5293"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5295"
+           d="M 262.29906,246.8676 C 262.29906,246.8676 254.88168,243.44853 254.88168,243.44853 C 254.50008,243.27263 253.95712,243.3153 253.66336,243.54406 C 253.66336,243.54406 248.0194,247.9394 248.0194,247.9394 C 247.72057,248.17212 247.78403,248.50657 248.1626,248.68946 C 248.1626,248.68946 255.52239,252.24492 255.52239,252.24492 C 255.9167,252.43541 256.47799,252.39569 256.77993,252.1557 C 256.77993,252.1557 262.48146,247.62397 262.48146,247.62397 C 262.77816,247.38815 262.69638,247.05074 262.29906,246.8676 C 262.29906,246.8676 262.29906,246.8676 262.29906,246.8676"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5297"
+           d="M 269.92313,240.18072 C 269.92313,240.18072 263.31143,237.09826 263.31143,237.09826 C 262.81933,236.86884 262.13676,236.90784 261.77997,237.18694 C 261.77997,237.18694 256.80537,241.07833 256.80537,241.07833 C 256.42536,241.3756 256.53169,241.80611 257.04493,242.04226 C 257.04493,242.04226 263.93919,245.21441 263.93919,245.21441 C 264.44655,245.44785 265.14436,245.3899 265.50271,245.08585 C 265.50271,245.08585 270.19479,241.10477 270.19479,241.10477 C 270.53139,240.81918 270.4098,240.40761 269.92313,240.18072 C 269.92313,240.18072 269.92313,240.18072 269.92313,240.18072"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5299"
+           d="M 284.15897,244.25926 C 284.15897,244.25926 290.08975,239.34554 290.08975,239.34554 C 290.3372,239.14053 290.25996,238.84705 289.91756,238.6876 C 289.91756,238.6876 282.05801,235.02758 282.05801,235.02758 C 281.73357,234.87649 281.27511,234.91294 281.02917,235.10924 C 281.02917,235.10924 275.13725,239.81168 275.13725,239.81168 C 274.88342,240.01427 274.9381,240.30589 275.26078,240.46561 C 275.26078,240.46561 283.08145,244.3369 283.08145,244.3369 C 283.42232,244.50564 283.90334,244.47105 284.15897,244.25926 C 284.15897,244.25926 284.15897,244.25926 284.15897,244.25926"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5301"
+           d="M 290.6347,228.41799 C 290.6347,228.41799 298.47677,231.7748 298.47677,231.7748 C 298.96904,231.98552 299.08998,232.38046 298.7459,232.65955 C 298.7459,232.65955 293.04479,237.28397 293.04479,237.28397 C 292.69915,237.56434 292.03399,237.60956 291.55538,237.38617 C 291.55538,237.38617 283.93166,233.82786 283.93166,233.82786 C 283.4873,233.62045 283.39853,233.2382 283.73091,232.97007 C 283.73091,232.97007 289.21363,228.54719 289.21363,228.54719 C 289.54456,228.28023 290.17756,228.22231 290.6347,228.41799 C 290.6347,228.41799 290.6347,228.41799 290.6347,228.41799"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5303"
+           d="M 301.86982,230.05516 C 301.86982,230.05516 306.72655,226.01075 306.72655,226.01075 C 307.20873,225.60921 307.07252,225.04406 306.41901,224.74173 C 306.41901,224.74173 299.71761,221.64153 299.71761,221.64153 C 299.02841,221.32268 298.06453,221.39571 297.55878,221.80728 C 297.55878,221.80728 292.46319,225.95398 292.46319,225.95398 C 291.94831,226.37298 292.11057,226.9602 292.82454,227.2686 C 292.82454,227.2686 299.76474,230.26652 299.76474,230.26652 C 300.44135,230.5588 301.37922,230.4637 301.86982,230.05516 C 301.86982,230.05516 301.86982,230.05516 301.86982,230.05516"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5305"
+           d="M 229.75901,221.51708 C 229.75901,221.51708 236.30365,224.3537 236.30365,224.3537 C 236.77779,224.55921 236.83816,224.95708 236.4374,225.2459 C 236.4374,225.2459 230.73685,229.35443 230.73685,229.35443 C 230.32395,229.65201 229.60775,229.71902 229.13267,229.50455 C 229.13267,229.50455 222.57677,226.54493 222.57677,226.54493 C 222.11612,226.33697 222.07952,225.93711 222.49312,225.64852 C 222.49312,225.64852 228.20494,221.66299 228.20494,221.66299 C 228.60659,221.38273 229.29903,221.31772 229.75901,221.51708 C 229.75901,221.51708 229.75901,221.51708 229.75901,221.51708"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5307"
+           d="M 241.4331,233.77444 C 241.4331,233.77444 234.86702,230.51453 234.86702,230.51453 C 234.2884,230.22725 233.46852,230.2512 233.03004,230.56861 C 233.03004,230.56861 228.00571,234.20578 228.00571,234.20578 C 227.5701,234.52112 227.69,235.00165 228.27321,235.28282 C 228.27321,235.28282 234.89177,238.47354 234.89177,238.47354 C 235.46008,238.74751 236.26364,238.71874 236.69472,238.40938 C 236.69472,238.40938 241.66657,234.84141 241.66657,234.84141 C 242.10045,234.53004 241.99687,234.05434 241.4331,233.77444 C 241.4331,233.77444 241.4331,233.77444 241.4331,233.77444"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5309"
+           d="M 250.21733,243.40142 C 250.21733,243.40142 255.39176,239.30139 255.39176,239.30139 C 255.89164,238.9053 255.71333,238.31587 254.9964,237.97824 C 254.9964,237.97824 247.93065,234.65074 247.93065,234.65074 C 247.23226,234.32185 246.25306,234.35802 245.73067,234.73327 C 245.73067,234.73327 240.32649,238.61528 240.32649,238.61528 C 239.76575,239.01809 239.8905,239.62564 240.61117,239.97588 C 240.61117,239.97588 247.90666,243.52145 247.90666,243.52145 C 248.64734,243.88142 249.68011,243.82709 250.21733,243.40142 C 250.21733,243.40142 250.21733,243.40142 250.21733,243.40142"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 259.03235,236.38432 C 259.03235,236.38432 264.00233,232.5172 264.00233,232.5172 C 264.49375,232.13483 264.28734,231.56751 263.54605,231.2468 C 263.54605,231.2468 256.4643,228.18292 256.4643,228.18292 C 255.78568,227.88932 254.86097,227.94181 254.38524,228.29923 C 254.38524,228.29923 249.57716,231.91156 249.57716,231.91156 C 249.08982,232.2777 249.22635,232.82706 249.889,233.14485 C 249.889,233.14485 256.80881,236.46342 256.80881,236.46342 C 257.53364,236.81102 258.52826,236.77654 259.03235,236.38432 C 259.03235,236.38432 259.03235,236.38432 259.03235,236.38432"
+           id="path5311"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5313"
+           d="M 249.92718,227.47874 C 249.92718,227.47874 243.57843,224.26363 243.57843,224.26363 C 242.94161,223.94113 242.03351,223.96693 241.54071,224.32288 C 241.54071,224.32288 236.50753,227.95826 236.50753,227.95826 C 235.99196,228.33065 236.10354,228.89802 236.75945,229.2289 C 236.75945,229.2289 243.29817,232.52743 243.29817,232.52743 C 243.95188,232.8572 244.8789,232.81481 245.37499,232.4339 C 245.37499,232.4339 250.21821,228.7151 250.21821,228.7151 C 250.69243,228.35098 250.56193,227.80019 249.92718,227.47874 C 249.92718,227.47874 249.92718,227.47874 249.92718,227.47874"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5315"
+           d="M 278.37779,233.42877 C 278.37779,233.42877 271.86628,230.49633 271.86628,230.49633 C 271.35525,230.26619 270.61009,230.34047 270.19363,230.66278 C 270.19363,230.66278 264.92861,234.73754 264.92861,234.73754 C 264.50283,235.06706 264.56772,235.52596 265.07602,235.76657 C 265.07602,235.76657 271.55435,238.83315 271.55435,238.83315 C 272.08251,239.08315 272.85436,239.01044 273.28293,238.66998 C 273.28293,238.66998 278.58112,234.46107 278.58112,234.46107 C 279.00009,234.12824 278.90852,233.66778 278.37779,233.42877 C 278.37779,233.42877 278.37779,233.42877 278.37779,233.42877"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 286.64967,226.73945 C 286.64967,226.73945 280.75469,223.85413 280.75469,223.85413 C 280.1553,223.56076 279.26816,223.63562 278.7659,224.02366 C 278.7659,224.02366 273.67528,227.95655 273.67528,227.95655 C 273.15783,228.35631 273.24091,228.914 273.86123,229.20522 C 273.86123,229.20522 279.9603,232.06855 279.9603,232.06855 C 280.56171,232.3509 281.4454,232.25691 281.94196,231.85949 C 281.94196,231.85949 286.82847,227.94856 286.82847,227.94856 C 287.31074,227.56258 287.23111,227.02404 286.64967,226.73945 C 286.64967,226.73945 286.64967,226.73945 286.64967,226.73945"
+           id="path5317"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5319"
+           d="M 267.6823,229.91307 C 267.6823,229.91307 272.46908,226.23566 272.46908,226.23566 C 272.94434,225.87054 272.74124,225.3253 272.01611,225.01342 C 272.01611,225.01342 264.88193,221.94496 264.88193,221.94496 C 264.17799,221.64219 263.23154,221.68466 262.7575,222.03994 C 262.7575,222.03994 257.98381,225.61763 257.98381,225.61763 C 257.50171,225.97894 257.67724,226.52125 258.37978,226.83381 C 258.37978,226.83381 265.50086,230.00199 265.50086,230.00199 C 266.22477,230.32406 267.1988,230.28451 267.6823,229.91307 C 267.6823,229.91307 267.6823,229.91307 267.6823,229.91307"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 258.51488,221.22986 C 258.51488,221.22986 252.19037,218.21416 252.19037,218.21416 C 251.57618,217.9213 250.69327,217.96397 250.20557,218.30991 C 250.20557,218.30991 245.20204,221.8591 245.20204,221.8591 C 244.68719,222.22431 244.76403,222.77336 245.37981,223.09013 C 245.37981,223.09013 251.72747,226.35547 251.72747,226.35547 C 252.38375,226.69307 253.32987,226.6498 253.84288,226.25849 C 253.84288,226.25849 258.82312,222.45965 258.82312,222.45965 C 259.30803,222.08977 259.16806,221.54132 258.51488,221.22986 C 258.51488,221.22986 258.51488,221.22986 258.51488,221.22986"
+           id="path5321"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5323"
+           d="M 239.22906,214.77058 C 239.22906,214.77058 245.75948,217.65839 245.75948,217.65839 C 246.22836,217.86574 246.27624,218.27214 245.86583,218.5699 C 245.86583,218.5699 240.04074,222.79608 240.04074,222.79608 C 239.61975,223.10153 238.89933,223.17644 238.42646,222.96374 C 238.42646,222.96374 231.84118,220.00174 231.84118,220.00174 C 231.37421,219.79169 231.34116,219.38081 231.76608,219.08076 C 231.76608,219.08076 237.64602,214.92874 237.64602,214.92874 C 238.06034,214.63619 238.76594,214.56577 239.22906,214.77058 C 239.22906,214.77058 239.22906,214.77058 239.22906,214.77058"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5325"
+           d="M 247.90724,208.64597 C 247.90724,208.64597 254.53759,211.50631 254.53759,211.50631 C 255.01636,211.71285 255.09618,212.10048 254.71562,212.37537 C 254.71562,212.37537 249.34818,216.25246 249.34818,216.25246 C 248.96271,216.5309 248.26761,216.58434 247.79061,216.37235 C 247.79061,216.37235 241.18538,213.43689 241.18538,213.43689 C 240.71963,213.22991 240.65428,212.84354 241.03791,212.57052 C 241.03791,212.57052 246.38024,208.76857 246.38024,208.76857 C 246.75905,208.49898 247.43968,208.44427 247.90724,208.64597 C 247.90724,208.64597 247.90724,208.64597 247.90724,208.64597"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5327"
+           d="M 266.53609,215.35847 C 266.53609,215.35847 260.2425,212.50539 260.2425,212.50539 C 259.63111,212.22823 258.77381,212.26339 258.31648,212.58345 C 258.31648,212.58345 253.69899,215.81504 253.69899,215.81504 C 253.23142,216.14228 253.3369,216.6411 253.93933,216.93434 C 253.93933,216.93434 260.14335,219.95417 260.14335,219.95417 C 260.78415,220.26608 261.68732,220.24081 262.16416,219.89654 C 262.16416,219.89654 266.87121,216.49816 266.87121,216.49816 C 267.3372,216.16172 267.18586,215.65303 266.53609,215.35847 C 266.53609,215.35847 266.53609,215.35847 266.53609,215.35847"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5329"
+           d="M 275.86123,223.49708 C 275.86123,223.49708 280.69597,219.76003 280.69597,219.76003 C 281.17477,219.38993 280.98509,218.85214 280.27334,218.55429 C 280.27334,218.55429 273.2735,215.62511 273.2735,215.62511 C 272.58309,215.3362 271.64347,215.39287 271.16418,215.75208 C 271.16418,215.75208 266.32552,219.37853 266.32552,219.37853 C 265.83563,219.74569 265.99513,220.28253 266.68578,220.58229 C 266.68578,220.58229 273.68954,223.62208 273.68954,223.62208 C 274.40184,223.93123 275.37164,223.87552 275.86123,223.49708 C 275.86123,223.49708 275.86123,223.49708 275.86123,223.49708"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 282.59083,221.04513 C 282.59083,221.04513 287.71903,217.05699 287.71903,217.05699 C 288.22587,216.66285 289.08472,216.56532 289.64647,216.83783 C 289.64647,216.83783 295.29725,219.57907 295.29725,219.57907 C 295.86731,219.85561 295.92338,220.40166 295.42118,220.80393 C 295.42118,220.80393 290.33924,224.87461 290.33924,224.87461 C 289.82349,225.28773 288.94404,225.3914 288.36924,225.1065 C 288.36924,225.1065 282.67221,222.28271 282.67221,222.28271 C 282.10592,222.00203 282.07045,221.44982 282.59083,221.04513 C 282.59083,221.04513 282.59083,221.04513 282.59083,221.04513"
+           id="path5331"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5333"
+           d="M 273.82385,209.9072 C 273.82385,209.9072 267.67742,207.1135 267.67742,207.1135 C 267.05691,206.83146 266.21983,206.85051 265.80038,207.15733 C 265.80038,207.15733 261.55003,210.26641 261.55003,210.26641 C 261.11807,210.58238 261.28238,211.06634 261.91859,211.35037 C 261.91859,211.35037 268.21969,214.1634 268.21969,214.1634 C 268.84554,214.4428 269.68458,214.41091 270.10075,214.09302 C 270.10075,214.09302 274.19645,210.96462 274.19645,210.96462 C 274.60069,210.65585 274.43443,210.18473 273.82385,209.9072 C 273.82385,209.9072 273.82385,209.9072 273.82385,209.9072"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5335"
+           d="M 257.3726,201.76885 C 257.3726,201.76885 264.64202,204.86157 264.64202,204.86157 C 264.95177,204.99335 264.97965,205.26073 264.70378,205.46149 C 264.70378,205.46149 257.98723,210.34941 257.98723,210.34941 C 257.69315,210.56343 257.19866,210.62527 256.87934,210.48761 C 256.87934,210.48761 249.38649,207.25742 249.38649,207.25742 C 249.07032,207.12112 249.06104,206.84361 249.36486,206.63564 C 249.36486,206.63564 256.30482,201.88518 256.30482,201.88518 C 256.58991,201.69003 257.06581,201.63832 257.3726,201.76885 C 257.3726,201.76885 257.3726,201.76885 257.3726,201.76885"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5337"
+           d="M 284.0905,217.40082 C 284.0905,217.40082 288.28521,214.18116 288.28521,214.18116 C 288.70353,213.86008 288.47298,213.36105 287.76806,213.06182 C 287.76806,213.06182 280.69745,210.06045 280.69745,210.06045 C 279.9862,209.75854 279.07181,209.77379 278.64748,210.09508 C 278.64748,210.09508 274.39236,213.31696 274.39236,213.31696 C 273.96443,213.64097 274.19761,214.14507 274.91495,214.44677 C 274.91495,214.44677 282.04596,217.44592 282.04596,217.44592 C 282.75689,217.74492 283.66866,217.7246 284.0905,217.40082 C 284.0905,217.40082 284.0905,217.40082 284.0905,217.40082"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5339"
+           d="M 300.10154,217.06983 C 300.10154,217.06983 316.31837,203.96829 316.31837,203.96829 C 317.07607,203.35614 317.4003,202.7538 317.04873,202.61609 C 317.04873,202.61609 310.03269,199.86796 310.03269,199.86796 C 309.70146,199.73822 308.83575,200.10447 308.08844,200.69068 C 308.08844,200.69068 292.11577,213.22035 292.11577,213.22035 C 291.30809,213.85393 290.91207,214.49139 291.23134,214.64793 C 291.23134,214.64793 298.003,217.96805 298.003,217.96805 C 298.34279,218.13465 299.2804,217.73322 300.10154,217.06983 C 300.10154,217.06983 300.10154,217.06983 300.10154,217.06983"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 291.15172,211.81971 C 291.15172,211.81971 294.95784,208.85652 294.95784,208.85652 C 295.33535,208.56261 295.06588,208.08714 294.35728,207.79127 C 294.35728,207.79127 287.46322,204.91266 287.46322,204.91266 C 286.7905,204.63177 285.951,204.6327 285.57753,204.91398 C 285.57753,204.91398 281.81341,207.749 281.81341,207.749 C 281.43293,208.03556 281.66197,208.50175 282.33038,208.79505 C 282.33038,208.79505 289.18243,211.80181 289.18243,211.80181 C 289.88692,212.11095 290.76688,212.11932 291.15172,211.81971 C 291.15172,211.81971 291.15172,211.81971 291.15172,211.81971"
+           id="path5341"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           d="M 279.78845,205.15721 C 279.78845,205.15721 273.64923,202.7238 273.64923,202.7238 C 273.07415,202.49585 272.35188,202.50572 272.02271,202.74427 C 272.02271,202.74427 268.6721,205.17246 268.6721,205.17246 C 268.33004,205.42034 268.50084,205.82253 269.06217,206.07617 C 269.06217,206.07617 275.0647,208.78839 275.0647,208.78839 C 275.70867,209.07937 276.52129,209.09274 276.87788,208.8159 C 276.87788,208.8159 280.365,206.10863 280.365,206.10863 C 280.70701,205.84311 280.44598,205.41784 279.78845,205.15721 C 279.78845,205.15721 279.78845,205.15721 279.78845,205.15721"
+           id="path5343"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5345"
+           d="M 266.63729,203.4528 C 266.63729,203.4528 259.22437,200.28887 259.22437,200.28887 C 258.90107,200.15088 259.26093,199.6067 260.02623,199.07179 C 260.02623,199.07179 276.04515,187.8754 276.04515,187.8754 C 276.70328,187.4154 277.47826,187.14286 277.78675,187.26189 C 277.78675,187.26189 284.85126,189.98782 284.85126,189.98782 C 285.16872,190.11032 284.90984,190.59119 284.26684,191.06857 C 284.26684,191.06857 268.59644,202.70261 268.59644,202.70261 C 267.84684,203.25912 266.97083,203.59516 266.63729,203.4528 C 266.63729,203.4528 266.63729,203.4528 266.63729,203.4528"
+           style="opacity:0.99720004;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+        <path
+           id="path5347"
+           sodipodi:nodetypes="cccccssssccccc"
+           d="M 306.08894,198.05464 C 306.08894,198.05464 289.86483,191.75754 289.86483,191.75754 C 288.75309,191.32603 287.34294,191.34222 286.72043,191.80216 C 286.72043,191.80216 275.86985,199.81916 275.86985,199.81916 C 275.21811,200.30071 275.08509,200.71469 276.34881,201.06644 L 282.51762,203.58752 C 283.51554,203.99535 283.51356,204.01646 284.38014,203.38499 L 285.53498,202.54346 C 285.7875,202.35945 286.54428,202.2881 287.35569,202.6377 L 295.71352,206.2387 C 296.63524,206.49525 297.43593,206.57335 297.95625,206.16455 C 297.95625,206.16455 306.65122,199.3329 306.65122,199.3329 C 307.15193,198.93949 306.90591,198.37173 306.08894,198.05464 C 306.08894,198.05464 306.08894,198.05464 306.08894,198.05464"
+           style="opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+      </g>
+      <path
+         d="M 195.84384,316.18138 C 198.01729,317.52381 197.72963,318.48268 195.26851,320.68809 C 192.78733,322.9115 196.00365,319.69726 196.67486,318.67446 C 197.37566,317.60657 193.70779,314.86206 195.84384,316.18138 z "
+         sodipodi:nodetypes="czzz"
+         id="path2379"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3350"
+         sodipodi:nodetypes="czzz"
+         d="M 216.172,300.16816 C 218.34546,301.51059 218.79294,302.1818 216.33181,304.38722 C 213.85064,306.61061 216.81125,303.42834 217.61032,302.34161 C 218.38754,301.28458 214.03595,298.84884 216.172,300.16816 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 206.4554,308.31861 C 208.62885,309.66103 209.20418,310.14046 206.74306,312.34587 C 204.26188,314.56927 207.35035,311.1313 207.82978,310.49205 C 208.39381,309.74001 204.31935,306.99928 206.4554,308.31861 z "
+         sodipodi:nodetypes="czzz"
+         id="path3352"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3354"
+         sodipodi:nodetypes="czzz"
+         d="M 225.50506,292.46519 C 227.67851,293.80762 228.25383,294.28706 225.79272,296.49247 C 223.31154,298.71586 227.0073,295.62948 226.78356,294.4149 C 226.56079,293.20564 223.36901,291.14588 225.50506,292.46519 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3356"
+         sodipodi:nodetypes="czzz"
+         d="M 203.49886,293.36015 C 205.63491,294.67947 206.0239,295.13407 203.48288,297.25957 C 200.9308,299.39433 204.5367,295.76662 204.79335,295.2779 C 205.04344,294.80165 201.36281,292.04083 203.49886,293.36015 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 189.00386,289.3009 C 191.59281,290.73922 191.94897,291.213 189.29153,293.32818 C 186.61856,295.45568 190.37824,292.20949 190.37824,291.21865 C 190.37824,290.19585 186.42834,287.87006 189.00386,289.3009 z "
+         sodipodi:nodetypes="czzz"
+         id="path3358"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3360"
+         sodipodi:nodetypes="czzz"
+         d="M 171.16877,285.43344 C 173.47007,286.64801 174.17325,287.38316 171.45643,289.46072 C 168.74413,291.53484 172.83082,288.34203 172.60709,287.38316 C 172.36917,286.36356 168.8898,284.23064 171.16877,285.43344 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 155.9546,281.50205 C 158.12806,282.78055 158.83124,283.54764 156.24227,285.52933 C 153.64201,287.51964 157.42488,284.66634 157.20114,283.38784 C 156.97829,282.11439 153.75272,280.20681 155.9546,281.50205 z "
+         sodipodi:nodetypes="czzz"
+         id="path3362"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 161.10057,292.56108 C 163.27403,293.9035 163.96914,294.4897 161.38823,296.58835 C 158.81117,298.6839 157.15517,299.80076 160.6531,296.71621 C 164.16898,293.61583 158.96453,291.24176 161.10057,292.56108 z "
+         sodipodi:nodetypes="czzz"
+         id="path3364"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 178.74389,296.62031 C 182.35566,298.53807 181.81229,298.31433 176.12296,302.85301 C 170.44917,307.37928 178.76168,300.08938 179.60688,299.14536 C 180.42197,298.23497 175.15702,294.71579 178.74389,296.62031 z "
+         sodipodi:nodetypes="czzz"
+         id="path3366"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 193.22292,300.42386 C 195.39636,301.76628 195.49224,303.36442 189.3874,308.28664 C 183.26317,313.22451 195.05807,303.89438 193.51058,301.89414 C 192.00596,299.94933 191.08687,299.10454 193.22292,300.42386 z "
+         sodipodi:nodetypes="czzz"
+         id="path3368"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3370"
+         sodipodi:nodetypes="czzz"
+         d="M 145.18324,288.69362 C 147.80416,290.22781 147.8681,290.89903 145.18324,292.91266 C 142.52875,294.90353 146.62155,291.63417 146.39782,290.67529 C 146.18115,289.74671 142.58609,287.17332 145.18324,288.69362 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3372"
+         sodipodi:nodetypes="czzz"
+         d="M 165.73514,274.56618 C 167.9086,275.9086 168.61177,276.57982 166.0228,278.59347 C 163.40256,280.63143 166.7899,277.25103 167.20542,276.73963 C 167.59048,276.2657 163.5991,273.24685 165.73514,274.56618 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 181.26892,278.14598 C 183.44237,279.48841 184.0177,279.96784 181.55659,282.17326 C 179.07541,284.39665 182.5794,281.34223 182.35566,280.06373 C 182.13279,278.79029 179.13287,276.82666 181.26892,278.14598 z "
+         sodipodi:nodetypes="czzz"
+         id="path3374"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3376"
+         sodipodi:nodetypes="czzz"
+         d="M 198.2659,281.884 C 200.43936,283.22641 201.01468,283.70585 198.55356,285.91126 C 196.07238,288.13466 199.57637,285.08024 199.35263,283.80174 C 199.12976,282.52829 196.12986,280.56467 198.2659,281.884 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 213.08942,285.57681 C 215.8957,286.96443 216.29022,287.35347 213.37708,289.60408 C 210.50713,291.82133 214.72017,288.27028 214.62817,287.40415 C 214.54091,286.58272 210.2463,284.17095 213.08942,285.57681 z "
+         sodipodi:nodetypes="czzz"
+         id="path3378"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3380"
+         sodipodi:nodetypes="czzz"
+         d="M 175.13212,267.66227 C 177.30557,269.00471 177.99635,269.63282 175.41979,271.68956 C 172.87468,273.72117 176.44259,270.7946 176.41062,269.64395 C 176.37866,268.4933 172.99607,266.34296 175.13212,267.66227 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3382"
+         sodipodi:nodetypes="czzz"
+         d="M 190.72984,270.85853 C 192.96721,272.0731 193.60646,272.74431 191.01751,274.88581 C 188.47105,276.99212 192.23208,274.11871 192.00834,272.84021 C 191.78548,271.56676 188.48472,269.63975 190.72984,270.85853 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 207.55763,274.67852 C 209.73109,276.02094 210.30641,276.50038 207.8453,278.70579 C 205.36412,280.92919 208.8681,277.87477 208.64437,276.59627 C 208.4215,275.32282 205.42158,273.35919 207.55763,274.67852 z "
+         sodipodi:nodetypes="czzz"
+         id="path3384"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3386"
+         sodipodi:nodetypes="czzz"
+         d="M 223.20376,277.63457 C 226.23604,279.29342 225.95254,279.45644 223.49142,281.66186 C 221.01023,283.88525 224.74999,280.23696 224.6521,279.41673 C 224.56485,278.6857 220.20621,275.99474 223.20376,277.63457 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 277.38117,248.70302 C 281.95031,251.26589 281.48626,250.13465 270.88855,258.92295 C 260.38024,267.63712 274.33964,254.68517 277.12282,252.1444 C 279.88337,249.62427 272.7431,246.10148 277.38117,248.70302 z "
+         sodipodi:nodetypes="czzz"
+         id="path3388"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3390"
+         sodipodi:nodetypes="czzz"
+         d="M 231.57794,270.34712 C 233.75139,271.68956 234.32672,272.16899 231.86561,274.37441 C 229.38442,276.5978 232.88839,273.54338 232.66467,272.26488 C 232.4418,270.99143 229.44189,269.02781 231.57794,270.34712 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 241.29454,263.69892 C 243.46799,265.04135 244.04332,265.52079 241.58221,267.72621 C 239.10103,269.9496 242.60501,266.89517 242.38128,265.61667 C 242.15841,264.34322 239.15849,262.3796 241.29454,263.69892 z "
+         sodipodi:nodetypes="czzz"
+         id="path3392"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3394"
+         sodipodi:nodetypes="czzz"
+         d="M 250.24405,256.53932 C 252.4175,257.88175 252.99283,258.36119 250.53172,260.5666 C 248.05053,262.78999 251.55451,259.73557 251.33077,258.45707 C 251.10792,257.18362 248.10801,255.21999 250.24405,256.53932 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 258.68216,249.37971 C 260.85561,250.72214 261.43093,251.20158 258.96982,253.40699 C 256.48864,255.63038 259.99262,252.57596 259.76888,251.29747 C 259.54603,250.02402 256.54611,248.06039 258.68216,249.37971 z "
+         sodipodi:nodetypes="czzz"
+         id="path3396"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3398"
+         sodipodi:nodetypes="czzz"
+         d="M 215.08528,267.40658 C 217.25872,268.749 217.83405,269.22845 215.37294,271.43386 C 212.89175,273.65725 216.39574,270.60283 216.172,269.32432 C 215.94914,268.05088 212.94923,266.08725 215.08528,267.40658 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 200.54781,263.45098 C 204.31939,265.43265 203.68014,265.40068 200.83547,267.47825 C 198.05046,269.51225 201.27066,266.64722 202.04136,265.54954 C 202.78342,264.49262 196.80222,261.48295 200.54781,263.45098 z "
+         sodipodi:nodetypes="czzz"
+         id="path3400"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3402"
+         sodipodi:nodetypes="czzz"
+         d="M 185.48798,260.50267 C 188.30069,262.16472 188.68423,262.2606 185.77564,264.52995 C 182.90663,266.76843 187.05414,263.76284 186.83041,262.48434 C 186.60755,261.21089 182.72253,258.86853 185.48798,260.50267 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 194.50141,253.47091 C 196.67486,254.81334 197.25018,255.29277 194.78907,257.49819 C 192.30789,259.72159 195.81187,256.66717 195.58814,255.38867 C 195.36528,254.11522 192.36537,252.15159 194.50141,253.47091 z "
+         sodipodi:nodetypes="czzz"
+         id="path3404"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3406"
+         sodipodi:nodetypes="czzz"
+         d="M 204.79335,246.88664 C 206.96679,248.22906 207.54211,248.7085 205.081,250.91391 C 202.59982,253.13731 206.10381,250.08289 205.88007,248.80439 C 205.65721,247.53094 202.6573,245.56731 204.79335,246.88664 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 209.97127,256.34754 C 212.14473,257.68997 212.72004,258.16941 210.25893,260.37482 C 207.77775,262.59821 211.28174,259.54379 211.058,258.26529 C 210.83513,256.99184 207.83522,255.02823 209.97127,256.34754 z "
+         sodipodi:nodetypes="czzz"
+         id="path3408"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3410"
+         sodipodi:nodetypes="czzz"
+         d="M 219.94358,248.99617 C 222.11703,250.33858 222.69236,250.81803 220.23124,253.02345 C 217.75006,255.24684 221.25405,252.19241 221.03031,250.91391 C 220.80745,249.64046 217.80754,247.67683 219.94358,248.99617 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 226.08039,260.5666 C 228.25383,261.90901 228.82916,262.38845 226.36805,264.59388 C 223.88687,266.81727 227.39085,263.76284 227.16711,262.48434 C 226.94426,261.21089 223.94434,259.24727 226.08039,260.5666 z "
+         sodipodi:nodetypes="czzz"
+         id="path3412"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3414"
+         sodipodi:nodetypes="czzz"
+         d="M 234.39063,254.11016 C 236.56408,255.4526 237.13941,255.93204 234.6783,258.13745 C 232.19712,260.36084 235.70111,257.30642 235.47737,256.02792 C 235.25451,254.75447 232.25458,252.79084 234.39063,254.11016 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 229.27663,242.2201 C 231.45009,243.56254 232.02541,244.04197 229.5643,246.24738 C 227.08312,248.47078 230.58711,245.41636 230.36337,244.13786 C 230.1405,242.86442 227.14058,240.90079 229.27663,242.2201 z "
+         sodipodi:nodetypes="czzz"
+         id="path3416"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3418"
+         sodipodi:nodetypes="czzz"
+         d="M 243.0141,247.59302 C 245.39457,248.78588 245.85328,249.41488 243.39217,251.6203 C 240.91099,253.84369 244.68618,250.11125 244.59804,249.42037 C 244.51044,248.73372 240.56163,246.3641 243.0141,247.59302 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 213.8707,239.98274 C 216.04415,241.32515 216.61948,241.8046 214.15837,244.01001 C 211.67717,246.23341 215.18116,243.17898 214.95742,241.90048 C 214.73456,240.62703 211.73465,238.66341 213.8707,239.98274 z "
+         sodipodi:nodetypes="czzz"
+         id="path3420"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3422"
+         sodipodi:nodetypes="czzz"
+         d="M 222.91837,233.04137 C 225.31782,234.20299 225.93836,234.99885 223.20602,237.06866 C 220.48021,239.13354 224.63565,235.424 224.54751,234.77832 C 224.44726,234.04379 220.53635,231.8882 222.91837,233.04137 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 246.64686,229.49127 C 249.00111,230.74329 249.89286,231.08713 246.93452,233.51855 C 243.99824,235.93184 248.36413,232.59712 248.14039,231.31861 C 247.91754,230.04517 244.31107,228.24907 246.64686,229.49127 z "
+         sodipodi:nodetypes="czzz"
+         id="path3424"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3426"
+         sodipodi:nodetypes="czzz"
+         d="M 237.71153,235.84633 C 240.02059,237.03055 240.64112,237.84899 237.9992,239.87362 C 235.36048,241.89576 239.38361,238.50015 239.25027,237.67368 C 239.10149,236.75149 235.41158,234.66678 237.71153,235.84633 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 284.38244,228.84004 C 286.66889,230.04686 287.0481,230.84805 284.67009,232.86732 C 282.27897,234.89772 285.51201,231.73741 285.85337,231.07421 C 286.18578,230.42841 282.11363,227.64254 284.38244,228.84004 z "
+         sodipodi:nodetypes="czzz"
+         id="path3428"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3430"
+         sodipodi:nodetypes="czzz"
+         d="M 232.52396,226.41189 C 234.78783,227.52831 235.40834,228.14335 232.81163,230.16796 C 230.19284,232.20977 234.24125,228.7945 234.06271,228.10364 C 233.88666,227.4224 230.2964,225.31338 232.52396,226.41189 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 242.49949,219.69334 C 245.873,221.52582 244.75103,221.244 242.19951,223.3138 C 239.67516,225.36158 243.76147,221.8425 243.54101,221.11387 C 243.32053,220.38523 239.12596,217.86086 242.49949,219.69334 z "
+         sodipodi:nodetypes="czzz"
+         id="path3432"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3434"
+         sodipodi:nodetypes="czzz"
+         d="M 255.29092,223.29442 C 257.75818,224.59164 258.28831,225.25187 255.57858,227.32169 C 252.87211,229.38902 256.89519,225.88045 256.80705,225.21217 C 256.71558,224.51847 252.84135,222.00649 255.29092,223.29442 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 269.10194,227.3895 C 271.27539,228.73192 271.85071,229.21136 269.3896,231.41677 C 266.90842,233.64017 270.41241,230.58576 270.18867,229.30725 C 269.96581,228.0338 266.96589,226.07017 269.10194,227.3895 z "
+         sodipodi:nodetypes="czzz"
+         id="path3436"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3438"
+         sodipodi:nodetypes="czzz"
+         d="M 260.47206,233.782 C 262.64551,235.12443 263.22084,235.60386 260.75972,237.80927 C 258.27854,240.03267 261.78252,236.97826 261.55878,235.69975 C 261.33593,234.42631 258.33601,232.46268 260.47206,233.782 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 252.00426,240.41924 C 254.76534,241.85208 254.88865,242.2863 252.29193,244.44653 C 249.69924,246.60338 253.58595,242.98267 253.49781,242.15619 C 253.40345,241.27142 249.33359,239.0316 252.00426,240.41924 z "
+         sodipodi:nodetypes="czzz"
+         id="path3440"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3442"
+         sodipodi:nodetypes="czzz"
+         d="M 266.73671,242.2201 C 268.91017,243.56254 269.4855,244.04197 267.02438,246.24738 C 264.54319,248.47078 268.04717,245.41636 267.82343,244.13786 C 267.60058,242.86442 264.60067,240.90079 266.73671,242.2201 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 275.77863,235.80888 C 278.29108,237.0157 278.6856,237.56294 276.08889,239.70055 C 273.50776,241.82532 277.52012,238.29917 277.43037,237.50061 C 277.34312,236.7244 273.32091,234.62837 275.77863,235.80888 z "
+         sodipodi:nodetypes="czzz"
+         id="path3444"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3446"
+         sodipodi:nodetypes="czzz"
+         d="M 286.95302,240.94161 C 289.63786,242.37993 289.92552,242.74749 287.24067,244.96888 C 284.57146,247.17734 287.44366,244.39791 288.24749,243.37076 C 289.03146,242.369 284.28786,239.51385 286.95302,240.94161 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 295.77487,234.08611 C 298.28732,235.22513 298.70444,235.86277 296.06253,238.11339 C 293.44279,240.34511 296.0466,237.62948 297.1102,236.38809 C 298.1513,235.17297 293.28962,232.95944 295.77487,234.08611 z "
+         sodipodi:nodetypes="czzz"
+         id="path3448"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3450"
+         sodipodi:nodetypes="czzz"
+         d="M 304.36663,227.14477 C 306.83388,228.44201 307.1154,228.96663 304.65428,231.17204 C 302.17311,233.39544 304.72941,230.49547 305.36295,229.31112 C 306.04412,228.03769 301.92637,225.86174 304.36663,227.14477 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 293.12178,221.54036 C 295.47102,222.691 295.98242,223.42615 293.40945,225.56763 C 290.83798,227.70786 294.60852,224.14154 294.54411,223.33025 C 294.48107,222.53624 290.77257,220.38972 293.12178,221.54036 z "
+         sodipodi:nodetypes="czzz"
+         id="path3452"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3454"
+         sodipodi:nodetypes="czzz"
+         d="M 277.5149,220.70385 C 280.36638,222.31747 280.39928,222.70652 277.80256,224.73113 C 275.18377,226.77294 276.77322,224.86768 278.55642,223.48044 C 280.36765,222.07138 274.77049,219.15079 277.5149,220.70385 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 263.22084,217.41719 C 265.39428,218.75961 265.96961,219.23905 263.5085,221.44447 C 261.02732,223.66786 264.5313,220.61343 264.30756,219.33494 C 264.08471,218.06149 261.08479,216.09787 263.22084,217.41719 z "
+         sodipodi:nodetypes="czzz"
+         id="path3456"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3458"
+         sodipodi:nodetypes="czzz"
+         d="M 251.15585,213.31501 C 254.39256,214.77625 254.35665,214.86567 251.3531,216.98068 C 248.32814,219.11078 252.86671,215.85366 252.60418,214.91636 C 252.33613,213.95933 247.95664,211.87072 251.15585,213.31501 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 260.85561,206.10245 C 263.02906,207.44489 263.60438,207.92432 261.14327,210.12974 C 258.66209,212.35313 262.16607,209.29871 261.94234,208.02022 C 261.71947,206.74677 258.71956,204.78313 260.85561,206.10245 z "
+         sodipodi:nodetypes="czzz"
+         id="path3460"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3462"
+         sodipodi:nodetypes="czzz"
+         d="M 271.01969,211.72786 C 273.19314,213.07028 273.76847,213.54972 271.30736,215.75513 C 268.82618,217.97853 272.33015,214.92411 272.10641,213.64561 C 271.88356,212.37216 268.88364,210.40854 271.01969,211.72786 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 284.99091,214.99901 C 287.70679,216.25102 288.10131,216.82086 285.27858,219.02628 C 282.51164,221.18811 285.03572,219.0993 286.34886,217.27838 C 287.55847,215.60098 282.34938,213.78125 284.99091,214.99901 z "
+         sodipodi:nodetypes="czzz"
+         id="path3464"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3466"
+         sodipodi:nodetypes="czzz"
+         d="M 276.90079,206.80563 C 279.07425,208.14806 279.64957,208.6275 277.18845,210.83291 C 274.70727,213.05631 278.21126,210.00188 277.98752,208.72339 C 277.76466,207.44994 274.76474,205.48631 276.90079,206.80563 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 292.17888,209.68226 C 294.35233,211.02468 294.92766,211.50412 292.46654,213.70954 C 289.98536,215.93294 293.48934,212.87852 293.2656,211.60001 C 293.04275,210.32657 290.04283,208.36293 292.17888,209.68226 z "
+         sodipodi:nodetypes="czzz"
+         id="path3468"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3470"
+         sodipodi:nodetypes="czzz"
+         d="M 314.68049,204.24863 C 317.7489,205.39928 317.87645,205.56214 311.38835,210.80095 C 304.93164,216.01443 312.98405,209.0753 315.15994,207.02937 C 317.35285,204.96744 311.62248,203.10188 314.68049,204.24863 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         d="M 303.62147,199.96565 C 305.79492,201.30808 306.37025,201.78752 303.90913,203.99293 C 301.42794,206.21632 304.93192,203.16191 304.70818,201.8834 C 304.48533,200.60995 301.48542,198.64633 303.62147,199.96565 z "
+         sodipodi:nodetypes="czzz"
+         id="path3472"
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3474"
+         sodipodi:nodetypes="czzz"
+         d="M 281.07787,191.30607 C 284.96899,192.8745 284.82107,192.72112 278.69861,197.41265 C 272.5826,202.09922 281.39387,194.86394 282.66181,193.63065 C 283.93119,192.39593 277.15462,189.7247 281.07787,191.30607 z "
+         style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         id="path3477"
+         d="M 247.0478,21.870476 L 41.113266,89.503174 L 48.496608,139.14097 C 116.54867,104.13919 192.87556,126.61352 246.15284,103.7265 L 247.0478,21.870476 z "
+         style="fill:url(#linearGradient2500);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         inkscape:transform-center-x="21.926591"
+         inkscape:transform-center-y="-98.60823"
+         d="M 27.882376,75.746856 C 32.065415,74.206702 33.389047,74.781014 34.949531,76.633878 L 65.088465,277.78657 L 56.641394,273.5449 L 27.882376,75.746856 z "
+         sodipodi:nodetypes="ccccc"
+         id="path2180"
+         style="fill:#1a1a1a;fill-rule:evenodd;stroke:#333333;stroke-width:0.81824058;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         id="path13298"
+         sodipodi:nodetypes="ccccc"
+         d="M 92.828627,259.35202 L 230.90672,174.71526 C 232.64824,175.78414 234.17161,176.98389 234.23083,179.06216 L 96.66413,263.82678 C 96.420949,261.86067 95.117349,260.38059 92.828627,259.35202 z "
+         style="fill:#808080;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         id="path14269"
+         sodipodi:nodetypes="ccccc"
+         d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z "
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z "
+         sodipodi:nodetypes="ccccc"
+         id="path16261"
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         d="M 114.5513,270.06913 L 130.10765,259.99286 L 130.63798,260.87674 L 116.14229,271.48335 L 114.5513,270.06913 z "
+         sodipodi:nodetypes="ccccc"
+         id="path16263"
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         transform="matrix(1.0228007,0,0,1.0228007,-6.1273429,-1.9735657)"
+         d="M 166.43526,236.83511 L 228.92582,195.64614 L 229.45615,196.53002 L 168.02625,238.24933 L 166.43526,236.83511 z "
+         sodipodi:nodetypes="ccccc"
+         id="path16267"
+         style="opacity:0.67000002;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;filter:url(#filter16257)" />
+      <path
+         d="M 104.05869,273.38134 C 106.59601,271.03005 113.13546,275.43531 112.49482,278.36804 L 104.05869,273.38134 z "
+         sodipodi:nodetypes="ccc"
+         id="path16269"
+         style="opacity:0.99720004;fill:#666666;fill-rule:evenodd;stroke:#000000;stroke-width:0.20456015;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+      <path
+         sodipodi:nodetypes="ccsccccc"
+         id="path25800"
+         d="M 304.06894,248.1971 C 302.1656,248.29093 300.4498,248.80189 299.33849,249.7313 C 299.33849,249.7313 260.56796,282.17326 260.56795,282.17326 C 257.93245,284.37739 258.86046,287.65621 262.70943,289.52464 C 262.70943,289.52464 267.73895,291.93253 272.68174,294.28706 C 306.66914,288.15442 283.9982,253.02759 313.94536,251.0098 C 312.61326,250.41591 309.88612,249.2199 309.88612,249.2199 C 308.06831,248.43649 305.97228,248.10326 304.06894,248.1971 z "
+         style="fill:url(#linearGradient2490);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:0.9527356;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dashoffset:0;stroke-opacity:1" />
+    </g>
+  </g>
+</svg>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg b/wpa_supplicant/wpa_gui-qt4/icons/wpa_gui.svg
new file mode 100644 (file)
index 0000000..b3abf0a
--- /dev/null
@@ -0,0 +1,256 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:xlink="http://www.w3.org/1999/xlink"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.0"
+   width="128"
+   height="128"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.46"
+   sodipodi:docname="wpa_gui.svg"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <metadata
+     id="metadata47">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     inkscape:window-height="771"
+     inkscape:window-width="640"
+     inkscape:pageshadow="2"
+     inkscape:pageopacity="0.0"
+     guidetolerance="10.0"
+     gridtolerance="10.0"
+     objecttolerance="10.0"
+     borderopacity="1.0"
+     bordercolor="#666666"
+     pagecolor="#ffffff"
+     id="base"
+     showgrid="false"
+     inkscape:zoom="4.2421875"
+     inkscape:cx="64"
+     inkscape:cy="64"
+     inkscape:window-x="634"
+     inkscape:window-y="0"
+     inkscape:current-layer="svg2" />
+  <defs
+     id="defs4">
+    <inkscape:perspective
+       sodipodi:type="inkscape:persp3d"
+       inkscape:vp_x="0 : 64 : 1"
+       inkscape:vp_y="0 : 1000 : 0"
+       inkscape:vp_z="128 : 64 : 1"
+       inkscape:persp3d-origin="64 : 42.666667 : 1"
+       id="perspective49" />
+    <linearGradient
+       id="linearGradient39133">
+      <stop
+         id="stop39135"
+         style="stop-color:#252525;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39137"
+         style="stop-color:#515151;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39139"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="0.28677997" />
+      <stop
+         id="stop39141"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0.92151743" />
+      <stop
+         id="stop39143"
+         style="stop-color:#ffffff;stop-opacity:0.73786408"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39119">
+      <stop
+         id="stop39121"
+         style="stop-color:#ffffff;stop-opacity:0.82905984"
+         offset="0" />
+      <stop
+         id="stop39123"
+         style="stop-color:#ffffff;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39106">
+      <stop
+         id="stop39108"
+         style="stop-color:#ffffff;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39110"
+         style="stop-color:#a8a8a8;stop-opacity:0"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39094">
+      <stop
+         id="stop39096"
+         style="stop-color:#000000;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39098"
+         style="stop-color:#333333;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       id="linearGradient39062">
+      <stop
+         id="stop39064"
+         style="stop-color:#252525;stop-opacity:1"
+         offset="0" />
+      <stop
+         id="stop39086"
+         style="stop-color:#515151;stop-opacity:1"
+         offset="0.21101321" />
+      <stop
+         id="stop39088"
+         style="stop-color:#878787;stop-opacity:1"
+         offset="0.75" />
+      <stop
+         id="stop39090"
+         style="stop-color:#6c6c6c;stop-opacity:1"
+         offset="0.875" />
+      <stop
+         id="stop39066"
+         style="stop-color:#1e1e1e;stop-opacity:1"
+         offset="1" />
+    </linearGradient>
+    <linearGradient
+       x1="4"
+       y1="40"
+       x2="124"
+       y2="60"
+       id="linearGradient39068"
+       xlink:href="#linearGradient39062"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       cx="100.70589"
+       cy="96"
+       r="60"
+       fx="158.07428"
+       fy="95.718063"
+       id="radialGradient39100"
+       xlink:href="#linearGradient39094"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(2.7837903e-8,-1,0.99999999,-2.1864248e-6,-32.000004,164.7061)" />
+    <radialGradient
+       cx="100.44444"
+       cy="34.363636"
+       r="32"
+       fx="83.18"
+       fy="34.228985"
+       id="radialGradient39104"
+       xlink:href="#linearGradient39106"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(3.1472435e-6,1.0227273,-0.87499999,-9.5061964e-8,94.067865,-4.7272712)" />
+    <radialGradient
+       cx="75.999977"
+       cy="-2.7730541"
+       r="48"
+       fx="55.266491"
+       fy="-2.5338216"
+       id="radialGradient39125"
+       xlink:href="#linearGradient39119"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0,0.83333324,-1.6666667,2.518705e-6,59.378243,-35.333302)" />
+    <radialGradient
+       cx="64.066589"
+       cy="63.713329"
+       r="60"
+       fx="64.066589"
+       fy="63.713329"
+       id="radialGradient39131"
+       xlink:href="#linearGradient39133"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1.1333333,5.1768857e-8,5.2556881e-6,1.1666667,-8.6091298,-10.332226)" />
+    <filter
+       id="filter39153">
+      <feGaussianBlur
+         id="feGaussianBlur39155"
+         stdDeviation="2.28"
+         inkscape:collect="always" />
+    </filter>
+    <filter
+       id="filter39159">
+      <feGaussianBlur
+         inkscape:collect="always"
+         stdDeviation="1.68"
+         id="feGaussianBlur39161" />
+    </filter>
+  </defs>
+  <g
+     id="layer1"
+     style="display:inline">
+    <path
+       d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z"
+       id="path39151"
+       style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39153)" />
+    <path
+       d="M 29,4 C 15.147058,4 4,15.14706 4,29 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,15.14706 112.85294,4 99,4 L 29,4 z"
+       id="path39157"
+       style="opacity:1;fill:#000000;fill-opacity:1;stroke:none;filter:url(#filter39159)" />
+    <rect
+       width="120"
+       height="120"
+       ry="25.00531"
+       x="4"
+       y="0"
+       id="rect2573"
+       style="opacity:1;fill:url(#radialGradient39100);fill-opacity:1;stroke:none" />
+    <path
+       d="M 29,0 C 15.147058,0 4,11.14706 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.14706 112.85294,0 99,0 L 29,0 z"
+       id="path39127"
+       style="opacity:0.20512821;fill:url(#radialGradient39131);fill-opacity:1;stroke:none" />
+    <path
+       d="m 44,68 40,0 12,40 c -20,7.27273 -44,7.27273 -64,0 L 44,68 z"
+       id="path39102"
+       style="opacity:0.53418801;fill:url(#radialGradient39104);fill-opacity:1;stroke:none" />
+    <path
+       d="M 25.339207,12 C 52,8 76,8 102.66079,12 107.83471,12 112,16.165286 112,21.339207 L 116,52 C 100,73.339207 28,73.339207 12,52 L 16,21.339207 C 16,16.165286 20.165286,12 25.339207,12 z"
+       id="rect39116"
+       style="opacity:0.92307691;fill:url(#radialGradient39125);fill-opacity:1;stroke:none" />
+    <path
+       d="M 29,8 C 15.147058,8 4,19.14706 4,33 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,19.14706 112.85294,8 99,8 L 29,8 z"
+       id="path39147"
+       style="opacity:0.20512821;fill:#000000;fill-opacity:1;stroke:none" />
+    <path
+       d="M 29,0 C 15.147058,0 4,11.147058 4,25 l 0,70 c 0,13.85294 11.147058,25 25,25 l 70,0 c 13.85294,0 25,-11.14706 25,-25 l 0,-70 C 124,11.147058 112.85294,0 99,0 L 29,0 z m 0,4 70,0 c 11.70613,0 21,9.293869 21,21 l 0,70 c 0,11.70613 -9.29387,21 -21,21 l -70,0 C 17.293869,116 8,106.70613 8,95 L 8,25 C 8,13.293869 17.293869,4 29,4 z"
+       id="rect39029"
+       style="opacity:1;fill:url(#linearGradient39068);fill-opacity:1;stroke:none" />
+    <path
+       d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782"
+       transform="matrix(-0.16680323,0.53082142,-0.53082142,-0.16680323,103.31027,53.117897)"
+       id="path3351"
+       style="opacity:1;fill:none;stroke:#ffffff;stroke-width:21.56673813;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+    <path
+       d="m 36,56 a 4,4 0 1 1 -8,0 4,4 0 1 1 8,0 z"
+       transform="matrix(1.4851301,0,0,1.4851301,16.475837,-23.948973)"
+       id="path3353"
+       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none" />
+    <path
+       d="M 66.35081,74.771345 A 36,36 0 1 1 54.34964,35.777782"
+       transform="matrix(-0.35033273,1.1148712,-1.1148712,-0.35033273,146.5624,46.88078)"
+       id="path2622"
+       style="opacity:1;fill:none;stroke:#ffffff;stroke-width:10.26852894;stroke-linecap:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+  </g>
+</svg>
diff --git a/wpa_supplicant/wpa_gui-qt4/icons_png.qrc b/wpa_supplicant/wpa_gui-qt4/icons_png.qrc
new file mode 100644 (file)
index 0000000..f262217
--- /dev/null
@@ -0,0 +1,7 @@
+<RCC>
+ <qresource prefix="/icons" >
+  <file alias="wpa_gui.png">icons/hicolor/16x16/apps/wpa_gui.png</file>
+  <file alias="ap.png">icons/hicolor/32x32/apps/ap.png</file>
+  <file alias="laptop.png">icons/hicolor/32x32/apps/laptop.png</file>
+ </qresource>
+</RCC>
diff --git a/wpa_supplicant/wpa_gui-qt4/lang/.gitignore b/wpa_supplicant/wpa_gui-qt4/lang/.gitignore
new file mode 100644 (file)
index 0000000..8df47d5
--- /dev/null
@@ -0,0 +1 @@
+*.qm
diff --git a/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts b/wpa_supplicant/wpa_gui-qt4/lang/wpa_gui_de.ts
new file mode 100644 (file)
index 0000000..d7a9c89
--- /dev/null
@@ -0,0 +1,1262 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE TS>
+<TS version="2.0" language="de_DE" sourcelanguage="en_US">
+<context>
+    <name>AddInterface</name>
+    <message>
+        <location filename="../addinterface.cpp" line="38"/>
+        <source>Select network interface to add</source>
+        <translation>Wähle die Netzwerkschnittstelle zum hinzufügen aus</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="47"/>
+        <source>driver</source>
+        <translation>Treiber</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="48"/>
+        <source>interface</source>
+        <translation>Schnittstelle</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="49"/>
+        <source>description</source>
+        <translation>Beschreibung</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="221"/>
+        <source>Add interface command could not be completed.</source>
+        <translation>Das Schnittstellen hinzufügen Kommando konnte nicht abgeschlossen werden.</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="229"/>
+        <source>Failed to add the interface.</source>
+        <translation>Fehler beim hinzufügen der Schnittstelle.</translation>
+    </message>
+    <message>
+        <location filename="../addinterface.cpp" line="238"/>
+        <source>Failed to add the interface into registry.</source>
+        <translation>Fehler beim hinzufügen der Schnittstelle in die Registry.</translation>
+    </message>
+</context>
+<context>
+    <name>ErrorMsg</name>
+    <message>
+        <location filename="../wpagui.cpp" line="1621"/>
+        <source>wpa_gui error</source>
+        <translation>wpa_gui Fehler</translation>
+    </message>
+</context>
+<context>
+    <name>EventHistory</name>
+    <message>
+        <location filename="../eventhistory.ui" line="13"/>
+        <source>Event history</source>
+        <translation>Ereignis Historie</translation>
+    </message>
+    <message>
+        <location filename="../eventhistory.ui" line="48"/>
+        <source>Close</source>
+        <translation>Schließen</translation>
+    </message>
+</context>
+<context>
+    <name>EventListModel</name>
+    <message>
+        <location filename="../eventhistory.cpp" line="62"/>
+        <source>Timestamp</source>
+        <translation>Zeit</translation>
+    </message>
+    <message>
+        <location filename="../eventhistory.cpp" line="64"/>
+        <source>Message</source>
+        <translation>Meldung</translation>
+    </message>
+</context>
+<context>
+    <name>NetworkConfig</name>
+    <message>
+        <location filename="../networkconfig.ui" line="13"/>
+        <source>NetworkConfig</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="19"/>
+        <source>Cancel</source>
+        <translation>Abbrechen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="35"/>
+        <source>SSID</source>
+        <translation>SSID</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="42"/>
+        <source>Network name (Service Set IDentifier)</source>
+        <translation>Netzwerkname (Service Set IDentifier)</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="52"/>
+        <source>Authentication</source>
+        <translation>Authentifizierung</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="60"/>
+        <source>Plaintext (open / no authentication)</source>
+        <translation>Plaintext (offen / keine Authentifizierung)</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="65"/>
+        <source>Static WEP (no authentication)</source>
+        <translation>Static WEP (keine Authentifizierung)</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="70"/>
+        <source>Static WEP (Shared Key authentication)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="75"/>
+        <source>IEEE 802.1X</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="80"/>
+        <source>WPA-Personal (PSK)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="85"/>
+        <source>WPA-Enterprise (EAP)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="90"/>
+        <source>WPA2-Personal (PSK)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="95"/>
+        <source>WPA2-Enterprise (EAP)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="103"/>
+        <source>Encryption</source>
+        <translation>Verschlüsselung</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="111"/>
+        <source>None</source>
+        <translation>Keine</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="116"/>
+        <source>WEP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="121"/>
+        <source>TKIP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="126"/>
+        <source>CCMP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="134"/>
+        <source>PSK</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="144"/>
+        <source>WPA/WPA2 pre-shared key or passphrase</source>
+        <translation>WPA/WPA2 Pre-Shared Key oder Passphrase</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="157"/>
+        <source>EAP method</source>
+        <translation>EAP Verfahren</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="171"/>
+        <source>Identity</source>
+        <translation>Identität</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="181"/>
+        <source>Username/Identity for EAP methods</source>
+        <translation>Nutzername/Identitär für die EAP Verfahren</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="188"/>
+        <source>Password</source>
+        <translation>Passwort</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="198"/>
+        <source>Password for EAP methods</source>
+        <translation>Passwort für die EAP Verfahren</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="208"/>
+        <source>CA certificate</source>
+        <translation>CA Zertifikat</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="225"/>
+        <source>WEP keys</source>
+        <translation>WEP Schlüssel</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="234"/>
+        <source>key 0</source>
+        <translation>Schlüssel 0</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="244"/>
+        <source>key 1</source>
+        <translation>Schlüssel 1</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="254"/>
+        <source>key 3</source>
+        <translation>Schlüssel 3</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="264"/>
+        <source>key 2</source>
+        <translation>Schlüssel 2</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="305"/>
+        <source>Optional Settings</source>
+        <translation>Optionale Einstellungen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="311"/>
+        <source>Network Identification String</source>
+        <translation>Netzwerk Indentifikations Zeichenfolge</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="318"/>
+        <source>Network Priority</source>
+        <translation>Netzwerk Priorität</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="331"/>
+        <source>IDString</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="338"/>
+        <source>Priority</source>
+        <translation>Priorität</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="345"/>
+        <source>Inner auth</source>
+        <translation>Geheime Auth</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="365"/>
+        <source>Add</source>
+        <translation>Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="375"/>
+        <source>Remove</source>
+        <translation>Entfernen</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.ui" line="398"/>
+        <source>WPS</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="202"/>
+        <source>WPA Pre-Shared Key Error</source>
+        <translation>WPA Pre Shared Key Fehler</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="203"/>
+        <source>WPA-PSK requires a passphrase of 8 to 63 characters
+or 64 hex digit PSK</source>
+        <translation>WPA PSK benötigt ein Passphrase mit 8 bis 63 Zeichen
+oder 64 hexadezimal stelligen PSK</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="215"/>
+        <source>Network ID Error</source>
+        <translation>Netzwerk ID Fehler</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="216"/>
+        <source>Network ID String contains non-word characters.
+It must be a simple string, without spaces, containing
+only characters in this range: [A-Za-z0-9_-]
+</source>
+        <translation>Netzwerk ID Zeichnfolge beinhaltet ungültige Zeichen.
+Es muss eine einfache Zeichnfolge aus [A-Za-z0-9_] ohne
+Leerzeichen sein
+</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="237"/>
+        <source>Failed to add network to wpa_supplicant
+configuration.</source>
+        <translation>Hinzufügen des Netzwerks in die wpa_supplicant
+Konfiguration fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="414"/>
+        <source>Failed to enable network in wpa_supplicant
+configuration.</source>
+        <translation>Aktivieren des Netzwerks in der wpa_supplicant
+Konfiguration fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="802"/>
+        <source>This will permanently remove the network
+from the configuration. Do you really want
+to remove this network?</source>
+        <translation>Dies wird das Netzwerk permanent aus
+der Konfiguration entfernen. Möchtest du
+das Netzwerk wirklich entfernen?</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="805"/>
+        <source>Yes</source>
+        <translation>Ja</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="805"/>
+        <source>No</source>
+        <translation>Nein</translation>
+    </message>
+    <message>
+        <location filename="../networkconfig.cpp" line="813"/>
+        <source>Failed to remove network from wpa_supplicant
+configuration.</source>
+        <translation>Entfernen des Netzwerks aus der wpa_supplicant
+Konfiguration fehlgeschlagen.</translation>
+    </message>
+</context>
+<context>
+    <name>Peers</name>
+    <message>
+        <location filename="../peers.ui" line="14"/>
+        <source>Peers</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="107"/>
+        <source>Associated station</source>
+        <translation>Verbundene Stationen</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="110"/>
+        <source>AP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="113"/>
+        <source>WPS AP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="116"/>
+        <source>WPS PIN needed</source>
+        <translation>WPS PIN wird benötigt</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="119"/>
+        <source>ER: WPS AP</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="122"/>
+        <source>ER: WPS AP (Unconfigured)</source>
+        <translation>ER: WPS AP (nicht konfiguriert)</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="125"/>
+        <source>ER: WPS Enrollee</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="128"/>
+        <source>WPS Enrollee</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="159"/>
+        <source>Enter WPS PIN</source>
+        <translation>WPS PIN Eingabe</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="164"/>
+        <source>Connect (PBC)</source>
+        <translation>Verbinden (PBC)</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="172"/>
+        <source>Enroll (PBC)</source>
+        <translation>Anmelden (PBC)</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="177"/>
+        <source>Learn Configuration</source>
+        <translation>Konfiguration lernen</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="181"/>
+        <source>Properties</source>
+        <translation>Eigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="184"/>
+        <source>Refresh</source>
+        <translation>Aktualisieren</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="205"/>
+        <source>PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="206"/>
+        <source>PIN for </source>
+        <translation>Pin für </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="227"/>
+        <source>Failed to set the WPS PIN.</source>
+        <translation>Setzten des WPS PIN fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="815"/>
+        <source>Peer Properties</source>
+        <translation>Peer Eigenschaften</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="820"/>
+        <source>Name: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="827"/>
+        <source>Address: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="831"/>
+        <source>UUID: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="835"/>
+        <source>Primary Device Type: </source>
+        <translation>Primärer Geräte Typ: </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="840"/>
+        <source>SSID: </source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="845"/>
+        <source>Configuration Methods: </source>
+        <translation>Konfigurationsverfahren: </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="847"/>
+        <source>[USBA]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="849"/>
+        <source>[Ethernet]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="851"/>
+        <source>[Label]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="853"/>
+        <source>[Display]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="855"/>
+        <source>[Ext. NFC Token]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="857"/>
+        <source>[Int. NFC Token]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="859"/>
+        <source>[NFC Interface]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="861"/>
+        <source>[Push Button]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="863"/>
+        <source>[Keypad]</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="869"/>
+        <source>Device Password ID: </source>
+        <translation>Geräte Passwort ID: </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="872"/>
+        <source> (Default PIN)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="875"/>
+        <source> (User-specified PIN)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="878"/>
+        <source> (Machine-specified PIN)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="881"/>
+        <source> (Rekey)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="884"/>
+        <source> (Push Button)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="887"/>
+        <source> (Registrar-specified)</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="924"/>
+        <source>Failed to start WPS PBC.</source>
+        <translation>Starten von WPS PBC fehlgeschlagen.</translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="937"/>
+        <source>AP PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="938"/>
+        <source>AP PIN for </source>
+        <translation>AP PIN für </translation>
+    </message>
+    <message>
+        <location filename="../peers.cpp" line="953"/>
+        <source>Failed to start learning AP configuration.</source>
+        <translation>Fehler beim erkennen der AP Konfiguration.</translation>
+    </message>
+</context>
+<context>
+    <name>ScanResults</name>
+    <message>
+        <location filename="../scanresults.ui" line="13"/>
+        <source>Scan results</source>
+        <translation>Scan Ergebnisse</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="32"/>
+        <source>SSID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="37"/>
+        <source>BSSID</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="42"/>
+        <source>frequency</source>
+        <translation>Frequenz</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="47"/>
+        <source>signal</source>
+        <translation>Signal</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="52"/>
+        <source>flags</source>
+        <translation>Flags</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="75"/>
+        <source>Scan</source>
+        <translation>Scannen</translation>
+    </message>
+    <message>
+        <location filename="../scanresults.ui" line="82"/>
+        <source>Close</source>
+        <translation>Schließen</translation>
+    </message>
+</context>
+<context>
+    <name>UserDataRequest</name>
+    <message>
+        <location filename="../userdatarequest.ui" line="16"/>
+        <source>Authentication credentials required</source>
+        <translation>Authentifzierungs Beglaubigung nötig</translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.ui" line="77"/>
+        <source>&amp;OK</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.ui" line="93"/>
+        <source>&amp;Cancel</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="67"/>
+        <source>Password: </source>
+        <translation>Passwort: </translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="70"/>
+        <source>New password: </source>
+        <translation>Neues Passwort: </translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="73"/>
+        <source>Identity: </source>
+        <translation>Identität: </translation>
+    </message>
+    <message>
+        <location filename="../userdatarequest.cpp" line="75"/>
+        <source>Private key passphrase: </source>
+        <translation>Privater Key Passphrase: </translation>
+    </message>
+</context>
+<context>
+    <name>WpaGui</name>
+    <message>
+        <location filename="../wpagui.ui" line="13"/>
+        <source>wpa_gui</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="24"/>
+        <source>Adapter:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="34"/>
+        <source>Network:</source>
+        <translation>Netzwerk:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="48"/>
+        <source>Current Status</source>
+        <translation>Aktueller Status</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="63"/>
+        <location filename="../wpagui.ui" line="300"/>
+        <source>Status:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="70"/>
+        <source>Last message:</source>
+        <translation>Letzte Meldung:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="77"/>
+        <source>Authentication:</source>
+        <translation>Authentifizierung:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="84"/>
+        <source>Encryption:</source>
+        <translation>Verschlüsselung:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="91"/>
+        <source>SSID:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="98"/>
+        <source>BSSID:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="105"/>
+        <source>IP address:</source>
+        <translation>IP Adresse:</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="177"/>
+        <source>Connect</source>
+        <translation>Verbinden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="184"/>
+        <source>Disconnect</source>
+        <translation>Trennen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="191"/>
+        <location filename="../wpagui.ui" line="286"/>
+        <source>Scan</source>
+        <translation>Scannen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="212"/>
+        <source>Manage Networks</source>
+        <translation>Netzwerke verwalten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="238"/>
+        <source>Enabled</source>
+        <translation>Aktiviert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="245"/>
+        <source>Edit</source>
+        <translation>Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="252"/>
+        <source>Remove</source>
+        <translation>Entfernen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="272"/>
+        <source>Disabled</source>
+        <translation>Deaktiviert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="279"/>
+        <source>Add</source>
+        <translation>Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="294"/>
+        <source>WPS</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="314"/>
+        <source>PBC - push button</source>
+        <translation>PBC - Taste</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="321"/>
+        <source>Generate PIN</source>
+        <translation>PIN erzeugen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="328"/>
+        <source>PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="348"/>
+        <source>Use AP PIN</source>
+        <translation>AP PIN verwenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="355"/>
+        <source>AP PIN:</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="390"/>
+        <source>&amp;File</source>
+        <translation>&amp;Datei</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="401"/>
+        <source>&amp;Network</source>
+        <translation>&amp;Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="413"/>
+        <source>&amp;Help</source>
+        <translation>&amp;Hilfe</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="426"/>
+        <source>Event &amp;History</source>
+        <translation>Ereignis &amp;Historie</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="431"/>
+        <source>&amp;Save Configuration</source>
+        <translation>Konfiguration &amp;Speichern</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="434"/>
+        <source>Ctrl+S</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="439"/>
+        <source>E&amp;xit</source>
+        <translation>&amp;Beenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="442"/>
+        <source>Ctrl+Q</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="447"/>
+        <source>&amp;Add</source>
+        <translation>&amp;Hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="452"/>
+        <source>&amp;Edit</source>
+        <translation>&amp;Bearbeiten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="457"/>
+        <source>&amp;Remove</source>
+        <translation>&amp;Entfernen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="462"/>
+        <source>E&amp;nable All</source>
+        <translation>Alle &amp;aktivieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="467"/>
+        <source>&amp;Disable All</source>
+        <translation>Alle &amp;deaktivieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="472"/>
+        <source>Re&amp;move All</source>
+        <translation>Alle &amp;entfernen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="480"/>
+        <source>&amp;Contents...</source>
+        <translation>&amp;Inhalt...</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="488"/>
+        <source>&amp;Index...</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="493"/>
+        <source>&amp;About</source>
+        <translation>&amp;Ãœber</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="501"/>
+        <source>&amp;Wi-Fi Protected Setup</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.ui" line="506"/>
+        <source>&amp;Peers</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="53"/>
+        <source>Stop Service</source>
+        <translation>Dienst stoppen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="58"/>
+        <source>Start Service</source>
+        <translation>Dienst starten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="67"/>
+        <source>Add Interface</source>
+        <translation>Schnittstelle hinzufügen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="167"/>
+        <source>connecting to wpa_supplicant</source>
+        <translation>Verbindungsaufbau zu wpa_supplicant</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="343"/>
+        <source>wpa_supplicant service is not running.
+Do you want to start it?</source>
+        <translation>wpa_supplicant ist nicht gestartet.
+Möchtest du ihn starten?</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="466"/>
+        <source>Disconnected</source>
+        <translation>Getrennt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="468"/>
+        <source>Inactive</source>
+        <translation>Inaktiv</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="470"/>
+        <source>Scanning</source>
+        <translation>Scannen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="472"/>
+        <source>Authenticating</source>
+        <translation>Authentifizieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="474"/>
+        <source>Associating</source>
+        <translation>Assoziieren</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="476"/>
+        <source>Associated</source>
+        <translation>Assoziiert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="478"/>
+        <source>4-Way Handshake</source>
+        <translation>4-Wege Handshake</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="480"/>
+        <source>Group Handshake</source>
+        <translation>Gruppen Handshake</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="482"/>
+        <source>Completed</source>
+        <translation>Abgeschlossen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="484"/>
+        <source>Unknown</source>
+        <translation>Unbekannt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="497"/>
+        <source>Could not get status from wpa_supplicant</source>
+        <translation>Status konnte nicht von wpa_supplicant abgerufen werden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="512"/>
+        <source>No network interfaces in use.
+Would you like to add one?</source>
+        <translation>Es ist keine Netzwerkschnittstelle in verwendung.
+Möchtest du eine hinzufügen?</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="682"/>
+        <location filename="../wpagui.cpp" line="974"/>
+        <location filename="../wpagui.cpp" line="1039"/>
+        <location filename="../wpagui.cpp" line="1117"/>
+        <source>Select any network</source>
+        <translation>Wähle beliebiges Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="883"/>
+        <source>Disconnected from network.</source>
+        <translation>Getrennt vom Netzwerk.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="886"/>
+        <source>Connection to network established.</source>
+        <translation>Verbindung zum Netzwerk wurde aufgebaut.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="890"/>
+        <location filename="../wpagui.cpp" line="1523"/>
+        <source>WPS AP in active PBC mode found</source>
+        <translation>WPS AP im aktiven PBC Modus gefunden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="894"/>
+        <source>Press the PBC button on the screen to start registration</source>
+        <translation>Drücke den PBC Knopf auf dem Bildschirm um die Registrierung zu starten</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="897"/>
+        <source>WPS AP with recently selected registrar</source>
+        <translation>WPS AP mit kürzlich ausgewähltem Registrator</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="903"/>
+        <source>WPS AP detected</source>
+        <translation>WPS AP erkannt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="905"/>
+        <source>PBC mode overlap detected</source>
+        <translation>PBC Modus Overlap erkannt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="906"/>
+        <source>More than one AP is currently in active WPS PBC mode. Wait couple of minutes and try again</source>
+        <translation>Mehr als ein AP ist momentan im aktiven WPS PBC Modus. Versuch es in ein paar Minuten nochmal</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="911"/>
+        <source>Network configuration received</source>
+        <translation>Netzwerk Konfiguration empfangen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="915"/>
+        <source>Registration started</source>
+        <translation>Registrierung gestartet</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="917"/>
+        <source>Registrar does not yet know PIN</source>
+        <translation>Registrator kennt den PIN noch nicht</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="919"/>
+        <source>Registration failed</source>
+        <translation>Registrierung fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="921"/>
+        <source>Registration succeeded</source>
+        <translation>Registrierung erfolgreich</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1069"/>
+        <location filename="../wpagui.cpp" line="1138"/>
+        <source>No Networks</source>
+        <translation>Keine Netzwerke</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1070"/>
+        <source>There are no networks to edit.
+</source>
+        <translation>Keine Netzwerke zum bearbeiten.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1081"/>
+        <location filename="../wpagui.cpp" line="1151"/>
+        <source>Select A Network</source>
+        <translation>Wähle ein Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1082"/>
+        <source>Select a network from the list to edit it.
+</source>
+        <translation>Wähle ein Netzwerk aus der Liste zum bearbeiten.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1139"/>
+        <source>There are no networks to remove.
+</source>
+        <translation>Es sind keine Netzwerke zum entfernen vorhanden.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1152"/>
+        <source>Select a network from the list to remove it.
+</source>
+        <translation>Wähle ein Netzwerk aus der Liste zum entfernen.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1264"/>
+        <source>Failed to save configuration</source>
+        <translation>Speichern der Konfiguration fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1265"/>
+        <source>The configuration could not be saved.
+
+The update_config=1 configuration option
+must be used for configuration saving to
+be permitted.
+</source>
+        <translation>Die Konfiguration konnte nicht gespeichert werden.
+
+Die Einstellung update_config=1 muss gesetzt sein,
+damit Konfigurationen gespeichert werden können.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1272"/>
+        <source>Saved configuration</source>
+        <translation>Konfiguration gespeichert</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1273"/>
+        <source>The current configuration was saved.
+</source>
+        <translation>Die aktuelle Konfiguration wurde gespeichert.
+</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1293"/>
+        <source> - wpa_supplicant user interface</source>
+        <translation> - wpa_supplicant Benutzerschnittstelle</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1307"/>
+        <source>&amp;Disconnect</source>
+        <translation>&amp;Trennen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1308"/>
+        <source>Re&amp;connect</source>
+        <translation>&amp;Wiederverbinden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1317"/>
+        <source>&amp;Event History</source>
+        <translation>&amp;Ereignis Historie</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1318"/>
+        <source>Scan &amp;Results</source>
+        <translation>Scan E&amp;rgebnisse</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1319"/>
+        <source>S&amp;tatus</source>
+        <translation></translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1328"/>
+        <source>&amp;Show Window</source>
+        <translation>&amp;Fenster anzeigen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1329"/>
+        <source>&amp;Hide Window</source>
+        <translation>&amp;Fenster ausblenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1330"/>
+        <source>&amp;Quit</source>
+        <translation>&amp;Beenden</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1462"/>
+        <source> will keep running in the system tray.</source>
+        <translation> wird weiterhin in der System Ablage laufen.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1466"/>
+        <source> systray</source>
+        <translation>System Ablage</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1467"/>
+        <source>The program will keep running in the system tray.</source>
+        <translation>Das Programm wird weiterhin in der System Ablage laufen.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1524"/>
+        <source>Press the push button on the AP to start the PBC mode.</source>
+        <translation>Drücke die Taste am AP um den PBC Modus zu starten.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1527"/>
+        <source>If you have not yet done so, press the push button on the AP to start the PBC mode.</source>
+        <translation>Wenn Sie es noch nicht getan haben, so drücken Sie die Taste am AP um den PBC Modus zu starten.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1531"/>
+        <location filename="../wpagui.cpp" line="1551"/>
+        <source>Waiting for Registrar</source>
+        <translation>Warte auf Registrator</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1548"/>
+        <source>Enter the generated PIN into the Registrar (either the internal one in the AP or an external one).</source>
+        <translation>Geben Sie den generierten PIN in der Registrierungsstelle ein (entweder der interne oder der externe im AP).</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1561"/>
+        <source>WPS AP selected from scan results</source>
+        <translation>WPS AP ausgewählt aus Scan Ergebnissen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1562"/>
+        <source>If you want to use an AP device PIN, e.g., from a label in the device, enter the eight digit AP PIN and click Use AP PIN button.</source>
+        <translation>Wenn Sie einen AP Geräte PIN verwenden möchten, z.B.: von einem Aufkleber am Gerät, geben Sie denn acht stelligen AP PIN ein und klicken Sie auf den AP PIN Knopf.</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1583"/>
+        <source>Waiting for AP/Enrollee</source>
+        <translation>Warte auf AP/Bewerber</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1591"/>
+        <source>Connected to the network</source>
+        <translation>Verbunden zum Netzwerk</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1592"/>
+        <source>Stopped</source>
+        <translation>Gestoppt</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1651"/>
+        <location filename="../wpagui.cpp" line="1679"/>
+        <source>OpenSCManager failed</source>
+        <translation>OpenSCManager fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1657"/>
+        <location filename="../wpagui.cpp" line="1685"/>
+        <source>OpenService failed</source>
+        <translation>OpenService fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1663"/>
+        <source>Failed to start wpa_supplicant service</source>
+        <translation>Starten des wpa_supplicant Dienstes fehlgeschlagen</translation>
+    </message>
+    <message>
+        <location filename="../wpagui.cpp" line="1691"/>
+        <source>Failed to stop wpa_supplicant service</source>
+        <translation>Stoppen des wpa_supplicant Dienstes fehlgeschlagen</translation>
+    </message>
+    <message>
+        <source>OpenSCManager failed: %d
+</source>
+        <translation type="obsolete">OpenSCManager fehlgeschlagen: %d
+</translation>
+    </message>
+    <message>
+        <source>OpenService failed: %d
+
+</source>
+        <translation type="obsolete">OpenService fehlgeschlagen: %d
+
+</translation>
+    </message>
+</context>
+</TS>
diff --git a/wpa_supplicant/wpa_gui-qt4/main.cpp b/wpa_supplicant/wpa_gui-qt4/main.cpp
new file mode 100644 (file)
index 0000000..6170b15
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * wpa_gui - Application startup
+ * Copyright (c) 2005-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.
+ */
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <QApplication>
+#include <QtCore/QLibraryInfo>
+#include <QtCore/QTranslator>
+#include "wpagui.h"
+
+
+class WpaGuiApp : public QApplication
+{
+public:
+       WpaGuiApp(int &argc, char **argv);
+
+#ifndef QT_NO_SESSIONMANAGER
+       virtual void saveState(QSessionManager &manager);
+#endif
+
+       WpaGui *w;
+};
+
+WpaGuiApp::WpaGuiApp(int &argc, char **argv) : QApplication(argc, argv)
+{
+}
+
+#ifndef QT_NO_SESSIONMANAGER
+void WpaGuiApp::saveState(QSessionManager &manager)
+{
+       QApplication::saveState(manager);
+       w->saveState();
+}
+#endif
+
+
+int main(int argc, char *argv[])
+{
+       WpaGuiApp app(argc, argv);
+       QTranslator translator;
+       QString locale;
+       QString resourceDir;
+       int ret;
+
+       locale = QLocale::system().name();
+       resourceDir = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
+       if (!translator.load("wpa_gui_" + locale, resourceDir))
+               translator.load("wpa_gui_" + locale, "lang");
+       app.installTranslator(&translator);
+
+       WpaGui w(&app);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+       WSADATA wsaData;
+       if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+               /* printf("Could not find a usable WinSock.dll\n"); */
+               return -1;
+       }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       app.w = &w;
+
+       ret = app.exec();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+       WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       return ret;
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp b/wpa_supplicant/wpa_gui-qt4/networkconfig.cpp
new file mode 100644 (file)
index 0000000..fe50a8d
--- /dev/null
@@ -0,0 +1,858 @@
+/*
+ * wpa_gui - NetworkConfig class
+ * Copyright (c) 2005-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.
+ */
+
+#include <cstdio>
+#include <QMessageBox>
+
+#include "networkconfig.h"
+#include "wpagui.h"
+
+enum {
+       AUTH_NONE_OPEN,
+       AUTH_NONE_WEP,
+       AUTH_NONE_WEP_SHARED,
+       AUTH_IEEE8021X,
+       AUTH_WPA_PSK,
+       AUTH_WPA_EAP,
+       AUTH_WPA2_PSK,
+       AUTH_WPA2_EAP
+};
+
+#define WPA_GUI_KEY_DATA "[key is configured]"
+
+
+NetworkConfig::NetworkConfig(QWidget *parent, const char *, bool, Qt::WFlags)
+       : QDialog(parent)
+{
+       setupUi(this);
+
+       encrSelect->setEnabled(false);
+       connect(authSelect, SIGNAL(activated(int)), this,
+               SLOT(authChanged(int)));
+       connect(cancelButton, SIGNAL(clicked()), this, SLOT(close()));
+       connect(addButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
+       connect(encrSelect, SIGNAL(activated(const QString &)), this,
+               SLOT(encrChanged(const QString &)));
+       connect(removeButton, SIGNAL(clicked()), this, SLOT(removeNetwork()));
+       connect(eapSelect, SIGNAL(activated(int)), this,
+               SLOT(eapChanged(int)));
+       connect(useWpsButton, SIGNAL(clicked()), this, SLOT(useWps()));
+
+       wpagui = NULL;
+       new_network = false;
+}
+
+
+NetworkConfig::~NetworkConfig()
+{
+}
+
+
+void NetworkConfig::languageChange()
+{
+       retranslateUi(this);
+}
+
+
+void NetworkConfig::paramsFromScanResults(QTreeWidgetItem *sel)
+{
+       new_network = true;
+
+       /* SSID BSSID frequency signal flags */
+       setWindowTitle(sel->text(0));
+       ssidEdit->setText(sel->text(0));
+
+       QString flags = sel->text(4);
+       int auth, encr = 0;
+       if (flags.indexOf("[WPA2-EAP") >= 0)
+               auth = AUTH_WPA2_EAP;
+       else if (flags.indexOf("[WPA-EAP") >= 0)
+               auth = AUTH_WPA_EAP;
+       else if (flags.indexOf("[WPA2-PSK") >= 0)
+               auth = AUTH_WPA2_PSK;
+       else if (flags.indexOf("[WPA-PSK") >= 0)
+               auth = AUTH_WPA_PSK;
+       else
+               auth = AUTH_NONE_OPEN;
+
+       if (flags.indexOf("-CCMP") >= 0)
+               encr = 1;
+       else if (flags.indexOf("-TKIP") >= 0)
+               encr = 0;
+       else if (flags.indexOf("WEP") >= 0) {
+               encr = 1;
+               if (auth == AUTH_NONE_OPEN)
+                       auth = AUTH_NONE_WEP;
+       } else
+               encr = 0;
+
+       authSelect->setCurrentIndex(auth);
+       authChanged(auth);
+       encrSelect->setCurrentIndex(encr);
+
+       wepEnabled(auth == AUTH_NONE_WEP);
+
+       getEapCapa();
+
+       if (flags.indexOf("[WPS") >= 0)
+               useWpsButton->setEnabled(true);
+       bssid = sel->text(1);
+}
+
+
+void NetworkConfig::authChanged(int sel)
+{
+       encrSelect->setEnabled(sel != AUTH_NONE_OPEN && sel != AUTH_NONE_WEP &&
+                              sel != AUTH_NONE_WEP_SHARED);
+       pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK);
+       bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP ||
+               sel == AUTH_WPA2_EAP;
+       eapSelect->setEnabled(eap);
+       identityEdit->setEnabled(eap);
+       passwordEdit->setEnabled(eap);
+       cacertEdit->setEnabled(eap);
+       phase2Select->setEnabled(eap);
+       if (eap)
+               eapChanged(eapSelect->currentIndex());
+
+       while (encrSelect->count())
+               encrSelect->removeItem(0);
+
+       if (sel == AUTH_NONE_OPEN || sel == AUTH_NONE_WEP ||
+           sel == AUTH_NONE_WEP_SHARED || sel == AUTH_IEEE8021X) {
+               encrSelect->addItem("None");
+               encrSelect->addItem("WEP");
+               encrSelect->setCurrentIndex(sel == AUTH_NONE_OPEN ? 0 : 1);
+       } else {
+               encrSelect->addItem("TKIP");
+               encrSelect->addItem("CCMP");
+               encrSelect->setCurrentIndex((sel == AUTH_WPA2_PSK ||
+                                            sel == AUTH_WPA2_EAP) ? 1 : 0);
+       }
+
+       wepEnabled(sel == AUTH_NONE_WEP || sel == AUTH_NONE_WEP_SHARED);
+}
+
+
+void NetworkConfig::eapChanged(int sel)
+{
+       QString prev_val = phase2Select->currentText();
+       while (phase2Select->count())
+               phase2Select->removeItem(0);
+
+       QStringList inner;
+       inner << "PEAP" << "TTLS" << "FAST";
+       if (!inner.contains(eapSelect->itemText(sel)))
+               return;
+
+       phase2Select->addItem("[ any ]");
+
+       /* Add special cases based on outer method */
+       if (eapSelect->currentText().compare("TTLS") == 0) {
+               phase2Select->addItem("PAP");
+               phase2Select->addItem("CHAP");
+               phase2Select->addItem("MSCHAP");
+               phase2Select->addItem("MSCHAPv2");
+       } else if (eapSelect->currentText().compare("FAST") == 0)
+               phase2Select->addItem("GTC(auth) + MSCHAPv2(prov)");
+
+       /* Add all enabled EAP methods that can be used in the tunnel */
+       int i;
+       QStringList allowed;
+       allowed << "MSCHAPV2" << "MD5" << "GTC" << "TLS" << "OTP" << "SIM"
+               << "AKA";
+       for (i = 0; i < eapSelect->count(); i++) {
+               if (allowed.contains(eapSelect->itemText(i))) {
+                       phase2Select->addItem("EAP-" + eapSelect->itemText(i));
+               }
+       }
+
+       for (i = 0; i < phase2Select->count(); i++) {
+               if (phase2Select->itemText(i).compare(prev_val) == 0) {
+                       phase2Select->setCurrentIndex(i);
+                       break;
+               }
+       }
+}
+
+
+void NetworkConfig::addNetwork()
+{
+       char reply[10], cmd[256];
+       size_t reply_len;
+       int id;
+       int psklen = pskEdit->text().length();
+       int auth = authSelect->currentIndex();
+
+       if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) {
+               if (psklen < 8 || psklen > 64) {
+                       QMessageBox::warning(
+                               this,
+                               tr("WPA Pre-Shared Key Error"),
+                               tr("WPA-PSK requires a passphrase of 8 to 63 "
+                                  "characters\n"
+                                  "or 64 hex digit PSK"));
+                       pskEdit->setFocus();
+                       return;
+               }
+       }
+
+       if (idstrEdit->isEnabled() && !idstrEdit->text().isEmpty()) {
+               QRegExp rx("^(\\w|-)+$");
+               if (rx.indexIn(idstrEdit->text()) < 0) {
+                       QMessageBox::warning(
+                               this, tr("Network ID Error"),
+                               tr("Network ID String contains non-word "
+                                  "characters.\n"
+                                  "It must be a simple string, "
+                                  "without spaces, containing\n"
+                                  "only characters in this range: "
+                                  "[A-Za-z0-9_-]\n"));
+                       idstrEdit->setFocus();
+                       return;
+               }
+       }
+
+       if (wpagui == NULL)
+               return;
+
+       memset(reply, 0, sizeof(reply));
+       reply_len = sizeof(reply) - 1;
+
+       if (new_network) {
+               wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len);
+               if (reply[0] == 'F') {
+                       QMessageBox::warning(this, "wpa_gui",
+                                            tr("Failed to add "
+                                               "network to wpa_supplicant\n"
+                                               "configuration."));
+                       return;
+               }
+               id = atoi(reply);
+       } else
+               id = edit_network_id;
+
+       setNetworkParam(id, "ssid", ssidEdit->text().toAscii().constData(),
+                       true);
+
+       const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL;
+       switch (auth) {
+       case AUTH_NONE_OPEN:
+       case AUTH_NONE_WEP:
+       case AUTH_NONE_WEP_SHARED:
+               key_mgmt = "NONE";
+               break;
+       case AUTH_IEEE8021X:
+               key_mgmt = "IEEE8021X";
+               break;
+       case AUTH_WPA_PSK:
+               key_mgmt = "WPA-PSK";
+               proto = "WPA";
+               break;
+       case AUTH_WPA_EAP:
+               key_mgmt = "WPA-EAP";
+               proto = "WPA";
+               break;
+       case AUTH_WPA2_PSK:
+               key_mgmt = "WPA-PSK";
+               proto = "WPA2";
+               break;
+       case AUTH_WPA2_EAP:
+               key_mgmt = "WPA-EAP";
+               proto = "WPA2";
+               break;
+       }
+
+       if (auth == AUTH_NONE_WEP_SHARED)
+               setNetworkParam(id, "auth_alg", "SHARED", false);
+       else
+               setNetworkParam(id, "auth_alg", "OPEN", false);
+
+       if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP ||
+           auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) {
+               int encr = encrSelect->currentIndex();
+               if (encr == 0)
+                       pairwise = "TKIP";
+               else
+                       pairwise = "CCMP";
+       }
+
+       if (proto)
+               setNetworkParam(id, "proto", proto, false);
+       if (key_mgmt)
+               setNetworkParam(id, "key_mgmt", key_mgmt, false);
+       if (pairwise) {
+               setNetworkParam(id, "pairwise", pairwise, false);
+               setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false);
+       }
+       if (pskEdit->isEnabled() &&
+           strcmp(pskEdit->text().toAscii().constData(),
+                  WPA_GUI_KEY_DATA) != 0)
+               setNetworkParam(id, "psk",
+                               pskEdit->text().toAscii().constData(),
+                               psklen != 64);
+       if (eapSelect->isEnabled()) {
+               const char *eap =
+                       eapSelect->currentText().toAscii().constData();
+               setNetworkParam(id, "eap", eap, false);
+               if (strcmp(eap, "SIM") == 0 || strcmp(eap, "AKA") == 0)
+                       setNetworkParam(id, "pcsc", "", true);
+               else
+                       setNetworkParam(id, "pcsc", "NULL", false);
+       }
+       if (phase2Select->isEnabled()) {
+               QString eap = eapSelect->currentText();
+               QString inner = phase2Select->currentText();
+               char phase2[32];
+               phase2[0] = '\0';
+               if (eap.compare("PEAP") == 0) {
+                       if (inner.startsWith("EAP-"))
+                               snprintf(phase2, sizeof(phase2), "auth=%s",
+                                        inner.right(inner.size() - 4).
+                                        toAscii().constData());
+               } else if (eap.compare("TTLS") == 0) {
+                       if (inner.startsWith("EAP-"))
+                               snprintf(phase2, sizeof(phase2), "autheap=%s",
+                                        inner.right(inner.size() - 4).
+                                        toAscii().constData());
+                       else
+                               snprintf(phase2, sizeof(phase2), "auth=%s",
+                                        inner.toAscii().constData());
+               } else if (eap.compare("FAST") == 0) {
+                       const char *provisioning = NULL;
+                       if (inner.startsWith("EAP-")) {
+                               snprintf(phase2, sizeof(phase2), "auth=%s",
+                                        inner.right(inner.size() - 4).
+                                        toAscii().constData());
+                               provisioning = "fast_provisioning=2";
+                       } else if (inner.compare("GTC(auth) + MSCHAPv2(prov)")
+                                  == 0) {
+                               snprintf(phase2, sizeof(phase2),
+                                        "auth=GTC auth=MSCHAPV2");
+                               provisioning = "fast_provisioning=1";
+                       } else
+                               provisioning = "fast_provisioning=3";
+                       if (provisioning) {
+                               char blob[32];
+                               setNetworkParam(id, "phase1", provisioning,
+                                               true);
+                               snprintf(blob, sizeof(blob),
+                                        "blob://fast-pac-%d", id);
+                               setNetworkParam(id, "pac_file", blob, true);
+                       }
+               }
+               if (phase2[0])
+                       setNetworkParam(id, "phase2", phase2, true);
+               else
+                       setNetworkParam(id, "phase2", "NULL", false);
+       } else
+               setNetworkParam(id, "phase2", "NULL", false);
+       if (identityEdit->isEnabled() && identityEdit->text().length() > 0)
+               setNetworkParam(id, "identity",
+                               identityEdit->text().toAscii().constData(),
+                               true);
+       else
+               setNetworkParam(id, "identity", "NULL", false);
+       if (passwordEdit->isEnabled() && passwordEdit->text().length() > 0 &&
+           strcmp(passwordEdit->text().toAscii().constData(),
+                  WPA_GUI_KEY_DATA) != 0)
+               setNetworkParam(id, "password",
+                               passwordEdit->text().toAscii().constData(),
+                               true);
+       else if (passwordEdit->text().length() == 0)
+               setNetworkParam(id, "password", "NULL", false);
+       if (cacertEdit->isEnabled() && cacertEdit->text().length() > 0)
+               setNetworkParam(id, "ca_cert",
+                               cacertEdit->text().toAscii().constData(),
+                               true);
+       else
+               setNetworkParam(id, "ca_cert", "NULL", false);
+       writeWepKey(id, wep0Edit, 0);
+       writeWepKey(id, wep1Edit, 1);
+       writeWepKey(id, wep2Edit, 2);
+       writeWepKey(id, wep3Edit, 3);
+
+       if (wep0Radio->isEnabled() && wep0Radio->isChecked())
+               setNetworkParam(id, "wep_tx_keyidx", "0", false);
+       else if (wep1Radio->isEnabled() && wep1Radio->isChecked())
+               setNetworkParam(id, "wep_tx_keyidx", "1", false);
+       else if (wep2Radio->isEnabled() && wep2Radio->isChecked())
+               setNetworkParam(id, "wep_tx_keyidx", "2", false);
+       else if (wep3Radio->isEnabled() && wep3Radio->isChecked())
+               setNetworkParam(id, "wep_tx_keyidx", "3", false);
+
+       if (idstrEdit->isEnabled() && idstrEdit->text().length() > 0)
+               setNetworkParam(id, "id_str",
+                               idstrEdit->text().toAscii().constData(),
+                               true);
+       else
+               setNetworkParam(id, "id_str", "NULL", false);
+
+       if (prioritySpinBox->isEnabled()) {
+               QString prio;
+               prio = prio.setNum(prioritySpinBox->value());
+               setNetworkParam(id, "priority", prio.toAscii().constData(),
+                               false);
+       }
+
+       snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id);
+       reply_len = sizeof(reply);
+       wpagui->ctrlRequest(cmd, reply, &reply_len);
+       if (strncmp(reply, "OK", 2) != 0) {
+               QMessageBox::warning(this, "wpa_gui",
+                                    tr("Failed to enable "
+                                       "network in wpa_supplicant\n"
+                                       "configuration."));
+               /* Network was added, so continue anyway */
+       }
+       wpagui->triggerUpdate();
+       wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len);
+
+       close();
+}
+
+
+void NetworkConfig::setWpaGui(WpaGui *_wpagui)
+{
+       wpagui = _wpagui;
+}
+
+
+int NetworkConfig::setNetworkParam(int id, const char *field,
+                                  const char *value, bool quote)
+{
+       char reply[10], cmd[256];
+       size_t reply_len;
+       snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s",
+                id, field, quote ? "\"" : "", value, quote ? "\"" : "");
+       reply_len = sizeof(reply);
+       wpagui->ctrlRequest(cmd, reply, &reply_len);
+       return strncmp(reply, "OK", 2) == 0 ? 0 : -1;
+}
+
+
+void NetworkConfig::encrChanged(const QString &)
+{
+}
+
+
+void NetworkConfig::wepEnabled(bool enabled)
+{
+       wep0Edit->setEnabled(enabled);
+       wep1Edit->setEnabled(enabled);
+       wep2Edit->setEnabled(enabled);
+       wep3Edit->setEnabled(enabled);
+       wep0Radio->setEnabled(enabled);
+       wep1Radio->setEnabled(enabled);
+       wep2Radio->setEnabled(enabled);
+       wep3Radio->setEnabled(enabled);
+}
+
+
+void NetworkConfig::writeWepKey(int network_id, QLineEdit *edit, int id)
+{
+       char buf[10];
+       bool hex;
+       const char *txt, *pos;
+       size_t len;
+
+       if (!edit->isEnabled() || edit->text().isEmpty())
+               return;
+
+       /*
+        * Assume hex key if only hex characters are present and length matches
+        * with 40, 104, or 128-bit key
+        */
+       txt = edit->text().toAscii().constData();
+       if (strcmp(txt, WPA_GUI_KEY_DATA) == 0)
+               return;
+       len = strlen(txt);
+       if (len == 0)
+               return;
+       pos = txt;
+       hex = true;
+       while (*pos) {
+               if (!((*pos >= '0' && *pos <= '9') ||
+                     (*pos >= 'a' && *pos <= 'f') ||
+                     (*pos >= 'A' && *pos <= 'F'))) {
+                       hex = false;
+                       break;
+               }
+               pos++;
+       }
+       if (hex && len != 10 && len != 26 && len != 32)
+               hex = false;
+       snprintf(buf, sizeof(buf), "wep_key%d", id);
+       setNetworkParam(network_id, buf, txt, !hex);
+}
+
+
+static int key_value_isset(const char *reply, size_t reply_len)
+{
+    return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0);
+}
+
+
+void NetworkConfig::paramsFromConfig(int network_id)
+{
+       int i, res;
+
+       edit_network_id = network_id;
+       getEapCapa();
+
+       char reply[1024], cmd[256], *pos;
+       size_t reply_len;
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+           reply_len >= 2 && reply[0] == '"') {
+               reply[reply_len] = '\0';
+               pos = strchr(reply + 1, '"');
+               if (pos)
+                       *pos = '\0';
+               ssidEdit->setText(reply + 1);
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id);
+       reply_len = sizeof(reply) - 1;
+       int wpa = 0;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+               reply[reply_len] = '\0';
+               if (strstr(reply, "RSN") || strstr(reply, "WPA2"))
+                       wpa = 2;
+               else if (strstr(reply, "WPA"))
+                       wpa = 1;
+       }
+
+       int auth = AUTH_NONE_OPEN, encr = 0;
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+               reply[reply_len] = '\0';
+               if (strstr(reply, "WPA-EAP"))
+                       auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP;
+               else if (strstr(reply, "WPA-PSK"))
+                       auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK;
+               else if (strstr(reply, "IEEE8021X")) {
+                       auth = AUTH_IEEE8021X;
+                       encr = 1;
+               }
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+               reply[reply_len] = '\0';
+               if (strstr(reply, "CCMP") && auth != AUTH_NONE_OPEN &&
+                   auth != AUTH_NONE_WEP && auth != AUTH_NONE_WEP_SHARED)
+                       encr = 1;
+               else if (strstr(reply, "TKIP"))
+                       encr = 0;
+               else if (strstr(reply, "WEP"))
+                       encr = 1;
+               else
+                       encr = 0;
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id);
+       reply_len = sizeof(reply) - 1;
+       res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+       if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+               reply[reply_len] = '\0';
+               pos = strchr(reply + 1, '"');
+               if (pos)
+                       *pos = '\0';
+               pskEdit->setText(reply + 1);
+       } else if (res >= 0 && key_value_isset(reply, reply_len)) {
+               pskEdit->setText(WPA_GUI_KEY_DATA);
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+           reply_len >= 2 && reply[0] == '"') {
+               reply[reply_len] = '\0';
+               pos = strchr(reply + 1, '"');
+               if (pos)
+                       *pos = '\0';
+               identityEdit->setText(reply + 1);
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id);
+       reply_len = sizeof(reply) - 1;
+       res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+       if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+               reply[reply_len] = '\0';
+               pos = strchr(reply + 1, '"');
+               if (pos)
+                       *pos = '\0';
+               passwordEdit->setText(reply + 1);
+       } else if (res >= 0 && key_value_isset(reply, reply_len)) {
+               passwordEdit->setText(WPA_GUI_KEY_DATA);
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+           reply_len >= 2 && reply[0] == '"') {
+               reply[reply_len] = '\0';
+               pos = strchr(reply + 1, '"');
+               if (pos)
+                       *pos = '\0';
+               cacertEdit->setText(reply + 1);
+       }
+
+       enum { NO_INNER, PEAP_INNER, TTLS_INNER, FAST_INNER } eap = NO_INNER;
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+           reply_len >= 1) {
+               reply[reply_len] = '\0';
+               for (i = 0; i < eapSelect->count(); i++) {
+                       if (eapSelect->itemText(i).compare(reply) == 0) {
+                               eapSelect->setCurrentIndex(i);
+                               if (strcmp(reply, "PEAP") == 0)
+                                       eap = PEAP_INNER;
+                               else if (strcmp(reply, "TTLS") == 0)
+                                       eap = TTLS_INNER;
+                               else if (strcmp(reply, "FAST") == 0)
+                                       eap = FAST_INNER;
+                               break;
+                       }
+               }
+       }
+
+       if (eap != NO_INNER) {
+               snprintf(cmd, sizeof(cmd), "GET_NETWORK %d phase2",
+                        network_id);
+               reply_len = sizeof(reply) - 1;
+               if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+                   reply_len >= 1) {
+                       reply[reply_len] = '\0';
+                       eapChanged(eapSelect->currentIndex());
+               } else
+                       eap = NO_INNER;
+       }
+
+       char *val;
+       val = reply + 1;
+       while (*(val + 1))
+               val++;
+       if (*val == '"')
+               *val = '\0';
+
+       switch (eap) {
+       case PEAP_INNER:
+               if (strncmp(reply, "\"auth=", 6))
+                       break;
+               val = reply + 2;
+               memcpy(val, "EAP-", 4);
+               break;
+       case TTLS_INNER:
+               if (strncmp(reply, "\"autheap=", 9) == 0) {
+                       val = reply + 5;
+                       memcpy(val, "EAP-", 4);
+               } else if (strncmp(reply, "\"auth=", 6) == 0)
+                       val = reply + 6;
+               break;
+       case FAST_INNER:
+               if (strncmp(reply, "\"auth=", 6))
+                       break;
+               if (strcmp(reply + 6, "GTC auth=MSCHAPV2") == 0) {
+                       val = (char *) "GTC(auth) + MSCHAPv2(prov)";
+                       break;
+               }
+               val = reply + 2;
+               memcpy(val, "EAP-", 4);
+               break;
+       case NO_INNER:
+               break;
+       }
+
+       for (i = 0; i < phase2Select->count(); i++) {
+               if (phase2Select->itemText(i).compare(val) == 0) {
+                       phase2Select->setCurrentIndex(i);
+                       break;
+               }
+       }
+
+       for (i = 0; i < 4; i++) {
+               QLineEdit *wepEdit;
+               switch (i) {
+               default:
+               case 0:
+                       wepEdit = wep0Edit;
+                       break;
+               case 1:
+                       wepEdit = wep1Edit;
+                       break;
+               case 2:
+                       wepEdit = wep2Edit;
+                       break;
+               case 3:
+                       wepEdit = wep3Edit;
+                       break;
+               }
+               snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d",
+                        network_id, i);
+               reply_len = sizeof(reply) - 1;
+               res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+               if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+                       reply[reply_len] = '\0';
+                       pos = strchr(reply + 1, '"');
+                       if (pos)
+                               *pos = '\0';
+                       if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) {
+                               if (auth == AUTH_NONE_OPEN)
+                                       auth = AUTH_NONE_WEP;
+                               encr = 1;
+                       }
+
+                       wepEdit->setText(reply + 1);
+               } else if (res >= 0 && key_value_isset(reply, reply_len)) {
+                       if (auth == AUTH_NONE_OPEN || auth == AUTH_IEEE8021X) {
+                               if (auth == AUTH_NONE_OPEN)
+                                       auth = AUTH_NONE_WEP;
+                               encr = 1;
+                       }
+                       wepEdit->setText(WPA_GUI_KEY_DATA);
+               }
+       }
+
+       if (auth == AUTH_NONE_WEP) {
+               snprintf(cmd, sizeof(cmd), "GET_NETWORK %d auth_alg",
+                        network_id);
+               reply_len = sizeof(reply) - 1;
+               if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+                       reply[reply_len] = '\0';
+                       if (strcmp(reply, "SHARED") == 0)
+                               auth = AUTH_NONE_WEP_SHARED;
+               }
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1)
+       {
+               reply[reply_len] = '\0';
+               switch (atoi(reply)) {
+               case 0:
+                       wep0Radio->setChecked(true);
+                       break;
+               case 1:
+                       wep1Radio->setChecked(true);
+                       break;
+               case 2:
+                       wep2Radio->setChecked(true);
+                       break;
+               case 3:
+                       wep3Radio->setChecked(true);
+                       break;
+               }
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 &&
+           reply_len >= 2 && reply[0] == '"') {
+               reply[reply_len] = '\0';
+               pos = strchr(reply + 1, '"');
+               if (pos)
+                       *pos = '\0';
+               idstrEdit->setText(reply + 1);
+       }
+
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d priority", network_id);
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1)
+       {
+               reply[reply_len] = '\0';
+               prioritySpinBox->setValue(atoi(reply));
+       }
+
+       authSelect->setCurrentIndex(auth);
+       authChanged(auth);
+       encrSelect->setCurrentIndex(encr);
+       wepEnabled(auth == AUTH_NONE_WEP || auth == AUTH_NONE_WEP_SHARED);
+
+       removeButton->setEnabled(true);
+       addButton->setText("Save");
+}
+
+
+void NetworkConfig::removeNetwork()
+{
+       char reply[10], cmd[256];
+       size_t reply_len;
+
+       if (QMessageBox::information(
+                   this, "wpa_gui",
+                   tr("This will permanently remove the network\n"
+                      "from the configuration. Do you really want\n"
+                      "to remove this network?"),
+                   tr("Yes"), tr("No")) != 0)
+               return;
+
+       snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id);
+       reply_len = sizeof(reply);
+       wpagui->ctrlRequest(cmd, reply, &reply_len);
+       if (strncmp(reply, "OK", 2) != 0) {
+               QMessageBox::warning(this, "wpa_gui",
+                                    tr("Failed to remove network from "
+                                       "wpa_supplicant\n"
+                                       "configuration."));
+       } else {
+               wpagui->triggerUpdate();
+               wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len);
+       }
+
+       close();
+}
+
+
+void NetworkConfig::newNetwork()
+{
+       new_network = true;
+       getEapCapa();
+}
+
+
+void NetworkConfig::getEapCapa()
+{
+       char reply[256];
+       size_t reply_len;
+
+       if (wpagui == NULL)
+               return;
+
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0)
+               return;
+       reply[reply_len] = '\0';
+
+       QString res(reply);
+       QStringList types = res.split(QChar(' '));
+       eapSelect->insertItems(-1, types);
+}
+
+
+void NetworkConfig::useWps()
+{
+       if (wpagui == NULL)
+               return;
+       wpagui->setBssFromScan(bssid);
+       wpagui->wpsDialog();
+       close();
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.h b/wpa_supplicant/wpa_gui-qt4/networkconfig.h
new file mode 100644 (file)
index 0000000..0ceeb41
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * wpa_gui - NetworkConfig class
+ * Copyright (c) 2005-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 NETWORKCONFIG_H
+#define NETWORKCONFIG_H
+
+#include <QObject>
+#include "ui_networkconfig.h"
+
+class WpaGui;
+
+class NetworkConfig : public QDialog, public Ui::NetworkConfig
+{
+       Q_OBJECT
+
+public:
+       NetworkConfig(QWidget *parent = 0, const char *name = 0,
+                     bool modal = false, Qt::WFlags fl = 0);
+       ~NetworkConfig();
+
+       virtual void paramsFromScanResults(QTreeWidgetItem *sel);
+       virtual void setWpaGui(WpaGui *_wpagui);
+       virtual int setNetworkParam(int id, const char *field,
+                                   const char *value, bool quote);
+       virtual void paramsFromConfig(int network_id);
+       virtual void newNetwork();
+
+public slots:
+       virtual void authChanged(int sel);
+       virtual void addNetwork();
+       virtual void encrChanged(const QString &sel);
+       virtual void writeWepKey(int network_id, QLineEdit *edit, int id);
+       virtual void removeNetwork();
+       virtual void eapChanged(int sel);
+       virtual void useWps();
+
+protected slots:
+       virtual void languageChange();
+
+private:
+       WpaGui *wpagui;
+       int edit_network_id;
+       bool new_network;
+       QString bssid;
+
+       virtual void wepEnabled(bool enabled);
+       virtual void getEapCapa();
+};
+
+#endif /* NETWORKCONFIG_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/networkconfig.ui b/wpa_supplicant/wpa_gui-qt4/networkconfig.ui
new file mode 100644 (file)
index 0000000..217a8ff
--- /dev/null
@@ -0,0 +1,435 @@
+<ui version="4.0" >
+ <class>NetworkConfig</class>
+ <widget class="QDialog" name="NetworkConfig" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>410</width>
+    <height>534</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>NetworkConfig</string>
+  </property>
+  <layout class="QGridLayout" >
+   <item row="1" column="3" >
+    <widget class="QPushButton" name="cancelButton" >
+     <property name="text" >
+      <string>Cancel</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="0" colspan="4" >
+    <widget class="QFrame" name="frame9" >
+     <property name="frameShape" >
+      <enum>QFrame::NoFrame</enum>
+     </property>
+     <property name="frameShadow" >
+      <enum>QFrame::Plain</enum>
+     </property>
+     <layout class="QGridLayout" >
+      <item row="0" column="0" >
+       <widget class="QLabel" name="ssidLabel" >
+        <property name="text" >
+         <string>SSID</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1" >
+       <widget class="QLineEdit" name="ssidEdit" >
+        <property name="toolTip" >
+         <string>Network name (Service Set IDentifier)</string>
+        </property>
+        <property name="text" >
+         <string/>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0" >
+       <widget class="QLabel" name="authLabel" >
+        <property name="text" >
+         <string>Authentication</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" >
+       <widget class="QComboBox" name="authSelect" >
+        <item>
+         <property name="text" >
+          <string>Plaintext (open / no authentication)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>Static WEP (no authentication)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>Static WEP (Shared Key authentication)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>IEEE 802.1X</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA-Personal (PSK)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA-Enterprise (EAP)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA2-Personal (PSK)</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WPA2-Enterprise (EAP)</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="2" column="0" >
+       <widget class="QLabel" name="encrLabel" >
+        <property name="text" >
+         <string>Encryption</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1" >
+       <widget class="QComboBox" name="encrSelect" >
+        <item>
+         <property name="text" >
+          <string>None</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>WEP</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>TKIP</string>
+         </property>
+        </item>
+        <item>
+         <property name="text" >
+          <string>CCMP</string>
+         </property>
+        </item>
+       </widget>
+      </item>
+      <item row="3" column="0" >
+       <widget class="QLabel" name="pskLabel" >
+        <property name="text" >
+         <string>PSK</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1" >
+       <widget class="QLineEdit" name="pskEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+        <property name="toolTip" >
+         <string>WPA/WPA2 pre-shared key or passphrase</string>
+        </property>
+        <property name="whatsThis" >
+         <string/>
+        </property>
+        <property name="echoMode" >
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0" >
+       <widget class="QLabel" name="eapLabel" >
+        <property name="text" >
+         <string>EAP method</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1" >
+       <widget class="QComboBox" name="eapSelect" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" >
+       <widget class="QLabel" name="identityLabel" >
+        <property name="text" >
+         <string>Identity</string>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="1" >
+       <widget class="QLineEdit" name="identityEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+        <property name="toolTip" >
+         <string>Username/Identity for EAP methods</string>
+        </property>
+       </widget>
+      </item>
+      <item row="6" column="0" >
+       <widget class="QLabel" name="passwordLabel" >
+        <property name="text" >
+         <string>Password</string>
+        </property>
+       </widget>
+      </item>
+      <item row="6" column="1" >
+       <widget class="QLineEdit" name="passwordEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+        <property name="toolTip" >
+         <string>Password for EAP methods</string>
+        </property>
+        <property name="echoMode" >
+         <enum>QLineEdit::Password</enum>
+        </property>
+       </widget>
+      </item>
+      <item row="7" column="0" >
+       <widget class="QLabel" name="cacertLabel" >
+        <property name="text" >
+         <string>CA certificate</string>
+        </property>
+       </widget>
+      </item>
+      <item row="7" column="1" >
+       <widget class="QLineEdit" name="cacertEdit" >
+        <property name="enabled" >
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="8" column="0" colspan="2" >
+       <widget class="QGroupBox" name="wepBox" >
+        <property name="enabled" >
+         <bool>true</bool>
+        </property>
+        <property name="title" >
+         <string>WEP keys</string>
+        </property>
+        <layout class="QGridLayout" >
+         <item row="0" column="0" >
+          <widget class="QRadioButton" name="wep0Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 0</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" >
+          <widget class="QRadioButton" name="wep1Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 1</string>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="0" >
+          <widget class="QRadioButton" name="wep3Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 3</string>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="0" >
+          <widget class="QRadioButton" name="wep2Radio" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+           <property name="text" >
+            <string>key 2</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="1" >
+          <widget class="QLineEdit" name="wep0Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1" >
+          <widget class="QLineEdit" name="wep1Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1" >
+          <widget class="QLineEdit" name="wep2Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="1" >
+          <widget class="QLineEdit" name="wep3Edit" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item row="9" column="0" colspan="2" >
+       <widget class="QGroupBox" name="optionalSettingsBox" >
+        <property name="enabled" >
+         <bool>true</bool>
+        </property>
+        <property name="title" >
+         <string>Optional Settings</string>
+        </property>
+        <layout class="QGridLayout" >
+         <item row="0" column="1" >
+          <widget class="QLineEdit" name="idstrEdit" >
+           <property name="toolTip" >
+            <string>Network Identification String</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="3" >
+          <widget class="QSpinBox" name="prioritySpinBox" >
+           <property name="toolTip" >
+            <string>Network Priority</string>
+           </property>
+           <property name="maximum" >
+            <number>10000</number>
+           </property>
+           <property name="singleStep" >
+            <number>10</number>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="0" >
+          <widget class="QLabel" name="idstrLabel" >
+           <property name="text" >
+            <string>IDString</string>
+           </property>
+          </widget>
+         </item>
+         <item row="0" column="2" >
+          <widget class="QLabel" name="priorityLabel" >
+           <property name="text" >
+            <string>Priority</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0" >
+          <widget class="QLabel" name="phase2Label" >
+           <property name="text" >
+            <string>Inner auth</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1" >
+          <widget class="QComboBox" name="phase2Select" >
+           <property name="enabled" >
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item row="1" column="2" >
+    <widget class="QPushButton" name="addButton" >
+     <property name="text" >
+      <string>Add</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="3" >
+    <widget class="QPushButton" name="removeButton" >
+     <property name="enabled" >
+      <bool>false</bool>
+     </property>
+     <property name="text" >
+      <string>Remove</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0" >
+    <spacer>
+     <property name="orientation" >
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" >
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+   <item row="1" column="1" >
+    <widget class="QPushButton" name="useWpsButton" >
+     <property name="enabled" >
+      <bool>false</bool>
+     </property>
+     <property name="text" >
+      <string>WPS</string>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <tabstops>
+  <tabstop>ssidEdit</tabstop>
+  <tabstop>authSelect</tabstop>
+  <tabstop>encrSelect</tabstop>
+  <tabstop>pskEdit</tabstop>
+  <tabstop>eapSelect</tabstop>
+  <tabstop>identityEdit</tabstop>
+  <tabstop>passwordEdit</tabstop>
+  <tabstop>cacertEdit</tabstop>
+  <tabstop>wep0Radio</tabstop>
+  <tabstop>wep0Edit</tabstop>
+  <tabstop>wep1Radio</tabstop>
+  <tabstop>wep1Edit</tabstop>
+  <tabstop>wep2Radio</tabstop>
+  <tabstop>wep2Edit</tabstop>
+  <tabstop>wep3Radio</tabstop>
+  <tabstop>wep3Edit</tabstop>
+  <tabstop>idstrEdit</tabstop>
+  <tabstop>prioritySpinBox</tabstop>
+  <tabstop>phase2Select</tabstop>
+  <tabstop>addButton</tabstop>
+  <tabstop>removeButton</tabstop>
+  <tabstop>cancelButton</tabstop>
+ </tabstops>
+ <includes>
+  <include location="global" >qtreewidget.h</include>
+ </includes>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.cpp b/wpa_supplicant/wpa_gui-qt4/peers.cpp
new file mode 100644 (file)
index 0000000..7a99299
--- /dev/null
@@ -0,0 +1,956 @@
+/*
+ * wpa_gui - Peers class
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 <cstdio>
+#include <QImageReader>
+#include <QMessageBox>
+
+#include "common/wpa_ctrl.h"
+#include "wpagui.h"
+#include "stringquery.h"
+#include "peers.h"
+
+
+enum {
+       peer_role_address = Qt::UserRole + 1,
+       peer_role_type,
+       peer_role_uuid,
+       peer_role_details,
+       peer_role_pri_dev_type,
+       peer_role_ssid,
+       peer_role_config_methods,
+       peer_role_dev_passwd_id,
+       peer_role_bss_id
+};
+
+/*
+ * TODO:
+ * - add current AP info (e.g., from WPS) in station mode
+ */
+
+enum peer_type {
+       PEER_TYPE_ASSOCIATED_STATION,
+       PEER_TYPE_AP,
+       PEER_TYPE_AP_WPS,
+       PEER_TYPE_WPS_PIN_NEEDED,
+       PEER_TYPE_WPS_ER_AP,
+       PEER_TYPE_WPS_ER_AP_UNCONFIGURED,
+       PEER_TYPE_WPS_ER_ENROLLEE,
+       PEER_TYPE_WPS_ENROLLEE
+};
+
+
+Peers::Peers(QWidget *parent, const char *, bool, Qt::WFlags)
+       : QDialog(parent)
+{
+       setupUi(this);
+
+       if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
+       {
+               default_icon = new QIcon(":/icons/wpa_gui.svg");
+               ap_icon = new QIcon(":/icons/ap.svg");
+               laptop_icon = new QIcon(":/icons/laptop.svg");
+       } else {
+               default_icon = new QIcon(":/icons/wpa_gui.png");
+               ap_icon = new QIcon(":/icons/ap.png");
+               laptop_icon = new QIcon(":/icons/laptop.png");
+       }
+
+       peers->setModel(&model);
+       peers->setResizeMode(QListView::Adjust);
+
+       peers->setContextMenuPolicy(Qt::CustomContextMenu);
+       connect(peers, SIGNAL(customContextMenuRequested(const QPoint &)),
+               this, SLOT(context_menu(const QPoint &)));
+
+       wpagui = NULL;
+}
+
+
+void Peers::setWpaGui(WpaGui *_wpagui)
+{
+       wpagui = _wpagui;
+       update_peers();
+}
+
+
+Peers::~Peers()
+{
+       delete default_icon;
+       delete ap_icon;
+       delete laptop_icon;
+}
+
+
+void Peers::languageChange()
+{
+       retranslateUi(this);
+}
+
+
+QString Peers::ItemType(int type)
+{
+       QString title;
+       switch (type) {
+       case PEER_TYPE_ASSOCIATED_STATION:
+               title = tr("Associated station");
+               break;
+       case PEER_TYPE_AP:
+               title = tr("AP");
+               break;
+       case PEER_TYPE_AP_WPS:
+               title = tr("WPS AP");
+               break;
+       case PEER_TYPE_WPS_PIN_NEEDED:
+               title = tr("WPS PIN needed");
+               break;
+       case PEER_TYPE_WPS_ER_AP:
+               title = tr("ER: WPS AP");
+               break;
+       case PEER_TYPE_WPS_ER_AP_UNCONFIGURED:
+               title = tr("ER: WPS AP (Unconfigured)");
+               break;
+       case PEER_TYPE_WPS_ER_ENROLLEE:
+               title = tr("ER: WPS Enrollee");
+               break;
+       case PEER_TYPE_WPS_ENROLLEE:
+               title = tr("WPS Enrollee");
+               break;
+       }
+       return title;
+}
+
+
+void Peers::context_menu(const QPoint &pos)
+{
+       QMenu *menu = new QMenu;
+       if (menu == NULL)
+               return;
+
+       QModelIndex idx = peers->indexAt(pos);
+       if (idx.isValid()) {
+               ctx_item = model.itemFromIndex(idx);
+               int type = ctx_item->data(peer_role_type).toInt();
+               menu->addAction(Peers::ItemType(type))->setEnabled(false);
+               menu->addSeparator();
+
+               int config_methods = -1;
+               QVariant var = ctx_item->data(peer_role_config_methods);
+               if (var.isValid())
+                       config_methods = var.toInt();
+
+               if ((type == PEER_TYPE_ASSOCIATED_STATION ||
+                    type == PEER_TYPE_AP_WPS ||
+                    type == PEER_TYPE_WPS_PIN_NEEDED ||
+                    type == PEER_TYPE_WPS_ER_ENROLLEE ||
+                    type == PEER_TYPE_WPS_ENROLLEE) &&
+                   (config_methods == -1 || (config_methods & 0x010c))) {
+                       menu->addAction(tr("Enter WPS PIN"), this,
+                                       SLOT(enter_pin()));
+               }
+
+               if (type == PEER_TYPE_AP_WPS) {
+                       menu->addAction(tr("Connect (PBC)"), this,
+                                       SLOT(connect_pbc()));
+               }
+
+               if ((type == PEER_TYPE_ASSOCIATED_STATION ||
+                    type == PEER_TYPE_WPS_ER_ENROLLEE ||
+                    type == PEER_TYPE_WPS_ENROLLEE) &&
+                   config_methods >= 0 && (config_methods & 0x0080)) {
+                       menu->addAction(tr("Enroll (PBC)"), this,
+                                       SLOT(connect_pbc()));
+               }
+
+               if (type == PEER_TYPE_WPS_ER_AP) {
+                       menu->addAction(tr("Learn Configuration"), this,
+                                       SLOT(learn_ap_config()));
+               }
+
+               menu->addAction(tr("Properties"), this, SLOT(properties()));
+       } else {
+               ctx_item = NULL;
+               menu->addAction(QString(tr("Refresh")), this,
+                               SLOT(ctx_refresh()));
+       }
+
+       menu->exec(peers->mapToGlobal(pos));
+}
+
+
+void Peers::enter_pin()
+{
+       if (ctx_item == NULL)
+               return;
+
+       int peer_type = ctx_item->data(peer_role_type).toInt();
+       QString uuid;
+       QString addr;
+       if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE)
+               uuid = ctx_item->data(peer_role_uuid).toString();
+       else
+               addr = ctx_item->data(peer_role_address).toString();
+
+       StringQuery input(tr("PIN:"));
+       input.setWindowTitle(tr("PIN for ") + ctx_item->text());
+       if (input.exec() != QDialog::Accepted)
+               return;
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+
+       if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
+               snprintf(cmd, sizeof(cmd), "WPS_ER_PIN %s %s",
+                        uuid.toAscii().constData(),
+                        input.get_string().toAscii().constData());
+       } else {
+               snprintf(cmd, sizeof(cmd), "WPS_PIN %s %s",
+                        addr.toAscii().constData(),
+                        input.get_string().toAscii().constData());
+       }
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to set the WPS PIN."));
+               msg.exec();
+       }
+}
+
+
+void Peers::ctx_refresh()
+{
+       update_peers();
+}
+
+
+void Peers::add_station(QString info)
+{
+       QStringList lines = info.split(QRegExp("\\n"));
+       QString name;
+
+       for (QStringList::Iterator it = lines.begin();
+            it != lines.end(); it++) {
+               int pos = (*it).indexOf('=') + 1;
+               if (pos < 1)
+                       continue;
+
+               if ((*it).startsWith("wpsDeviceName="))
+                       name = (*it).mid(pos);
+       }
+
+       if (name.isEmpty())
+               name = lines[0];
+
+       QStandardItem *item = new QStandardItem(*laptop_icon, name);
+       if (item) {
+               item->setData(lines[0], peer_role_address);
+               item->setData(PEER_TYPE_ASSOCIATED_STATION,
+                             peer_role_type);
+               item->setData(info, peer_role_details);
+               item->setToolTip(ItemType(PEER_TYPE_ASSOCIATED_STATION));
+               model.appendRow(item);
+       }
+}
+
+
+void Peers::add_stations()
+{
+       char reply[2048];
+       size_t reply_len;
+       char cmd[30];
+       int res;
+
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest("STA-FIRST", reply, &reply_len) < 0)
+               return;
+
+       do {
+               reply[reply_len] = '\0';
+               QString info(reply);
+               char *txt = reply;
+               while (*txt != '\0' && *txt != '\n')
+                       txt++;
+               *txt++ = '\0';
+               if (strncmp(reply, "FAIL", 4) == 0 ||
+                   strncmp(reply, "UNKNOWN", 7) == 0)
+                       break;
+
+               add_station(info);
+
+               reply_len = sizeof(reply) - 1;
+               snprintf(cmd, sizeof(cmd), "STA-NEXT %s", reply);
+               res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+       } while (res >= 0);
+}
+
+
+void Peers::add_single_station(const char *addr)
+{
+       char reply[2048];
+       size_t reply_len;
+       char cmd[30];
+
+       reply_len = sizeof(reply) - 1;
+       snprintf(cmd, sizeof(cmd), "STA %s", addr);
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+               return;
+
+       reply[reply_len] = '\0';
+       QString info(reply);
+       char *txt = reply;
+       while (*txt != '\0' && *txt != '\n')
+               txt++;
+       *txt++ = '\0';
+       if (strncmp(reply, "FAIL", 4) == 0 ||
+           strncmp(reply, "UNKNOWN", 7) == 0)
+               return;
+
+       add_station(info);
+}
+
+
+void Peers::remove_bss(int id)
+{
+       if (model.rowCount() == 0)
+               return;
+
+       QModelIndexList lst = model.match(model.index(0, 0), peer_role_bss_id,
+                                         id);
+       if (lst.size() == 0)
+               return;
+       model.removeRow(lst[0].row());
+}
+
+
+bool Peers::add_bss(const char *cmd)
+{
+       char reply[2048];
+       size_t reply_len;
+
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+               return false;
+       reply[reply_len] = '\0';
+
+       QString bss(reply);
+       if (bss.isEmpty() || bss.startsWith("FAIL"))
+               return false;
+
+       QString ssid, bssid, flags, wps_name, pri_dev_type;
+       int id = -1;
+
+       QStringList lines = bss.split(QRegExp("\\n"));
+       for (QStringList::Iterator it = lines.begin();
+            it != lines.end(); it++) {
+               int pos = (*it).indexOf('=') + 1;
+               if (pos < 1)
+                       continue;
+
+               if ((*it).startsWith("bssid="))
+                       bssid = (*it).mid(pos);
+               else if ((*it).startsWith("id="))
+                       id = (*it).mid(pos).toInt();
+               else if ((*it).startsWith("flags="))
+                       flags = (*it).mid(pos);
+               else if ((*it).startsWith("ssid="))
+                       ssid = (*it).mid(pos);
+               else if ((*it).startsWith("wps_device_name="))
+                       wps_name = (*it).mid(pos);
+               else if ((*it).startsWith("wps_primary_device_type="))
+                       pri_dev_type = (*it).mid(pos);
+       }
+
+       QString name = wps_name;
+       if (name.isEmpty())
+               name = ssid + "\n" + bssid;
+
+       QStandardItem *item = new QStandardItem(*ap_icon, name);
+       if (item) {
+               item->setData(bssid, peer_role_address);
+               if (id >= 0)
+                       item->setData(id, peer_role_bss_id);
+               int type;
+               if (flags.contains("[WPS"))
+                       type = PEER_TYPE_AP_WPS;
+               else
+                       type = PEER_TYPE_AP;
+               item->setData(type, peer_role_type);
+
+               for (int i = 0; i < lines.size(); i++) {
+                       if (lines[i].length() > 60) {
+                               lines[i].remove(60, lines[i].length());
+                               lines[i] += "..";
+                       }
+               }
+               item->setToolTip(ItemType(type));
+               item->setData(lines.join("\n"), peer_role_details);
+               if (!pri_dev_type.isEmpty())
+                       item->setData(pri_dev_type,
+                                     peer_role_pri_dev_type);
+               if (!ssid.isEmpty())
+                       item->setData(ssid, peer_role_ssid);
+               model.appendRow(item);
+       }
+
+       return true;
+}
+
+
+void Peers::add_scan_results()
+{
+       int index;
+       char cmd[20];
+
+       index = 0;
+       while (wpagui) {
+               snprintf(cmd, sizeof(cmd), "BSS %d", index++);
+               if (index > 1000)
+                       break;
+
+               if (!add_bss(cmd))
+                       break;
+       }
+}
+
+
+void Peers::update_peers()
+{
+       model.clear();
+       if (wpagui == NULL)
+               return;
+
+       char reply[20];
+       size_t replylen = sizeof(reply) - 1;
+       wpagui->ctrlRequest("WPS_ER_START", reply, &replylen);
+
+       add_stations();
+       add_scan_results();
+}
+
+
+QStandardItem * Peers::find_addr(QString addr)
+{
+       if (model.rowCount() == 0)
+               return NULL;
+
+       QModelIndexList lst = model.match(model.index(0, 0), peer_role_address,
+                                         addr);
+       if (lst.size() == 0)
+               return NULL;
+       return model.itemFromIndex(lst[0]);
+}
+
+
+QStandardItem * Peers::find_uuid(QString uuid)
+{
+       if (model.rowCount() == 0)
+               return NULL;
+
+       QModelIndexList lst = model.match(model.index(0, 0), peer_role_uuid,
+                                         uuid);
+       if (lst.size() == 0)
+               return NULL;
+       return model.itemFromIndex(lst[0]);
+}
+
+
+void Peers::event_notify(WpaMsg msg)
+{
+       QString text = msg.getMsg();
+
+       if (text.startsWith(WPS_EVENT_PIN_NEEDED)) {
+               /*
+                * WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7
+                * 02:2a:c4:18:5b:f3
+                * [Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
+                */
+               QStringList items = text.split(' ');
+               QString uuid = items[1];
+               QString addr = items[2];
+               QString name = "";
+
+               QStandardItem *item = find_addr(addr);
+               if (item)
+                       return;
+
+               int pos = text.indexOf('[');
+               if (pos >= 0) {
+                       int pos2 = text.lastIndexOf(']');
+                       if (pos2 >= pos) {
+                               items = text.mid(pos + 1, pos2 - pos - 1).
+                                       split('|');
+                               name = items[0];
+                               items.append(addr);
+                       }
+               }
+
+               item = new QStandardItem(*laptop_icon, name);
+               if (item) {
+                       item->setData(addr, peer_role_address);
+                       item->setData(PEER_TYPE_WPS_PIN_NEEDED,
+                                     peer_role_type);
+                       item->setToolTip(ItemType(PEER_TYPE_WPS_PIN_NEEDED));
+                       item->setData(items.join("\n"), peer_role_details);
+                       item->setData(items[5], peer_role_pri_dev_type);
+                       model.appendRow(item);
+               }
+               return;
+       }
+
+       if (text.startsWith(AP_STA_CONNECTED)) {
+               /* AP-STA-CONNECTED 02:2a:c4:18:5b:f3 */
+               QStringList items = text.split(' ');
+               QString addr = items[1];
+               QStandardItem *item = find_addr(addr);
+               if (item == NULL || item->data(peer_role_type).toInt() !=
+                   PEER_TYPE_ASSOCIATED_STATION)
+                       add_single_station(addr.toAscii().constData());
+               return;
+       }
+
+       if (text.startsWith(AP_STA_DISCONNECTED)) {
+               /* AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3 */
+               QStringList items = text.split(' ');
+               QString addr = items[1];
+
+               if (model.rowCount() == 0)
+                       return;
+
+               QModelIndexList lst = model.match(model.index(0, 0),
+                                                 peer_role_address, addr);
+               for (int i = 0; i < lst.size(); i++) {
+                       QStandardItem *item = model.itemFromIndex(lst[i]);
+                       if (item && item->data(peer_role_type).toInt() ==
+                           PEER_TYPE_ASSOCIATED_STATION)
+                               model.removeRow(lst[i].row());
+               }
+               return;
+       }
+
+       if (text.startsWith(WPS_EVENT_ER_AP_ADD)) {
+               /*
+                * WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002
+                * 02:11:22:33:44:55 pri_dev_type=6-0050F204-1 wps_state=1
+                * |Very friendly name|Company|Long description of the model|
+                * WAP|http://w1.fi/|http://w1.fi/hostapd/
+                */
+               QStringList items = text.split(' ');
+               if (items.size() < 5)
+                       return;
+               QString uuid = items[1];
+               QString addr = items[2];
+               QString pri_dev_type = items[3].mid(13);
+               int wps_state = items[4].mid(10).toInt();
+
+               int pos = text.indexOf('|');
+               if (pos < 0)
+                       return;
+               items = text.mid(pos + 1).split('|');
+               if (items.size() < 1)
+                       return;
+
+               QStandardItem *item = find_uuid(uuid);
+               if (item)
+                       return;
+
+               item = new QStandardItem(*ap_icon, items[0]);
+               if (item) {
+                       item->setData(uuid, peer_role_uuid);
+                       item->setData(addr, peer_role_address);
+                       int type = wps_state == 2 ? PEER_TYPE_WPS_ER_AP:
+                               PEER_TYPE_WPS_ER_AP_UNCONFIGURED;
+                       item->setData(type, peer_role_type);
+                       item->setToolTip(ItemType(type));
+                       item->setData(pri_dev_type, peer_role_pri_dev_type);
+                       item->setData(items.join(QString("\n")),
+                                     peer_role_details);
+                       model.appendRow(item);
+               }
+
+               return;
+       }
+
+       if (text.startsWith(WPS_EVENT_ER_AP_REMOVE)) {
+               /* WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002 */
+               QStringList items = text.split(' ');
+               if (items.size() < 2)
+                       return;
+               if (model.rowCount() == 0)
+                       return;
+
+               QModelIndexList lst = model.match(model.index(0, 0),
+                                                 peer_role_uuid, items[1]);
+               for (int i = 0; i < lst.size(); i++) {
+                       QStandardItem *item = model.itemFromIndex(lst[i]);
+                       if (item &&
+                           (item->data(peer_role_type).toInt() ==
+                            PEER_TYPE_WPS_ER_AP ||
+                            item->data(peer_role_type).toInt() ==
+                            PEER_TYPE_WPS_ER_AP_UNCONFIGURED))
+                               model.removeRow(lst[i].row());
+               }
+               return;
+       }
+
+       if (text.startsWith(WPS_EVENT_ER_ENROLLEE_ADD)) {
+               /*
+                * WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
+                * 02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
+                * pri_dev_type=1-0050F204-1
+                * |Wireless Client|Company|cmodel|123|12345|
+                */
+               QStringList items = text.split(' ');
+               if (items.size() < 3)
+                       return;
+               QString uuid = items[1];
+               QString addr = items[2];
+               QString pri_dev_type = items[6].mid(13);
+               int config_methods = -1;
+               int dev_passwd_id = -1;
+
+               for (int i = 3; i < items.size(); i++) {
+                       int pos = items[i].indexOf('=') + 1;
+                       if (pos < 1)
+                               continue;
+                       QString val = items[i].mid(pos);
+                       if (items[i].startsWith("config_methods=")) {
+                               config_methods = val.toInt(0, 0);
+                       } else if (items[i].startsWith("dev_passwd_id=")) {
+                               dev_passwd_id = val.toInt();
+                       }
+               }
+
+               int pos = text.indexOf('|');
+               if (pos < 0)
+                       return;
+               items = text.mid(pos + 1).split('|');
+               if (items.size() < 1)
+                       return;
+               QString name = items[0];
+               if (name.length() == 0)
+                       name = addr;
+
+               remove_enrollee_uuid(uuid);
+
+               QStandardItem *item;
+               item = new QStandardItem(*laptop_icon, name);
+               if (item) {
+                       item->setData(uuid, peer_role_uuid);
+                       item->setData(addr, peer_role_address);
+                       item->setData(PEER_TYPE_WPS_ER_ENROLLEE,
+                                     peer_role_type);
+                       item->setToolTip(ItemType(PEER_TYPE_WPS_ER_ENROLLEE));
+                       item->setData(items.join(QString("\n")),
+                                     peer_role_details);
+                       item->setData(pri_dev_type, peer_role_pri_dev_type);
+                       if (config_methods >= 0)
+                               item->setData(config_methods,
+                                             peer_role_config_methods);
+                       if (dev_passwd_id >= 0)
+                               item->setData(dev_passwd_id,
+                                             peer_role_dev_passwd_id);
+                       model.appendRow(item);
+               }
+
+               return;
+       }
+
+       if (text.startsWith(WPS_EVENT_ER_ENROLLEE_REMOVE)) {
+               /*
+                * WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
+                * 02:66:a0:ee:17:27
+                */
+               QStringList items = text.split(' ');
+               if (items.size() < 2)
+                       return;
+               remove_enrollee_uuid(items[1]);
+               return;
+       }
+
+       if (text.startsWith(WPS_EVENT_ENROLLEE_SEEN)) {
+               /* TODO: need to time out this somehow or remove on successful
+                * WPS run, etc. */
+               /*
+                * WPS-ENROLLEE-SEEN 02:00:00:00:01:00
+                * 572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
+                * [Wireless Client]
+                * (MAC addr, UUID-E, pri dev type, config methods,
+                * dev passwd id, request type, [dev name])
+                */
+               QStringList items = text.split(' ');
+               if (items.size() < 7)
+                       return;
+               QString addr = items[1];
+               QString uuid = items[2];
+               QString pri_dev_type = items[3];
+               int config_methods = items[4].toInt(0, 0);
+               int dev_passwd_id = items[5].toInt();
+               QString name;
+
+               int pos = text.indexOf('[');
+               if (pos >= 0) {
+                       int pos2 = text.lastIndexOf(']');
+                       if (pos2 >= pos) {
+                               QStringList items2 =
+                                       text.mid(pos + 1, pos2 - pos - 1).
+                                       split('|');
+                               name = items2[0];
+                       }
+               }
+               if (name.isEmpty())
+                       name = addr;
+
+               QStandardItem *item;
+
+               item = find_uuid(uuid);
+               if (item) {
+                       QVariant var = item->data(peer_role_config_methods);
+                       QVariant var2 = item->data(peer_role_dev_passwd_id);
+                       if ((var.isValid() && config_methods != var.toInt()) ||
+                           (var2.isValid() && dev_passwd_id != var2.toInt()))
+                               remove_enrollee_uuid(uuid);
+                       else
+                               return;
+               }
+
+               item = new QStandardItem(*laptop_icon, name);
+               if (item) {
+                       item->setData(uuid, peer_role_uuid);
+                       item->setData(addr, peer_role_address);
+                       item->setData(PEER_TYPE_WPS_ENROLLEE,
+                                     peer_role_type);
+                       item->setToolTip(ItemType(PEER_TYPE_WPS_ENROLLEE));
+                       item->setData(items.join(QString("\n")),
+                                     peer_role_details);
+                       item->setData(pri_dev_type, peer_role_pri_dev_type);
+                       item->setData(config_methods,
+                                     peer_role_config_methods);
+                       item->setData(dev_passwd_id, peer_role_dev_passwd_id);
+                       model.appendRow(item);
+               }
+
+               return;
+       }
+
+       if (text.startsWith(WPA_EVENT_BSS_ADDED)) {
+               /* CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55 */
+               QStringList items = text.split(' ');
+               if (items.size() < 2)
+                       return;
+               char cmd[20];
+               snprintf(cmd, sizeof(cmd), "BSS ID-%d", items[1].toInt());
+               add_bss(cmd);
+               return;
+       }
+
+       if (text.startsWith(WPA_EVENT_BSS_REMOVED)) {
+               /* CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55 */
+               QStringList items = text.split(' ');
+               if (items.size() < 2)
+                       return;
+               remove_bss(items[1].toInt());
+               return;
+       }
+}
+
+
+void Peers::closeEvent(QCloseEvent *)
+{
+       if (wpagui) {
+               char reply[20];
+               size_t replylen = sizeof(reply) - 1;
+               wpagui->ctrlRequest("WPS_ER_STOP", reply, &replylen);
+       }
+}
+
+
+void Peers::done(int r)
+{
+       QDialog::done(r);
+       close();
+}
+
+
+void Peers::remove_enrollee_uuid(QString uuid)
+{
+       if (model.rowCount() == 0)
+               return;
+
+       QModelIndexList lst = model.match(model.index(0, 0),
+                                         peer_role_uuid, uuid);
+       for (int i = 0; i < lst.size(); i++) {
+               QStandardItem *item = model.itemFromIndex(lst[i]);
+               if (item == NULL)
+                       continue;
+               int type = item->data(peer_role_type).toInt();
+               if (type == PEER_TYPE_WPS_ER_ENROLLEE ||
+                   type == PEER_TYPE_WPS_ENROLLEE)
+                       model.removeRow(lst[i].row());
+       }
+}
+
+
+void Peers::properties()
+{
+       if (ctx_item == NULL)
+               return;
+
+       QMessageBox msg(this);
+       msg.setStandardButtons(QMessageBox::Ok);
+       msg.setDefaultButton(QMessageBox::Ok);
+       msg.setEscapeButton(QMessageBox::Ok);
+       msg.setWindowTitle(tr("Peer Properties"));
+
+       int type = ctx_item->data(peer_role_type).toInt();
+       QString title = Peers::ItemType(type);
+
+       msg.setText(title + QString("\n") + tr("Name: ") + ctx_item->text());
+
+       QVariant var;
+       QString info;
+
+       var = ctx_item->data(peer_role_address);
+       if (var.isValid())
+               info += tr("Address: ") + var.toString() + QString("\n");
+
+       var = ctx_item->data(peer_role_uuid);
+       if (var.isValid())
+               info += tr("UUID: ") + var.toString() + QString("\n");
+
+       var = ctx_item->data(peer_role_pri_dev_type);
+       if (var.isValid())
+               info += tr("Primary Device Type: ") + var.toString() +
+                       QString("\n");
+
+       var = ctx_item->data(peer_role_ssid);
+       if (var.isValid())
+               info += tr("SSID: ") + var.toString() + QString("\n");
+
+       var = ctx_item->data(peer_role_config_methods);
+       if (var.isValid()) {
+               int methods = var.toInt();
+               info += tr("Configuration Methods: ");
+               if (methods & 0x0001)
+                       info += tr("[USBA]");
+               if (methods & 0x0002)
+                       info += tr("[Ethernet]");
+               if (methods & 0x0004)
+                       info += tr("[Label]");
+               if (methods & 0x0008)
+                       info += tr("[Display]");
+               if (methods & 0x0010)
+                       info += tr("[Ext. NFC Token]");
+               if (methods & 0x0020)
+                       info += tr("[Int. NFC Token]");
+               if (methods & 0x0040)
+                       info += tr("[NFC Interface]");
+               if (methods & 0x0080)
+                       info += tr("[Push Button]");
+               if (methods & 0x0100)
+                       info += tr("[Keypad]");
+               info += "\n";
+       }
+
+       var = ctx_item->data(peer_role_dev_passwd_id);
+       if (var.isValid()) {
+               info += tr("Device Password ID: ") + var.toString();
+               switch (var.toInt()) {
+               case 0:
+                       info += tr(" (Default PIN)");
+                       break;
+               case 1:
+                       info += tr(" (User-specified PIN)");
+                       break;
+               case 2:
+                       info += tr(" (Machine-specified PIN)");
+                       break;
+               case 3:
+                       info += tr(" (Rekey)");
+                       break;
+               case 4:
+                       info += tr(" (Push Button)");
+                       break;
+               case 5:
+                       info += tr(" (Registrar-specified)");
+                       break;
+               }
+               info += "\n";
+       }
+
+       msg.setInformativeText(info);
+
+       var = ctx_item->data(peer_role_details);
+       if (var.isValid())
+               msg.setDetailedText(var.toString());
+
+       msg.exec();
+}
+
+
+void Peers::connect_pbc()
+{
+       if (ctx_item == NULL)
+               return;
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+
+       int peer_type = ctx_item->data(peer_role_type).toInt();
+       if (peer_type == PEER_TYPE_WPS_ER_ENROLLEE) {
+               snprintf(cmd, sizeof(cmd), "WPS_ER_PBC %s",
+                        ctx_item->data(peer_role_uuid).toString().toAscii().
+                        constData());
+       } else {
+               snprintf(cmd, sizeof(cmd), "WPS_PBC");
+       }
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to start WPS PBC."));
+               msg.exec();
+       }
+}
+
+
+void Peers::learn_ap_config()
+{
+       if (ctx_item == NULL)
+               return;
+
+       QString uuid = ctx_item->data(peer_role_uuid).toString();
+
+       StringQuery input(tr("AP PIN:"));
+       input.setWindowTitle(tr("AP PIN for ") + ctx_item->text());
+       if (input.exec() != QDialog::Accepted)
+               return;
+
+       char cmd[100];
+       char reply[100];
+       size_t reply_len;
+
+       snprintf(cmd, sizeof(cmd), "WPS_ER_LEARN %s %s",
+                uuid.toAscii().constData(),
+                input.get_string().toAscii().constData());
+       reply_len = sizeof(reply) - 1;
+       if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0) {
+               QMessageBox msg;
+               msg.setIcon(QMessageBox::Warning);
+               msg.setText(tr("Failed to start learning AP configuration."));
+               msg.exec();
+       }
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.h b/wpa_supplicant/wpa_gui-qt4/peers.h
new file mode 100644 (file)
index 0000000..89b7b5b
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * wpa_gui - Peers class
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 PEERS_H
+#define PEERS_H
+
+#include <QObject>
+#include <QStandardItemModel>
+#include "wpamsg.h"
+#include "ui_peers.h"
+
+class WpaGui;
+
+class Peers : public QDialog, public Ui::Peers
+{
+       Q_OBJECT
+
+public:
+       Peers(QWidget *parent = 0, const char *name = 0,
+                   bool modal = false, Qt::WFlags fl = 0);
+       ~Peers();
+       void setWpaGui(WpaGui *_wpagui);
+       void event_notify(WpaMsg msg);
+
+public slots:
+       virtual void context_menu(const QPoint &pos);
+       virtual void enter_pin();
+       virtual void connect_pbc();
+       virtual void learn_ap_config();
+       virtual void ctx_refresh();
+       virtual void properties();
+
+protected slots:
+       virtual void languageChange();
+       virtual void closeEvent(QCloseEvent *event);
+
+private:
+       void add_station(QString info);
+       void add_stations();
+       void add_single_station(const char *addr);
+       bool add_bss(const char *cmd);
+       void remove_bss(int id);
+       void add_scan_results();
+       void update_peers();
+       QStandardItem * find_addr(QString addr);
+       QStandardItem * find_uuid(QString uuid);
+       void done(int r);
+       void remove_enrollee_uuid(QString uuid);
+       QString ItemType(int type);
+
+       WpaGui *wpagui;
+       QStandardItemModel model;
+       QIcon *default_icon;
+       QIcon *ap_icon;
+       QIcon *laptop_icon;
+       QStandardItem *ctx_item;
+};
+
+#endif /* PEERS_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/peers.ui b/wpa_supplicant/wpa_gui-qt4/peers.ui
new file mode 100644 (file)
index 0000000..9508c25
--- /dev/null
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>Peers</class>
+ <widget class="QDialog" name="Peers">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>300</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Peers</string>
+  </property>
+  <layout class="QGridLayout">
+   <item row="0" column="0">
+    <widget class="QListView" name="peers">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="mouseTracking">
+      <bool>true</bool>
+     </property>
+     <property name="editTriggers">
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="viewMode">
+      <enum>QListView::IconMode</enum>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.cpp b/wpa_supplicant/wpa_gui-qt4/scanresults.cpp
new file mode 100644 (file)
index 0000000..459aa8c
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * wpa_gui - ScanResults class
+ * Copyright (c) 2005-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.
+ */
+
+#include <cstdio>
+
+#include "scanresults.h"
+#include "wpagui.h"
+#include "networkconfig.h"
+
+
+ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WFlags)
+       : QDialog(parent)
+{
+       setupUi(this);
+
+       connect(closeButton, SIGNAL(clicked()), this, SLOT(close()));
+       connect(scanButton, SIGNAL(clicked()), this, SLOT(scanRequest()));
+       connect(scanResultsWidget,
+               SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this,
+               SLOT(bssSelected(QTreeWidgetItem *)));
+
+       wpagui = NULL;
+       scanResultsWidget->setItemsExpandable(FALSE);
+       scanResultsWidget->setRootIsDecorated(FALSE);
+}
+
+
+ScanResults::~ScanResults()
+{
+}
+
+
+void ScanResults::languageChange()
+{
+       retranslateUi(this);
+}
+
+
+void ScanResults::setWpaGui(WpaGui *_wpagui)
+{
+       wpagui = _wpagui;
+       updateResults();
+}
+
+
+void ScanResults::updateResults()
+{
+       char reply[2048];
+       size_t reply_len;
+       int index;
+       char cmd[20];
+
+       scanResultsWidget->clear();
+
+       index = 0;
+       while (wpagui) {
+               snprintf(cmd, sizeof(cmd), "BSS %d", index++);
+               if (index > 1000)
+                       break;
+
+               reply_len = sizeof(reply) - 1;
+               if (wpagui->ctrlRequest(cmd, reply, &reply_len) < 0)
+                       break;
+               reply[reply_len] = '\0';
+
+               QString bss(reply);
+               if (bss.isEmpty() || bss.startsWith("FAIL"))
+                       break;
+
+               QString ssid, bssid, freq, signal, flags;
+
+               QStringList lines = bss.split(QRegExp("\\n"));
+               for (QStringList::Iterator it = lines.begin();
+                    it != lines.end(); it++) {
+                       int pos = (*it).indexOf('=') + 1;
+                       if (pos < 1)
+                               continue;
+
+                       if ((*it).startsWith("bssid="))
+                               bssid = (*it).mid(pos);
+                       else if ((*it).startsWith("freq="))
+                               freq = (*it).mid(pos);
+                       else if ((*it).startsWith("qual="))
+                               signal = (*it).mid(pos);
+                       else if ((*it).startsWith("flags="))
+                               flags = (*it).mid(pos);
+                       else if ((*it).startsWith("ssid="))
+                               ssid = (*it).mid(pos);
+               }
+
+               QTreeWidgetItem *item = new QTreeWidgetItem(scanResultsWidget);
+               if (item) {
+                       item->setText(0, ssid);
+                       item->setText(1, bssid);
+                       item->setText(2, freq);
+                       item->setText(3, signal);
+                       item->setText(4, flags);
+               }
+
+               if (bssid.isEmpty())
+                       break;
+       }
+}
+
+
+void ScanResults::scanRequest()
+{
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+    
+       if (wpagui == NULL)
+               return;
+    
+       wpagui->ctrlRequest("SCAN", reply, &reply_len);
+}
+
+
+void ScanResults::getResults()
+{
+       updateResults();
+}
+
+
+void ScanResults::bssSelected(QTreeWidgetItem *sel)
+{
+       NetworkConfig *nc = new NetworkConfig();
+       if (nc == NULL)
+               return;
+       nc->setWpaGui(wpagui);
+       nc->paramsFromScanResults(sel);
+       nc->show();
+       nc->exec();
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.h b/wpa_supplicant/wpa_gui-qt4/scanresults.h
new file mode 100644 (file)
index 0000000..2c4a1b0
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * wpa_gui - ScanResults class
+ * Copyright (c) 2005-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 SCANRESULTS_H
+#define SCANRESULTS_H
+
+#include <QObject>
+#include "ui_scanresults.h"
+
+class WpaGui;
+
+class ScanResults : public QDialog, public Ui::ScanResults
+{
+       Q_OBJECT
+
+public:
+       ScanResults(QWidget *parent = 0, const char *name = 0,
+                   bool modal = false, Qt::WFlags fl = 0);
+       ~ScanResults();
+
+public slots:
+       virtual void setWpaGui(WpaGui *_wpagui);
+       virtual void updateResults();
+       virtual void scanRequest();
+       virtual void getResults();
+       virtual void bssSelected(QTreeWidgetItem *sel);
+
+protected slots:
+       virtual void languageChange();
+
+private:
+       WpaGui *wpagui;
+};
+
+#endif /* SCANRESULTS_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/scanresults.ui b/wpa_supplicant/wpa_gui-qt4/scanresults.ui
new file mode 100644 (file)
index 0000000..81e405e
--- /dev/null
@@ -0,0 +1,94 @@
+<ui version="4.0" >
+ <class>ScanResults</class>
+ <widget class="QDialog" name="ScanResults" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>452</width>
+    <height>244</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>Scan results</string>
+  </property>
+  <layout class="QVBoxLayout" >
+   <item>
+    <widget class="QTreeWidget" name="scanResultsWidget" >
+     <property name="editTriggers" >
+      <set>QAbstractItemView::NoEditTriggers</set>
+     </property>
+     <property name="uniformRowHeights" >
+      <bool>true</bool>
+     </property>
+     <property name="sortingEnabled" >
+      <bool>true</bool>
+     </property>
+     <property name="columnCount" >
+      <number>5</number>
+     </property>
+     <column>
+      <property name="text" >
+       <string>SSID</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>BSSID</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>frequency</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>signal</string>
+      </property>
+     </column>
+     <column>
+      <property name="text" >
+       <string>flags</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" >
+     <item>
+      <spacer>
+       <property name="orientation" >
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" >
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="scanButton" >
+       <property name="text" >
+        <string>Scan</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="closeButton" >
+       <property name="text" >
+        <string>Close</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/wpa_supplicant/wpa_gui-qt4/stringquery.cpp b/wpa_supplicant/wpa_gui-qt4/stringquery.cpp
new file mode 100644 (file)
index 0000000..1ca98d9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * wpa_gui - StringQuery class
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 <cstdio>
+#include <QLabel>
+
+#include "stringquery.h"
+
+
+StringQuery::StringQuery(QString label)
+{
+       edit = new QLineEdit;
+       edit->setFocus();
+       QGridLayout *layout = new QGridLayout;
+       layout->addWidget(new QLabel(label), 0, 0);
+       layout->addWidget(edit, 0, 1);
+       setLayout(layout);
+
+       connect(edit, SIGNAL(returnPressed()), this, SLOT(accept()));
+}
+
+
+QString StringQuery::get_string()
+{
+       return edit->text();
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/stringquery.h b/wpa_supplicant/wpa_gui-qt4/stringquery.h
new file mode 100644 (file)
index 0000000..1b68217
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * wpa_gui - StringQuery class
+ * Copyright (c) 2009, Atheros Communications
+ *
+ * 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 STRINGQUERY_H
+#define STRINGQUERY_H
+
+#include <QDialog>
+#include <QLineEdit>
+#include <QGridLayout>
+
+class StringQuery : public QDialog
+{
+       Q_OBJECT
+
+public:
+       StringQuery(QString label);
+       QString get_string();
+
+private:
+       QLineEdit *edit;
+};
+
+#endif /* STRINGQUERY_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp b/wpa_supplicant/wpa_gui-qt4/userdatarequest.cpp
new file mode 100644 (file)
index 0000000..345f965
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * wpa_gui - UserDataRequest class
+ * Copyright (c) 2005-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.
+ */
+
+#include "userdatarequest.h"
+#include "wpagui.h"
+#include "common/wpa_ctrl.h"
+
+
+UserDataRequest::UserDataRequest(QWidget *parent, const char *, bool,
+                                Qt::WFlags)
+       : QDialog(parent)
+{
+       setupUi(this);
+
+       connect(buttonOk, SIGNAL(clicked()), this, SLOT(sendReply()));
+       connect(buttonCancel, SIGNAL(clicked()), this, SLOT(reject()));
+       connect(queryEdit, SIGNAL(returnPressed()), this, SLOT(sendReply()));
+}
+
+
+UserDataRequest::~UserDataRequest()
+{
+}
+
+
+void UserDataRequest::languageChange()
+{
+       retranslateUi(this);
+}
+
+
+int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg)
+{
+       char *tmp, *pos, *pos2;
+       wpagui = _wpagui;
+       tmp = strdup(reqMsg);
+       if (tmp == NULL)
+               return -1;
+       pos = strchr(tmp, '-');
+       if (pos == NULL) {
+               free(tmp);
+               return -1;
+       }
+       *pos++ = '\0';
+       field = tmp;
+       pos2 = strchr(pos, ':');
+       if (pos2 == NULL) {
+               free(tmp);
+               return -1;
+       }
+       *pos2++ = '\0';
+
+       networkid = atoi(pos);
+       queryInfo->setText(pos2);
+       if (strcmp(tmp, "PASSWORD") == 0) {
+               queryField->setText(tr("Password: "));
+               queryEdit->setEchoMode(QLineEdit::Password);
+       } else if (strcmp(tmp, "NEW_PASSWORD") == 0) {
+               queryField->setText(tr("New password: "));
+               queryEdit->setEchoMode(QLineEdit::Password);
+       } else if (strcmp(tmp, "IDENTITY") == 0)
+               queryField->setText(tr("Identity: "));
+       else if (strcmp(tmp, "PASSPHRASE") == 0) {
+               queryField->setText(tr("Private key passphrase: "));
+               queryEdit->setEchoMode(QLineEdit::Password);
+       } else
+               queryField->setText(field + ":");
+       free(tmp);
+
+       return 0;
+}
+
+
+void UserDataRequest::sendReply()
+{
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+
+       if (wpagui == NULL) {
+               reject();
+               return;
+       }
+
+       QString cmd = QString(WPA_CTRL_RSP) + field + '-' +
+               QString::number(networkid) + ':' +
+               queryEdit->text();
+       wpagui->ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+       accept();
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.h b/wpa_supplicant/wpa_gui-qt4/userdatarequest.h
new file mode 100644 (file)
index 0000000..2b6e837
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * wpa_gui - UserDataRequest class
+ * Copyright (c) 2005-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 USERDATAREQUEST_H
+#define USERDATAREQUEST_H
+
+#include <QObject>
+#include "ui_userdatarequest.h"
+
+class WpaGui;
+
+class UserDataRequest : public QDialog, public Ui::UserDataRequest
+{
+       Q_OBJECT
+
+public:
+       UserDataRequest(QWidget *parent = 0, const char *name = 0,
+                       bool modal = false, Qt::WFlags fl = 0);
+       ~UserDataRequest();
+
+       int setParams(WpaGui *_wpagui, const char *reqMsg);
+
+public slots:
+       virtual void sendReply();
+
+protected slots:
+       virtual void languageChange();
+
+private:
+       WpaGui *wpagui;
+       int networkid;
+       QString field;
+};
+
+#endif /* USERDATAREQUEST_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui b/wpa_supplicant/wpa_gui-qt4/userdatarequest.ui
new file mode 100644 (file)
index 0000000..1de2a26
--- /dev/null
@@ -0,0 +1,109 @@
+<ui version="4.0" stdsetdef="1" >
+  <author></author>
+  <comment></comment>
+  <exportmacro></exportmacro>
+  <class>UserDataRequest</class>
+  <widget class="QDialog" name="UserDataRequest" >
+    <property name="geometry" >
+      <rect>
+        <x>0</x>
+        <y>0</y>
+        <width>216</width>
+        <height>103</height>
+      </rect>
+    </property>
+    <property name="windowTitle" >
+      <string>Authentication credentials required</string>
+    </property>
+    <property name="sizeGripEnabled" >
+      <bool>true</bool>
+    </property>
+    <layout class="QVBoxLayout" >
+      <item>
+        <widget class="QLabel" name="queryInfo" >
+          <property name="text" >
+            <string/>
+          </property>
+        </widget>
+      </item>
+      <item>
+        <layout class="QHBoxLayout" >
+          <property name="margin" >
+            <number>0</number>
+          </property>
+          <item>
+            <widget class="QLabel" name="queryField" >
+              <property name="text" >
+                <string/>
+              </property>
+            </widget>
+          </item>
+          <item>
+            <widget class="QLineEdit" name="queryEdit" >
+              <property name="enabled" >
+                <bool>true</bool>
+              </property>
+              <property name="echoMode" >
+                <enum>QLineEdit::Password</enum>
+              </property>
+            </widget>
+          </item>
+        </layout>
+      </item>
+      <item>
+        <layout class="QHBoxLayout" >
+          <property name="margin" >
+            <number>0</number>
+          </property>
+          <item>
+            <spacer name="spacer4" >
+              <property name="sizeHint" >
+                <size>
+                  <width>20</width>
+                  <height>20</height>
+                </size>
+              </property>
+              <property name="sizeType" >
+                <enum>Expanding</enum>
+              </property>
+              <property name="orientation" >
+                <enum>Horizontal</enum>
+              </property>
+            </spacer>
+          </item>
+          <item>
+            <widget class="QPushButton" name="buttonOk" >
+              <property name="text" >
+                <string>&amp;OK</string>
+              </property>
+              <property name="shortcut" >
+                <string/>
+              </property>
+              <property name="autoDefault" >
+                <bool>true</bool>
+              </property>
+              <property name="default" >
+                <bool>true</bool>
+              </property>
+            </widget>
+          </item>
+          <item>
+            <widget class="QPushButton" name="buttonCancel" >
+              <property name="text" >
+                <string>&amp;Cancel</string>
+              </property>
+              <property name="shortcut" >
+                <string/>
+              </property>
+              <property name="autoDefault" >
+                <bool>true</bool>
+              </property>
+            </widget>
+          </item>
+        </layout>
+      </item>
+    </layout>
+  </widget>
+  <layoutdefault spacing="6" margin="11" />
+  <pixmapfunction></pixmapfunction>
+</ui>
diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop b/wpa_supplicant/wpa_gui-qt4/wpa_gui.desktop
new file mode 100644 (file)
index 0000000..ccc7d87
--- /dev/null
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Version=1.0
+Name=wpa_gui
+Comment=Graphical user interface for wpa_supplicant
+Exec=wpa_gui
+Icon=wpa_gui
+GenericName=wpa_supplicant user interface
+Terminal=false
+Type=Application
+Categories=Qt;Network;
diff --git a/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro b/wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
new file mode 100644 (file)
index 0000000..85848d7
--- /dev/null
@@ -0,0 +1,68 @@
+TEMPLATE       = app
+LANGUAGE       = C++
+TRANSLATIONS   = lang/wpa_gui_de.ts
+
+CONFIG += qt warn_on release
+
+DEFINES += CONFIG_CTRL_IFACE
+
+win32 {
+  LIBS += -lws2_32 -static
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  SOURCES += ../../src/utils/os_win32.c
+} else:win32-g++ {
+  # cross compilation to win32
+  LIBS += -lws2_32 -static -mwindows
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  SOURCES += ../../src/utils/os_win32.c
+  RESOURCES += icons_png.qrc
+} else:win32-x-g++ {
+  # cross compilation to win32
+  LIBS += -lws2_32 -static -mwindows
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  DEFINES += _X86_
+  SOURCES += ../../src/utils/os_win32.c
+  RESOURCES += icons_png.qrc
+} else {
+  DEFINES += CONFIG_CTRL_IFACE_UNIX
+  SOURCES += ../../src/utils/os_unix.c
+}
+
+INCLUDEPATH    += . .. ../../src ../../src/utils
+
+HEADERS        += wpamsg.h \
+       wpagui.h \
+       eventhistory.h \
+       scanresults.h \
+       userdatarequest.h \
+       networkconfig.h \
+       addinterface.h \
+       peers.h \
+       stringquery.h
+
+SOURCES        += main.cpp \
+       wpagui.cpp \
+       eventhistory.cpp \
+       scanresults.cpp \
+       userdatarequest.cpp \
+       networkconfig.cpp \
+       addinterface.cpp \
+       peers.cpp \
+       stringquery.cpp \
+       ../../src/common/wpa_ctrl.c
+
+RESOURCES += icons.qrc
+
+FORMS  = wpagui.ui \
+       eventhistory.ui \
+       scanresults.ui \
+       userdatarequest.ui \
+       networkconfig.ui \
+       peers.ui
+
+
+unix {
+  UI_DIR = .ui
+  MOC_DIR = .moc
+  OBJECTS_DIR = .obj
+}
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.cpp b/wpa_supplicant/wpa_gui-qt4/wpagui.cpp
new file mode 100644 (file)
index 0000000..2057d67
--- /dev/null
@@ -0,0 +1,1732 @@
+/*
+ * wpa_gui - WpaGui class
+ * Copyright (c) 2005-2010, 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.
+ */
+
+#ifdef __MINGW32__
+/* Need to get getopt() */
+#include <unistd.h>
+#endif
+
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <windows.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+#include <cstdio>
+#include <QMessageBox>
+#include <QCloseEvent>
+#include <QImageReader>
+#include <QSettings>
+
+#include "wpagui.h"
+#include "dirent.h"
+#include "common/wpa_ctrl.h"
+#include "userdatarequest.h"
+#include "networkconfig.h"
+
+#if 1
+/* Silence stdout */
+#define printf wpagui_printf
+static int wpagui_printf(const char *, ...)
+{
+       return 0;
+}
+#endif
+
+WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags)
+       : QMainWindow(parent), app(_app)
+{
+       setupUi(this);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+       fileStopServiceAction = new QAction(this);
+       fileStopServiceAction->setObjectName("Stop Service");
+       fileStopServiceAction->setIconText(tr("Stop Service"));
+       fileMenu->insertAction(actionWPS, fileStopServiceAction);
+
+       fileStartServiceAction = new QAction(this);
+       fileStartServiceAction->setObjectName("Start Service");
+       fileStartServiceAction->setIconText(tr("Start Service"));
+       fileMenu->insertAction(fileStopServiceAction, fileStartServiceAction);
+
+       connect(fileStartServiceAction, SIGNAL(triggered()), this,
+               SLOT(startService()));
+       connect(fileStopServiceAction, SIGNAL(triggered()), this,
+               SLOT(stopService()));
+
+       addInterfaceAction = new QAction(this);
+       addInterfaceAction->setIconText(tr("Add Interface"));
+       fileMenu->insertAction(fileStartServiceAction, addInterfaceAction);
+
+       connect(addInterfaceAction, SIGNAL(triggered()), this,
+               SLOT(addInterface()));
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       (void) statusBar();
+
+       /*
+        * Disable WPS tab by default; it will be enabled if wpa_supplicant is
+        * built with WPS support.
+        */
+       wpsTab->setEnabled(false);
+       wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), false);
+
+       connect(fileEventHistoryAction, SIGNAL(triggered()), this,
+               SLOT(eventHistory()));
+       connect(fileSaveConfigAction, SIGNAL(triggered()), this,
+               SLOT(saveConfig()));
+       connect(actionWPS, SIGNAL(triggered()), this, SLOT(wpsDialog()));
+       connect(actionPeers, SIGNAL(triggered()), this, SLOT(peersDialog()));
+       connect(fileExitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+       connect(networkAddAction, SIGNAL(triggered()), this,
+               SLOT(addNetwork()));
+       connect(networkEditAction, SIGNAL(triggered()), this,
+               SLOT(editSelectedNetwork()));
+       connect(networkRemoveAction, SIGNAL(triggered()), this,
+               SLOT(removeSelectedNetwork()));
+       connect(networkEnableAllAction, SIGNAL(triggered()), this,
+               SLOT(enableAllNetworks()));
+       connect(networkDisableAllAction, SIGNAL(triggered()), this,
+               SLOT(disableAllNetworks()));
+       connect(networkRemoveAllAction, SIGNAL(triggered()), this,
+               SLOT(removeAllNetworks()));
+       connect(helpIndexAction, SIGNAL(triggered()), this, SLOT(helpIndex()));
+       connect(helpContentsAction, SIGNAL(triggered()), this,
+               SLOT(helpContents()));
+       connect(helpAboutAction, SIGNAL(triggered()), this, SLOT(helpAbout()));
+       connect(disconnectButton, SIGNAL(clicked()), this, SLOT(disconnect()));
+       connect(scanButton, SIGNAL(clicked()), this, SLOT(scan()));
+       connect(connectButton, SIGNAL(clicked()), this, SLOT(connectB()));
+       connect(adapterSelect, SIGNAL(activated(const QString&)), this,
+               SLOT(selectAdapter(const QString&)));
+       connect(networkSelect, SIGNAL(activated(const QString&)), this,
+               SLOT(selectNetwork(const QString&)));
+       connect(addNetworkButton, SIGNAL(clicked()), this, SLOT(addNetwork()));
+       connect(editNetworkButton, SIGNAL(clicked()), this,
+               SLOT(editListedNetwork()));
+       connect(removeNetworkButton, SIGNAL(clicked()), this,
+               SLOT(removeListedNetwork()));
+       connect(networkList, SIGNAL(itemSelectionChanged()), this,
+               SLOT(updateNetworkDisabledStatus()));
+       connect(enableRadioButton, SIGNAL(toggled(bool)), this,
+               SLOT(enableListedNetwork(bool)));
+       connect(disableRadioButton, SIGNAL(toggled(bool)), this,
+               SLOT(disableListedNetwork(bool)));
+       connect(scanNetworkButton, SIGNAL(clicked()), this, SLOT(scan()));
+       connect(networkList, SIGNAL(itemDoubleClicked(QListWidgetItem *)),
+               this, SLOT(editListedNetwork()));
+       connect(wpaguiTab, SIGNAL(currentChanged(int)), this,
+               SLOT(tabChanged(int)));
+       connect(wpsPbcButton, SIGNAL(clicked()), this, SLOT(wpsPbc()));
+       connect(wpsPinButton, SIGNAL(clicked()), this, SLOT(wpsGeneratePin()));
+       connect(wpsApPinEdit, SIGNAL(textChanged(const QString &)), this,
+               SLOT(wpsApPinChanged(const QString &)));
+       connect(wpsApPinButton, SIGNAL(clicked()), this, SLOT(wpsApPin()));
+
+       eh = NULL;
+       scanres = NULL;
+       peers = NULL;
+       add_iface = NULL;
+       udr = NULL;
+       tray_icon = NULL;
+       startInTray = false;
+       ctrl_iface = NULL;
+       ctrl_conn = NULL;
+       monitor_conn = NULL;
+       msgNotifier = NULL;
+       ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
+
+       parse_argv();
+
+#ifndef QT_NO_SESSIONMANAGER
+       if (app->isSessionRestored()) {
+               QSettings settings("wpa_supplicant", "wpa_gui");
+               settings.beginGroup("state");
+               if (app->sessionId().compare(settings.value("session_id").
+                                            toString()) == 0)
+                       startInTray = settings.value("in_tray").toBool();
+               settings.endGroup();
+       }
+#endif
+
+       if (QSystemTrayIcon::isSystemTrayAvailable())
+               createTrayIcon(startInTray);
+       else
+               show();
+
+       connectedToService = false;
+       textStatus->setText(tr("connecting to wpa_supplicant"));
+       timer = new QTimer(this);
+       connect(timer, SIGNAL(timeout()), SLOT(ping()));
+       timer->setSingleShot(FALSE);
+       timer->start(1000);
+
+       if (openCtrlConnection(ctrl_iface) < 0) {
+               printf("Failed to open control connection to "
+                      "wpa_supplicant.\n");
+       }
+
+       updateStatus();
+       networkMayHaveChanged = true;
+       updateNetworks();
+}
+
+
+WpaGui::~WpaGui()
+{
+       delete msgNotifier;
+
+       if (monitor_conn) {
+               wpa_ctrl_detach(monitor_conn);
+               wpa_ctrl_close(monitor_conn);
+               monitor_conn = NULL;
+       }
+       if (ctrl_conn) {
+               wpa_ctrl_close(ctrl_conn);
+               ctrl_conn = NULL;
+       }
+
+       if (eh) {
+               eh->close();
+               delete eh;
+               eh = NULL;
+       }
+
+       if (scanres) {
+               scanres->close();
+               delete scanres;
+               scanres = NULL;
+       }
+
+       if (peers) {
+               peers->close();
+               delete peers;
+               peers = NULL;
+       }
+
+       if (add_iface) {
+               add_iface->close();
+               delete add_iface;
+               add_iface = NULL;
+       }
+
+       if (udr) {
+               udr->close();
+               delete udr;
+               udr = NULL;
+       }
+
+       free(ctrl_iface);
+       ctrl_iface = NULL;
+
+       free(ctrl_iface_dir);
+       ctrl_iface_dir = NULL;
+}
+
+
+void WpaGui::languageChange()
+{
+       retranslateUi(this);
+}
+
+
+void WpaGui::parse_argv()
+{
+       int c;
+       for (;;) {
+               c = getopt(qApp->argc(), qApp->argv(), "i:p:t");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'i':
+                       free(ctrl_iface);
+                       ctrl_iface = strdup(optarg);
+                       break;
+               case 'p':
+                       free(ctrl_iface_dir);
+                       ctrl_iface_dir = strdup(optarg);
+                       break;
+               case 't':
+                       startInTray = true;
+                       break;
+               }
+       }
+}
+
+
+int WpaGui::openCtrlConnection(const char *ifname)
+{
+       char *cfile;
+       int flen;
+       char buf[2048], *pos, *pos2;
+       size_t len;
+
+       if (ifname) {
+               if (ifname != ctrl_iface) {
+                       free(ctrl_iface);
+                       ctrl_iface = strdup(ifname);
+               }
+       } else {
+#ifdef CONFIG_CTRL_IFACE_UDP
+               free(ctrl_iface);
+               ctrl_iface = strdup("udp");
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+               struct dirent *dent;
+               DIR *dir = opendir(ctrl_iface_dir);
+               free(ctrl_iface);
+               ctrl_iface = NULL;
+               if (dir) {
+                       while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+                               /* Skip the file if it is not a socket.
+                                * Also accept DT_UNKNOWN (0) in case
+                                * the C library or underlying file
+                                * system does not support d_type. */
+                               if (dent->d_type != DT_SOCK &&
+                                   dent->d_type != DT_UNKNOWN)
+                                       continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+                               if (strcmp(dent->d_name, ".") == 0 ||
+                                   strcmp(dent->d_name, "..") == 0)
+                                       continue;
+                               printf("Selected interface '%s'\n",
+                                      dent->d_name);
+                               ctrl_iface = strdup(dent->d_name);
+                               break;
+                       }
+                       closedir(dir);
+               }
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+               struct wpa_ctrl *ctrl;
+               int ret;
+
+               free(ctrl_iface);
+               ctrl_iface = NULL;
+
+               ctrl = wpa_ctrl_open(NULL);
+               if (ctrl) {
+                       len = sizeof(buf) - 1;
+                       ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf,
+                                              &len, NULL);
+                       if (ret >= 0) {
+                               connectedToService = true;
+                               buf[len] = '\0';
+                               pos = strchr(buf, '\n');
+                               if (pos)
+                                       *pos = '\0';
+                               ctrl_iface = strdup(buf);
+                       }
+                       wpa_ctrl_close(ctrl);
+               }
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+       }
+
+       if (ctrl_iface == NULL) {
+#ifdef CONFIG_NATIVE_WINDOWS
+               static bool first = true;
+               if (first && !serviceRunning()) {
+                       first = false;
+                       if (QMessageBox::warning(
+                                   this, qAppName(),
+                                   tr("wpa_supplicant service is not "
+                                      "running.\n"
+                                      "Do you want to start it?"),
+                                   QMessageBox::Yes | QMessageBox::No) ==
+                           QMessageBox::Yes)
+                               startService();
+               }
+#endif /* CONFIG_NATIVE_WINDOWS */
+               return -1;
+       }
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+       flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
+       cfile = (char *) malloc(flen);
+       if (cfile == NULL)
+               return -1;
+       snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
+#else /* CONFIG_CTRL_IFACE_UNIX */
+       flen = strlen(ctrl_iface) + 1;
+       cfile = (char *) malloc(flen);
+       if (cfile == NULL)
+               return -1;
+       snprintf(cfile, flen, "%s", ctrl_iface);
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+       if (ctrl_conn) {
+               wpa_ctrl_close(ctrl_conn);
+               ctrl_conn = NULL;
+       }
+
+       if (monitor_conn) {
+               delete msgNotifier;
+               msgNotifier = NULL;
+               wpa_ctrl_detach(monitor_conn);
+               wpa_ctrl_close(monitor_conn);
+               monitor_conn = NULL;
+       }
+
+       printf("Trying to connect to '%s'\n", cfile);
+       ctrl_conn = wpa_ctrl_open(cfile);
+       if (ctrl_conn == NULL) {
+               free(cfile);
+               return -1;
+       }
+       monitor_conn = wpa_ctrl_open(cfile);
+       free(cfile);
+       if (monitor_conn == NULL) {
+               wpa_ctrl_close(ctrl_conn);
+               return -1;
+       }
+       if (wpa_ctrl_attach(monitor_conn)) {
+               printf("Failed to attach to wpa_supplicant\n");
+               wpa_ctrl_close(monitor_conn);
+               monitor_conn = NULL;
+               wpa_ctrl_close(ctrl_conn);
+               ctrl_conn = NULL;
+               return -1;
+       }
+
+#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
+       msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
+                                         QSocketNotifier::Read, this);
+       connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
+#endif
+
+       adapterSelect->clear();
+       adapterSelect->addItem(ctrl_iface);
+       adapterSelect->setCurrentIndex(0);
+
+       len = sizeof(buf) - 1;
+       if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >=
+           0) {
+               buf[len] = '\0';
+               pos = buf;
+               while (*pos) {
+                       pos2 = strchr(pos, '\n');
+                       if (pos2)
+                               *pos2 = '\0';
+                       if (strcmp(pos, ctrl_iface) != 0)
+                               adapterSelect->addItem(pos);
+                       if (pos2)
+                               pos = pos2 + 1;
+                       else
+                               break;
+               }
+       }
+
+       len = sizeof(buf) - 1;
+       if (wpa_ctrl_request(ctrl_conn, "GET_CAPABILITY eap", 18, buf, &len,
+                            NULL) >= 0) {
+               buf[len] = '\0';
+
+               QString res(buf);
+               QStringList types = res.split(QChar(' '));
+               bool wps = types.contains("WSC");
+               actionWPS->setEnabled(wps);
+               wpsTab->setEnabled(wps);
+               wpaguiTab->setTabEnabled(wpaguiTab->indexOf(wpsTab), wps);
+       }
+
+       return 0;
+}
+
+
+int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
+{
+       int ret;
+
+       if (ctrl_conn == NULL)
+               return -3;
+       ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL);
+       if (ret == -2)
+               printf("'%s' command timed out.\n", cmd);
+       else if (ret < 0)
+               printf("'%s' command failed.\n", cmd);
+
+       return ret;
+}
+
+
+QString WpaGui::wpaStateTranslate(char *state)
+{
+       if (!strcmp(state, "DISCONNECTED"))
+               return tr("Disconnected");
+       else if (!strcmp(state, "INACTIVE"))
+               return tr("Inactive");
+       else if (!strcmp(state, "SCANNING"))
+               return tr("Scanning");
+       else if (!strcmp(state, "AUTHENTICATING"))
+               return tr("Authenticating");
+       else if (!strcmp(state, "ASSOCIATING"))
+               return tr("Associating");
+       else if (!strcmp(state, "ASSOCIATED"))
+               return tr("Associated");
+       else if (!strcmp(state, "4WAY_HANDSHAKE"))
+               return tr("4-Way Handshake");
+       else if (!strcmp(state, "GROUP_HANDSHAKE"))
+               return tr("Group Handshake");
+       else if (!strcmp(state, "COMPLETED"))
+               return tr("Completed");
+       else
+               return tr("Unknown");
+}
+
+
+void WpaGui::updateStatus()
+{
+       char buf[2048], *start, *end, *pos;
+       size_t len;
+
+       pingsToStatusUpdate = 10;
+
+       len = sizeof(buf) - 1;
+       if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
+               textStatus->setText(tr("Could not get status from "
+                                      "wpa_supplicant"));
+               textAuthentication->clear();
+               textEncryption->clear();
+               textSsid->clear();
+               textBssid->clear();
+               textIpAddress->clear();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+               static bool first = true;
+               if (first && connectedToService &&
+                   (ctrl_iface == NULL || *ctrl_iface == '\0')) {
+                       first = false;
+                       if (QMessageBox::information(
+                                   this, qAppName(),
+                                   tr("No network interfaces in use.\n"
+                                      "Would you like to add one?"),
+                                   QMessageBox::Yes | QMessageBox::No) ==
+                           QMessageBox::Yes)
+                               addInterface();
+               }
+#endif /* CONFIG_NATIVE_WINDOWS */
+               return;
+       }
+
+       buf[len] = '\0';
+
+       bool auth_updated = false, ssid_updated = false;
+       bool bssid_updated = false, ipaddr_updated = false;
+       bool status_updated = false;
+       char *pairwise_cipher = NULL, *group_cipher = NULL;
+       char *mode = NULL;
+
+       start = buf;
+       while (*start) {
+               bool last = false;
+               end = strchr(start, '\n');
+               if (end == NULL) {
+                       last = true;
+                       end = start;
+                       while (end[0] && end[1])
+                               end++;
+               }
+               *end = '\0';
+
+               pos = strchr(start, '=');
+               if (pos) {
+                       *pos++ = '\0';
+                       if (strcmp(start, "bssid") == 0) {
+                               bssid_updated = true;
+                               textBssid->setText(pos);
+                       } else if (strcmp(start, "ssid") == 0) {
+                               ssid_updated = true;
+                               textSsid->setText(pos);
+                       } else if (strcmp(start, "ip_address") == 0) {
+                               ipaddr_updated = true;
+                               textIpAddress->setText(pos);
+                       } else if (strcmp(start, "wpa_state") == 0) {
+                               status_updated = true;
+                               textStatus->setText(wpaStateTranslate(pos));
+                       } else if (strcmp(start, "key_mgmt") == 0) {
+                               auth_updated = true;
+                               textAuthentication->setText(pos);
+                               /* TODO: could add EAP status to this */
+                       } else if (strcmp(start, "pairwise_cipher") == 0) {
+                               pairwise_cipher = pos;
+                       } else if (strcmp(start, "group_cipher") == 0) {
+                               group_cipher = pos;
+                       } else if (strcmp(start, "mode") == 0) {
+                               mode = pos;
+                       }
+               }
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+       if (status_updated && mode)
+               textStatus->setText(textStatus->text() + " (" + mode + ")");
+
+       if (pairwise_cipher || group_cipher) {
+               QString encr;
+               if (pairwise_cipher && group_cipher &&
+                   strcmp(pairwise_cipher, group_cipher) != 0) {
+                       encr.append(pairwise_cipher);
+                       encr.append(" + ");
+                       encr.append(group_cipher);
+               } else if (pairwise_cipher) {
+                       encr.append(pairwise_cipher);
+               } else {
+                       encr.append(group_cipher);
+                       encr.append(" [group key only]");
+               }
+               textEncryption->setText(encr);
+       } else
+               textEncryption->clear();
+
+       if (!status_updated)
+               textStatus->clear();
+       if (!auth_updated)
+               textAuthentication->clear();
+       if (!ssid_updated)
+               textSsid->clear();
+       if (!bssid_updated)
+               textBssid->clear();
+       if (!ipaddr_updated)
+               textIpAddress->clear();
+}
+
+
+void WpaGui::updateNetworks()
+{
+       char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+       size_t len;
+       int first_active = -1;
+       int was_selected = -1;
+       bool current = false;
+
+       if (!networkMayHaveChanged)
+               return;
+
+       if (networkList->currentRow() >= 0)
+               was_selected = networkList->currentRow();
+
+       networkSelect->clear();
+       networkList->clear();
+
+       if (ctrl_conn == NULL)
+               return;
+
+       len = sizeof(buf) - 1;
+       if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
+               return;
+
+       buf[len] = '\0';
+       start = strchr(buf, '\n');
+       if (start == NULL)
+               return;
+       start++;
+
+       while (*start) {
+               bool last = false;
+               end = strchr(start, '\n');
+               if (end == NULL) {
+                       last = true;
+                       end = start;
+                       while (end[0] && end[1])
+                               end++;
+               }
+               *end = '\0';
+
+               id = start;
+               ssid = strchr(id, '\t');
+               if (ssid == NULL)
+                       break;
+               *ssid++ = '\0';
+               bssid = strchr(ssid, '\t');
+               if (bssid == NULL)
+                       break;
+               *bssid++ = '\0';
+               flags = strchr(bssid, '\t');
+               if (flags == NULL)
+                       break;
+               *flags++ = '\0';
+
+               QString network(id);
+               network.append(": ");
+               network.append(ssid);
+               networkSelect->addItem(network);
+               networkList->addItem(network);
+
+               if (strstr(flags, "[CURRENT]")) {
+                       networkSelect->setCurrentIndex(networkSelect->count() -
+                                                     1);
+                       current = true;
+               } else if (first_active < 0 &&
+                          strstr(flags, "[DISABLED]") == NULL)
+                       first_active = networkSelect->count() - 1;
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+
+       if (networkSelect->count() > 1)
+               networkSelect->addItem(tr("Select any network"));
+
+       if (!current && first_active >= 0)
+               networkSelect->setCurrentIndex(first_active);
+
+       if (was_selected >= 0 && networkList->count() > 0) {
+               if (was_selected < networkList->count())
+                       networkList->setCurrentRow(was_selected);
+               else
+                       networkList->setCurrentRow(networkList->count() - 1);
+       }
+       else
+               networkList->setCurrentRow(networkSelect->currentIndex());
+
+       networkMayHaveChanged = false;
+}
+
+
+void WpaGui::helpIndex()
+{
+       printf("helpIndex\n");
+}
+
+
+void WpaGui::helpContents()
+{
+       printf("helpContents\n");
+}
+
+
+void WpaGui::helpAbout()
+{
+       QMessageBox::about(this, "wpa_gui for wpa_supplicant",
+                          "Copyright (c) 2003-2010,\n"
+                          "Jouni Malinen <j@w1.fi>\n"
+                          "and contributors.\n"
+                          "\n"
+                          "This program is free software. You can\n"
+                          "distribute it and/or modify it under the terms "
+                          "of\n"
+                          "the GNU General Public License version 2.\n"
+                          "\n"
+                          "Alternatively, this software may be distributed\n"
+                          "under the terms of the BSD license.\n"
+                          "\n"
+                          "This product includes software developed\n"
+                          "by the OpenSSL Project for use in the\n"
+                          "OpenSSL Toolkit (http://www.openssl.org/)\n");
+}
+
+
+void WpaGui::disconnect()
+{
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+       ctrlRequest("DISCONNECT", reply, &reply_len);
+       stopWpsRun(false);
+}
+
+
+void WpaGui::scan()
+{
+       if (scanres) {
+               scanres->close();
+               delete scanres;
+       }
+
+       scanres = new ScanResults();
+       if (scanres == NULL)
+               return;
+       scanres->setWpaGui(this);
+       scanres->show();
+       scanres->exec();
+}
+
+
+void WpaGui::eventHistory()
+{
+       if (eh) {
+               eh->close();
+               delete eh;
+       }
+
+       eh = new EventHistory();
+       if (eh == NULL)
+               return;
+       eh->addEvents(msgs);
+       eh->show();
+       eh->exec();
+}
+
+
+void WpaGui::ping()
+{
+       char buf[10];
+       size_t len;
+
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+       /*
+        * QSocketNotifier cannot be used with Windows named pipes, so use a
+        * timer to check for received messages for now. This could be
+        * optimized be doing something specific to named pipes or Windows
+        * events, but it is not clear what would be the best way of doing that
+        * in Qt.
+        */
+       receiveMsgs();
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+       if (scanres && !scanres->isVisible()) {
+               delete scanres;
+               scanres = NULL;
+       }
+
+       if (eh && !eh->isVisible()) {
+               delete eh;
+               eh = NULL;
+       }
+
+       if (udr && !udr->isVisible()) {
+               delete udr;
+               udr = NULL;
+       }
+
+       len = sizeof(buf) - 1;
+       if (ctrlRequest("PING", buf, &len) < 0) {
+               printf("PING failed - trying to reconnect\n");
+               if (openCtrlConnection(ctrl_iface) >= 0) {
+                       printf("Reconnected successfully\n");
+                       pingsToStatusUpdate = 0;
+               }
+       }
+
+       pingsToStatusUpdate--;
+       if (pingsToStatusUpdate <= 0) {
+               updateStatus();
+               updateNetworks();
+       }
+
+#ifndef CONFIG_CTRL_IFACE_NAMED_PIPE
+       /* Use less frequent pings and status updates when the main window is
+        * hidden (running in taskbar). */
+       int interval = isHidden() ? 5000 : 1000;
+       if (timer->interval() != interval)
+               timer->setInterval(interval);
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+}
+
+
+static int str_match(const char *a, const char *b)
+{
+       return strncmp(a, b, strlen(b)) == 0;
+}
+
+
+void WpaGui::processMsg(char *msg)
+{
+       char *pos = msg, *pos2;
+       int priority = 2;
+
+       if (*pos == '<') {
+               /* skip priority */
+               pos++;
+               priority = atoi(pos);
+               pos = strchr(pos, '>');
+               if (pos)
+                       pos++;
+               else
+                       pos = msg;
+       }
+
+       WpaMsg wm(pos, priority);
+       if (eh)
+               eh->addEvent(wm);
+       if (peers)
+               peers->event_notify(wm);
+       msgs.append(wm);
+       while (msgs.count() > 100)
+               msgs.pop_front();
+
+       /* Update last message with truncated version of the event */
+       if (strncmp(pos, "CTRL-", 5) == 0) {
+               pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
+               if (pos2)
+                       pos2++;
+               else
+                       pos2 = pos;
+       } else
+               pos2 = pos;
+       QString lastmsg = pos2;
+       lastmsg.truncate(40);
+       textLastMessage->setText(lastmsg);
+
+       pingsToStatusUpdate = 0;
+       networkMayHaveChanged = true;
+
+       if (str_match(pos, WPA_CTRL_REQ))
+               processCtrlReq(pos + strlen(WPA_CTRL_REQ));
+       else if (str_match(pos, WPA_EVENT_SCAN_RESULTS) && scanres)
+               scanres->updateResults();
+       else if (str_match(pos, WPA_EVENT_DISCONNECTED))
+               showTrayMessage(QSystemTrayIcon::Information, 3,
+                               tr("Disconnected from network."));
+       else if (str_match(pos, WPA_EVENT_CONNECTED)) {
+               showTrayMessage(QSystemTrayIcon::Information, 3,
+                               tr("Connection to network established."));
+               QTimer::singleShot(5 * 1000, this, SLOT(showTrayStatus()));
+               stopWpsRun(true);
+       } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PBC)) {
+               wpsStatusText->setText(tr("WPS AP in active PBC mode found"));
+               if (textStatus->text() == "INACTIVE" ||
+                   textStatus->text() == "DISCONNECTED")
+                       wpaguiTab->setCurrentWidget(wpsTab);
+               wpsInstructions->setText(tr("Press the PBC button on the "
+                                           "screen to start registration"));
+       } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE_PIN)) {
+               wpsStatusText->setText(tr("WPS AP with recently selected "
+                                         "registrar"));
+               if (textStatus->text() == "INACTIVE" ||
+                   textStatus->text() == "DISCONNECTED")
+                       wpaguiTab->setCurrentWidget(wpsTab);
+       } else if (str_match(pos, WPS_EVENT_AP_AVAILABLE)) {
+               wpsStatusText->setText(tr("WPS AP detected"));
+       } else if (str_match(pos, WPS_EVENT_OVERLAP)) {
+               wpsStatusText->setText(tr("PBC mode overlap detected"));
+               wpsInstructions->setText(tr("More than one AP is currently in "
+                                           "active WPS PBC mode. Wait couple "
+                                           "of minutes and try again"));
+               wpaguiTab->setCurrentWidget(wpsTab);
+       } else if (str_match(pos, WPS_EVENT_CRED_RECEIVED)) {
+               wpsStatusText->setText(tr("Network configuration received"));
+               wpaguiTab->setCurrentWidget(wpsTab);
+       } else if (str_match(pos, WPA_EVENT_EAP_METHOD)) {
+               if (strstr(pos, "(WSC)"))
+                       wpsStatusText->setText(tr("Registration started"));
+       } else if (str_match(pos, WPS_EVENT_M2D)) {
+               wpsStatusText->setText(tr("Registrar does not yet know PIN"));
+       } else if (str_match(pos, WPS_EVENT_FAIL)) {
+               wpsStatusText->setText(tr("Registration failed"));
+       } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
+               wpsStatusText->setText(tr("Registration succeeded"));
+       }
+}
+
+
+void WpaGui::processCtrlReq(const char *req)
+{
+       if (udr) {
+               udr->close();
+               delete udr;
+       }
+       udr = new UserDataRequest();
+       if (udr == NULL)
+               return;
+       if (udr->setParams(this, req) < 0) {
+               delete udr;
+               udr = NULL;
+               return;
+       }
+       udr->show();
+       udr->exec();
+}
+
+
+void WpaGui::receiveMsgs()
+{
+       char buf[256];
+       size_t len;
+
+       while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
+               len = sizeof(buf) - 1;
+               if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
+                       buf[len] = '\0';
+                       processMsg(buf);
+               }
+       }
+}
+
+
+void WpaGui::connectB()
+{
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+       ctrlRequest("REASSOCIATE", reply, &reply_len);
+}
+
+
+void WpaGui::selectNetwork( const QString &sel )
+{
+       QString cmd(sel);
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+
+       if (cmd.contains(QRegExp("^\\d+:")))
+               cmd.truncate(cmd.indexOf(':'));
+       else
+               cmd = "any";
+       cmd.prepend("SELECT_NETWORK ");
+       ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+       triggerUpdate();
+       stopWpsRun(false);
+}
+
+
+void WpaGui::enableNetwork(const QString &sel)
+{
+       QString cmd(sel);
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+
+       if (cmd.contains(QRegExp("^\\d+:")))
+               cmd.truncate(cmd.indexOf(':'));
+       else if (!cmd.startsWith("all")) {
+               printf("Invalid editNetwork '%s'\n",
+                      cmd.toAscii().constData());
+               return;
+       }
+       cmd.prepend("ENABLE_NETWORK ");
+       ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+       triggerUpdate();
+}
+
+
+void WpaGui::disableNetwork(const QString &sel)
+{
+       QString cmd(sel);
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+
+       if (cmd.contains(QRegExp("^\\d+:")))
+               cmd.truncate(cmd.indexOf(':'));
+       else if (!cmd.startsWith("all")) {
+               printf("Invalid editNetwork '%s'\n",
+                      cmd.toAscii().constData());
+               return;
+       }
+       cmd.prepend("DISABLE_NETWORK ");
+       ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+       triggerUpdate();
+}
+
+
+void WpaGui::editNetwork(const QString &sel)
+{
+       QString cmd(sel);
+       int id = -1;
+
+       if (cmd.contains(QRegExp("^\\d+:"))) {
+               cmd.truncate(cmd.indexOf(':'));
+               id = cmd.toInt();
+       }
+
+       NetworkConfig *nc = new NetworkConfig();
+       if (nc == NULL)
+               return;
+       nc->setWpaGui(this);
+
+       if (id >= 0)
+               nc->paramsFromConfig(id);
+       else
+               nc->newNetwork();
+
+       nc->show();
+       nc->exec();
+}
+
+
+void WpaGui::editSelectedNetwork()
+{
+       if (networkSelect->count() < 1) {
+               QMessageBox::information(
+                       this, tr("No Networks"),
+                       tr("There are no networks to edit.\n"));
+               return;
+       }
+       QString sel(networkSelect->currentText());
+       editNetwork(sel);
+}
+
+
+void WpaGui::editListedNetwork()
+{
+       if (networkList->currentRow() < 0) {
+               QMessageBox::information(this, tr("Select A Network"),
+                                        tr("Select a network from the list to"
+                                           " edit it.\n"));
+               return;
+       }
+       QString sel(networkList->currentItem()->text());
+       editNetwork(sel);
+}
+
+
+void WpaGui::triggerUpdate()
+{
+       updateStatus();
+       networkMayHaveChanged = true;
+       updateNetworks();
+}
+
+
+void WpaGui::addNetwork()
+{
+       NetworkConfig *nc = new NetworkConfig();
+       if (nc == NULL)
+               return;
+       nc->setWpaGui(this);
+       nc->newNetwork();
+       nc->show();
+       nc->exec();
+}
+
+
+void WpaGui::removeNetwork(const QString &sel)
+{
+       QString cmd(sel);
+       char reply[10];
+       size_t reply_len = sizeof(reply);
+
+       if (cmd.contains(QRegExp("^\\d+:")))
+               cmd.truncate(cmd.indexOf(':'));
+       else if (!cmd.startsWith("all")) {
+               printf("Invalid editNetwork '%s'\n",
+                      cmd.toAscii().constData());
+               return;
+       }
+       cmd.prepend("REMOVE_NETWORK ");
+       ctrlRequest(cmd.toAscii().constData(), reply, &reply_len);
+       triggerUpdate();
+}
+
+
+void WpaGui::removeSelectedNetwork()
+{
+       if (networkSelect->count() < 1) {
+               QMessageBox::information(this, tr("No Networks"),
+                                        tr("There are no networks to remove."
+                                           "\n"));
+               return;
+       }
+       QString sel(networkSelect->currentText());
+       removeNetwork(sel);
+}
+
+
+void WpaGui::removeListedNetwork()
+{
+       if (networkList->currentRow() < 0) {
+               QMessageBox::information(this, tr("Select A Network"),
+                                        tr("Select a network from the list "
+                                           "to remove it.\n"));
+               return;
+       }
+       QString sel(networkList->currentItem()->text());
+       removeNetwork(sel);
+}
+
+
+void WpaGui::enableAllNetworks()
+{
+       QString sel("all");
+       enableNetwork(sel);
+}
+
+
+void WpaGui::disableAllNetworks()
+{
+       QString sel("all");
+       disableNetwork(sel);
+}
+
+
+void WpaGui::removeAllNetworks()
+{
+       QString sel("all");
+       removeNetwork(sel);
+}
+
+
+int WpaGui::getNetworkDisabled(const QString &sel)
+{
+       QString cmd(sel);
+       char reply[10];
+       size_t reply_len = sizeof(reply) - 1;
+       int pos = cmd.indexOf(':');
+       if (pos < 0) {
+               printf("Invalid getNetworkDisabled '%s'\n",
+                      cmd.toAscii().constData());
+               return -1;
+       }
+       cmd.truncate(pos);
+       cmd.prepend("GET_NETWORK ");
+       cmd.append(" disabled");
+
+       if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) >= 0
+           && reply_len >= 1) {
+               reply[reply_len] = '\0';
+               if (!str_match(reply, "FAIL"))
+                       return atoi(reply);
+       }
+
+       return -1;
+}
+
+
+void WpaGui::updateNetworkDisabledStatus()
+{
+       if (networkList->currentRow() < 0)
+               return;
+
+       QString sel(networkList->currentItem()->text());
+
+       switch (getNetworkDisabled(sel)) {
+       case 0:
+               if (!enableRadioButton->isChecked())
+                       enableRadioButton->setChecked(true);
+               return;
+       case 1:
+               if (!disableRadioButton->isChecked())
+                       disableRadioButton->setChecked(true);
+               return;
+       }
+}
+
+
+void WpaGui::enableListedNetwork(bool enabled)
+{
+       if (networkList->currentRow() < 0 || !enabled)
+               return;
+
+       QString sel(networkList->currentItem()->text());
+
+       if (getNetworkDisabled(sel) == 1)
+               enableNetwork(sel);
+}
+
+
+void WpaGui::disableListedNetwork(bool disabled)
+{
+       if (networkList->currentRow() < 0 || !disabled)
+               return;
+
+       QString sel(networkList->currentItem()->text());
+
+       if (getNetworkDisabled(sel) == 0)
+               disableNetwork(sel);
+}
+
+
+void WpaGui::saveConfig()
+{
+       char buf[10];
+       size_t len;
+
+       len = sizeof(buf) - 1;
+       ctrlRequest("SAVE_CONFIG", buf, &len);
+
+       buf[len] = '\0';
+
+       if (str_match(buf, "FAIL"))
+               QMessageBox::warning(
+                       this, tr("Failed to save configuration"),
+                       tr("The configuration could not be saved.\n"
+                          "\n"
+                          "The update_config=1 configuration option\n"
+                          "must be used for configuration saving to\n"
+                          "be permitted.\n"));
+       else
+               QMessageBox::information(
+                       this, tr("Saved configuration"),
+                       tr("The current configuration was saved."
+                          "\n"));
+}
+
+
+void WpaGui::selectAdapter( const QString & sel )
+{
+       if (openCtrlConnection(sel.toAscii().constData()) < 0)
+               printf("Failed to open control connection to "
+                      "wpa_supplicant.\n");
+       updateStatus();
+       updateNetworks();
+}
+
+
+void WpaGui::createTrayIcon(bool trayOnly)
+{
+       QApplication::setQuitOnLastWindowClosed(false);
+
+       tray_icon = new QSystemTrayIcon(this);
+       tray_icon->setToolTip(qAppName() + tr(" - wpa_supplicant user interface"));
+       if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
+               tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg"));
+       else
+               tray_icon->setIcon(QIcon(":/icons/wpa_gui.png"));
+
+       connect(tray_icon,
+               SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
+               this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
+
+       ackTrayIcon = false;
+
+       tray_menu = new QMenu(this);
+
+       disconnectAction = new QAction(tr("&Disconnect"), this);
+       reconnectAction = new QAction(tr("Re&connect"), this);
+       connect(disconnectAction, SIGNAL(triggered()), this,
+               SLOT(disconnect()));
+       connect(reconnectAction, SIGNAL(triggered()), this,
+               SLOT(connectB()));
+       tray_menu->addAction(disconnectAction);
+       tray_menu->addAction(reconnectAction);
+       tray_menu->addSeparator();
+
+       eventAction = new QAction(tr("&Event History"), this);
+       scanAction = new QAction(tr("Scan &Results"), this);
+       statAction = new QAction(tr("S&tatus"), this);
+       connect(eventAction, SIGNAL(triggered()), this, SLOT(eventHistory()));
+       connect(scanAction, SIGNAL(triggered()), this, SLOT(scan()));
+       connect(statAction, SIGNAL(triggered()), this, SLOT(showTrayStatus()));
+       tray_menu->addAction(eventAction);
+       tray_menu->addAction(scanAction);
+       tray_menu->addAction(statAction);
+       tray_menu->addSeparator();
+
+       showAction = new QAction(tr("&Show Window"), this);
+       hideAction = new QAction(tr("&Hide Window"), this);
+       quitAction = new QAction(tr("&Quit"), this);
+       connect(showAction, SIGNAL(triggered()), this, SLOT(show()));
+       connect(hideAction, SIGNAL(triggered()), this, SLOT(hide()));
+       connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
+       tray_menu->addAction(showAction);
+       tray_menu->addAction(hideAction);
+       tray_menu->addSeparator();
+       tray_menu->addAction(quitAction);
+
+       tray_icon->setContextMenu(tray_menu);
+
+       tray_icon->show();
+
+       if (!trayOnly)
+               show();
+       inTray = trayOnly;
+}
+
+
+void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
+                            const QString & msg)
+{
+       if (!QSystemTrayIcon::supportsMessages())
+               return;
+
+       if (isVisible() || !tray_icon || !tray_icon->isVisible())
+               return;
+
+       tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
+}
+
+
+void WpaGui::trayActivated(QSystemTrayIcon::ActivationReason how)
+ {
+       switch (how) {
+       /* use close() here instead of hide() and allow the
+        * custom closeEvent handler take care of children */
+       case QSystemTrayIcon::Trigger:
+               ackTrayIcon = true;
+               if (isVisible()) {
+                       close();
+                       inTray = true;
+               } else {
+                       show();
+                       inTray = false;
+               }
+               break;
+       case QSystemTrayIcon::MiddleClick:
+               showTrayStatus();
+               break;
+       default:
+               break;
+       }
+}
+
+
+void WpaGui::showTrayStatus()
+{
+       char buf[2048];
+       size_t len;
+
+       len = sizeof(buf) - 1;
+       if (ctrlRequest("STATUS", buf, &len) < 0)
+               return;
+       buf[len] = '\0';
+
+       QString msg, status(buf);
+
+       QStringList lines = status.split(QRegExp("\\n"));
+       for (QStringList::Iterator it = lines.begin();
+            it != lines.end(); it++) {
+               int pos = (*it).indexOf('=') + 1;
+               if (pos < 1)
+                       continue;
+
+               if ((*it).startsWith("bssid="))
+                       msg.append("BSSID:\t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("ssid="))
+                       msg.append("SSID: \t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("pairwise_cipher="))
+                       msg.append("PAIR: \t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("group_cipher="))
+                       msg.append("GROUP:\t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("key_mgmt="))
+                       msg.append("AUTH: \t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("wpa_state="))
+                       msg.append("STATE:\t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("ip_address="))
+                       msg.append("IP:   \t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("Supplicant PAE state="))
+                       msg.append("PAE:  \t" + (*it).mid(pos) + "\n");
+               else if ((*it).startsWith("EAP state="))
+                       msg.append("EAP:  \t" + (*it).mid(pos) + "\n");
+       }
+
+       if (!msg.isEmpty())
+               showTrayMessage(QSystemTrayIcon::Information, 10, msg);
+}
+
+
+void WpaGui::closeEvent(QCloseEvent *event)
+{
+       if (eh) {
+               eh->close();
+               delete eh;
+               eh = NULL;
+       }
+
+       if (scanres) {
+               scanres->close();
+               delete scanres;
+               scanres = NULL;
+       }
+
+       if (peers) {
+               peers->close();
+               delete peers;
+               peers = NULL;
+       }
+
+       if (udr) {
+               udr->close();
+               delete udr;
+               udr = NULL;
+       }
+
+       if (tray_icon && !ackTrayIcon) {
+               /* give user a visual hint that the tray icon exists */
+               if (QSystemTrayIcon::supportsMessages()) {
+                       hide();
+                       showTrayMessage(QSystemTrayIcon::Information, 3,
+                                       qAppName() +
+                                       tr(" will keep running in "
+                                          "the system tray."));
+               } else {
+                       QMessageBox::information(this, qAppName() +
+                                                tr(" systray"),
+                                                tr("The program will keep "
+                                                   "running in the system "
+                                                   "tray."));
+               }
+               ackTrayIcon = true;
+       }
+
+       event->accept();
+}
+
+
+void WpaGui::wpsDialog()
+{
+       wpaguiTab->setCurrentWidget(wpsTab);
+}
+
+
+void WpaGui::peersDialog()
+{
+       if (peers) {
+               peers->close();
+               delete peers;
+       }
+
+       peers = new Peers();
+       if (peers == NULL)
+               return;
+       peers->setWpaGui(this);
+       peers->show();
+       peers->exec();
+}
+
+
+void WpaGui::tabChanged(int index)
+{
+       if (index != 2)
+               return;
+
+       if (wpsRunning)
+               return;
+
+       wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
+       if (bssFromScan.isEmpty())
+               wpsApPinButton->setEnabled(false);
+}
+
+
+void WpaGui::wpsPbc()
+{
+       char reply[20];
+       size_t reply_len = sizeof(reply);
+
+       if (ctrlRequest("WPS_PBC", reply, &reply_len) < 0)
+               return;
+
+       wpsPinEdit->setEnabled(false);
+       if (wpsStatusText->text().compare(tr("WPS AP in active PBC mode found"))) {
+               wpsInstructions->setText(tr("Press the push button on the AP to "
+                                        "start the PBC mode."));
+       } else {
+               wpsInstructions->setText(tr("If you have not yet done so, press "
+                                        "the push button on the AP to start "
+                                        "the PBC mode."));
+       }
+       wpsStatusText->setText(tr("Waiting for Registrar"));
+       wpsRunning = true;
+}
+
+
+void WpaGui::wpsGeneratePin()
+{
+       char reply[20];
+       size_t reply_len = sizeof(reply) - 1;
+
+       if (ctrlRequest("WPS_PIN any", reply, &reply_len) < 0)
+               return;
+
+       reply[reply_len] = '\0';
+
+       wpsPinEdit->setText(reply);
+       wpsPinEdit->setEnabled(true);
+       wpsInstructions->setText(tr("Enter the generated PIN into the Registrar "
+                                "(either the internal one in the AP or an "
+                                "external one)."));
+       wpsStatusText->setText(tr("Waiting for Registrar"));
+       wpsRunning = true;
+}
+
+
+void WpaGui::setBssFromScan(const QString &bssid)
+{
+       bssFromScan = bssid;
+       wpsApPinEdit->setEnabled(!bssFromScan.isEmpty());
+       wpsApPinButton->setEnabled(wpsApPinEdit->text().length() == 8);
+       wpsStatusText->setText(tr("WPS AP selected from scan results"));
+       wpsInstructions->setText(tr("If you want to use an AP device PIN, e.g., "
+                                "from a label in the device, enter the eight "
+                                "digit AP PIN and click Use AP PIN button."));
+}
+
+
+void WpaGui::wpsApPinChanged(const QString &text)
+{
+       wpsApPinButton->setEnabled(text.length() == 8);
+}
+
+
+void WpaGui::wpsApPin()
+{
+       char reply[20];
+       size_t reply_len = sizeof(reply);
+
+       QString cmd("WPS_REG " + bssFromScan + " " + wpsApPinEdit->text());
+       if (ctrlRequest(cmd.toAscii().constData(), reply, &reply_len) < 0)
+               return;
+
+       wpsStatusText->setText(tr("Waiting for AP/Enrollee"));
+       wpsRunning = true;
+}
+
+
+void WpaGui::stopWpsRun(bool success)
+{
+       if (wpsRunning)
+               wpsStatusText->setText(success ? tr("Connected to the network") :
+                                      tr("Stopped"));
+       else
+               wpsStatusText->setText("");
+       wpsPinEdit->setEnabled(false);
+       wpsInstructions->setText("");
+       wpsRunning = false;
+       bssFromScan = "";
+       wpsApPinEdit->setEnabled(false);
+       wpsApPinButton->setEnabled(false);
+}
+
+
+#ifdef CONFIG_NATIVE_WINDOWS
+
+#ifndef WPASVC_NAME
+#define WPASVC_NAME TEXT("wpasvc")
+#endif
+
+class ErrorMsg : public QMessageBox {
+public:
+       ErrorMsg(QWidget *parent, DWORD last_err = GetLastError());
+       void showMsg(QString msg);
+private:
+       DWORD err;
+};
+
+ErrorMsg::ErrorMsg(QWidget *parent, DWORD last_err) :
+       QMessageBox(parent), err(last_err)
+{
+       setWindowTitle(tr("wpa_gui error"));
+       setIcon(QMessageBox::Warning);
+}
+
+void ErrorMsg::showMsg(QString msg)
+{
+       LPTSTR buf;
+
+       setText(msg);
+       if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                         FORMAT_MESSAGE_FROM_SYSTEM,
+                         NULL, err, 0, (LPTSTR) (void *) &buf,
+                         0, NULL) > 0) {
+               QString msg = QString::fromWCharArray(buf);
+               setInformativeText(QString("[%1] %2").arg(err).arg(msg));
+               LocalFree(buf);
+       } else {
+               setInformativeText(QString("[%1]").arg(err));
+       }
+
+       exec();
+}
+
+
+void WpaGui::startService()
+{
+       SC_HANDLE svc, scm;
+
+       scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+       if (!scm) {
+               ErrorMsg(this).showMsg(tr("OpenSCManager failed"));
+               return;
+       }
+
+       svc = OpenService(scm, WPASVC_NAME, SERVICE_START);
+       if (!svc) {
+               ErrorMsg(this).showMsg(tr("OpenService failed"));
+               CloseServiceHandle(scm);
+               return;
+       }
+
+       if (!StartService(svc, 0, NULL)) {
+               ErrorMsg(this).showMsg(tr("Failed to start wpa_supplicant "
+                                      "service"));
+       }
+
+       CloseServiceHandle(svc);
+       CloseServiceHandle(scm);
+}
+
+
+void WpaGui::stopService()
+{
+       SC_HANDLE svc, scm;
+       SERVICE_STATUS status;
+
+       scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+       if (!scm) {
+               ErrorMsg(this).showMsg(tr("OpenSCManager failed"));
+               return;
+       }
+
+       svc = OpenService(scm, WPASVC_NAME, SERVICE_STOP);
+       if (!svc) {
+               ErrorMsg(this).showMsg(tr("OpenService failed"));
+               CloseServiceHandle(scm);
+               return;
+       }
+
+       if (!ControlService(svc, SERVICE_CONTROL_STOP, &status)) {
+               ErrorMsg(this).showMsg(tr("Failed to stop wpa_supplicant "
+                                      "service"));
+       }
+
+       CloseServiceHandle(svc);
+       CloseServiceHandle(scm);
+}
+
+
+bool WpaGui::serviceRunning()
+{
+       SC_HANDLE svc, scm;
+       SERVICE_STATUS status;
+       bool running = false;
+
+       scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+       if (!scm) {
+               printf("OpenSCManager failed: %d\n", (int) GetLastError());
+               return false;
+       }
+
+       svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS);
+       if (!svc) {
+               printf("OpenService failed: %d\n\n", (int) GetLastError());
+               CloseServiceHandle(scm);
+               return false;
+       }
+
+       if (QueryServiceStatus(svc, &status)) {
+               if (status.dwCurrentState != SERVICE_STOPPED)
+                       running = true;
+       }
+
+       CloseServiceHandle(svc);
+       CloseServiceHandle(scm);
+
+       return running;
+}
+
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+void WpaGui::addInterface()
+{
+       if (add_iface) {
+               add_iface->close();
+               delete add_iface;
+       }
+       add_iface = new AddInterface(this, this);
+       add_iface->show();
+       add_iface->exec();
+}
+
+
+#ifndef QT_NO_SESSIONMANAGER
+void WpaGui::saveState()
+{
+       QSettings settings("wpa_supplicant", "wpa_gui");
+       settings.beginGroup("state");
+       settings.setValue("session_id", app->sessionId());
+       settings.setValue("in_tray", inTray);
+       settings.endGroup();
+}
+#endif
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.h b/wpa_supplicant/wpa_gui-qt4/wpagui.h
new file mode 100644 (file)
index 0000000..2e1af8e
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * wpa_gui - WpaGui class
+ * Copyright (c) 2005-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 WPAGUI_H
+#define WPAGUI_H
+
+#include <QSystemTrayIcon>
+#include <QObject>
+#include "ui_wpagui.h"
+#include "addinterface.h"
+
+class UserDataRequest;
+
+
+class WpaGui : public QMainWindow, public Ui::WpaGui
+{
+       Q_OBJECT
+
+public:
+       WpaGui(QApplication *app, QWidget *parent = 0, const char *name = 0,
+              Qt::WFlags fl = 0);
+       ~WpaGui();
+
+       virtual int ctrlRequest(const char *cmd, char *buf, size_t *buflen);
+       virtual void triggerUpdate();
+       virtual void editNetwork(const QString &sel);
+       virtual void removeNetwork(const QString &sel);
+       virtual void enableNetwork(const QString &sel);
+       virtual void disableNetwork(const QString &sel);
+       virtual int getNetworkDisabled(const QString &sel);
+       void setBssFromScan(const QString &bssid);
+#ifndef QT_NO_SESSIONMANAGER
+       void saveState();
+#endif
+
+public slots:
+       virtual void parse_argv();
+       virtual void updateStatus();
+       virtual void updateNetworks();
+       virtual void helpIndex();
+       virtual void helpContents();
+       virtual void helpAbout();
+       virtual void disconnect();
+       virtual void scan();
+       virtual void eventHistory();
+       virtual void ping();
+       virtual void processMsg(char *msg);
+       virtual void processCtrlReq(const char *req);
+       virtual void receiveMsgs();
+       virtual void connectB();
+       virtual void selectNetwork(const QString &sel);
+       virtual void editSelectedNetwork();
+       virtual void editListedNetwork();
+       virtual void removeSelectedNetwork();
+       virtual void removeListedNetwork();
+       virtual void addNetwork();
+       virtual void enableAllNetworks();
+       virtual void disableAllNetworks();
+       virtual void removeAllNetworks();
+       virtual void saveConfig();
+       virtual void selectAdapter(const QString &sel);
+       virtual void updateNetworkDisabledStatus();
+       virtual void enableListedNetwork(bool);
+       virtual void disableListedNetwork(bool);
+       virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type,
+                                    int sec, const QString &msg);
+       virtual void showTrayStatus();
+       virtual void wpsDialog();
+       virtual void peersDialog();
+       virtual void tabChanged(int index);
+       virtual void wpsPbc();
+       virtual void wpsGeneratePin();
+       virtual void wpsApPinChanged(const QString &text);
+       virtual void wpsApPin();
+#ifdef CONFIG_NATIVE_WINDOWS
+       virtual void startService();
+       virtual void stopService();
+#endif /* CONFIG_NATIVE_WINDOWS */
+       virtual void addInterface();
+
+protected slots:
+       virtual void languageChange();
+       virtual void trayActivated(QSystemTrayIcon::ActivationReason how);
+       virtual void closeEvent(QCloseEvent *event);
+
+private:
+       ScanResults *scanres;
+       Peers *peers;
+       bool networkMayHaveChanged;
+       char *ctrl_iface;
+       EventHistory *eh;
+       struct wpa_ctrl *ctrl_conn;
+       QSocketNotifier *msgNotifier;
+       QTimer *timer;
+       int pingsToStatusUpdate;
+       WpaMsgList msgs;
+       char *ctrl_iface_dir;
+       struct wpa_ctrl *monitor_conn;
+       UserDataRequest *udr;
+       QAction *disconnectAction;
+       QAction *reconnectAction;
+       QAction *eventAction;
+       QAction *scanAction;
+       QAction *statAction;
+       QAction *showAction;
+       QAction *hideAction;
+       QAction *quitAction;
+       QMenu *tray_menu;
+       QSystemTrayIcon *tray_icon;
+       QString wpaStateTranslate(char *state);
+       void createTrayIcon(bool);
+       bool ackTrayIcon;
+       bool startInTray;
+
+       int openCtrlConnection(const char *ifname);
+
+       bool wpsRunning;
+
+       QString bssFromScan;
+
+       void stopWpsRun(bool success);
+
+#ifdef CONFIG_NATIVE_WINDOWS
+       QAction *fileStartServiceAction;
+       QAction *fileStopServiceAction;
+
+       bool serviceRunning();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+       QAction *addInterfaceAction;
+       AddInterface *add_iface;
+
+       bool connectedToService;
+
+       QApplication *app;
+       bool inTray;
+};
+
+#endif /* WPAGUI_H */
diff --git a/wpa_supplicant/wpa_gui-qt4/wpagui.ui b/wpa_supplicant/wpa_gui-qt4/wpagui.ui
new file mode 100644 (file)
index 0000000..9f9039f
--- /dev/null
@@ -0,0 +1,524 @@
+<ui version="4.0" >
+ <class>WpaGui</class>
+ <widget class="QMainWindow" name="WpaGui" >
+  <property name="geometry" >
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>345</width>
+    <height>330</height>
+   </rect>
+  </property>
+  <property name="windowTitle" >
+   <string>wpa_gui</string>
+  </property>
+  <property name="windowIcon" >
+   <iconset resource="icons.qrc" >
+    <normaloff>:/icons/wpa_gui.svg</normaloff>:/icons/wpa_gui.svg</iconset>
+  </property>
+  <widget class="QWidget" name="widget" >
+   <layout class="QGridLayout" >
+    <item row="0" column="0" >
+     <widget class="QLabel" name="adapterLabel" >
+      <property name="text" >
+       <string>Adapter:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="0" column="1" >
+     <widget class="QComboBox" name="adapterSelect" />
+    </item>
+    <item row="1" column="0" >
+     <widget class="QLabel" name="networkLabel" >
+      <property name="text" >
+       <string>Network:</string>
+      </property>
+     </widget>
+    </item>
+    <item row="1" column="1" >
+     <widget class="QComboBox" name="networkSelect" />
+    </item>
+    <item row="2" column="0" colspan="2" >
+     <widget class="QTabWidget" name="wpaguiTab" >
+      <property name="currentIndex" >
+       <number>0</number>
+      </property>
+      <widget class="QWidget" name="statusTab" >
+       <attribute name="title" >
+        <string>Current Status</string>
+       </attribute>
+       <layout class="QGridLayout" >
+        <item row="0" column="0" colspan="5" >
+         <widget class="QFrame" name="frame3" >
+          <property name="frameShape" >
+           <enum>QFrame::NoFrame</enum>
+          </property>
+          <property name="frameShadow" >
+           <enum>QFrame::Plain</enum>
+          </property>
+          <layout class="QGridLayout" >
+           <item row="0" column="0" >
+            <widget class="QLabel" name="statusLabel" >
+             <property name="text" >
+              <string>Status:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="0" >
+            <widget class="QLabel" name="lastMessageLabel" >
+             <property name="text" >
+              <string>Last message:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="0" >
+            <widget class="QLabel" name="authenticationLabel" >
+             <property name="text" >
+              <string>Authentication:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="0" >
+            <widget class="QLabel" name="encryptionLabel" >
+             <property name="text" >
+              <string>Encryption:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="0" >
+            <widget class="QLabel" name="ssidLabel" >
+             <property name="text" >
+              <string>SSID:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="0" >
+            <widget class="QLabel" name="bssidLabel" >
+             <property name="text" >
+              <string>BSSID:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="0" >
+            <widget class="QLabel" name="ipAddressLabel" >
+             <property name="text" >
+              <string>IP address:</string>
+             </property>
+            </widget>
+           </item>
+           <item row="0" column="1" >
+            <widget class="QLabel" name="textStatus" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="1" column="1" colspan="3" >
+            <widget class="QLabel" name="textLastMessage" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="2" column="1" >
+            <widget class="QLabel" name="textAuthentication" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="3" column="1" >
+            <widget class="QLabel" name="textEncryption" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="4" column="1" >
+            <widget class="QLabel" name="textSsid" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="5" column="1" >
+            <widget class="QLabel" name="textBssid" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+           <item row="6" column="1" >
+            <widget class="QLabel" name="textIpAddress" >
+             <property name="text" >
+              <string/>
+             </property>
+            </widget>
+           </item>
+          </layout>
+         </widget>
+        </item>
+        <item row="1" column="0" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="1" column="1" >
+         <widget class="QPushButton" name="connectButton" >
+          <property name="text" >
+           <string>Connect</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2" >
+         <widget class="QPushButton" name="disconnectButton" >
+          <property name="text" >
+           <string>Disconnect</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="3" >
+         <widget class="QPushButton" name="scanButton" >
+          <property name="text" >
+           <string>Scan</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="4" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>40</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="networkconfigTab" >
+       <attribute name="title" >
+        <string>Manage Networks</string>
+       </attribute>
+       <layout class="QGridLayout" >
+        <item row="0" column="0" colspan="5" >
+         <widget class="QListWidget" name="networkList" >
+          <property name="selectionRectVisible" >
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item rowspan="2" row="1" column="0" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>61</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="1" column="1" >
+         <widget class="QRadioButton" name="enableRadioButton" >
+          <property name="text" >
+           <string>Enabled</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="2" >
+         <widget class="QPushButton" name="editNetworkButton" >
+          <property name="text" >
+           <string>Edit</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="3" >
+         <widget class="QPushButton" name="removeNetworkButton" >
+          <property name="text" >
+           <string>Remove</string>
+          </property>
+         </widget>
+        </item>
+        <item rowspan="2" row="1" column="4" >
+         <spacer>
+          <property name="orientation" >
+           <enum>Qt::Vertical</enum>
+          </property>
+          <property name="sizeHint" >
+           <size>
+            <width>20</width>
+            <height>61</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="2" column="1" >
+         <widget class="QRadioButton" name="disableRadioButton" >
+          <property name="text" >
+           <string>Disabled</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2" >
+         <widget class="QPushButton" name="addNetworkButton" >
+          <property name="text" >
+           <string>Add</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="3" >
+         <widget class="QPushButton" name="scanNetworkButton" >
+          <property name="text" >
+           <string>Scan</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+      <widget class="QWidget" name="wpsTab" >
+       <attribute name="title" >
+        <string>WPS</string>
+       </attribute>
+       <layout class="QGridLayout" name="wpsGridLayout" >
+        <item row="0" column="0" >
+         <widget class="QLabel" name="label_2" >
+          <property name="text" >
+           <string>Status:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1" colspan="3" >
+         <widget class="QLabel" name="wpsStatusText" >
+          <property name="text" >
+           <string/>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0" colspan="2" >
+         <widget class="QPushButton" name="wpsPbcButton" >
+          <property name="text" >
+           <string>PBC - push button</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0" colspan="2" >
+         <widget class="QPushButton" name="wpsPinButton" >
+          <property name="text" >
+           <string>Generate PIN</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2" >
+         <widget class="QLabel" name="label" >
+          <property name="text" >
+           <string>PIN:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="3" >
+         <widget class="QLineEdit" name="wpsPinEdit" >
+          <property name="enabled" >
+           <bool>false</bool>
+          </property>
+          <property name="readOnly" >
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="0" colspan="2" >
+         <widget class="QPushButton" name="wpsApPinButton" >
+          <property name="enabled" >
+           <bool>false</bool>
+          </property>
+          <property name="text" >
+           <string>Use AP PIN</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="2" >
+         <widget class="QLabel" name="label_3" >
+          <property name="text" >
+           <string>AP PIN:</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="3" >
+         <widget class="QLineEdit" name="wpsApPinEdit" >
+          <property name="enabled" >
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="0" colspan="4" >
+         <widget class="QTextEdit" name="wpsInstructions" >
+          <property name="readOnly" >
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="MenuBar" >
+   <property name="geometry" >
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>345</width>
+     <height>24</height>
+    </rect>
+   </property>
+   <widget class="QMenu" name="fileMenu" >
+    <property name="title" >
+     <string>&amp;File</string>
+    </property>
+    <addaction name="fileEventHistoryAction" />
+    <addaction name="fileSaveConfigAction" />
+    <addaction name="actionWPS" />
+    <addaction name="actionPeers" />
+    <addaction name="separator" />
+    <addaction name="fileExitAction" />
+   </widget>
+   <widget class="QMenu" name="networkMenu" >
+    <property name="title" >
+     <string>&amp;Network</string>
+    </property>
+    <addaction name="networkAddAction" />
+    <addaction name="networkEditAction" />
+    <addaction name="networkRemoveAction" />
+    <addaction name="separator" />
+    <addaction name="networkEnableAllAction" />
+    <addaction name="networkDisableAllAction" />
+    <addaction name="networkRemoveAllAction" />
+   </widget>
+   <widget class="QMenu" name="helpMenu" >
+    <property name="title" >
+     <string>&amp;Help</string>
+    </property>
+    <addaction name="helpContentsAction" />
+    <addaction name="helpIndexAction" />
+    <addaction name="separator" />
+    <addaction name="helpAboutAction" />
+   </widget>
+   <addaction name="fileMenu" />
+   <addaction name="networkMenu" />
+   <addaction name="helpMenu" />
+  </widget>
+  <action name="fileEventHistoryAction" >
+   <property name="text" >
+    <string>Event &amp;History</string>
+   </property>
+  </action>
+  <action name="fileSaveConfigAction" >
+   <property name="text" >
+    <string>&amp;Save Configuration</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+S</string>
+   </property>
+  </action>
+  <action name="fileExitAction" >
+   <property name="text" >
+    <string>E&amp;xit</string>
+   </property>
+   <property name="shortcut" >
+    <string>Ctrl+Q</string>
+   </property>
+  </action>
+  <action name="networkAddAction" >
+   <property name="text" >
+    <string>&amp;Add</string>
+   </property>
+  </action>
+  <action name="networkEditAction" >
+   <property name="text" >
+    <string>&amp;Edit</string>
+   </property>
+  </action>
+  <action name="networkRemoveAction" >
+   <property name="text" >
+    <string>&amp;Remove</string>
+   </property>
+  </action>
+  <action name="networkEnableAllAction" >
+   <property name="text" >
+    <string>E&amp;nable All</string>
+   </property>
+  </action>
+  <action name="networkDisableAllAction" >
+   <property name="text" >
+    <string>&amp;Disable All</string>
+   </property>
+  </action>
+  <action name="networkRemoveAllAction" >
+   <property name="text" >
+    <string>Re&amp;move All</string>
+   </property>
+  </action>
+  <action name="helpContentsAction" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>&amp;Contents...</string>
+   </property>
+  </action>
+  <action name="helpIndexAction" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>&amp;Index...</string>
+   </property>
+  </action>
+  <action name="helpAboutAction" >
+   <property name="text" >
+    <string>&amp;About</string>
+   </property>
+  </action>
+  <action name="actionWPS" >
+   <property name="enabled" >
+    <bool>false</bool>
+   </property>
+   <property name="text" >
+    <string>&amp;Wi-Fi Protected Setup</string>
+   </property>
+  </action>
+  <action name="actionPeers" >
+   <property name="text" >
+    <string>&amp;Peers</string>
+   </property>
+  </action>
+ </widget>
+ <layoutdefault spacing="6" margin="11" />
+ <pixmapfunction></pixmapfunction>
+ <includes>
+  <include location="global" >qtimer.h</include>
+  <include location="global" >qsocketnotifier.h</include>
+  <include location="local" >wpamsg.h</include>
+  <include location="local" >eventhistory.h</include>
+  <include location="local" >scanresults.h</include>
+  <include location="local" >peers.h</include>
+ </includes>
+ <resources>
+  <include location="icons.qrc" />
+ </resources>
+ <connections/>
+</ui>
diff --git a/wpa_supplicant/wpa_gui-qt4/wpamsg.h b/wpa_supplicant/wpa_gui-qt4/wpamsg.h
new file mode 100644 (file)
index 0000000..4950b21
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * wpa_gui - WpaMsg class for storing event messages
+ * Copyright (c) 2005-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 WPAMSG_H
+#define WPAMSG_H
+
+#include <QDateTime>
+#include <QLinkedList>
+
+class WpaMsg {
+public:
+       WpaMsg(const QString &_msg, int _priority = 2)
+               : msg(_msg), priority(_priority)
+       {
+               timestamp = QDateTime::currentDateTime();
+       }
+
+       QString getMsg() const { return msg; }
+       int getPriority() const { return priority; }
+       QDateTime getTimestamp() const { return timestamp; }
+
+private:
+       QString msg;
+       int priority;
+       QDateTime timestamp;
+};
+
+typedef QLinkedList<WpaMsg> WpaMsgList;
+
+#endif /* WPAMSG_H */
diff --git a/wpa_supplicant/wpa_gui/.gitignore b/wpa_supplicant/wpa_gui/.gitignore
new file mode 100644 (file)
index 0000000..11963c8
--- /dev/null
@@ -0,0 +1,5 @@
+.moc
+.obj
+.ui
+Makefile
+wpa_gui
diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui b/wpa_supplicant/wpa_gui/eventhistory.ui
new file mode 100644 (file)
index 0000000..3735fb7
--- /dev/null
@@ -0,0 +1,125 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>EventHistory</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>EventHistory</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>533</width>
+            <height>285</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Event history</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QListView">
+            <column>
+                <property name="text">
+                    <string>Timestamp</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>Message</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <property name="name">
+                <cstring>eventListView</cstring>
+            </property>
+            <property name="sizePolicy">
+                <sizepolicy>
+                    <hsizetype>7</hsizetype>
+                    <vsizetype>7</vsizetype>
+                    <horstretch>0</horstretch>
+                    <verstretch>0</verstretch>
+                </sizepolicy>
+            </property>
+            <property name="resizePolicy">
+                <enum>Manual</enum>
+            </property>
+            <property name="selectionMode">
+                <enum>NoSelection</enum>
+            </property>
+            <property name="resizeMode">
+                <enum>LastColumn</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout30</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer3</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>closeButton</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>closeButton</sender>
+        <signal>clicked()</signal>
+        <receiver>EventHistory</receiver>
+        <slot>close()</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="local" impldecl="in declaration">wpamsg.h</include>
+    <include location="local" impldecl="in implementation">eventhistory.ui.h</include>
+</includes>
+<slots>
+    <slot>addEvents( WpaMsgList msgs )</slot>
+    <slot>addEvent( WpaMsg msg )</slot>
+</slots>
+<functions>
+    <function access="private" specifier="non virtual">init()</function>
+    <function access="private" specifier="non virtual">destroy()</function>
+</functions>
+<pixmapinproject/>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wpa_supplicant/wpa_gui/eventhistory.ui.h b/wpa_supplicant/wpa_gui/eventhistory.ui.h
new file mode 100644 (file)
index 0000000..cb2caab
--- /dev/null
@@ -0,0 +1,41 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you want to add, delete, or rename functions or slots, use
+** Qt Designer to update this file, preserving your code.
+**
+** You should not define a constructor or destructor in this file.
+** Instead, write your code in functions called init() and destroy().
+** These will automatically be called by the form's constructor and
+** destructor.
+*****************************************************************************/
+
+void EventHistory::init()
+{
+}
+
+
+void EventHistory::destroy()
+{
+}
+
+
+void EventHistory::addEvents(WpaMsgList msgs)
+{
+    WpaMsgList::iterator it;
+    for (it = msgs.begin(); it != msgs.end(); it++) {
+       addEvent(*it);
+    }
+}
+
+
+void EventHistory::addEvent(WpaMsg msg)
+{
+    Q3ListViewItem *item;
+    item = new Q3ListViewItem(eventListView,
+                            msg.getTimestamp().toString("yyyy-MM-dd hh:mm:ss.zzz"),
+                            msg.getMsg());
+    if (item == NULL)
+       return;
+    eventListView->setSelected(item, false);
+}
diff --git a/wpa_supplicant/wpa_gui/main.cpp b/wpa_supplicant/wpa_gui/main.cpp
new file mode 100644 (file)
index 0000000..a78473a
--- /dev/null
@@ -0,0 +1,30 @@
+#ifdef CONFIG_NATIVE_WINDOWS
+#include <winsock.h>
+#endif /* CONFIG_NATIVE_WINDOWS */
+#include <qapplication.h>
+#include "wpagui.h"
+
+int main( int argc, char ** argv )
+{
+    QApplication a( argc, argv );
+    WpaGui w;
+    int ret;
+
+#ifdef CONFIG_NATIVE_WINDOWS
+    WSADATA wsaData;
+    if (WSAStartup(MAKEWORD(2, 0), &wsaData)) {
+       printf("Could not find a usable WinSock.dll\n");
+       return -1;
+    }
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+    w.show();
+    a.connect( &a, SIGNAL( lastWindowClosed() ), &a, SLOT( quit() ) );
+    ret = a.exec();
+
+#ifdef CONFIG_NATIVE_WINDOWS
+    WSACleanup();
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+    return ret;
+}
diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui b/wpa_supplicant/wpa_gui/networkconfig.ui
new file mode 100644 (file)
index 0000000..019ecf7
--- /dev/null
@@ -0,0 +1,475 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>NetworkConfig</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>NetworkConfig</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>380</width>
+            <height>430</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>NetworkConfig</string>
+    </property>
+    <grid>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QPushButton" row="1" column="3">
+            <property name="name">
+                <cstring>cancelButton</cstring>
+            </property>
+            <property name="text">
+                <string>Cancel</string>
+            </property>
+        </widget>
+        <widget class="QFrame" row="0" column="0" rowspan="1" colspan="4">
+            <property name="name">
+                <cstring>frame9</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>StyledPanel</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Raised</enum>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>textLabel0</cstring>
+                    </property>
+                    <property name="text">
+                        <string>SSID</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="0" column="1">
+                    <property name="name">
+                        <cstring>ssidEdit</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Network name (Service Set IDentifier)</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Network ID</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="1" column="1">
+                    <property name="name">
+                        <cstring>idstrEdit</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Network Identification String</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>textLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Authentication</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox" row="2" column="1">
+                    <item>
+                        <property name="text">
+                            <string>Plaintext or static WEP</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>IEEE 802.1X</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>WPA-Personal (PSK)</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>WPA-Enterprise (EAP)</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>WPA2-Personal (PSK)</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>WPA2-Enterprise (EAP)</string>
+                        </property>
+                    </item>
+                    <property name="name">
+                        <cstring>authSelect</cstring>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>textLabel3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Encryption</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox" row="3" column="1">
+                    <item>
+                        <property name="text">
+                            <string>None</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>WEP</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>TKIP</string>
+                        </property>
+                    </item>
+                    <item>
+                        <property name="text">
+                            <string>CCMP</string>
+                        </property>
+                    </item>
+                    <property name="name">
+                        <cstring>encrSelect</cstring>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="4" column="0">
+                    <property name="name">
+                        <cstring>textLabel4</cstring>
+                    </property>
+                    <property name="text">
+                        <string>PSK</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="4" column="1">
+                    <property name="name">
+                        <cstring>pskEdit</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>WPA/WPA2 pre-shared key or passphrase</string>
+                    </property>
+                    <property name="whatsThis" stdset="0">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="5" column="0">
+                    <property name="name">
+                        <cstring>textLabel5</cstring>
+                    </property>
+                    <property name="text">
+                        <string>EAP method</string>
+                    </property>
+                </widget>
+                <widget class="QComboBox" row="5" column="1">
+                    <property name="name">
+                        <cstring>eapSelect</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="6" column="0">
+                    <property name="name">
+                        <cstring>textLabel6</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Identity</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="6" column="1">
+                    <property name="name">
+                        <cstring>identityEdit</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Username/Identity for EAP methods</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="7" column="0">
+                    <property name="name">
+                        <cstring>textLabel7</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Password</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="7" column="1">
+                    <property name="name">
+                        <cstring>passwordEdit</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                    <property name="toolTip" stdset="0">
+                        <string>Password for EAP methods</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="8" column="0">
+                    <property name="name">
+                        <cstring>textLabel1_2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>CA certificate</string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit" row="8" column="1">
+                    <property name="name">
+                        <cstring>cacertEdit</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>false</bool>
+                    </property>
+                </widget>
+                <widget class="QButtonGroup" row="9" column="0" rowspan="1" colspan="2">
+                    <property name="name">
+                        <cstring>buttonGroup1</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>true</bool>
+                    </property>
+                    <property name="title">
+                        <string>WEP keys</string>
+                    </property>
+                    <grid>
+                        <property name="name">
+                            <cstring>unnamed</cstring>
+                        </property>
+                        <widget class="QRadioButton" row="0" column="0">
+                            <property name="name">
+                                <cstring>wep0Radio</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                            <property name="text">
+                                <string>key 0</string>
+                            </property>
+                        </widget>
+                        <widget class="QRadioButton" row="1" column="0">
+                            <property name="name">
+                                <cstring>wep1Radio</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                            <property name="text">
+                                <string>key 1</string>
+                            </property>
+                        </widget>
+                        <widget class="QRadioButton" row="3" column="0">
+                            <property name="name">
+                                <cstring>wep3Radio</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                            <property name="text">
+                                <string>key 3</string>
+                            </property>
+                        </widget>
+                        <widget class="QRadioButton" row="2" column="0">
+                            <property name="name">
+                                <cstring>wep2Radio</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                            <property name="text">
+                                <string>key 2</string>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit" row="0" column="1">
+                            <property name="name">
+                                <cstring>wep0Edit</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit" row="1" column="1">
+                            <property name="name">
+                                <cstring>wep1Edit</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit" row="2" column="1">
+                            <property name="name">
+                                <cstring>wep2Edit</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                        </widget>
+                        <widget class="QLineEdit" row="3" column="1">
+                            <property name="name">
+                                <cstring>wep3Edit</cstring>
+                            </property>
+                            <property name="enabled">
+                                <bool>false</bool>
+                            </property>
+                        </widget>
+                    </grid>
+                </widget>
+            </grid>
+        </widget>
+        <spacer row="1" column="0">
+            <property name="name">
+                <cstring>spacer5</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>130</width>
+                    <height>20</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="QPushButton" row="1" column="1">
+            <property name="name">
+                <cstring>addButton</cstring>
+            </property>
+            <property name="text">
+                <string>Add</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="1" column="2">
+            <property name="name">
+                <cstring>removeButton</cstring>
+            </property>
+            <property name="enabled">
+                <bool>false</bool>
+            </property>
+            <property name="text">
+                <string>Remove</string>
+            </property>
+        </widget>
+    </grid>
+</widget>
+<connections>
+    <connection>
+        <sender>authSelect</sender>
+        <signal>activated(int)</signal>
+        <receiver>NetworkConfig</receiver>
+        <slot>authChanged(int)</slot>
+    </connection>
+    <connection>
+        <sender>cancelButton</sender>
+        <signal>clicked()</signal>
+        <receiver>NetworkConfig</receiver>
+        <slot>close()</slot>
+    </connection>
+    <connection>
+        <sender>addButton</sender>
+        <signal>clicked()</signal>
+        <receiver>NetworkConfig</receiver>
+        <slot>addNetwork()</slot>
+    </connection>
+    <connection>
+        <sender>encrSelect</sender>
+        <signal>activated(const QString&amp;)</signal>
+        <receiver>NetworkConfig</receiver>
+        <slot>encrChanged(const QString&amp;)</slot>
+    </connection>
+    <connection>
+        <sender>removeButton</sender>
+        <signal>clicked()</signal>
+        <receiver>NetworkConfig</receiver>
+        <slot>removeNetwork()</slot>
+    </connection>
+</connections>
+<tabstops>
+    <tabstop>ssidEdit</tabstop>
+    <tabstop>idstrEdit</tabstop>
+    <tabstop>authSelect</tabstop>
+    <tabstop>encrSelect</tabstop>
+    <tabstop>pskEdit</tabstop>
+    <tabstop>eapSelect</tabstop>
+    <tabstop>identityEdit</tabstop>
+    <tabstop>passwordEdit</tabstop>
+    <tabstop>cacertEdit</tabstop>
+    <tabstop>wep0Radio</tabstop>
+    <tabstop>wep1Radio</tabstop>
+    <tabstop>wep2Radio</tabstop>
+    <tabstop>wep3Radio</tabstop>
+    <tabstop>wep0Edit</tabstop>
+    <tabstop>wep1Edit</tabstop>
+    <tabstop>wep2Edit</tabstop>
+    <tabstop>wep3Edit</tabstop>
+    <tabstop>addButton</tabstop>
+    <tabstop>removeButton</tabstop>
+    <tabstop>cancelButton</tabstop>
+</tabstops>
+<includes>
+    <include location="global" impldecl="in declaration">qlistview.h</include>
+    <include location="global" impldecl="in implementation">qmessagebox.h</include>
+    <include location="local" impldecl="in implementation">wpagui.h</include>
+    <include location="local" impldecl="in implementation">networkconfig.ui.h</include>
+</includes>
+<forwards>
+    <forward>class WpaGui;</forward>
+</forwards>
+<variables>
+    <variable access="private">WpaGui *wpagui;</variable>
+    <variable access="private">int edit_network_id;</variable>
+    <variable access="private">bool new_network;</variable>
+</variables>
+<slots>
+    <slot>authChanged( int sel )</slot>
+    <slot>addNetwork()</slot>
+    <slot>encrChanged( const QString &amp; sel )</slot>
+    <slot>writeWepKey( int network_id, QLineEdit * edit, int id )</slot>
+    <slot>removeNetwork()</slot>
+</slots>
+<functions>
+    <function access="private" specifier="non virtual">init()</function>
+    <function>paramsFromScanResults( QListViewItem * sel )</function>
+    <function>setWpaGui( WpaGui * _wpagui )</function>
+    <function returnType="int">setNetworkParam( int id, const char * field, const char * value, bool quote )</function>
+    <function access="private">wepEnabled( bool enabled )</function>
+    <function>paramsFromConfig( int network_id )</function>
+    <function>newNetwork()</function>
+    <function access="private">getEapCapa()</function>
+</functions>
+<pixmapinproject/>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wpa_supplicant/wpa_gui/networkconfig.ui.h b/wpa_supplicant/wpa_gui/networkconfig.ui.h
new file mode 100644 (file)
index 0000000..501d5d2
--- /dev/null
@@ -0,0 +1,552 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you want to add, delete, or rename functions or slots, use
+** Qt Designer to update this file, preserving your code.
+**
+** You should not define a constructor or destructor in this file.
+** Instead, write your code in functions called init() and destroy().
+** These will automatically be called by the form's constructor and
+** destructor.
+*****************************************************************************/
+
+#include <stdlib.h>
+
+enum {
+    AUTH_NONE = 0,
+    AUTH_IEEE8021X = 1,
+    AUTH_WPA_PSK = 2,
+    AUTH_WPA_EAP = 3,
+    AUTH_WPA2_PSK = 4,
+    AUTH_WPA2_EAP = 5
+};
+
+#define WPA_GUI_KEY_DATA "[key is configured]"
+
+void NetworkConfig::init()
+{
+    wpagui = NULL;
+    new_network = false;
+}
+
+void NetworkConfig::paramsFromScanResults(Q3ListViewItem *sel)
+{
+    new_network = true;
+
+    /* SSID BSSID frequency signal flags */
+    setCaption(sel->text(0));
+    ssidEdit->setText(sel->text(0));
+    
+    QString flags = sel->text(4);
+    int auth, encr = 0;
+    if (flags.find("[WPA2-EAP") >= 0)
+       auth = AUTH_WPA2_EAP;
+    else if (flags.find("[WPA-EAP") >= 0)
+       auth = AUTH_WPA_EAP;
+    else if (flags.find("[WPA2-PSK") >= 0)
+       auth = AUTH_WPA2_PSK;
+    else if (flags.find("[WPA-PSK") >= 0)
+       auth = AUTH_WPA_PSK;
+    else
+       auth = AUTH_NONE;
+    
+    if (flags.find("-CCMP") >= 0)
+       encr = 1;
+    else if (flags.find("-TKIP") >= 0)
+       encr = 0;
+    else if (flags.find("WEP") >= 0)
+       encr = 1;
+    else
+       encr = 0;
+    authSelect->setCurrentItem(auth);
+    authChanged(auth);
+    encrSelect->setCurrentItem(encr);
+
+    getEapCapa();
+}
+
+
+void NetworkConfig::authChanged(int sel)
+{
+    pskEdit->setEnabled(sel == AUTH_WPA_PSK || sel == AUTH_WPA2_PSK);
+    bool eap = sel == AUTH_IEEE8021X || sel == AUTH_WPA_EAP ||
+              sel == AUTH_WPA2_EAP;
+    eapSelect->setEnabled(eap);
+    identityEdit->setEnabled(eap);
+    passwordEdit->setEnabled(eap);
+    cacertEdit->setEnabled(eap);
+   
+    while (encrSelect->count())
+       encrSelect->removeItem(0);
+    
+    if (sel == AUTH_NONE || sel == AUTH_IEEE8021X) {
+       encrSelect->insertItem("None");
+       encrSelect->insertItem("WEP");
+       encrSelect->setCurrentItem(sel == AUTH_NONE ? 0 : 1);
+    } else {
+       encrSelect->insertItem("TKIP");
+       encrSelect->insertItem("CCMP");
+       encrSelect->setCurrentItem((sel == AUTH_WPA2_PSK ||
+                                   sel == AUTH_WPA2_EAP) ? 1 : 0);
+    }
+    
+    wepEnabled(sel == AUTH_IEEE8021X);
+}
+
+
+void NetworkConfig::addNetwork()
+{
+    char reply[10], cmd[256];
+    size_t reply_len;
+    int id;
+    int psklen = pskEdit->text().length();
+    int auth = authSelect->currentItem();
+
+    if (auth == AUTH_WPA_PSK || auth == AUTH_WPA2_PSK) {
+       if (psklen < 8 || psklen > 64) {
+           QMessageBox::warning(this, "wpa_gui", "WPA-PSK requires a passphrase "
+                                "of 8 to 63 characters\n"
+                                "or 64 hex digit PSK");
+           return;
+       }
+    }
+        
+    if (wpagui == NULL)
+       return;
+    
+    memset(reply, 0, sizeof(reply));
+    reply_len = sizeof(reply) - 1;
+    
+    if (new_network) {
+       wpagui->ctrlRequest("ADD_NETWORK", reply, &reply_len);
+       if (reply[0] == 'F') {
+           QMessageBox::warning(this, "wpa_gui", "Failed to add network to wpa_supplicant\n"
+                                "configuration.");
+           return;
+       }
+       id = atoi(reply);
+    } else {
+       id = edit_network_id;
+    }
+
+    setNetworkParam(id, "ssid", ssidEdit->text().ascii(), true);
+    
+    if (idstrEdit->isEnabled())
+       setNetworkParam(id, "id_str", idstrEdit->text().ascii(), true);
+
+    const char *key_mgmt = NULL, *proto = NULL, *pairwise = NULL;
+    switch (auth) {
+    case AUTH_NONE:
+       key_mgmt = "NONE";
+       break;
+    case AUTH_IEEE8021X:
+       key_mgmt = "IEEE8021X";
+       break;
+    case AUTH_WPA_PSK:
+       key_mgmt = "WPA-PSK";
+       proto = "WPA";
+       break;
+    case AUTH_WPA_EAP:
+       key_mgmt = "WPA-EAP";
+       proto = "WPA";
+       break;
+    case AUTH_WPA2_PSK:
+       key_mgmt = "WPA-PSK";
+       proto = "WPA2";
+       break;
+    case AUTH_WPA2_EAP:
+       key_mgmt = "WPA-EAP";
+       proto = "WPA2";
+       break;
+    }
+    
+    if (auth == AUTH_WPA_PSK || auth == AUTH_WPA_EAP ||
+       auth == AUTH_WPA2_PSK || auth == AUTH_WPA2_EAP) {
+       int encr = encrSelect->currentItem();
+       if (encr == 0)
+           pairwise = "TKIP";
+       else
+           pairwise = "CCMP";
+    }
+    
+    if (proto)
+       setNetworkParam(id, "proto", proto, false);
+    if (key_mgmt)
+       setNetworkParam(id, "key_mgmt", key_mgmt, false);
+    if (pairwise) {
+       setNetworkParam(id, "pairwise", pairwise, false);
+       setNetworkParam(id, "group", "TKIP CCMP WEP104 WEP40", false);
+    }
+    if (pskEdit->isEnabled() &&
+       strcmp(pskEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0)
+       setNetworkParam(id, "psk", pskEdit->text().ascii(), psklen != 64);
+    if (eapSelect->isEnabled())
+       setNetworkParam(id, "eap", eapSelect->currentText().ascii(), false);
+    if (identityEdit->isEnabled())
+       setNetworkParam(id, "identity", identityEdit->text().ascii(), true);
+    if (passwordEdit->isEnabled() &&
+       strcmp(passwordEdit->text().ascii(), WPA_GUI_KEY_DATA) != 0)
+       setNetworkParam(id, "password", passwordEdit->text().ascii(), true);
+    if (cacertEdit->isEnabled())
+       setNetworkParam(id, "ca_cert", cacertEdit->text().ascii(), true);
+    writeWepKey(id, wep0Edit, 0);
+    writeWepKey(id, wep1Edit, 1);
+    writeWepKey(id, wep2Edit, 2);
+    writeWepKey(id, wep3Edit, 3);
+  
+    if (wep0Radio->isEnabled() && wep0Radio->isChecked())
+       setNetworkParam(id, "wep_tx_keyidx", "0", false);
+    else if (wep1Radio->isEnabled() && wep1Radio->isChecked())
+       setNetworkParam(id, "wep_tx_keyidx", "1", false);
+    else if (wep2Radio->isEnabled() && wep2Radio->isChecked())
+       setNetworkParam(id, "wep_tx_keyidx", "2", false);
+    else if (wep3Radio->isEnabled() && wep3Radio->isChecked())
+       setNetworkParam(id, "wep_tx_keyidx", "3", false);
+
+    snprintf(cmd, sizeof(cmd), "ENABLE_NETWORK %d", id);
+    reply_len = sizeof(reply);
+    wpagui->ctrlRequest(cmd, reply, &reply_len);
+    if (strncmp(reply, "OK", 2) != 0) {
+       QMessageBox::warning(this, "wpa_gui", "Failed to enable network in wpa_supplicant\n"
+                            "configuration.");
+       /* Network was added, so continue anyway */
+    }
+    wpagui->triggerUpdate();
+    wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len);
+
+    close();
+}
+
+
+void NetworkConfig::setWpaGui( WpaGui *_wpagui )
+{
+    wpagui = _wpagui;
+}
+
+
+int NetworkConfig::setNetworkParam(int id, const char *field, const char *value, bool quote)
+{
+    char reply[10], cmd[256];
+    size_t reply_len;
+    snprintf(cmd, sizeof(cmd), "SET_NETWORK %d %s %s%s%s",
+            id, field, quote ? "\"" : "", value, quote ? "\"" : "");
+    reply_len = sizeof(reply);
+    wpagui->ctrlRequest(cmd, reply, &reply_len);
+    return strncmp(reply, "OK", 2) == 0 ? 0 : -1;
+}
+
+
+void NetworkConfig::encrChanged( const QString &sel )
+{
+    wepEnabled(sel.find("WEP") == 0);
+}
+
+
+void NetworkConfig::wepEnabled( bool enabled )
+{
+    wep0Edit->setEnabled(enabled);
+    wep1Edit->setEnabled(enabled);
+    wep2Edit->setEnabled(enabled);
+    wep3Edit->setEnabled(enabled);
+    wep0Radio->setEnabled(enabled);
+    wep1Radio->setEnabled(enabled);
+    wep2Radio->setEnabled(enabled);
+    wep3Radio->setEnabled(enabled);
+}
+
+
+void NetworkConfig::writeWepKey( int network_id, QLineEdit *edit, int id )
+{
+    char buf[10];
+    bool hex;
+    const char *txt, *pos;
+    size_t len;
+  
+    if (!edit->isEnabled() || edit->text().isEmpty())
+       return;
+    
+    /*
+        * Assume hex key if only hex characters are present and length matches
+       * with 40, 104, or 128-bit key
+       */
+    txt = edit->text().ascii();
+    if (strcmp(txt, WPA_GUI_KEY_DATA) == 0)
+       return;
+    len = strlen(txt);
+    if (len == 0)
+       return;
+    pos = txt;
+    hex = true;
+    while (*pos) {
+       if (!((*pos >= '0' && *pos <= '9') || (*pos >= 'a' && *pos <= 'f') ||
+             (*pos >= 'A' && *pos <= 'F'))) {
+           hex = false;
+           break;
+       }
+       pos++;
+    }
+    if (hex && len != 10 && len != 26 && len != 32)
+       hex = false;
+    snprintf(buf, sizeof(buf), "wep_key%d", id);
+    setNetworkParam(network_id, buf, txt, !hex);
+}
+
+
+static int key_value_isset(const char *reply, size_t reply_len)
+{
+    return reply_len > 0 && (reply_len < 4 || memcmp(reply, "FAIL", 4) != 0);
+}
+
+
+void NetworkConfig::paramsFromConfig( int network_id )
+{
+    int i, res;
+
+    edit_network_id = network_id;
+    getEapCapa();
+    
+    char reply[1024], cmd[256], *pos;
+    size_t reply_len;
+    
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ssid", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 &&
+       reply[0] == '"') {
+       reply[reply_len] = '\0';
+       pos = strchr(reply + 1, '"');
+       if (pos)
+           *pos = '\0';
+       ssidEdit->setText(reply + 1);
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d id_str", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 &&
+       reply[0] == '"') {
+       reply[reply_len] = '\0';
+       pos = strchr(reply + 1, '"');
+       if (pos)
+           *pos = '\0';
+       idstrEdit->setText(reply + 1);
+    }
+    
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d proto", network_id);
+    reply_len = sizeof(reply) - 1;
+    int wpa = 0;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+       reply[reply_len] = '\0';
+       if (strstr(reply, "RSN") || strstr(reply, "WPA2"))
+           wpa = 2;
+       else if (strstr(reply, "WPA"))
+           wpa = 1;
+    }
+
+    int auth = AUTH_NONE, encr = 0;
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d key_mgmt", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+       reply[reply_len] = '\0';
+       if (strstr(reply, "WPA-EAP"))
+           auth = wpa & 2 ? AUTH_WPA2_EAP : AUTH_WPA_EAP;
+       else if (strstr(reply, "WPA-PSK"))
+           auth = wpa & 2 ? AUTH_WPA2_PSK : AUTH_WPA_PSK;
+       else if (strstr(reply, "IEEE8021X")) {
+           auth = AUTH_IEEE8021X;
+           encr = 1;
+       }
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d pairwise", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0) {
+       reply[reply_len] = '\0';
+       if (strstr(reply, "CCMP") && auth != AUTH_NONE)
+           encr = 1;
+       else if (strstr(reply, "TKIP"))
+           encr = 0;
+       else if (strstr(reply, "WEP"))
+           encr = 1;
+       else
+           encr = 0;
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d psk", network_id);
+    reply_len = sizeof(reply) - 1;
+    res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+    if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+       reply[reply_len] = '\0';
+       pos = strchr(reply + 1, '"');
+       if (pos)
+           *pos = '\0';
+       pskEdit->setText(reply + 1);
+    } else if (res >= 0 && key_value_isset(reply, reply_len)) {
+       pskEdit->setText(WPA_GUI_KEY_DATA);
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d identity", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 &&
+       reply[0] == '"') {
+       reply[reply_len] = '\0';
+       pos = strchr(reply + 1, '"');
+       if (pos)
+           *pos = '\0';
+       identityEdit->setText(reply + 1);
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d password", network_id);
+    reply_len = sizeof(reply) - 1;
+    res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+    if (res >= 0 && reply_len >= 2 &&
+       reply[0] == '"') {
+       reply[reply_len] = '\0';
+       pos = strchr(reply + 1, '"');
+       if (pos)
+           *pos = '\0';
+       passwordEdit->setText(reply + 1);
+    } else if (res >= 0 && key_value_isset(reply, reply_len)) {
+       passwordEdit->setText(WPA_GUI_KEY_DATA);
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d ca_cert", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 2 &&
+       reply[0] == '"') {
+       reply[reply_len] = '\0';
+       pos = strchr(reply + 1, '"');
+       if (pos)
+           *pos = '\0';
+       cacertEdit->setText(reply + 1);
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d eap", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) {
+       reply[reply_len] = '\0';
+       for (i = 0; i < eapSelect->count(); i++) {
+           if (eapSelect->text(i).compare(reply) == 0) {
+               eapSelect->setCurrentItem(i);
+               break;
+           }
+       }
+    }
+
+    for (i = 0; i < 4; i++) {
+       QLineEdit *wepEdit;
+       switch (i) {
+       default:
+       case 0:
+           wepEdit = wep0Edit;
+           break;
+       case 1:
+           wepEdit = wep1Edit;
+           break;
+       case 2:
+           wepEdit = wep2Edit;
+           break;
+       case 3:
+           wepEdit = wep3Edit;
+           break;
+       }
+       snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_key%d", network_id, i);
+       reply_len = sizeof(reply) - 1;
+       res = wpagui->ctrlRequest(cmd, reply, &reply_len);
+       if (res >= 0 && reply_len >= 2 && reply[0] == '"') {
+           reply[reply_len] = '\0';
+           pos = strchr(reply + 1, '"');
+           if (pos)
+               *pos = '\0';
+           if (auth == AUTH_NONE || auth == AUTH_IEEE8021X)
+               encr = 1;
+
+           wepEdit->setText(reply + 1);
+       } else if (res >= 0 && key_value_isset(reply, reply_len)) {
+           if (auth == AUTH_NONE || auth == AUTH_IEEE8021X)
+               encr = 1;
+           wepEdit->setText(WPA_GUI_KEY_DATA);
+       }
+    }
+
+    snprintf(cmd, sizeof(cmd), "GET_NETWORK %d wep_tx_keyidx", network_id);
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest(cmd, reply, &reply_len) >= 0 && reply_len >= 1) {
+       reply[reply_len] = '\0';
+       switch (atoi(reply)) {
+       case 0:
+           wep0Radio->setChecked(true);
+           break;
+       case 1:
+           wep1Radio->setChecked(true);
+           break;
+       case 2:
+           wep2Radio->setChecked(true);
+           break;
+       case 3:
+           wep3Radio->setChecked(true);
+           break;
+       }
+    }
+
+    authSelect->setCurrentItem(auth);
+    authChanged(auth);
+    encrSelect->setCurrentItem(encr);
+    if (auth == AUTH_NONE || auth == AUTH_IEEE8021X)
+       wepEnabled(encr == 1);
+
+    removeButton->setEnabled(true);
+    addButton->setText("Save");
+}
+
+
+void NetworkConfig::removeNetwork()
+{
+    char reply[10], cmd[256];
+    size_t reply_len;
+    
+    if (QMessageBox::information(this, "wpa_gui",
+                                "This will permanently remove the network\n"
+                                "from the configuration. Do you really want\n"
+                                "to remove this network?", "Yes", "No") != 0)
+       return;
+    
+    snprintf(cmd, sizeof(cmd), "REMOVE_NETWORK %d", edit_network_id);
+    reply_len = sizeof(reply);
+    wpagui->ctrlRequest(cmd, reply, &reply_len);
+    if (strncmp(reply, "OK", 2) != 0) {
+       QMessageBox::warning(this, "wpa_gui",
+                            "Failed to remove network from wpa_supplicant\n"
+                            "configuration.");
+    } else {
+       wpagui->triggerUpdate();
+       wpagui->ctrlRequest("SAVE_CONFIG", reply, &reply_len);
+    }
+
+    close();
+}
+
+
+void NetworkConfig::newNetwork()
+{
+    new_network = true;
+    getEapCapa();
+}
+
+
+void NetworkConfig::getEapCapa()
+{
+    char reply[256];
+    size_t reply_len;
+    
+    if (wpagui == NULL)
+       return;
+
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest("GET_CAPABILITY eap", reply, &reply_len) < 0)
+       return;
+    reply[reply_len] = '\0';
+    
+    QString res(reply);
+    QStringList types = QStringList::split(QChar(' '), res);
+    eapSelect->insertStringList(types);
+}
diff --git a/wpa_supplicant/wpa_gui/scanresults.ui b/wpa_supplicant/wpa_gui/scanresults.ui
new file mode 100644 (file)
index 0000000..dea305b
--- /dev/null
@@ -0,0 +1,179 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ScanResults</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>ScanResults</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>452</width>
+            <height>225</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Scan results</string>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QListView">
+            <column>
+                <property name="text">
+                    <string>SSID</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>BSSID</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>frequency</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>signal</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <column>
+                <property name="text">
+                    <string>flags</string>
+                </property>
+                <property name="clickable">
+                    <bool>true</bool>
+                </property>
+                <property name="resizable">
+                    <bool>true</bool>
+                </property>
+            </column>
+            <property name="name">
+                <cstring>scanResultsView</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>StyledPanel</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Sunken</enum>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout24</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer6</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>50</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>scanButton</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Scan</string>
+                    </property>
+                </widget>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>closeButton</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Close</string>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>closeButton</sender>
+        <signal>clicked()</signal>
+        <receiver>ScanResults</receiver>
+        <slot>close()</slot>
+    </connection>
+    <connection>
+        <sender>scanButton</sender>
+        <signal>clicked()</signal>
+        <receiver>ScanResults</receiver>
+        <slot>scanRequest()</slot>
+    </connection>
+    <connection>
+        <sender>scanResultsView</sender>
+        <signal>doubleClicked(QListViewItem*)</signal>
+        <receiver>ScanResults</receiver>
+        <slot>bssSelected(QListViewItem*)</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="local" impldecl="in implementation">common/wpa_ctrl.h</include>
+    <include location="local" impldecl="in implementation">wpagui.h</include>
+    <include location="local" impldecl="in implementation">networkconfig.h</include>
+    <include location="local" impldecl="in implementation">scanresults.ui.h</include>
+</includes>
+<forwards>
+    <forward>class WpaGui;</forward>
+</forwards>
+<variables>
+    <variable access="private">WpaGui *wpagui;</variable>
+    <variable access="private">QTimer *timer;</variable>
+</variables>
+<slots>
+    <slot>setWpaGui( WpaGui * _wpagui )</slot>
+    <slot>updateResults()</slot>
+    <slot>scanRequest()</slot>
+    <slot>getResults()</slot>
+    <slot>bssSelected( QListViewItem * sel )</slot>
+</slots>
+<functions>
+    <function access="private" specifier="non virtual">init()</function>
+    <function access="private" specifier="non virtual">destroy()</function>
+</functions>
+<pixmapinproject/>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wpa_supplicant/wpa_gui/scanresults.ui.h b/wpa_supplicant/wpa_gui/scanresults.ui.h
new file mode 100644 (file)
index 0000000..530d2e6
--- /dev/null
@@ -0,0 +1,101 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you want to add, delete, or rename functions or slots, use
+** Qt Designer to update this file, preserving your code.
+**
+** You should not define a constructor or destructor in this file.
+** Instead, write your code in functions called init() and destroy().
+** These will automatically be called by the form's constructor and
+** destructor.
+*****************************************************************************/
+
+void ScanResults::init()
+{
+    wpagui = NULL;
+}
+
+
+void ScanResults::destroy()
+{
+    delete timer;
+}
+
+
+void ScanResults::setWpaGui(WpaGui *_wpagui)
+{
+    wpagui = _wpagui;
+    updateResults();
+    
+    timer = new QTimer(this);
+    connect(timer, SIGNAL(timeout()), SLOT(getResults()));
+    timer->start(10000, FALSE);
+}
+
+
+void ScanResults::updateResults()
+{
+    char reply[8192];
+    size_t reply_len;
+    
+    if (wpagui == NULL)
+       return;
+
+    reply_len = sizeof(reply) - 1;
+    if (wpagui->ctrlRequest("SCAN_RESULTS", reply, &reply_len) < 0)
+       return;
+    reply[reply_len] = '\0';
+
+    scanResultsView->clear();
+    
+    QString res(reply);
+    QStringList lines = QStringList::split(QChar('\n'), res);
+    bool first = true;
+    for (QStringList::Iterator it = lines.begin(); it != lines.end(); it++) {
+       if (first) {
+           first = false;
+           continue;
+       }
+       
+       QStringList cols = QStringList::split(QChar('\t'), *it, true);
+       QString ssid, bssid, freq, signal, flags;
+       bssid = cols.count() > 0 ? cols[0] : "";
+       freq = cols.count() > 1 ? cols[1] : "";
+       signal = cols.count() > 2 ? cols[2] : "";
+       flags = cols.count() > 3 ? cols[3] : "";
+       ssid = cols.count() > 4 ? cols[4] : "";
+       new Q3ListViewItem(scanResultsView, ssid, bssid, freq, signal, flags);
+    }
+}
+
+
+void ScanResults::scanRequest()
+{
+    char reply[10];
+    size_t reply_len = sizeof(reply);
+    
+    if (wpagui == NULL)
+       return;
+    
+    wpagui->ctrlRequest("SCAN", reply, &reply_len);
+}
+
+
+void ScanResults::getResults()
+{
+    updateResults();
+}
+
+
+
+
+void ScanResults::bssSelected( Q3ListViewItem * sel )
+{
+    NetworkConfig *nc = new NetworkConfig();
+    if (nc == NULL)
+       return;
+    nc->setWpaGui(wpagui);
+    nc->paramsFromScanResults(sel);
+    nc->show();
+    nc->exec();
+}
diff --git a/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling b/wpa_supplicant/wpa_gui/setup-mingw-cross-compiling
new file mode 100755 (executable)
index 0000000..e173b00
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# qmake seems to be forcing include and lib paths from the original build
+# and I have no idea how to change these. For now, just override the
+# directories in the Makefile.Release file after qmake run.
+
+qmake -spec /q/jm/qt4-win/4.0.0/mkspecs/win32-g++ wpa_gui.pro -o Makefile
+cat Makefile.Release |
+    sed s%qt4/lib%qt4-win/4.0.0/lib%g |
+    sed s%qt4/include%qt4-win/4.0.0/include%g > tmp.Makefile.Release &&
+mv -f tmp.Makefile.Release Makefile.Release
diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui b/wpa_supplicant/wpa_gui/userdatarequest.ui
new file mode 100644 (file)
index 0000000..6106b1e
--- /dev/null
@@ -0,0 +1,163 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>UserDataRequest</class>
+<widget class="QDialog">
+    <property name="name">
+        <cstring>UserDataRequest</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>216</width>
+            <height>103</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>Authentication credentials required</string>
+    </property>
+    <property name="sizeGripEnabled">
+        <bool>true</bool>
+    </property>
+    <vbox>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLabel">
+            <property name="name">
+                <cstring>queryInfo</cstring>
+            </property>
+            <property name="text">
+                <string></string>
+            </property>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout28</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel">
+                    <property name="name">
+                        <cstring>queryField</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLineEdit">
+                    <property name="name">
+                        <cstring>queryEdit</cstring>
+                    </property>
+                    <property name="enabled">
+                        <bool>true</bool>
+                    </property>
+                    <property name="echoMode">
+                        <enum>Password</enum>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+        <widget class="QLayoutWidget">
+            <property name="name">
+                <cstring>layout27</cstring>
+            </property>
+            <hbox>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <spacer>
+                    <property name="name">
+                        <cstring>spacer4</cstring>
+                    </property>
+                    <property name="orientation">
+                        <enum>Horizontal</enum>
+                    </property>
+                    <property name="sizeType">
+                        <enum>Expanding</enum>
+                    </property>
+                    <property name="sizeHint">
+                        <size>
+                            <width>20</width>
+                            <height>20</height>
+                        </size>
+                    </property>
+                </spacer>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>buttonOk</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;OK</string>
+                    </property>
+                    <property name="accel">
+                        <string></string>
+                    </property>
+                    <property name="autoDefault">
+                        <bool>true</bool>
+                    </property>
+                    <property name="default">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+                <widget class="QPushButton">
+                    <property name="name">
+                        <cstring>buttonCancel</cstring>
+                    </property>
+                    <property name="text">
+                        <string>&amp;Cancel</string>
+                    </property>
+                    <property name="accel">
+                        <string></string>
+                    </property>
+                    <property name="autoDefault">
+                        <bool>true</bool>
+                    </property>
+                </widget>
+            </hbox>
+        </widget>
+    </vbox>
+</widget>
+<connections>
+    <connection>
+        <sender>buttonOk</sender>
+        <signal>clicked()</signal>
+        <receiver>UserDataRequest</receiver>
+        <slot>sendReply()</slot>
+    </connection>
+    <connection>
+        <sender>buttonCancel</sender>
+        <signal>clicked()</signal>
+        <receiver>UserDataRequest</receiver>
+        <slot>reject()</slot>
+    </connection>
+    <connection>
+        <sender>queryEdit</sender>
+        <signal>returnPressed()</signal>
+        <receiver>UserDataRequest</receiver>
+        <slot>sendReply()</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="local" impldecl="in implementation">common/wpa_ctrl.h</include>
+    <include location="local" impldecl="in implementation">wpagui.h</include>
+    <include location="local" impldecl="in implementation">userdatarequest.ui.h</include>
+</includes>
+<forwards>
+    <forward>class WpaGui;</forward>
+</forwards>
+<variables>
+    <variable access="private">WpaGui *wpagui;</variable>
+    <variable access="private">int networkid;</variable>
+    <variable access="private">QString field;</variable>
+</variables>
+<slots>
+    <slot>sendReply()</slot>
+</slots>
+<functions>
+    <function specifier="non virtual" returnType="int">setParams( WpaGui * _wpagui, const char * reqMsg )</function>
+</functions>
+<pixmapinproject/>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wpa_supplicant/wpa_gui/userdatarequest.ui.h b/wpa_supplicant/wpa_gui/userdatarequest.ui.h
new file mode 100644 (file)
index 0000000..66d4478
--- /dev/null
@@ -0,0 +1,72 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you want to add, delete, or rename functions or slots, use
+** Qt Designer to update this file, preserving your code.
+**
+** You should not define a constructor or destructor in this file.
+** Instead, write your code in functions called init() and destroy().
+** These will automatically be called by the form's constructor and
+** destructor.
+*****************************************************************************/
+
+#include <stdlib.h>
+
+int UserDataRequest::setParams(WpaGui *_wpagui, const char *reqMsg)
+{
+    char *tmp, *pos, *pos2;
+    wpagui = _wpagui;
+    tmp = strdup(reqMsg);
+    if (tmp == NULL)
+       return -1;
+    pos = strchr(tmp, '-');
+    if (pos == NULL) {
+       free(tmp);
+       return -1;
+    }
+    *pos++ = '\0';
+    field = tmp;
+    pos2 = strchr(pos, ':');
+    if (pos2 == NULL) {
+       free(tmp);
+       return -1;
+    }
+    *pos2++ = '\0';
+    
+    networkid = atoi(pos);
+    queryInfo->setText(pos2);
+    if (strcmp(tmp, "PASSWORD") == 0) {
+       queryField->setText("Password: ");
+       queryEdit->setEchoMode(QLineEdit::Password);
+    } else if (strcmp(tmp, "NEW_PASSWORD") == 0) {
+       queryField->setText("New password: ");
+       queryEdit->setEchoMode(QLineEdit::Password);
+    } else if (strcmp(tmp, "IDENTITY") == 0)
+       queryField->setText("Identity: ");
+    else if (strcmp(tmp, "PASSPHRASE") == 0) {
+       queryField->setText("Private key passphrase: ");
+       queryEdit->setEchoMode(QLineEdit::Password);
+    } else
+       queryField->setText(field + ":");
+    free(tmp);
+    
+    return 0;
+}
+
+
+void UserDataRequest::sendReply()
+{
+    char reply[10];
+    size_t reply_len = sizeof(reply);
+    
+    if (wpagui == NULL) {
+       reject();
+       return;
+    }
+    
+    QString cmd = QString(WPA_CTRL_RSP) + field + '-' +
+                 QString::number(networkid) + ':' +
+                 queryEdit->text();
+    wpagui->ctrlRequest(cmd.ascii(), reply, &reply_len);
+    accept();
+}
diff --git a/wpa_supplicant/wpa_gui/wpa_gui.pro b/wpa_supplicant/wpa_gui/wpa_gui.pro
new file mode 100644 (file)
index 0000000..a42a4ac
--- /dev/null
@@ -0,0 +1,50 @@
+TEMPLATE       = app
+LANGUAGE       = C++
+
+CONFIG += qt warn_on release
+
+DEFINES += CONFIG_CTRL_IFACE
+
+win32 {
+  LIBS += -lws2_32 -static
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  SOURCES += ../../src/utils/os_win32.c
+} else:win32-g++ {
+  # cross compilation to win32
+  LIBS += -lws2_32 -static
+  DEFINES += CONFIG_NATIVE_WINDOWS CONFIG_CTRL_IFACE_NAMED_PIPE
+  SOURCES += ../../src/utils/os_win32.c
+} else {
+  DEFINES += CONFIG_CTRL_IFACE_UNIX
+  SOURCES += ../../src/utils/os_unix.c
+}
+
+INCLUDEPATH    += . .. ../../src ../../src/utils
+
+HEADERS        += wpamsg.h
+
+SOURCES        += main.cpp \
+       ../../src/common/wpa_ctrl.c
+
+FORMS  = wpagui.ui \
+       eventhistory.ui \
+       scanresults.ui \
+       userdatarequest.ui \
+       networkconfig.ui
+
+
+unix {
+  UI_DIR = .ui
+  MOC_DIR = .moc
+  OBJECTS_DIR = .obj
+}
+
+qtver = $$[QT_VERSION]
+isEmpty( qtver ) {
+       message(Compiling for Qt 3.x)
+       DEFINES += Q3ListViewItem=QListViewItem
+} else {
+       message(Compiling for Qt $$qtver)
+       QT += qt3support
+       CONFIG += uic3
+}
diff --git a/wpa_supplicant/wpa_gui/wpagui.ui b/wpa_supplicant/wpa_gui/wpagui.ui
new file mode 100644 (file)
index 0000000..b49d96b
--- /dev/null
@@ -0,0 +1,471 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>WpaGui</class>
+<widget class="QMainWindow">
+    <property name="name">
+        <cstring>WpaGui</cstring>
+    </property>
+    <property name="geometry">
+        <rect>
+            <x>0</x>
+            <y>0</y>
+            <width>279</width>
+            <height>308</height>
+        </rect>
+    </property>
+    <property name="caption">
+        <string>wpa_gui</string>
+    </property>
+    <grid>
+        <property name="name">
+            <cstring>unnamed</cstring>
+        </property>
+        <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>textLabel16</cstring>
+            </property>
+            <property name="text">
+                <string>Adapter:</string>
+            </property>
+        </widget>
+        <widget class="QComboBox" row="0" column="2" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>adapterSelect</cstring>
+            </property>
+        </widget>
+        <widget class="QLabel" row="1" column="0" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>textLabel8</cstring>
+            </property>
+            <property name="text">
+                <string>Network:</string>
+            </property>
+        </widget>
+        <widget class="QComboBox" row="1" column="2" rowspan="1" colspan="2">
+            <property name="name">
+                <cstring>networkSelect</cstring>
+            </property>
+        </widget>
+        <widget class="QFrame" row="2" column="0" rowspan="1" colspan="4">
+            <property name="name">
+                <cstring>frame3</cstring>
+            </property>
+            <property name="frameShape">
+                <enum>StyledPanel</enum>
+            </property>
+            <property name="frameShadow">
+                <enum>Raised</enum>
+            </property>
+            <grid>
+                <property name="name">
+                    <cstring>unnamed</cstring>
+                </property>
+                <widget class="QLabel" row="0" column="0">
+                    <property name="name">
+                        <cstring>textLabel1</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Status:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="0">
+                    <property name="name">
+                        <cstring>textLabel2</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Last message:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="0">
+                    <property name="name">
+                        <cstring>textLabel3</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Authentication:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="0">
+                    <property name="name">
+                        <cstring>textLabel4</cstring>
+                    </property>
+                    <property name="text">
+                        <string>Encryption:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="4" column="0">
+                    <property name="name">
+                        <cstring>textLabel5</cstring>
+                    </property>
+                    <property name="text">
+                        <string>SSID:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="5" column="0">
+                    <property name="name">
+                        <cstring>textLabel6</cstring>
+                    </property>
+                    <property name="text">
+                        <string>BSSID:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="6" column="0">
+                    <property name="name">
+                        <cstring>textLabel7</cstring>
+                    </property>
+                    <property name="text">
+                        <string>IP address:</string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="0" column="1">
+                    <property name="name">
+                        <cstring>textStatus</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="1" column="1" rowspan="1" colspan="3">
+                    <property name="name">
+                        <cstring>textLastMessage</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="2" column="1">
+                    <property name="name">
+                        <cstring>textAuthentication</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="3" column="1">
+                    <property name="name">
+                        <cstring>textEncryption</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="4" column="1">
+                    <property name="name">
+                        <cstring>textSsid</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="5" column="1">
+                    <property name="name">
+                        <cstring>textBssid</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+                <widget class="QLabel" row="6" column="1">
+                    <property name="name">
+                        <cstring>textIpAddress</cstring>
+                    </property>
+                    <property name="text">
+                        <string></string>
+                    </property>
+                </widget>
+            </grid>
+        </widget>
+        <spacer row="3" column="0">
+            <property name="name">
+                <cstring>spacer7</cstring>
+            </property>
+            <property name="orientation">
+                <enum>Horizontal</enum>
+            </property>
+            <property name="sizeType">
+                <enum>Expanding</enum>
+            </property>
+            <property name="sizeHint">
+                <size>
+                    <width>16</width>
+                    <height>16</height>
+                </size>
+            </property>
+        </spacer>
+        <widget class="QPushButton" row="3" column="1">
+            <property name="name">
+                <cstring>connectButton</cstring>
+            </property>
+            <property name="text">
+                <string>Connect</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="3" column="2">
+            <property name="name">
+                <cstring>disconnectButton</cstring>
+            </property>
+            <property name="text">
+                <string>Disconnect</string>
+            </property>
+        </widget>
+        <widget class="QPushButton" row="3" column="3">
+            <property name="name">
+                <cstring>scanButton</cstring>
+            </property>
+            <property name="text">
+                <string>Scan</string>
+            </property>
+        </widget>
+    </grid>
+</widget>
+<menubar>
+    <property name="name">
+        <cstring>MenuBar</cstring>
+    </property>
+    <item text="&amp;File" name="fileMenu">
+        <separator/>
+        <action name="fileEventHistoryAction"/>
+        <action name="fileAdd_NetworkAction"/>
+        <action name="fileEdit_networkAction"/>
+        <separator/>
+        <action name="fileExitAction"/>
+    </item>
+    <item text="&amp;Help" name="helpMenu">
+        <action name="helpContentsAction"/>
+        <action name="helpIndexAction"/>
+        <separator/>
+        <action name="helpAboutAction"/>
+    </item>
+</menubar>
+<toolbars>
+</toolbars>
+<actions>
+    <action>
+        <property name="name">
+            <cstring>fileExitAction</cstring>
+        </property>
+        <property name="text">
+            <string>Exit</string>
+        </property>
+        <property name="menuText">
+            <string>E&amp;xit</string>
+        </property>
+        <property name="accel">
+            <string>Ctrl+Q</string>
+        </property>
+    </action>
+    <action>
+        <property name="name">
+            <cstring>helpContentsAction</cstring>
+        </property>
+        <property name="enabled">
+            <bool>false</bool>
+        </property>
+        <property name="text">
+            <string>Contents</string>
+        </property>
+        <property name="menuText">
+            <string>&amp;Contents...</string>
+        </property>
+        <property name="accel">
+            <string></string>
+        </property>
+    </action>
+    <action>
+        <property name="name">
+            <cstring>helpIndexAction</cstring>
+        </property>
+        <property name="enabled">
+            <bool>false</bool>
+        </property>
+        <property name="text">
+            <string>Index</string>
+        </property>
+        <property name="menuText">
+            <string>&amp;Index...</string>
+        </property>
+        <property name="accel">
+            <string></string>
+        </property>
+    </action>
+    <action>
+        <property name="name">
+            <cstring>helpAboutAction</cstring>
+        </property>
+        <property name="text">
+            <string>About</string>
+        </property>
+        <property name="menuText">
+            <string>&amp;About</string>
+        </property>
+        <property name="accel">
+            <string></string>
+        </property>
+    </action>
+    <action>
+        <property name="name">
+            <cstring>fileEventHistoryAction</cstring>
+        </property>
+        <property name="text">
+            <string>Event History</string>
+        </property>
+        <property name="menuText">
+            <string>Event &amp;History</string>
+        </property>
+    </action>
+    <action>
+        <property name="name">
+            <cstring>fileAdd_NetworkAction</cstring>
+        </property>
+        <property name="text">
+            <string>Add Network</string>
+        </property>
+        <property name="menuText">
+            <string>&amp;Add Network</string>
+        </property>
+    </action>
+    <action>
+        <property name="name">
+            <cstring>fileEdit_networkAction</cstring>
+        </property>
+        <property name="text">
+            <string>Edit Network</string>
+        </property>
+        <property name="menuText">
+            <string>&amp;Edit Network</string>
+        </property>
+    </action>
+</actions>
+<connections>
+    <connection>
+        <sender>helpIndexAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>helpIndex()</slot>
+    </connection>
+    <connection>
+        <sender>helpContentsAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>helpContents()</slot>
+    </connection>
+    <connection>
+        <sender>helpAboutAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>helpAbout()</slot>
+    </connection>
+    <connection>
+        <sender>fileExitAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>close()</slot>
+    </connection>
+    <connection>
+        <sender>disconnectButton</sender>
+        <signal>clicked()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>disconnect()</slot>
+    </connection>
+    <connection>
+        <sender>scanButton</sender>
+        <signal>clicked()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>scan()</slot>
+    </connection>
+    <connection>
+        <sender>connectButton</sender>
+        <signal>clicked()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>connectB()</slot>
+    </connection>
+    <connection>
+        <sender>fileEventHistoryAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>eventHistory()</slot>
+    </connection>
+    <connection>
+        <sender>networkSelect</sender>
+        <signal>activated(const QString&amp;)</signal>
+        <receiver>WpaGui</receiver>
+        <slot>selectNetwork(const QString&amp;)</slot>
+    </connection>
+    <connection>
+        <sender>fileEdit_networkAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>editNetwork()</slot>
+    </connection>
+    <connection>
+        <sender>fileAdd_NetworkAction</sender>
+        <signal>activated()</signal>
+        <receiver>WpaGui</receiver>
+        <slot>addNetwork()</slot>
+    </connection>
+    <connection>
+        <sender>adapterSelect</sender>
+        <signal>activated(const QString&amp;)</signal>
+        <receiver>WpaGui</receiver>
+        <slot>selectAdapter(const QString&amp;)</slot>
+    </connection>
+</connections>
+<includes>
+    <include location="global" impldecl="in declaration">qtimer.h</include>
+    <include location="global" impldecl="in declaration">qsocketnotifier.h</include>
+    <include location="local" impldecl="in declaration">wpamsg.h</include>
+    <include location="local" impldecl="in declaration">eventhistory.h</include>
+    <include location="local" impldecl="in declaration">scanresults.h</include>
+    <include location="local" impldecl="in implementation">common/wpa_ctrl.h</include>
+    <include location="global" impldecl="in implementation">dirent.h</include>
+    <include location="global" impldecl="in implementation">qmessagebox.h</include>
+    <include location="global" impldecl="in implementation">qapplication.h</include>
+    <include location="local" impldecl="in implementation">userdatarequest.h</include>
+    <include location="local" impldecl="in implementation">networkconfig.h</include>
+    <include location="local" impldecl="in implementation">wpagui.ui.h</include>
+</includes>
+<forwards>
+    <forward>class UserDataRequest;</forward>
+</forwards>
+<variables>
+    <variable access="private">ScanResults *scanres;</variable>
+    <variable access="private">bool networkMayHaveChanged;</variable>
+    <variable access="private">char *ctrl_iface;</variable>
+    <variable access="private">EventHistory *eh;</variable>
+    <variable access="private">struct wpa_ctrl *ctrl_conn;</variable>
+    <variable access="private">QSocketNotifier *msgNotifier;</variable>
+    <variable access="private">QTimer *timer;</variable>
+    <variable access="private">int pingsToStatusUpdate;</variable>
+    <variable access="private">WpaMsgList msgs;</variable>
+    <variable access="private">char *ctrl_iface_dir;</variable>
+    <variable access="private">struct wpa_ctrl *monitor_conn;</variable>
+    <variable access="private">UserDataRequest *udr;</variable>
+</variables>
+<slots>
+    <slot>parse_argv()</slot>
+    <slot>updateStatus()</slot>
+    <slot>updateNetworks()</slot>
+    <slot>helpIndex()</slot>
+    <slot>helpContents()</slot>
+    <slot>helpAbout()</slot>
+    <slot>disconnect()</slot>
+    <slot>scan()</slot>
+    <slot>eventHistory()</slot>
+    <slot>ping()</slot>
+    <slot>processMsg( char * msg )</slot>
+    <slot>processCtrlReq( const char * req )</slot>
+    <slot>receiveMsgs()</slot>
+    <slot>connectB()</slot>
+    <slot>selectNetwork( const QString &amp; sel )</slot>
+    <slot>editNetwork()</slot>
+    <slot>addNetwork()</slot>
+    <slot>selectAdapter( const QString &amp; sel )</slot>
+</slots>
+<functions>
+    <function access="private" specifier="non virtual">init()</function>
+    <function access="private" specifier="non virtual">destroy()</function>
+    <function access="private" specifier="non virtual" returnType="int">openCtrlConnection( const char * ifname )</function>
+    <function returnType="int">ctrlRequest( const char * cmd, char * buf, size_t * buflen )</function>
+    <function>triggerUpdate()</function>
+</functions>
+<pixmapinproject/>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/wpa_supplicant/wpa_gui/wpagui.ui.h b/wpa_supplicant/wpa_gui/wpagui.ui.h
new file mode 100644 (file)
index 0000000..678ff1b
--- /dev/null
@@ -0,0 +1,730 @@
+/****************************************************************************
+** ui.h extension file, included from the uic-generated form implementation.
+**
+** If you want to add, delete, or rename functions or slots, use
+** Qt Designer to update this file, preserving your code.
+**
+** You should not define a constructor or destructor in this file.
+** Instead, write your code in functions called init() and destroy().
+** These will automatically be called by the form's constructor and
+** destructor.
+*****************************************************************************/
+
+
+#ifdef __MINGW32__
+/* Need to get getopt() */
+#include <unistd.h>
+#endif
+
+#include <stdlib.h>
+
+void WpaGui::init()
+{
+    eh = NULL;
+    scanres = NULL;
+    udr = NULL;
+    ctrl_iface = NULL;
+    ctrl_conn = NULL;
+    monitor_conn = NULL;
+    msgNotifier = NULL;
+    ctrl_iface_dir = strdup("/var/run/wpa_supplicant");
+    
+    parse_argv();
+
+    textStatus->setText("connecting to wpa_supplicant");
+    timer = new QTimer(this);
+    connect(timer, SIGNAL(timeout()), SLOT(ping()));
+    timer->start(1000, FALSE);
+    
+    if (openCtrlConnection(ctrl_iface) < 0) {
+       printf("Failed to open control connection to wpa_supplicant.\n");
+    }
+    
+    updateStatus();
+    networkMayHaveChanged = true;
+    updateNetworks();
+}
+
+
+void WpaGui::destroy()
+{
+    delete msgNotifier;
+
+    if (monitor_conn) {
+       wpa_ctrl_detach(monitor_conn);
+       wpa_ctrl_close(monitor_conn);
+       monitor_conn = NULL;
+    }
+    if (ctrl_conn) {
+       wpa_ctrl_close(ctrl_conn);
+       ctrl_conn = NULL;
+    }
+    
+    if (eh) {
+       eh->close();
+       delete eh;
+       eh = NULL;
+    }
+    
+    if (scanres) {
+       scanres->close();
+       delete scanres;
+       scanres = NULL;
+    }
+    
+    if (udr) {
+       udr->close();
+       delete udr;
+       udr = NULL;
+    }
+    
+    free(ctrl_iface);
+    ctrl_iface = NULL;
+    
+    free(ctrl_iface_dir);
+    ctrl_iface_dir = NULL;
+}
+
+
+void WpaGui::parse_argv()
+{
+    int c;
+    for (;;) {
+       c = getopt(qApp->argc(), qApp->argv(), "i:p:");
+       if (c < 0)
+           break;
+       switch (c) {
+       case 'i':
+           free(ctrl_iface);
+           ctrl_iface = strdup(optarg);
+           break;
+       case 'p':
+           free(ctrl_iface_dir);
+           ctrl_iface_dir = strdup(optarg);
+           break;
+       }
+    }
+}
+
+
+int WpaGui::openCtrlConnection(const char *ifname)
+{
+    char *cfile;
+    int flen;
+    char buf[2048], *pos, *pos2;
+    size_t len;
+
+    if (ifname) {
+       if (ifname != ctrl_iface) {
+           free(ctrl_iface);
+           ctrl_iface = strdup(ifname);
+       }
+    } else {
+#ifdef CONFIG_CTRL_IFACE_UDP
+       free(ctrl_iface);
+       ctrl_iface = strdup("udp");
+#endif /* CONFIG_CTRL_IFACE_UDP */
+#ifdef CONFIG_CTRL_IFACE_UNIX
+       struct dirent *dent;
+       DIR *dir = opendir(ctrl_iface_dir);
+       free(ctrl_iface);
+       ctrl_iface = NULL;
+       if (dir) {
+           while ((dent = readdir(dir))) {
+#ifdef _DIRENT_HAVE_D_TYPE
+               /* Skip the file if it is not a socket.
+                * Also accept DT_UNKNOWN (0) in case
+                * the C library or underlying file
+                * system does not support d_type. */
+               if (dent->d_type != DT_SOCK &&
+                   dent->d_type != DT_UNKNOWN)
+                   continue;
+#endif /* _DIRENT_HAVE_D_TYPE */
+
+               if (strcmp(dent->d_name, ".") == 0 ||
+                   strcmp(dent->d_name, "..") == 0)
+                   continue;
+               printf("Selected interface '%s'\n", dent->d_name);
+               ctrl_iface = strdup(dent->d_name);
+               break;
+           }
+           closedir(dir);
+       }
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+       struct wpa_ctrl *ctrl;
+       int ret;
+
+       free(ctrl_iface);
+       ctrl_iface = NULL;
+
+       ctrl = wpa_ctrl_open(NULL);
+       if (ctrl) {
+           len = sizeof(buf) - 1;
+           ret = wpa_ctrl_request(ctrl, "INTERFACES", 10, buf, &len, NULL);
+           if (ret >= 0) {
+               buf[len] = '\0';
+               pos = strchr(buf, '\n');
+               if (pos)
+                   *pos = '\0';
+               ctrl_iface = strdup(buf);
+           }
+           wpa_ctrl_close(ctrl);
+       }
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+    }
+    
+    if (ctrl_iface == NULL)
+       return -1;
+
+#ifdef CONFIG_CTRL_IFACE_UNIX
+    flen = strlen(ctrl_iface_dir) + strlen(ctrl_iface) + 2;
+    cfile = (char *) malloc(flen);
+    if (cfile == NULL)
+       return -1;
+    snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ctrl_iface);
+#else /* CONFIG_CTRL_IFACE_UNIX */
+    flen = strlen(ctrl_iface) + 1;
+    cfile = (char *) malloc(flen);
+    if (cfile == NULL)
+       return -1;
+    snprintf(cfile, flen, "%s", ctrl_iface);
+#endif /* CONFIG_CTRL_IFACE_UNIX */
+
+    if (ctrl_conn) {
+       wpa_ctrl_close(ctrl_conn);
+       ctrl_conn = NULL;
+    }
+
+    if (monitor_conn) {
+       delete msgNotifier;
+       msgNotifier = NULL;
+       wpa_ctrl_detach(monitor_conn);
+       wpa_ctrl_close(monitor_conn);
+       monitor_conn = NULL;
+    }
+
+    printf("Trying to connect to '%s'\n", cfile);
+    ctrl_conn = wpa_ctrl_open(cfile);
+    if (ctrl_conn == NULL) {
+       free(cfile);
+       return -1;
+    }
+    monitor_conn = wpa_ctrl_open(cfile);
+    free(cfile);
+    if (monitor_conn == NULL) {
+       wpa_ctrl_close(ctrl_conn);
+       return -1;
+    }
+    if (wpa_ctrl_attach(monitor_conn)) {
+       printf("Failed to attach to wpa_supplicant\n");
+       wpa_ctrl_close(monitor_conn);
+       monitor_conn = NULL;
+       wpa_ctrl_close(ctrl_conn);
+       ctrl_conn = NULL;
+       return -1;
+    }
+
+#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP)
+    msgNotifier = new QSocketNotifier(wpa_ctrl_get_fd(monitor_conn),
+                                     QSocketNotifier::Read, this);
+    connect(msgNotifier, SIGNAL(activated(int)), SLOT(receiveMsgs()));
+#endif
+
+    adapterSelect->clear();
+    adapterSelect->insertItem(ctrl_iface);
+    adapterSelect->setCurrentItem(0);
+
+    len = sizeof(buf) - 1;
+    if (wpa_ctrl_request(ctrl_conn, "INTERFACES", 10, buf, &len, NULL) >= 0) {
+       buf[len] = '\0';
+       pos = buf;
+       while (*pos) {
+               pos2 = strchr(pos, '\n');
+               if (pos2)
+                       *pos2 = '\0';
+               if (strcmp(pos, ctrl_iface) != 0)
+                       adapterSelect->insertItem(pos);
+               if (pos2)
+                       pos = pos2 + 1;
+               else
+                       break;
+       }
+    }
+
+    return 0;
+}
+
+
+static void wpa_gui_msg_cb(char *msg, size_t)
+{
+    /* This should not happen anymore since two control connections are used. */
+    printf("missed message: %s\n", msg);
+}
+
+
+int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
+{
+    int ret;
+    
+    if (ctrl_conn == NULL)
+       return -3;
+    ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen,
+                          wpa_gui_msg_cb);
+    if (ret == -2) {
+       printf("'%s' command timed out.\n", cmd);
+    } else if (ret < 0) {
+       printf("'%s' command failed.\n", cmd);
+    }
+    
+    return ret;
+}
+
+
+void WpaGui::updateStatus()
+{
+    char buf[2048], *start, *end, *pos;
+    size_t len;
+
+    pingsToStatusUpdate = 10;
+
+    len = sizeof(buf) - 1;
+    if (ctrl_conn == NULL || ctrlRequest("STATUS", buf, &len) < 0) {
+       textStatus->setText("Could not get status from wpa_supplicant");
+       textAuthentication->clear();
+       textEncryption->clear();
+       textSsid->clear();
+       textBssid->clear();
+       textIpAddress->clear();
+       return;
+    }
+    
+    buf[len] = '\0';
+    
+    bool auth_updated = false, ssid_updated = false;
+    bool bssid_updated = false, ipaddr_updated = false;
+    bool status_updated = false;
+    char *pairwise_cipher = NULL, *group_cipher = NULL;
+    
+    start = buf;
+    while (*start) {
+       bool last = false;
+       end = strchr(start, '\n');
+       if (end == NULL) {
+           last = true;
+           end = start;
+           while (end[0] && end[1])
+               end++;
+       }
+       *end = '\0';
+       
+       pos = strchr(start, '=');
+       if (pos) {
+           *pos++ = '\0';
+           if (strcmp(start, "bssid") == 0) {
+               bssid_updated = true;
+               textBssid->setText(pos);
+           } else if (strcmp(start, "ssid") == 0) {
+               ssid_updated = true;
+               textSsid->setText(pos);
+           } else if (strcmp(start, "ip_address") == 0) {
+               ipaddr_updated = true;
+               textIpAddress->setText(pos);
+           } else if (strcmp(start, "wpa_state") == 0) {
+               status_updated = true;
+               textStatus->setText(pos);
+           } else if (strcmp(start, "key_mgmt") == 0) {
+               auth_updated = true;
+               textAuthentication->setText(pos);
+               /* TODO: could add EAP status to this */
+           } else if (strcmp(start, "pairwise_cipher") == 0) {
+               pairwise_cipher = pos;
+           } else if (strcmp(start, "group_cipher") == 0) {
+               group_cipher = pos;
+           }
+       }
+       
+       if (last)
+           break;
+       start = end + 1;
+    }
+    
+    if (pairwise_cipher || group_cipher) {
+       QString encr;
+       if (pairwise_cipher && group_cipher &&
+           strcmp(pairwise_cipher, group_cipher) != 0) {
+           encr.append(pairwise_cipher);
+           encr.append(" + ");
+           encr.append(group_cipher);
+       } else if (pairwise_cipher) {
+           encr.append(pairwise_cipher);
+       } else {
+           encr.append(group_cipher);
+           encr.append(" [group key only]");
+       }
+       textEncryption->setText(encr);
+    } else
+       textEncryption->clear();
+
+    if (!status_updated)
+       textStatus->clear();
+    if (!auth_updated)
+       textAuthentication->clear();
+    if (!ssid_updated)
+       textSsid->clear();
+    if (!bssid_updated)
+       textBssid->clear();
+    if (!ipaddr_updated)
+       textIpAddress->clear();
+}
+
+
+void WpaGui::updateNetworks()
+{
+    char buf[2048], *start, *end, *id, *ssid, *bssid, *flags;
+    size_t len;
+    int first_active = -1;
+    bool selected = false;
+
+    if (!networkMayHaveChanged)
+       return;
+
+    networkSelect->clear();
+
+    if (ctrl_conn == NULL)
+       return;
+    
+    len = sizeof(buf) - 1;
+    if (ctrlRequest("LIST_NETWORKS", buf, &len) < 0)
+       return;
+    
+    buf[len] = '\0';
+    start = strchr(buf, '\n');
+    if (start == NULL)
+       return;
+    start++;
+
+    while (*start) {
+       bool last = false;
+       end = strchr(start, '\n');
+       if (end == NULL) {
+           last = true;
+           end = start;
+           while (end[0] && end[1])
+               end++;
+       }
+       *end = '\0';
+       
+       id = start;
+       ssid = strchr(id, '\t');
+       if (ssid == NULL)
+           break;
+       *ssid++ = '\0';
+       bssid = strchr(ssid, '\t');
+       if (bssid == NULL)
+           break;
+       *bssid++ = '\0';
+       flags = strchr(bssid, '\t');
+       if (flags == NULL)
+           break;
+       *flags++ = '\0';
+       
+       QString network(id);
+       network.append(": ");
+       network.append(ssid);
+       networkSelect->insertItem(network);
+       
+       if (strstr(flags, "[CURRENT]")) {
+           networkSelect->setCurrentItem(networkSelect->count() - 1);
+           selected = true;
+       } else if (first_active < 0 && strstr(flags, "[DISABLED]") == NULL)
+           first_active = networkSelect->count() - 1;
+       
+       if (last)
+           break;
+       start = end + 1;
+    }
+
+    if (!selected && first_active >= 0)
+       networkSelect->setCurrentItem(first_active);
+
+    networkMayHaveChanged = false;
+}
+
+
+void WpaGui::helpIndex()
+{
+    printf("helpIndex\n");
+}
+
+
+void WpaGui::helpContents()
+{
+    printf("helpContents\n");
+}
+
+
+void WpaGui::helpAbout()
+{
+    QMessageBox::about(this, "wpa_gui for wpa_supplicant",
+                      "Copyright (c) 2003-2008,\n"
+                      "Jouni Malinen <j@w1.fi>\n"
+                      "and contributors.\n"
+                      "\n"
+                      "This program is free software. You can\n"
+                      "distribute it and/or modify it under the terms of\n"
+                      "the GNU General Public License version 2.\n"
+                      "\n"
+                      "Alternatively, this software may be distributed\n"
+                      "under the terms of the BSD license.\n"
+                      "\n"
+                      "This product includes software developed\n"
+                      "by the OpenSSL Project for use in the\n"
+                      "OpenSSL Toolkit (http://www.openssl.org/)\n");
+}
+
+
+void WpaGui::disconnect()
+{
+    char reply[10];
+    size_t reply_len = sizeof(reply);
+    ctrlRequest("DISCONNECT", reply, &reply_len);
+}
+
+
+void WpaGui::scan()
+{
+    if (scanres) {
+       scanres->close();
+       delete scanres;
+    }
+
+    scanres = new ScanResults();
+    if (scanres == NULL)
+       return;
+    scanres->setWpaGui(this);
+    scanres->show();
+    scanres->exec();
+}
+
+
+void WpaGui::eventHistory()
+{
+    if (eh) {
+       eh->close();
+       delete eh;
+    }
+
+    eh = new EventHistory();
+    if (eh == NULL)
+       return;
+    eh->addEvents(msgs);
+    eh->show();
+    eh->exec();
+}
+
+
+void WpaGui::ping()
+{
+    char buf[10];
+    size_t len;
+    
+#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE
+    /*
+     * QSocketNotifier cannot be used with Windows named pipes, so use a timer
+     * to check for received messages for now. This could be optimized be doing
+     * something specific to named pipes or Windows events, but it is not clear
+     * what would be the best way of doing that in Qt.
+     */
+    receiveMsgs();
+#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */
+
+    if (scanres && !scanres->isVisible()) {
+       delete scanres;
+       scanres = NULL;
+    }
+    
+    if (eh && !eh->isVisible()) {
+       delete eh;
+       eh = NULL;
+    }
+    
+    if (udr && !udr->isVisible()) {
+       delete udr;
+       udr = NULL;
+    }
+    
+    len = sizeof(buf) - 1;
+    if (ctrlRequest("PING", buf, &len) < 0) {
+       printf("PING failed - trying to reconnect\n");
+       if (openCtrlConnection(ctrl_iface) >= 0) {
+           printf("Reconnected successfully\n");
+           pingsToStatusUpdate = 0;
+       }
+    }
+
+    pingsToStatusUpdate--;
+    if (pingsToStatusUpdate <= 0) {
+       updateStatus();
+       updateNetworks();
+    }
+}
+
+
+static int str_match(const char *a, const char *b)
+{
+    return strncmp(a, b, strlen(b)) == 0;
+}
+
+
+void WpaGui::processMsg(char *msg)
+{
+    char *pos = msg, *pos2;
+    int priority = 2;
+    
+    if (*pos == '<') {
+       /* skip priority */
+       pos++;
+       priority = atoi(pos);
+       pos = strchr(pos, '>');
+       if (pos)
+           pos++;
+       else
+           pos = msg;
+    }
+
+    WpaMsg wm(pos, priority);
+    if (eh)
+       eh->addEvent(wm);
+    msgs.append(wm);
+    while (msgs.count() > 100)
+       msgs.pop_front();
+    
+    /* Update last message with truncated version of the event */
+    if (strncmp(pos, "CTRL-", 5) == 0) {
+       pos2 = strchr(pos, str_match(pos, WPA_CTRL_REQ) ? ':' : ' ');
+       if (pos2)
+           pos2++;
+       else
+           pos2 = pos;
+    } else
+       pos2 = pos;
+    QString lastmsg = pos2;
+    lastmsg.truncate(40);
+    textLastMessage->setText(lastmsg);
+    
+    pingsToStatusUpdate = 0;
+    networkMayHaveChanged = true;
+    
+    if (str_match(pos, WPA_CTRL_REQ))
+       processCtrlReq(pos + strlen(WPA_CTRL_REQ));
+}
+
+
+void WpaGui::processCtrlReq(const char *req)
+{
+    if (udr) {
+       udr->close();
+       delete udr;
+    }
+    udr = new UserDataRequest();
+    if (udr == NULL)
+       return;
+    if (udr->setParams(this, req) < 0) {
+       delete udr;
+       udr = NULL;
+       return;
+    }
+    udr->show();
+    udr->exec();
+}
+
+
+void WpaGui::receiveMsgs()
+{
+    char buf[256];
+    size_t len;
+    
+    while (monitor_conn && wpa_ctrl_pending(monitor_conn) > 0) {
+       len = sizeof(buf) - 1;
+       if (wpa_ctrl_recv(monitor_conn, buf, &len) == 0) {
+           buf[len] = '\0';
+           processMsg(buf);
+       }
+    }
+}
+
+
+void WpaGui::connectB()
+{
+    char reply[10];
+    size_t reply_len = sizeof(reply);
+    ctrlRequest("REASSOCIATE", reply, &reply_len);
+}
+
+
+void WpaGui::selectNetwork( const QString &sel )
+{
+    QString cmd(sel);
+    char reply[10];
+    size_t reply_len = sizeof(reply);
+    
+    int pos = cmd.find(':');
+    if (pos < 0) {
+       printf("Invalid selectNetwork '%s'\n", cmd.ascii());
+       return;
+    }
+    cmd.truncate(pos);
+    cmd.prepend("SELECT_NETWORK ");
+    ctrlRequest(cmd.ascii(), reply, &reply_len);
+}
+
+
+void WpaGui::editNetwork()
+{
+    QString sel(networkSelect->currentText());
+    int pos = sel.find(':');
+    if (pos < 0) {
+       printf("Invalid selectNetwork '%s'\n", sel.ascii());
+       return;
+    }
+    sel.truncate(pos);
+    
+    NetworkConfig *nc = new NetworkConfig();
+    if (nc == NULL)
+       return;
+    nc->setWpaGui(this);
+    
+    nc->paramsFromConfig(sel.toInt());
+    nc->show();
+    nc->exec();
+}
+
+
+void WpaGui::triggerUpdate()
+{
+    updateStatus();
+    networkMayHaveChanged = true;
+    updateNetworks();
+}
+
+
+void WpaGui::addNetwork()
+{
+    NetworkConfig *nc = new NetworkConfig();
+    if (nc == NULL)
+       return;
+    nc->setWpaGui(this);
+    nc->newNetwork();
+    nc->show();
+    nc->exec();
+}
+
+
+void WpaGui::selectAdapter( const QString & sel )
+{
+    if (openCtrlConnection(sel.ascii()) < 0)
+       printf("Failed to open control connection to wpa_supplicant.\n");
+    updateStatus();
+    updateNetworks();
+}
diff --git a/wpa_supplicant/wpa_gui/wpamsg.h b/wpa_supplicant/wpa_gui/wpamsg.h
new file mode 100644 (file)
index 0000000..f3fce06
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef WPAMSG_H
+#define WPAMSG_H
+
+class WpaMsg;
+
+#if QT_VERSION >= 0x040000
+#include <QDateTime>
+#include <QLinkedList>
+typedef QLinkedList<WpaMsg> WpaMsgList;
+#else
+#include <qdatetime.h>
+typedef QValueList<WpaMsg> WpaMsgList;
+#endif
+
+class WpaMsg {
+public:
+    WpaMsg() {}
+    WpaMsg(const QString &_msg, int _priority = 2)
+       : msg(_msg), priority(_priority)
+    {
+       timestamp = QDateTime::currentDateTime();
+    }
+    
+    QString getMsg() const { return msg; }
+    int getPriority() const { return priority; }
+    QDateTime getTimestamp() const { return timestamp; }
+    
+private:
+    QString msg;
+    int priority;
+    QDateTime timestamp;
+};
+
+#endif /* WPAMSG_H */
diff --git a/wpa_supplicant/wpa_passphrase.c b/wpa_supplicant/wpa_passphrase.c
new file mode 100644 (file)
index 0000000..67465aa
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * WPA Supplicant - ASCII passphrase to WPA PSK tool
+ * 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.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/sha1.h"
+
+
+int main(int argc, char *argv[])
+{
+       unsigned char psk[32];
+       int i;
+       char *ssid, *passphrase, buf[64], *pos;
+
+       if (argc < 2) {
+               printf("usage: wpa_passphrase <ssid> [passphrase]\n"
+                       "\nIf passphrase is left out, it will be read from "
+                       "stdin\n");
+               return 1;
+       }
+
+       ssid = argv[1];
+
+       if (argc > 2) {
+               passphrase = argv[2];
+       } else {
+               printf("# reading passphrase from stdin\n");
+               if (fgets(buf, sizeof(buf), stdin) == NULL) {
+                       printf("Failed to read passphrase\n");
+                       return 1;
+               }
+               buf[sizeof(buf) - 1] = '\0';
+               pos = buf;
+               while (*pos != '\0') {
+                       if (*pos == '\r' || *pos == '\n') {
+                               *pos = '\0';
+                               break;
+                       }
+                       pos++;
+               }
+               passphrase = buf;
+       }
+
+       if (os_strlen(passphrase) < 8 || os_strlen(passphrase) > 63) {
+               printf("Passphrase must be 8..63 characters\n");
+               return 1;
+       }
+
+       pbkdf2_sha1(passphrase, ssid, os_strlen(ssid), 4096, psk, 32);
+
+       printf("network={\n");
+       printf("\tssid=\"%s\"\n", ssid);
+       printf("\t#psk=\"%s\"\n", passphrase);
+       printf("\tpsk=");
+       for (i = 0; i < 32; i++)
+               printf("%02x", psk[i]);
+       printf("\n");
+       printf("}\n");
+
+       return 0;
+}
diff --git a/wpa_supplicant/wpa_priv.c b/wpa_supplicant/wpa_priv.c
new file mode 100644 (file)
index 0000000..d2a991b
--- /dev/null
@@ -0,0 +1,1040 @@
+/*
+ * WPA Supplicant / privileged helper program
+ * Copyright (c) 2007-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"
+#ifdef __linux__
+#include <fcntl.h>
+#endif /* __linux__ */
+#include <sys/un.h>
+#include <sys/stat.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "common/version.h"
+#include "drivers/driver.h"
+#include "l2_packet/l2_packet.h"
+#include "common/privsep_commands.h"
+#include "common/ieee802_11_defs.h"
+
+
+struct wpa_priv_interface {
+       struct wpa_priv_interface *next;
+       char *driver_name;
+       char *ifname;
+       char *sock_name;
+       int fd;
+
+       struct wpa_driver_ops *driver;
+       void *drv_priv;
+       struct sockaddr_un drv_addr;
+       int wpas_registered;
+
+       /* TODO: add support for multiple l2 connections */
+       struct l2_packet_data *l2;
+       struct sockaddr_un l2_addr;
+};
+
+
+static void wpa_priv_cmd_register(struct wpa_priv_interface *iface,
+                                 struct sockaddr_un *from)
+{
+       if (iface->drv_priv) {
+               wpa_printf(MSG_DEBUG, "Cleaning up forgotten driver instance");
+               if (iface->driver->deinit)
+                       iface->driver->deinit(iface->drv_priv);
+               iface->drv_priv = NULL;
+               iface->wpas_registered = 0;
+       }
+
+       if (iface->l2) {
+               wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
+                          "instance");
+               l2_packet_deinit(iface->l2);
+               iface->l2 = NULL;
+       }
+
+       if (iface->driver->init == NULL)
+               return;
+
+       iface->drv_priv = iface->driver->init(iface, iface->ifname);
+       if (iface->drv_priv == NULL) {
+               wpa_printf(MSG_DEBUG, "Failed to initialize driver wrapper");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "Driver wrapper '%s' initialized for interface "
+                  "'%s'", iface->driver_name, iface->ifname);
+
+       os_memcpy(&iface->drv_addr, from, sizeof(iface->drv_addr));
+       iface->wpas_registered = 1;
+
+       if (iface->driver->set_param &&
+           iface->driver->set_param(iface->drv_priv, NULL) < 0) {
+               wpa_printf(MSG_ERROR, "Driver interface rejected param");
+       }
+}
+
+
+static void wpa_priv_cmd_unregister(struct wpa_priv_interface *iface,
+                                   struct sockaddr_un *from)
+{
+       if (iface->drv_priv) {
+               if (iface->driver->deinit)
+                       iface->driver->deinit(iface->drv_priv);
+               iface->drv_priv = NULL;
+               iface->wpas_registered = 0;
+       }
+}
+
+
+static void wpa_priv_cmd_scan(struct wpa_priv_interface *iface,
+                             char *buf, size_t len)
+{
+       struct wpa_driver_scan_params params;
+
+       if (iface->drv_priv == NULL)
+               return;
+
+       os_memset(&params, 0, sizeof(params));
+       if (len) {
+               params.ssids[0].ssid = (u8 *) buf;
+               params.ssids[0].ssid_len = len;
+               params.num_ssids = 1;
+       }
+
+       if (iface->driver->scan2)
+               iface->driver->scan2(iface->drv_priv, &params);
+}
+
+
+static void wpa_priv_get_scan_results2(struct wpa_priv_interface *iface,
+                                      struct sockaddr_un *from)
+{
+       struct wpa_scan_results *res;
+       u8 *buf = NULL, *pos, *end;
+       int val;
+       size_t i;
+
+       res = iface->driver->get_scan_results2(iface->drv_priv);
+       if (res == NULL)
+               goto fail;
+
+       buf = os_malloc(60000);
+       if (buf == NULL)
+               goto fail;
+       pos = buf;
+       end = buf + 60000;
+       val = res->num;
+       os_memcpy(pos, &val, sizeof(int));
+       pos += sizeof(int);
+
+       for (i = 0; i < res->num; i++) {
+               struct wpa_scan_res *r = res->res[i];
+               val = sizeof(*r) + r->ie_len;
+               if (end - pos < (int) sizeof(int) + val)
+                       break;
+               os_memcpy(pos, &val, sizeof(int));
+               pos += sizeof(int);
+               os_memcpy(pos, r, val);
+               pos += val;
+       }
+
+       sendto(iface->fd, buf, pos - buf, 0, (struct sockaddr *) from,
+              sizeof(*from));
+
+       os_free(buf);
+       wpa_scan_results_free(res);
+       return;
+
+fail:
+       os_free(buf);
+       wpa_scan_results_free(res);
+       sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_get_scan_results(struct wpa_priv_interface *iface,
+                                         struct sockaddr_un *from)
+{
+       if (iface->drv_priv == NULL)
+               return;
+
+       if (iface->driver->get_scan_results2)
+               wpa_priv_get_scan_results2(iface, from);
+       else
+               sendto(iface->fd, "", 0, 0, (struct sockaddr *) from,
+                      sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
+                                  void *buf, size_t len)
+{
+       struct wpa_driver_associate_params params;
+       struct privsep_cmd_associate *assoc;
+       u8 *bssid;
+       int res;
+
+       if (iface->drv_priv == NULL || iface->driver->associate == NULL)
+               return;
+
+       if (len < sizeof(*assoc)) {
+               wpa_printf(MSG_DEBUG, "Invalid association request");
+               return;
+       }
+
+       assoc = buf;
+       if (sizeof(*assoc) + assoc->wpa_ie_len > len) {
+               wpa_printf(MSG_DEBUG, "Association request overflow");
+               return;
+       }
+
+       os_memset(&params, 0, sizeof(params));
+       bssid = assoc->bssid;
+       if (bssid[0] | bssid[1] | bssid[2] | bssid[3] | bssid[4] | bssid[5])
+               params.bssid = bssid;
+       params.ssid = assoc->ssid;
+       if (assoc->ssid_len > 32)
+               return;
+       params.ssid_len = assoc->ssid_len;
+       params.freq = assoc->freq;
+       if (assoc->wpa_ie_len) {
+               params.wpa_ie = (u8 *) (assoc + 1);
+               params.wpa_ie_len = assoc->wpa_ie_len;
+       }
+       params.pairwise_suite = assoc->pairwise_suite;
+       params.group_suite = assoc->group_suite;
+       params.key_mgmt_suite = assoc->key_mgmt_suite;
+       params.auth_alg = assoc->auth_alg;
+       params.mode = assoc->mode;
+
+       res = iface->driver->associate(iface->drv_priv, &params);
+       wpa_printf(MSG_DEBUG, "drv->associate: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_get_bssid(struct wpa_priv_interface *iface,
+                                  struct sockaddr_un *from)
+{
+       u8 bssid[ETH_ALEN];
+
+       if (iface->drv_priv == NULL)
+               goto fail;
+
+       if (iface->driver->get_bssid == NULL ||
+           iface->driver->get_bssid(iface->drv_priv, bssid) < 0)
+               goto fail;
+
+       sendto(iface->fd, bssid, ETH_ALEN, 0, (struct sockaddr *) from,
+              sizeof(*from));
+       return;
+
+fail:
+       sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_get_ssid(struct wpa_priv_interface *iface,
+                                 struct sockaddr_un *from)
+{
+       u8 ssid[sizeof(int) + 32];
+       int res;
+
+       if (iface->drv_priv == NULL)
+               goto fail;
+
+       if (iface->driver->get_ssid == NULL)
+               goto fail;
+
+       res = iface->driver->get_ssid(iface->drv_priv, &ssid[sizeof(int)]);
+       if (res < 0 || res > 32)
+               goto fail;
+       os_memcpy(ssid, &res, sizeof(int));
+
+       sendto(iface->fd, ssid, sizeof(ssid), 0, (struct sockaddr *) from,
+              sizeof(*from));
+       return;
+
+fail:
+       sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_cmd_set_key(struct wpa_priv_interface *iface,
+                                void *buf, size_t len)
+{
+       struct privsep_cmd_set_key *params;
+       int res;
+
+       if (iface->drv_priv == NULL || iface->driver->set_key == NULL)
+               return;
+
+       if (len != sizeof(*params)) {
+               wpa_printf(MSG_DEBUG, "Invalid set_key request");
+               return;
+       }
+
+       params = buf;
+
+       res = iface->driver->set_key(iface->ifname, iface->drv_priv,
+                                    params->alg,
+                                    params->addr, params->key_idx,
+                                    params->set_tx,
+                                    params->seq_len ? params->seq : NULL,
+                                    params->seq_len,
+                                    params->key_len ? params->key : NULL,
+                                    params->key_len);
+       wpa_printf(MSG_DEBUG, "drv->set_key: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_get_capa(struct wpa_priv_interface *iface,
+                                 struct sockaddr_un *from)
+{
+       struct wpa_driver_capa capa;
+
+       if (iface->drv_priv == NULL)
+               goto fail;
+
+       if (iface->driver->get_capa == NULL ||
+           iface->driver->get_capa(iface->drv_priv, &capa) < 0)
+               goto fail;
+
+       sendto(iface->fd, &capa, sizeof(capa), 0, (struct sockaddr *) from,
+              sizeof(*from));
+       return;
+
+fail:
+       sendto(iface->fd, "", 0, 0, (struct sockaddr *) from, sizeof(*from));
+}
+
+
+static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+                          size_t len)
+{
+       struct wpa_priv_interface *iface = ctx;
+       struct msghdr msg;
+       struct iovec io[2];
+
+       io[0].iov_base = (u8 *) src_addr;
+       io[0].iov_len = ETH_ALEN;
+       io[1].iov_base = (u8 *) buf;
+       io[1].iov_len = len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 2;
+       msg.msg_name = &iface->l2_addr;
+       msg.msg_namelen = sizeof(iface->l2_addr);
+
+       if (sendmsg(iface->fd, &msg, 0) < 0) {
+               perror("sendmsg(l2 rx)");
+       }
+}
+
+
+static void wpa_priv_cmd_l2_register(struct wpa_priv_interface *iface,
+                                    struct sockaddr_un *from,
+                                    void *buf, size_t len)
+{
+       int *reg_cmd = buf;
+       u8 own_addr[ETH_ALEN];
+       int res;
+       u16 proto;
+
+       if (len != 2 * sizeof(int)) {
+               wpa_printf(MSG_DEBUG, "Invalid l2_register length %lu",
+                          (unsigned long) len);
+               return;
+       }
+
+       proto = reg_cmd[0];
+       if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+               wpa_printf(MSG_DEBUG, "Refused l2_packet connection for "
+                          "ethertype 0x%x", proto);
+               return;
+       }
+
+       if (iface->l2) {
+               wpa_printf(MSG_DEBUG, "Cleaning up forgotten l2_packet "
+                          "instance");
+               l2_packet_deinit(iface->l2);
+               iface->l2 = NULL;
+       }
+
+       os_memcpy(&iface->l2_addr, from, sizeof(iface->l2_addr));
+
+       iface->l2 = l2_packet_init(iface->ifname, NULL, proto,
+                                  wpa_priv_l2_rx, iface, reg_cmd[1]);
+       if (iface->l2 == NULL) {
+               wpa_printf(MSG_DEBUG, "Failed to initialize l2_packet "
+                          "instance for protocol %d", proto);
+               return;
+       }
+
+       if (l2_packet_get_own_addr(iface->l2, own_addr) < 0) {
+               wpa_printf(MSG_DEBUG, "Failed to get own address from "
+                          "l2_packet");
+               l2_packet_deinit(iface->l2);
+               iface->l2 = NULL;
+               return;
+       }
+
+       res = sendto(iface->fd, own_addr, ETH_ALEN, 0,
+                    (struct sockaddr *) from, sizeof(*from));
+       wpa_printf(MSG_DEBUG, "L2 registration: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_l2_unregister(struct wpa_priv_interface *iface,
+                                      struct sockaddr_un *from)
+{
+       if (iface->l2) {
+               l2_packet_deinit(iface->l2);
+               iface->l2 = NULL;
+       }
+}
+
+
+static void wpa_priv_cmd_l2_notify_auth_start(struct wpa_priv_interface *iface,
+                                             struct sockaddr_un *from)
+{
+       if (iface->l2)
+               l2_packet_notify_auth_start(iface->l2);
+}
+
+
+static void wpa_priv_cmd_l2_send(struct wpa_priv_interface *iface,
+                                struct sockaddr_un *from,
+                                void *buf, size_t len)
+{
+       u8 *dst_addr;
+       u16 proto;
+       int res;
+
+       if (iface->l2 == NULL)
+               return;
+
+       if (len < ETH_ALEN + 2) {
+               wpa_printf(MSG_DEBUG, "Too short L2 send packet (len=%lu)",
+                          (unsigned long) len);
+               return;
+       }
+
+       dst_addr = buf;
+       os_memcpy(&proto, buf + ETH_ALEN, 2);
+
+       if (proto != ETH_P_EAPOL && proto != ETH_P_RSN_PREAUTH) {
+               wpa_printf(MSG_DEBUG, "Refused l2_packet send for ethertype "
+                          "0x%x", proto);
+               return;
+       }
+
+       res = l2_packet_send(iface->l2, dst_addr, proto, buf + ETH_ALEN + 2,
+                            len - ETH_ALEN - 2);
+       wpa_printf(MSG_DEBUG, "L2 send: res=%d", res);
+}
+
+
+static void wpa_priv_cmd_set_country(struct wpa_priv_interface *iface,
+                                    char *buf)
+{
+       if (iface->drv_priv == NULL || iface->driver->set_country == NULL ||
+           *buf == '\0')
+               return;
+
+       iface->driver->set_country(iface->drv_priv, buf);
+}
+
+
+static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_priv_interface *iface = eloop_ctx;
+       char buf[2000], *pos;
+       void *cmd_buf;
+       size_t cmd_len;
+       int res, cmd;
+       struct sockaddr_un from;
+       socklen_t fromlen = sizeof(from);
+
+       res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
+                      &fromlen);
+       if (res < 0) {
+               perror("recvfrom");
+               return;
+       }
+
+       if (res < (int) sizeof(int)) {
+               wpa_printf(MSG_DEBUG, "Too short command (len=%d)", res);
+               return;
+       }
+
+       os_memcpy(&cmd, buf, sizeof(int));
+       wpa_printf(MSG_DEBUG, "Command %d for interface %s",
+                  cmd, iface->ifname);
+       cmd_buf = &buf[sizeof(int)];
+       cmd_len = res - sizeof(int);
+
+       switch (cmd) {
+       case PRIVSEP_CMD_REGISTER:
+               wpa_priv_cmd_register(iface, &from);
+               break;
+       case PRIVSEP_CMD_UNREGISTER:
+               wpa_priv_cmd_unregister(iface, &from);
+               break;
+       case PRIVSEP_CMD_SCAN:
+               wpa_priv_cmd_scan(iface, cmd_buf, cmd_len);
+               break;
+       case PRIVSEP_CMD_GET_SCAN_RESULTS:
+               wpa_priv_cmd_get_scan_results(iface, &from);
+               break;
+       case PRIVSEP_CMD_ASSOCIATE:
+               wpa_priv_cmd_associate(iface, cmd_buf, cmd_len);
+               break;
+       case PRIVSEP_CMD_GET_BSSID:
+               wpa_priv_cmd_get_bssid(iface, &from);
+               break;
+       case PRIVSEP_CMD_GET_SSID:
+               wpa_priv_cmd_get_ssid(iface, &from);
+               break;
+       case PRIVSEP_CMD_SET_KEY:
+               wpa_priv_cmd_set_key(iface, cmd_buf, cmd_len);
+               break;
+       case PRIVSEP_CMD_GET_CAPA:
+               wpa_priv_cmd_get_capa(iface, &from);
+               break;
+       case PRIVSEP_CMD_L2_REGISTER:
+               wpa_priv_cmd_l2_register(iface, &from, cmd_buf, cmd_len);
+               break;
+       case PRIVSEP_CMD_L2_UNREGISTER:
+               wpa_priv_cmd_l2_unregister(iface, &from);
+               break;
+       case PRIVSEP_CMD_L2_NOTIFY_AUTH_START:
+               wpa_priv_cmd_l2_notify_auth_start(iface, &from);
+               break;
+       case PRIVSEP_CMD_L2_SEND:
+               wpa_priv_cmd_l2_send(iface, &from, cmd_buf, cmd_len);
+               break;
+       case PRIVSEP_CMD_SET_COUNTRY:
+               pos = cmd_buf;
+               if (pos + cmd_len >= buf + sizeof(buf))
+                       break;
+               pos[cmd_len] = '\0';
+               wpa_priv_cmd_set_country(iface, pos);
+               break;
+       }
+}
+
+
+static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
+{
+       if (iface->drv_priv && iface->driver->deinit)
+               iface->driver->deinit(iface->drv_priv);
+
+       if (iface->fd >= 0) {
+               eloop_unregister_read_sock(iface->fd);
+               close(iface->fd);
+               unlink(iface->sock_name);
+       }
+
+       if (iface->l2)
+               l2_packet_deinit(iface->l2);
+
+       os_free(iface->ifname);
+       os_free(iface->driver_name);
+       os_free(iface->sock_name);
+       os_free(iface);
+}
+
+
+extern struct wpa_driver_ops *wpa_drivers[];
+
+static struct wpa_priv_interface *
+wpa_priv_interface_init(const char *dir, const char *params)
+{
+       struct wpa_priv_interface *iface;
+       char *pos;
+       size_t len;
+       struct sockaddr_un addr;
+       int i;
+
+       pos = os_strchr(params, ':');
+       if (pos == NULL)
+               return NULL;
+
+       iface = os_zalloc(sizeof(*iface));
+       if (iface == NULL)
+               return NULL;
+       iface->fd = -1;
+
+       len = pos - params;
+       iface->driver_name = os_malloc(len + 1);
+       if (iface->driver_name == NULL) {
+               wpa_priv_interface_deinit(iface);
+               return NULL;
+       }
+       os_memcpy(iface->driver_name, params, len);
+       iface->driver_name[len] = '\0';
+
+       for (i = 0; wpa_drivers[i]; i++) {
+               if (os_strcmp(iface->driver_name,
+                             wpa_drivers[i]->name) == 0) {
+                       iface->driver = wpa_drivers[i];
+                       break;
+               }
+       }
+       if (iface->driver == NULL) {
+               wpa_printf(MSG_ERROR, "Unsupported driver '%s'",
+                          iface->driver_name);
+               wpa_priv_interface_deinit(iface);
+               return NULL;
+       }
+
+       pos++;
+       iface->ifname = os_strdup(pos);
+       if (iface->ifname == NULL) {
+               wpa_priv_interface_deinit(iface);
+               return NULL;
+       }
+
+       len = os_strlen(dir) + 1 + os_strlen(iface->ifname);
+       iface->sock_name = os_malloc(len + 1);
+       if (iface->sock_name == NULL) {
+               wpa_priv_interface_deinit(iface);
+               return NULL;
+       }
+
+       os_snprintf(iface->sock_name, len + 1, "%s/%s", dir, iface->ifname);
+       if (os_strlen(iface->sock_name) >= sizeof(addr.sun_path)) {
+               wpa_priv_interface_deinit(iface);
+               return NULL;
+       }
+
+       iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
+       if (iface->fd < 0) {
+               perror("socket(PF_UNIX)");
+               wpa_priv_interface_deinit(iface);
+               return NULL;
+       }
+
+       os_memset(&addr, 0, sizeof(addr));
+       addr.sun_family = AF_UNIX;
+       os_strlcpy(addr.sun_path, iface->sock_name, sizeof(addr.sun_path));
+
+       if (bind(iface->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+               wpa_printf(MSG_DEBUG, "bind(PF_UNIX) failed: %s",
+                          strerror(errno));
+               if (connect(iface->fd, (struct sockaddr *) &addr,
+                           sizeof(addr)) < 0) {
+                       wpa_printf(MSG_DEBUG, "Socket exists, but does not "
+                                  "allow connections - assuming it was "
+                                  "leftover from forced program termination");
+                       if (unlink(iface->sock_name) < 0) {
+                               perror("unlink[ctrl_iface]");
+                               wpa_printf(MSG_ERROR, "Could not unlink "
+                                          "existing ctrl_iface socket '%s'",
+                                          iface->sock_name);
+                               goto fail;
+                       }
+                       if (bind(iface->fd, (struct sockaddr *) &addr,
+                                sizeof(addr)) < 0) {
+                               perror("bind(PF_UNIX)");
+                               goto fail;
+                       }
+                       wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
+                                  "socket '%s'", iface->sock_name);
+               } else {
+                       wpa_printf(MSG_INFO, "Socket exists and seems to be "
+                                  "in use - cannot override it");
+                       wpa_printf(MSG_INFO, "Delete '%s' manually if it is "
+                                  "not used anymore", iface->sock_name);
+                       goto fail;
+               }
+       }
+
+       if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+               perror("chmod");
+               goto fail;
+       }
+
+       eloop_register_read_sock(iface->fd, wpa_priv_receive, iface, NULL);
+
+       return iface;
+
+fail:
+       wpa_priv_interface_deinit(iface);
+       return NULL;
+}
+
+
+static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
+                              const void *data, size_t data_len)
+{
+       struct msghdr msg;
+       struct iovec io[2];
+
+       io[0].iov_base = &event;
+       io[0].iov_len = sizeof(event);
+       io[1].iov_base = (u8 *) data;
+       io[1].iov_len = data_len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = data ? 2 : 1;
+       msg.msg_name = &iface->drv_addr;
+       msg.msg_namelen = sizeof(iface->drv_addr);
+
+       if (sendmsg(iface->fd, &msg, 0) < 0) {
+               perror("sendmsg(wpas_socket)");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void wpa_priv_send_assoc(struct wpa_priv_interface *iface, int event,
+                               union wpa_event_data *data)
+{
+       size_t buflen = 3 * sizeof(int);
+       u8 *buf, *pos;
+       int len;
+
+       if (data) {
+               buflen += data->assoc_info.req_ies_len +
+                       data->assoc_info.resp_ies_len +
+                       data->assoc_info.beacon_ies_len;
+       }
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+
+       pos = buf;
+
+       if (data && data->assoc_info.req_ies) {
+               len = data->assoc_info.req_ies_len;
+               os_memcpy(pos, &len, sizeof(int));
+               pos += sizeof(int);
+               os_memcpy(pos, data->assoc_info.req_ies, len);
+               pos += len;
+       } else {
+               len = 0;
+               os_memcpy(pos, &len, sizeof(int));
+               pos += sizeof(int);
+       }
+
+       if (data && data->assoc_info.resp_ies) {
+               len = data->assoc_info.resp_ies_len;
+               os_memcpy(pos, &len, sizeof(int));
+               pos += sizeof(int);
+               os_memcpy(pos, data->assoc_info.resp_ies, len);
+               pos += len;
+       } else {
+               len = 0;
+               os_memcpy(pos, &len, sizeof(int));
+               pos += sizeof(int);
+       }
+
+       if (data && data->assoc_info.beacon_ies) {
+               len = data->assoc_info.beacon_ies_len;
+               os_memcpy(pos, &len, sizeof(int));
+               pos += sizeof(int);
+               os_memcpy(pos, data->assoc_info.beacon_ies, len);
+               pos += len;
+       } else {
+               len = 0;
+               os_memcpy(pos, &len, sizeof(int));
+               pos += sizeof(int);
+       }
+
+       wpa_priv_send_event(iface, event, buf, buflen);
+
+       os_free(buf);
+}
+
+
+static void wpa_priv_send_interface_status(struct wpa_priv_interface *iface,
+                                          union wpa_event_data *data)
+{
+       int ievent;
+       size_t len, maxlen;
+       u8 *buf;
+       char *ifname;
+
+       if (data == NULL)
+               return;
+
+       ievent = data->interface_status.ievent;
+       maxlen = sizeof(data->interface_status.ifname);
+       ifname = data->interface_status.ifname;
+       for (len = 0; len < maxlen && ifname[len]; len++)
+               ;
+
+       buf = os_malloc(sizeof(int) + len);
+       if (buf == NULL)
+               return;
+
+       os_memcpy(buf, &ievent, sizeof(int));
+       os_memcpy(buf + sizeof(int), ifname, len);
+
+       wpa_priv_send_event(iface, PRIVSEP_EVENT_INTERFACE_STATUS,
+                           buf, sizeof(int) + len);
+
+       os_free(buf);
+
+}
+
+
+static void wpa_priv_send_ft_response(struct wpa_priv_interface *iface,
+                                     union wpa_event_data *data)
+{
+       size_t len;
+       u8 *buf, *pos;
+
+       if (data == NULL || data->ft_ies.ies == NULL)
+               return;
+
+       len = sizeof(int) + ETH_ALEN + data->ft_ies.ies_len;
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return;
+
+       pos = buf;
+       os_memcpy(pos, &data->ft_ies.ft_action, sizeof(int));
+       pos += sizeof(int);
+       os_memcpy(pos, data->ft_ies.target_ap, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, data->ft_ies.ies, data->ft_ies.ies_len);
+
+       wpa_priv_send_event(iface, PRIVSEP_EVENT_FT_RESPONSE, buf, len);
+
+       os_free(buf);
+
+}
+
+
+void wpa_supplicant_event(void *ctx, wpa_event_type event,
+                         union wpa_event_data *data)
+{
+       struct wpa_priv_interface *iface = ctx;
+
+       wpa_printf(MSG_DEBUG, "%s - event=%d", __func__, event);
+
+       if (!iface->wpas_registered) {
+               wpa_printf(MSG_DEBUG, "Driver event received, but "
+                          "wpa_supplicant not registered");
+               return;
+       }
+
+       switch (event) {
+       case EVENT_ASSOC:
+               wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOC, data);
+               break;
+       case EVENT_DISASSOC:
+               wpa_priv_send_event(iface, PRIVSEP_EVENT_DISASSOC, NULL, 0);
+               break;
+       case EVENT_ASSOCINFO:
+               if (data == NULL)
+                       return;
+               wpa_priv_send_assoc(iface, PRIVSEP_EVENT_ASSOCINFO, data);
+               break;
+       case EVENT_MICHAEL_MIC_FAILURE:
+               if (data == NULL)
+                       return;
+               wpa_priv_send_event(iface, PRIVSEP_EVENT_MICHAEL_MIC_FAILURE,
+                                   &data->michael_mic_failure.unicast,
+                                   sizeof(int));
+               break;
+       case EVENT_SCAN_RESULTS:
+               wpa_priv_send_event(iface, PRIVSEP_EVENT_SCAN_RESULTS, NULL,
+                                   0);
+               break;
+       case EVENT_INTERFACE_STATUS:
+               wpa_priv_send_interface_status(iface, data);
+               break;
+       case EVENT_PMKID_CANDIDATE:
+               if (data == NULL)
+                       return;
+               wpa_priv_send_event(iface, PRIVSEP_EVENT_PMKID_CANDIDATE,
+                                   &data->pmkid_candidate,
+                                   sizeof(struct pmkid_candidate));
+               break;
+       case EVENT_STKSTART:
+               if (data == NULL)
+                       return;
+               wpa_priv_send_event(iface, PRIVSEP_EVENT_STKSTART,
+                                   &data->stkstart.peer, ETH_ALEN);
+               break;
+       case EVENT_FT_RESPONSE:
+               wpa_priv_send_ft_response(iface, data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "Unsupported driver event %d - TODO",
+                          event);
+               break;
+       }
+}
+
+
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+                            const u8 *buf, size_t len)
+{
+       struct wpa_priv_interface *iface = ctx;
+       struct msghdr msg;
+       struct iovec io[3];
+       int event = PRIVSEP_EVENT_RX_EAPOL;
+
+       wpa_printf(MSG_DEBUG, "RX EAPOL from driver");
+       io[0].iov_base = &event;
+       io[0].iov_len = sizeof(event);
+       io[1].iov_base = (u8 *) src_addr;
+       io[1].iov_len = ETH_ALEN;
+       io[2].iov_base = (u8 *) buf;
+       io[2].iov_len = len;
+
+       os_memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = io;
+       msg.msg_iovlen = 3;
+       msg.msg_name = &iface->drv_addr;
+       msg.msg_namelen = sizeof(iface->drv_addr);
+
+       if (sendmsg(iface->fd, &msg, 0) < 0)
+               perror("sendmsg(wpas_socket)");
+}
+
+
+static void wpa_priv_terminate(int sig, void *eloop_ctx, void *signal_ctx)
+{
+       wpa_printf(MSG_DEBUG, "wpa_priv termination requested");
+       eloop_terminate();
+}
+
+
+static void wpa_priv_fd_workaround(void)
+{
+#ifdef __linux__
+       int s, i;
+       /* When started from pcmcia-cs scripts, wpa_supplicant might start with
+        * fd 0, 1, and 2 closed. This will cause some issues because many
+        * places in wpa_supplicant are still printing out to stdout. As a
+        * workaround, make sure that fd's 0, 1, and 2 are not used for other
+        * sockets. */
+       for (i = 0; i < 3; i++) {
+               s = open("/dev/null", O_RDWR);
+               if (s > 2) {
+                       close(s);
+                       break;
+               }
+       }
+#endif /* __linux__ */
+}
+
+
+static void usage(void)
+{
+       printf("wpa_priv v" VERSION_STR "\n"
+              "Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> and "
+              "contributors\n"
+              "\n"
+              "usage:\n"
+              "  wpa_priv [-Bdd] [-P<pid file>] <driver:ifname> "
+              "[driver:ifname ...]\n");
+}
+
+
+extern int wpa_debug_level;
+
+int main(int argc, char *argv[])
+{
+       int c, i;
+       int ret = -1;
+       char *pid_file = NULL;
+       int daemonize = 0;
+       char *ctrl_dir = "/var/run/wpa_priv";
+       struct wpa_priv_interface *interfaces = NULL, *iface;
+
+       if (os_program_init())
+               return -1;
+
+       wpa_priv_fd_workaround();
+
+       for (;;) {
+               c = getopt(argc, argv, "Bc:dP:");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'B':
+                       daemonize++;
+                       break;
+               case 'c':
+                       ctrl_dir = optarg;
+                       break;
+               case 'd':
+                       wpa_debug_level--;
+                       break;
+               case 'P':
+                       pid_file = os_rel2abs_path(optarg);
+                       break;
+               default:
+                       usage();
+                       goto out;
+               }
+       }
+
+       if (optind >= argc) {
+               usage();
+               goto out;
+       }
+
+       wpa_printf(MSG_DEBUG, "wpa_priv control directory: '%s'", ctrl_dir);
+
+       if (eloop_init()) {
+               wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+               goto out;
+       }
+
+       for (i = optind; i < argc; i++) {
+               wpa_printf(MSG_DEBUG, "Adding driver:interface %s", argv[i]);
+               iface = wpa_priv_interface_init(ctrl_dir, argv[i]);
+               if (iface == NULL)
+                       goto out;
+               iface->next = interfaces;
+               interfaces = iface;
+       }
+
+       if (daemonize && os_daemonize(pid_file))
+               goto out;
+
+       eloop_register_signal_terminate(wpa_priv_terminate, NULL);
+       eloop_run();
+
+       ret = 0;
+
+out:
+       iface = interfaces;
+       while (iface) {
+               struct wpa_priv_interface *prev = iface;
+               iface = iface->next;
+               wpa_priv_interface_deinit(prev);
+       }
+
+       eloop_destroy();
+
+       os_daemonize_terminate(pid_file);
+       os_free(pid_file);
+       os_program_deinit();
+
+       return ret;
+}
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
new file mode 100644 (file)
index 0000000..37a539d
--- /dev/null
@@ -0,0 +1,2410 @@
+/*
+ * WPA Supplicant
+ * Copyright (c) 2003-2010, 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.
+ *
+ * This file implements functions for registering and unregistering
+ * %wpa_supplicant interfaces. In addition, this file contains number of
+ * functions for managing network connections.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "eap_peer/eap.h"
+#include "eap_server/eap_methods.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "ctrl_iface.h"
+#include "pcsc_funcs.h"
+#include "common/version.h"
+#include "rsn_supp/preauth.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "common/wpa_ctrl.h"
+#include "mlme.h"
+#include "common/ieee802_11_defs.h"
+#include "blacklist.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "ibss_rsn.h"
+#include "sme.h"
+#include "ap.h"
+#include "notify.h"
+#include "bgscan.h"
+#include "bss.h"
+#include "scan.h"
+
+const char *wpa_supplicant_version =
+"wpa_supplicant v" VERSION_STR "\n"
+"Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> and contributors";
+
+const char *wpa_supplicant_license =
+"This program is free software. You can distribute it and/or modify it\n"
+"under the terms of the GNU General Public License version 2.\n"
+"\n"
+"Alternatively, this software may be distributed under the terms of the\n"
+"BSD license. See README and COPYING for more details.\n"
+#ifdef EAP_TLS_OPENSSL
+"\nThis product includes software developed by the OpenSSL Project\n"
+"for use in the OpenSSL Toolkit (http://www.openssl.org/)\n"
+#endif /* EAP_TLS_OPENSSL */
+;
+
+#ifndef CONFIG_NO_STDOUT_DEBUG
+/* Long text divided into parts in order to fit in C89 strings size limits. */
+const char *wpa_supplicant_full_license1 =
+"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";
+const char *wpa_supplicant_full_license2 =
+"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";
+const char *wpa_supplicant_full_license3 =
+"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";
+const char *wpa_supplicant_full_license4 =
+"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";
+const char *wpa_supplicant_full_license5 =
+"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";
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+extern struct wpa_driver_ops *wpa_drivers[];
+
+/* Configure default/group WEP keys for static WEP */
+int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+       int i, set = 0;
+
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (ssid->wep_key_len[i] == 0)
+                       continue;
+
+               set = 1;
+               wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+                               (u8 *) "\xff\xff\xff\xff\xff\xff",
+                               i, i == ssid->wep_tx_keyidx, (u8 *) "", 0,
+                               ssid->wep_key[i], ssid->wep_key_len[i]);
+       }
+
+       return set;
+}
+
+
+static int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
+                                          struct wpa_ssid *ssid)
+{
+       u8 key[32];
+       size_t keylen;
+       enum wpa_alg alg;
+       u8 seq[6] = { 0 };
+
+       /* IBSS/WPA-None uses only one key (Group) for both receiving and
+        * sending unicast and multicast packets. */
+
+       if (ssid->mode != WPAS_MODE_IBSS) {
+               wpa_printf(MSG_INFO, "WPA: Invalid mode %d (not IBSS/ad-hoc) "
+                          "for WPA-None", ssid->mode);
+               return -1;
+       }
+
+       if (!ssid->psk_set) {
+               wpa_printf(MSG_INFO, "WPA: No PSK configured for WPA-None");
+               return -1;
+       }
+
+       switch (wpa_s->group_cipher) {
+       case WPA_CIPHER_CCMP:
+               os_memcpy(key, ssid->psk, 16);
+               keylen = 16;
+               alg = WPA_ALG_CCMP;
+               break;
+       case WPA_CIPHER_TKIP:
+               /* WPA-None uses the same Michael MIC key for both TX and RX */
+               os_memcpy(key, ssid->psk, 16 + 8);
+               os_memcpy(key + 16 + 8, ssid->psk + 16, 8);
+               keylen = 32;
+               alg = WPA_ALG_TKIP;
+               break;
+       default:
+               wpa_printf(MSG_INFO, "WPA: Invalid group cipher %d for "
+                          "WPA-None", wpa_s->group_cipher);
+               return -1;
+       }
+
+       /* TODO: should actually remember the previously used seq#, both for TX
+        * and RX from each STA.. */
+
+       return wpa_drv_set_key(wpa_s, alg, (u8 *) "\xff\xff\xff\xff\xff\xff",
+                              0, 1, seq, 6, key, keylen);
+}
+
+
+static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       const u8 *bssid = wpa_s->bssid;
+       if (is_zero_ether_addr(bssid))
+               bssid = wpa_s->pending_bssid;
+       wpa_msg(wpa_s, MSG_INFO, "Authentication with " MACSTR " timed out.",
+               MAC2STR(bssid));
+       wpa_blacklist_add(wpa_s, bssid);
+       wpa_sm_notify_disassoc(wpa_s->wpa);
+       wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+       wpa_s->reassociate = 1;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+/**
+ * wpa_supplicant_req_auth_timeout - Schedule a timeout for authentication
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @sec: Number of seconds after which to time out authentication
+ * @usec: Number of microseconds after which to time out authentication
+ *
+ * This function is used to schedule a timeout for the current authentication
+ * attempt.
+ */
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+                                    int sec, int usec)
+{
+       if (wpa_s->conf && wpa_s->conf->ap_scan == 0 &&
+           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+               return;
+
+       wpa_msg(wpa_s, MSG_DEBUG, "Setting authentication timeout: %d sec "
+               "%d usec", sec, usec);
+       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+       eloop_register_timeout(sec, usec, wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_cancel_auth_timeout - Cancel authentication timeout
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to cancel authentication timeout scheduled with
+ * wpa_supplicant_req_auth_timeout() and it is called when authentication has
+ * been completed.
+ */
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s)
+{
+       wpa_msg(wpa_s, MSG_DEBUG, "Cancelling authentication timeout");
+       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+       wpa_blacklist_del(wpa_s, wpa_s->bssid);
+}
+
+
+/**
+ * wpa_supplicant_initiate_eapol - Configure EAPOL state machine
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to configure EAPOL state machine based on the selected
+ * authentication mode.
+ */
+void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+       struct eapol_config eapol_conf;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+#ifdef CONFIG_IBSS_RSN
+       if (ssid->mode == WPAS_MODE_IBSS &&
+           wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+           wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
+               /*
+                * RSN IBSS authentication is per-STA and we can disable the
+                * per-BSSID EAPOL authentication.
+                */
+               eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+               eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+               eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+               return;
+       }
+#endif /* CONFIG_IBSS_RSN */
+
+       eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+       eapol_sm_notify_eap_fail(wpa_s->eapol, FALSE);
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE)
+               eapol_sm_notify_portControl(wpa_s->eapol, ForceAuthorized);
+       else
+               eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+
+       os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               eapol_conf.accept_802_1x_keys = 1;
+               eapol_conf.required_keys = 0;
+               if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_UNICAST) {
+                       eapol_conf.required_keys |= EAPOL_REQUIRE_KEY_UNICAST;
+               }
+               if (ssid->eapol_flags & EAPOL_FLAG_REQUIRE_KEY_BROADCAST) {
+                       eapol_conf.required_keys |=
+                               EAPOL_REQUIRE_KEY_BROADCAST;
+               }
+
+               if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+                       eapol_conf.required_keys = 0;
+       }
+       if (wpa_s->conf)
+               eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+       eapol_conf.workaround = ssid->eap_workaround;
+       eapol_conf.eap_disabled =
+               !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
+               wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+               wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
+       eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+#endif /* IEEE8021X_EAPOL */
+}
+
+
+/**
+ * wpa_supplicant_set_non_wpa_policy - Set WPA parameters to non-WPA mode
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @ssid: Configuration data for the network
+ *
+ * This function is used to configure WPA state machine and related parameters
+ * to a mode where WPA is not enabled. This is called as part of the
+ * authentication configuration when the selected network does not use WPA.
+ */
+void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+                                      struct wpa_ssid *ssid)
+{
+       int i;
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
+               wpa_s->key_mgmt = WPA_KEY_MGMT_WPS;
+       else if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+       else
+               wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
+       wpa_sm_set_ap_wpa_ie(wpa_s->wpa, NULL, 0);
+       wpa_sm_set_ap_rsn_ie(wpa_s->wpa, NULL, 0);
+       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+       wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+       wpa_s->group_cipher = WPA_CIPHER_NONE;
+       wpa_s->mgmt_group_cipher = 0;
+
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (ssid->wep_key_len[i] > 5) {
+                       wpa_s->pairwise_cipher = WPA_CIPHER_WEP104;
+                       wpa_s->group_cipher = WPA_CIPHER_WEP104;
+                       break;
+               } else if (ssid->wep_key_len[i] > 0) {
+                       wpa_s->pairwise_cipher = WPA_CIPHER_WEP40;
+                       wpa_s->group_cipher = WPA_CIPHER_WEP40;
+                       break;
+               }
+       }
+
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED, 0);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+                        wpa_s->pairwise_cipher);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+#ifdef CONFIG_IEEE80211W
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+                        wpa_s->mgmt_group_cipher);
+#endif /* CONFIG_IEEE80211W */
+
+       pmksa_cache_clear_current(wpa_s->wpa);
+}
+
+
+static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
+{
+       bgscan_deinit(wpa_s);
+       scard_deinit(wpa_s->scard);
+       wpa_s->scard = NULL;
+       wpa_sm_set_scard_ctx(wpa_s->wpa, NULL);
+       eapol_sm_register_scard_ctx(wpa_s->eapol, NULL);
+       l2_packet_deinit(wpa_s->l2);
+       wpa_s->l2 = NULL;
+       if (wpa_s->l2_br) {
+               l2_packet_deinit(wpa_s->l2_br);
+               wpa_s->l2_br = NULL;
+       }
+
+       if (wpa_s->ctrl_iface) {
+               wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+               wpa_s->ctrl_iface = NULL;
+       }
+       if (wpa_s->conf != NULL) {
+               struct wpa_ssid *ssid;
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+                       wpas_notify_network_removed(wpa_s, ssid);
+               wpa_config_free(wpa_s->conf);
+               wpa_s->conf = NULL;
+       }
+
+       os_free(wpa_s->confname);
+       wpa_s->confname = NULL;
+
+       wpa_sm_set_eapol(wpa_s->wpa, NULL);
+       eapol_sm_deinit(wpa_s->eapol);
+       wpa_s->eapol = NULL;
+
+       rsn_preauth_deinit(wpa_s->wpa);
+
+       pmksa_candidate_free(wpa_s->wpa);
+       wpa_sm_deinit(wpa_s->wpa);
+       wpa_s->wpa = NULL;
+       wpa_blacklist_clear(wpa_s);
+
+       wpa_bss_deinit(wpa_s);
+
+       wpa_supplicant_cancel_scan(wpa_s);
+       wpa_supplicant_cancel_auth_timeout(wpa_s);
+
+       ieee80211_sta_deinit(wpa_s);
+
+       wpas_wps_deinit(wpa_s);
+
+       wpabuf_free(wpa_s->pending_eapol_rx);
+       wpa_s->pending_eapol_rx = NULL;
+
+#ifdef CONFIG_IBSS_RSN
+       ibss_rsn_deinit(wpa_s->ibss_rsn);
+       wpa_s->ibss_rsn = NULL;
+#endif /* CONFIG_IBSS_RSN */
+
+#ifdef CONFIG_SME
+       os_free(wpa_s->sme.ft_ies);
+       wpa_s->sme.ft_ies = NULL;
+       wpa_s->sme.ft_ies_len = 0;
+#endif /* CONFIG_SME */
+
+#ifdef CONFIG_AP
+       wpa_supplicant_ap_deinit(wpa_s);
+#endif /* CONFIG_AP */
+}
+
+
+/**
+ * wpa_clear_keys - Clear keys configured for the driver
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @addr: Previously used BSSID or %NULL if not available
+ *
+ * This function clears the encryption keys that has been previously configured
+ * for the driver.
+ */
+void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+       u8 *bcast = (u8 *) "\xff\xff\xff\xff\xff\xff";
+
+       if (wpa_s->keys_cleared) {
+               /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
+                * timing issues with keys being cleared just before new keys
+                * are set or just after association or something similar. This
+                * shows up in group key handshake failing often because of the
+                * client not receiving the first encrypted packets correctly.
+                * Skipping some of the extra key clearing steps seems to help
+                * in completing group key handshake more reliably. */
+               wpa_printf(MSG_DEBUG, "No keys have been configured - "
+                          "skip key clearing");
+               return;
+       }
+
+       /* MLME-DELETEKEYS.request */
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 0, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 1, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 2, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 3, 0, NULL, 0, NULL, 0);
+#ifdef CONFIG_IEEE80211W
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 4, 0, NULL, 0, NULL, 0);
+       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, bcast, 5, 0, NULL, 0, NULL, 0);
+#endif /* CONFIG_IEEE80211W */
+       if (addr) {
+               wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
+                               0);
+               /* MLME-SETPROTECTION.request(None) */
+               wpa_drv_mlme_setprotection(
+                       wpa_s, addr,
+                       MLME_SETPROTECTION_PROTECT_TYPE_NONE,
+                       MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
+       }
+       wpa_s->keys_cleared = 1;
+}
+
+
+/**
+ * wpa_supplicant_state_txt - Get the connection state name as a text string
+ * @state: State (wpa_state; WPA_*)
+ * Returns: The state name as a printable text string
+ */
+const char * wpa_supplicant_state_txt(enum wpa_states state)
+{
+       switch (state) {
+       case WPA_DISCONNECTED:
+               return "DISCONNECTED";
+       case WPA_INACTIVE:
+               return "INACTIVE";
+       case WPA_SCANNING:
+               return "SCANNING";
+       case WPA_AUTHENTICATING:
+               return "AUTHENTICATING";
+       case WPA_ASSOCIATING:
+               return "ASSOCIATING";
+       case WPA_ASSOCIATED:
+               return "ASSOCIATED";
+       case WPA_4WAY_HANDSHAKE:
+               return "4WAY_HANDSHAKE";
+       case WPA_GROUP_HANDSHAKE:
+               return "GROUP_HANDSHAKE";
+       case WPA_COMPLETED:
+               return "COMPLETED";
+       default:
+               return "UNKNOWN";
+       }
+}
+
+
+/**
+ * wpa_supplicant_set_state - Set current connection state
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @state: The new connection state
+ *
+ * This function is called whenever the connection state changes, e.g.,
+ * association is completed for WPA/WPA2 4-Way Handshake is started.
+ */
+void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+                             enum wpa_states state)
+{
+       enum wpa_states old_state = wpa_s->wpa_state;
+
+       wpa_printf(MSG_DEBUG, "State: %s -> %s",
+                  wpa_supplicant_state_txt(wpa_s->wpa_state),
+                  wpa_supplicant_state_txt(state));
+
+       if (state != WPA_SCANNING)
+               wpa_supplicant_notify_scanning(wpa_s, 0);
+
+       if (state == WPA_COMPLETED && wpa_s->new_connection) {
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+               struct wpa_ssid *ssid = wpa_s->current_ssid;
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
+                       MACSTR " completed %s [id=%d id_str=%s]",
+                       MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ?
+                       "(reauth)" : "(auth)",
+                       ssid ? ssid->id : -1,
+                       ssid && ssid->id_str ? ssid->id_str : "");
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+               wpa_s->new_connection = 0;
+               wpa_s->reassociated_connection = 1;
+               wpa_drv_set_operstate(wpa_s, 1);
+               wpa_s->after_wps = 0;
+       } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
+                  state == WPA_ASSOCIATED) {
+               wpa_s->new_connection = 1;
+               wpa_drv_set_operstate(wpa_s, 0);
+       }
+       wpa_s->wpa_state = state;
+
+       if (wpa_s->wpa_state != old_state)
+               wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+}
+
+
+void wpa_supplicant_terminate_proc(struct wpa_global *global)
+{
+       int pending = 0;
+#ifdef CONFIG_WPS
+       struct wpa_supplicant *wpa_s = global->ifaces;
+       while (wpa_s) {
+               if (wpas_wps_terminate_pending(wpa_s) == 1)
+                       pending = 1;
+               wpa_s = wpa_s->next;
+       }
+#endif /* CONFIG_WPS */
+       if (pending)
+               return;
+       eloop_terminate();
+}
+
+
+static void wpa_supplicant_terminate(int sig, void *signal_ctx)
+{
+       struct wpa_global *global = signal_ctx;
+       struct wpa_supplicant *wpa_s;
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TERMINATING "- signal %d "
+                       "received", sig);
+       }
+       wpa_supplicant_terminate_proc(global);
+}
+
+
+static void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s)
+{
+       enum wpa_states old_state = wpa_s->wpa_state;
+
+       wpa_s->pairwise_cipher = 0;
+       wpa_s->group_cipher = 0;
+       wpa_s->mgmt_group_cipher = 0;
+       wpa_s->key_mgmt = 0;
+       wpa_s->wpa_state = WPA_DISCONNECTED;
+
+       if (wpa_s->wpa_state != old_state)
+               wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
+}
+
+
+/**
+ * wpa_supplicant_reload_configuration - Reload configuration data
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success or -1 if configuration parsing failed
+ *
+ * This function can be used to request that the configuration data is reloaded
+ * (e.g., after configuration file change). This function is reloading
+ * configuration only for one interface, so this may need to be called multiple
+ * times if %wpa_supplicant is controlling multiple interfaces and all
+ * interfaces need reconfiguration.
+ */
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_config *conf;
+       struct wpa_ssid *old_ssid;
+       int reconf_ctrl;
+       int old_ap_scan;
+
+       if (wpa_s->confname == NULL)
+               return -1;
+       conf = wpa_config_read(wpa_s->confname);
+       if (conf == NULL) {
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to parse the configuration "
+                       "file '%s' - exiting", wpa_s->confname);
+               return -1;
+       }
+
+       reconf_ctrl = !!conf->ctrl_interface != !!wpa_s->conf->ctrl_interface
+               || (conf->ctrl_interface && wpa_s->conf->ctrl_interface &&
+                   os_strcmp(conf->ctrl_interface,
+                             wpa_s->conf->ctrl_interface) != 0);
+
+       if (reconf_ctrl && wpa_s->ctrl_iface) {
+               wpa_supplicant_ctrl_iface_deinit(wpa_s->ctrl_iface);
+               wpa_s->ctrl_iface = NULL;
+       }
+
+       eapol_sm_invalidate_cached_session(wpa_s->eapol);
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = NULL;
+       if (old_ssid != wpa_s->current_ssid)
+               wpas_notify_network_changed(wpa_s);
+
+       /*
+        * TODO: should notify EAPOL SM about changes in opensc_engine_path,
+        * pkcs11_engine_path, pkcs11_module_path.
+        */
+       if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
+               /*
+                * Clear forced success to clear EAP state for next
+                * authentication.
+                */
+               eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
+       }
+       eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+       wpa_sm_set_config(wpa_s->wpa, NULL);
+       wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
+       rsn_preauth_deinit(wpa_s->wpa);
+
+       old_ap_scan = wpa_s->conf->ap_scan;
+       wpa_config_free(wpa_s->conf);
+       wpa_s->conf = conf;
+       if (old_ap_scan != wpa_s->conf->ap_scan)
+               wpas_notify_ap_scan_changed(wpa_s);
+
+       if (reconf_ctrl)
+               wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+
+       wpa_supplicant_clear_status(wpa_s);
+       wpa_s->reassociate = 1;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+       wpa_msg(wpa_s, MSG_DEBUG, "Reconfiguration completed");
+       return 0;
+}
+
+
+static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
+{
+       struct wpa_global *global = signal_ctx;
+       struct wpa_supplicant *wpa_s;
+       wpa_printf(MSG_DEBUG, "Signal %d received - reconfiguring", sig);
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (wpa_supplicant_reload_configuration(wpa_s) < 0) {
+                       wpa_supplicant_terminate_proc(global);
+               }
+       }
+}
+
+
+enum wpa_cipher cipher_suite2driver(int cipher)
+{
+       switch (cipher) {
+       case WPA_CIPHER_NONE:
+               return CIPHER_NONE;
+       case WPA_CIPHER_WEP40:
+               return CIPHER_WEP40;
+       case WPA_CIPHER_WEP104:
+               return CIPHER_WEP104;
+       case WPA_CIPHER_CCMP:
+               return CIPHER_CCMP;
+       case WPA_CIPHER_TKIP:
+       default:
+               return CIPHER_TKIP;
+       }
+}
+
+
+enum wpa_key_mgmt key_mgmt2driver(int key_mgmt)
+{
+       switch (key_mgmt) {
+       case WPA_KEY_MGMT_NONE:
+               return KEY_MGMT_NONE;
+       case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
+               return KEY_MGMT_802_1X_NO_WPA;
+       case WPA_KEY_MGMT_IEEE8021X:
+               return KEY_MGMT_802_1X;
+       case WPA_KEY_MGMT_WPA_NONE:
+               return KEY_MGMT_WPA_NONE;
+       case WPA_KEY_MGMT_FT_IEEE8021X:
+               return KEY_MGMT_FT_802_1X;
+       case WPA_KEY_MGMT_FT_PSK:
+               return KEY_MGMT_FT_PSK;
+       case WPA_KEY_MGMT_IEEE8021X_SHA256:
+               return KEY_MGMT_802_1X_SHA256;
+       case WPA_KEY_MGMT_PSK_SHA256:
+               return KEY_MGMT_PSK_SHA256;
+       case WPA_KEY_MGMT_WPS:
+               return KEY_MGMT_WPS;
+       case WPA_KEY_MGMT_PSK:
+       default:
+               return KEY_MGMT_PSK;
+       }
+}
+
+
+static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
+                                        struct wpa_ssid *ssid,
+                                        struct wpa_ie_data *ie)
+{
+       int ret = wpa_sm_parse_own_wpa_ie(wpa_s->wpa, ie);
+       if (ret) {
+               if (ret == -2) {
+                       wpa_msg(wpa_s, MSG_INFO, "WPA: Failed to parse WPA IE "
+                               "from association info");
+               }
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPA: Using WPA IE from AssocReq to set cipher "
+                  "suites");
+       if (!(ie->group_cipher & ssid->group_cipher)) {
+               wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled group "
+                       "cipher 0x%x (mask 0x%x) - reject",
+                       ie->group_cipher, ssid->group_cipher);
+               return -1;
+       }
+       if (!(ie->pairwise_cipher & ssid->pairwise_cipher)) {
+               wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled pairwise "
+                       "cipher 0x%x (mask 0x%x) - reject",
+                       ie->pairwise_cipher, ssid->pairwise_cipher);
+               return -1;
+       }
+       if (!(ie->key_mgmt & ssid->key_mgmt)) {
+               wpa_msg(wpa_s, MSG_INFO, "WPA: Driver used disabled key "
+                       "management 0x%x (mask 0x%x) - reject",
+                       ie->key_mgmt, ssid->key_mgmt);
+               return -1;
+       }
+
+#ifdef CONFIG_IEEE80211W
+       if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
+           ssid->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) {
+               wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
+                       "that does not support management frame protection - "
+                       "reject");
+               return -1;
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_suites - Set authentication and encryption parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ * @wpa_ie: Buffer for the WPA/RSN IE
+ * @wpa_ie_len: Maximum wpa_ie buffer size on input. This is changed to be the
+ * used buffer length in case the functions returns success.
+ * Returns: 0 on success or -1 on failure
+ *
+ * This function is used to configure authentication and encryption parameters
+ * based on the network configuration and scan result for the selected BSS (if
+ * available).
+ */
+int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+                             struct wpa_bss *bss, struct wpa_ssid *ssid,
+                             u8 *wpa_ie, size_t *wpa_ie_len)
+{
+       struct wpa_ie_data ie;
+       int sel, proto;
+       const u8 *bss_wpa, *bss_rsn;
+
+       if (bss) {
+               bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+               bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+       } else
+               bss_wpa = bss_rsn = NULL;
+
+       if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
+           wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
+           (ie.group_cipher & ssid->group_cipher) &&
+           (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+           (ie.key_mgmt & ssid->key_mgmt)) {
+               wpa_msg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
+               proto = WPA_PROTO_RSN;
+       } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
+                  wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 &&
+                  (ie.group_cipher & ssid->group_cipher) &&
+                  (ie.pairwise_cipher & ssid->pairwise_cipher) &&
+                  (ie.key_mgmt & ssid->key_mgmt)) {
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
+               proto = WPA_PROTO_WPA;
+       } else if (bss) {
+               wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
+               return -1;
+       } else {
+               if (ssid->proto & WPA_PROTO_RSN)
+                       proto = WPA_PROTO_RSN;
+               else
+                       proto = WPA_PROTO_WPA;
+               if (wpa_supplicant_suites_from_ai(wpa_s, ssid, &ie) < 0) {
+                       os_memset(&ie, 0, sizeof(ie));
+                       ie.group_cipher = ssid->group_cipher;
+                       ie.pairwise_cipher = ssid->pairwise_cipher;
+                       ie.key_mgmt = ssid->key_mgmt;
+#ifdef CONFIG_IEEE80211W
+                       ie.mgmt_group_cipher =
+                               ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION ?
+                               WPA_CIPHER_AES_128_CMAC : 0;
+#endif /* CONFIG_IEEE80211W */
+                       wpa_printf(MSG_DEBUG, "WPA: Set cipher suites based "
+                                  "on configuration");
+               } else
+                       proto = ie.proto;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPA: Selected cipher suites: group %d "
+                  "pairwise %d key_mgmt %d proto %d",
+                  ie.group_cipher, ie.pairwise_cipher, ie.key_mgmt, proto);
+#ifdef CONFIG_IEEE80211W
+       if (ssid->ieee80211w) {
+               wpa_printf(MSG_DEBUG, "WPA: Selected mgmt group cipher %d",
+                          ie.mgmt_group_cipher);
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
+                        !!(ssid->proto & WPA_PROTO_RSN));
+
+       if (bss || !wpa_s->ap_ies_from_associnfo) {
+               if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
+                                        bss_wpa ? 2 + bss_wpa[1] : 0) ||
+                   wpa_sm_set_ap_rsn_ie(wpa_s->wpa, bss_rsn,
+                                        bss_rsn ? 2 + bss_rsn[1] : 0))
+                       return -1;
+       }
+
+       sel = ie.group_cipher & ssid->group_cipher;
+       if (sel & WPA_CIPHER_CCMP) {
+               wpa_s->group_cipher = WPA_CIPHER_CCMP;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK CCMP");
+       } else if (sel & WPA_CIPHER_TKIP) {
+               wpa_s->group_cipher = WPA_CIPHER_TKIP;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK TKIP");
+       } else if (sel & WPA_CIPHER_WEP104) {
+               wpa_s->group_cipher = WPA_CIPHER_WEP104;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP104");
+       } else if (sel & WPA_CIPHER_WEP40) {
+               wpa_s->group_cipher = WPA_CIPHER_WEP40;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using GTK WEP40");
+       } else {
+               wpa_printf(MSG_WARNING, "WPA: Failed to select group cipher.");
+               return -1;
+       }
+
+       sel = ie.pairwise_cipher & ssid->pairwise_cipher;
+       if (sel & WPA_CIPHER_CCMP) {
+               wpa_s->pairwise_cipher = WPA_CIPHER_CCMP;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK CCMP");
+       } else if (sel & WPA_CIPHER_TKIP) {
+               wpa_s->pairwise_cipher = WPA_CIPHER_TKIP;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK TKIP");
+       } else if (sel & WPA_CIPHER_NONE) {
+               wpa_s->pairwise_cipher = WPA_CIPHER_NONE;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using PTK NONE");
+       } else {
+               wpa_printf(MSG_WARNING, "WPA: Failed to select pairwise "
+                          "cipher.");
+               return -1;
+       }
+
+       sel = ie.key_mgmt & ssid->key_mgmt;
+       if (0) {
+#ifdef CONFIG_IEEE80211R
+       } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/802.1X");
+       } else if (sel & WPA_KEY_MGMT_FT_PSK) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_FT_PSK;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT FT/PSK");
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+       } else if (sel & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256;
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "WPA: using KEY_MGMT 802.1X with SHA256");
+       } else if (sel & WPA_KEY_MGMT_PSK_SHA256) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_PSK_SHA256;
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "WPA: using KEY_MGMT PSK with SHA256");
+#endif /* CONFIG_IEEE80211W */
+       } else if (sel & WPA_KEY_MGMT_IEEE8021X) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT 802.1X");
+       } else if (sel & WPA_KEY_MGMT_PSK) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-PSK");
+       } else if (sel & WPA_KEY_MGMT_WPA_NONE) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+       } else {
+               wpa_printf(MSG_WARNING, "WPA: Failed to select authenticated "
+                          "key management type.");
+               return -1;
+       }
+
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_KEY_MGMT, wpa_s->key_mgmt);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PAIRWISE,
+                        wpa_s->pairwise_cipher);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_GROUP, wpa_s->group_cipher);
+
+#ifdef CONFIG_IEEE80211W
+       sel = ie.mgmt_group_cipher;
+       if (ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION ||
+           !(ie.capabilities & WPA_CAPABILITY_MFPC))
+               sel = 0;
+       if (sel & WPA_CIPHER_AES_128_CMAC) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "AES-128-CMAC");
+       } else {
+               wpa_s->mgmt_group_cipher = 0;
+               wpa_msg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
+       }
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
+                        wpa_s->mgmt_group_cipher);
+       wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP, ssid->ieee80211w);
+#endif /* CONFIG_IEEE80211W */
+
+       if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
+               wpa_printf(MSG_WARNING, "WPA: Failed to generate WPA IE.");
+               return -1;
+       }
+
+       if (ssid->key_mgmt &
+           (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_FT_PSK | WPA_KEY_MGMT_PSK_SHA256))
+               wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
+       else
+               wpa_sm_set_pmk_from_pmksa(wpa_s->wpa);
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_associate - Request association
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Scan results for the selected BSS, or %NULL if not available
+ * @ssid: Configuration data for the selected network
+ *
+ * This function is used to request %wpa_supplicant to associate with a BSS.
+ */
+void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+                             struct wpa_bss *bss, struct wpa_ssid *ssid)
+{
+       u8 wpa_ie[80];
+       size_t wpa_ie_len;
+       int use_crypt, ret, i, bssid_changed;
+       int algs = WPA_AUTH_ALG_OPEN;
+       enum wpa_cipher cipher_pairwise, cipher_group;
+       struct wpa_driver_associate_params params;
+       int wep_keys_set = 0;
+       struct wpa_driver_capa capa;
+       int assoc_failed = 0;
+       struct wpa_ssid *old_ssid;
+
+       if (ssid->mode == WPAS_MODE_AP) {
+#ifdef CONFIG_AP
+               if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP)) {
+                       wpa_printf(MSG_INFO, "Driver does not support AP "
+                                  "mode");
+                       return;
+               }
+               wpa_supplicant_create_ap(wpa_s, ssid);
+               wpa_s->current_bss = bss;
+#else /* CONFIG_AP */
+               wpa_printf(MSG_ERROR, "AP mode support not included in the "
+                          "build");
+#endif /* CONFIG_AP */
+               return;
+       }
+
+       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME) &&
+           ssid->mode == IEEE80211_MODE_INFRA) {
+               sme_authenticate(wpa_s, bss, ssid);
+               return;
+       }
+
+       wpa_s->reassociate = 0;
+       if (bss) {
+#ifdef CONFIG_IEEE80211R
+               const u8 *ie, *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+                       " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+                       wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
+               bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+               os_memset(wpa_s->bssid, 0, ETH_ALEN);
+               os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+               if (bssid_changed)
+                       wpas_notify_bssid_changed(wpa_s);
+#ifdef CONFIG_IEEE80211R
+               ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+               if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+                       md = ie + 2;
+               wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+               if (md) {
+                       /* Prepare for the next transition */
+                       wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+               }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+       } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
+                  wpa_s->conf->ap_scan == 2 &&
+                  (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+               /* Use ap_scan==1 style network selection to find the network
+                */
+               wpa_s->scan_req = 2;
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               return;
+#endif /* CONFIG_WPS */
+       } else {
+               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+       }
+       wpa_supplicant_cancel_scan(wpa_s);
+
+       /* Starting new association, so clear the possibly used WPA IE from the
+        * previous association. */
+       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef IEEE8021X_EAPOL
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               if (ssid->leap) {
+                       if (ssid->non_leap == 0)
+                               algs = WPA_AUTH_ALG_LEAP;
+                       else
+                               algs |= WPA_AUTH_ALG_LEAP;
+               }
+       }
+#endif /* IEEE8021X_EAPOL */
+       wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+       if (ssid->auth_alg) {
+               algs = ssid->auth_alg;
+               wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
+                          algs);
+       }
+
+       if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+                   wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+           (ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
+                              WPA_KEY_MGMT_FT_IEEE8021X |
+                              WPA_KEY_MGMT_FT_PSK |
+                              WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                              WPA_KEY_MGMT_PSK_SHA256))) {
+               int try_opportunistic;
+               try_opportunistic = ssid->proactive_key_caching &&
+                       (ssid->proto & WPA_PROTO_RSN);
+               if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+                                           wpa_s->current_ssid,
+                                           try_opportunistic) == 0)
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+               wpa_ie_len = sizeof(wpa_ie);
+               if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
+                                             wpa_ie, &wpa_ie_len)) {
+                       wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
+                                  "management and encryption suites");
+                       return;
+               }
+       } else if (ssid->key_mgmt &
+                  (WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
+                   WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK |
+                   WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 |
+                   WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+               wpa_ie_len = sizeof(wpa_ie);
+               if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
+                                             wpa_ie, &wpa_ie_len)) {
+                       wpa_printf(MSG_WARNING, "WPA: Failed to set WPA key "
+                                  "management and encryption suites (no scan "
+                                  "results)");
+                       return;
+               }
+#ifdef CONFIG_WPS
+       } else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+               struct wpabuf *wps_ie;
+               wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
+               if (wps_ie && wpabuf_len(wps_ie) <= sizeof(wpa_ie)) {
+                       wpa_ie_len = wpabuf_len(wps_ie);
+                       os_memcpy(wpa_ie, wpabuf_head(wps_ie), wpa_ie_len);
+               } else
+                       wpa_ie_len = 0;
+               wpabuf_free(wps_ie);
+               wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+#endif /* CONFIG_WPS */
+       } else {
+               wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
+               wpa_ie_len = 0;
+       }
+
+       wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
+       use_crypt = 1;
+       cipher_pairwise = cipher_suite2driver(wpa_s->pairwise_cipher);
+       cipher_group = cipher_suite2driver(wpa_s->group_cipher);
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
+                       use_crypt = 0;
+               if (wpa_set_wep_keys(wpa_s, ssid)) {
+                       use_crypt = 1;
+                       wep_keys_set = 1;
+               }
+       }
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)
+               use_crypt = 0;
+
+#ifdef IEEE8021X_EAPOL
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               if ((ssid->eapol_flags &
+                    (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
+                     EAPOL_FLAG_REQUIRE_KEY_BROADCAST)) == 0 &&
+                   !wep_keys_set) {
+                       use_crypt = 0;
+               } else {
+                       /* Assume that dynamic WEP-104 keys will be used and
+                        * set cipher suites in order for drivers to expect
+                        * encryption. */
+                       cipher_pairwise = cipher_group = CIPHER_WEP104;
+               }
+       }
+#endif /* IEEE8021X_EAPOL */
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+               /* Set the key before (and later after) association */
+               wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+       }
+
+       wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
+       os_memset(&params, 0, sizeof(params));
+       if (bss) {
+               params.bssid = bss->bssid;
+               params.ssid = bss->ssid;
+               params.ssid_len = bss->ssid_len;
+               params.freq = bss->freq;
+       } else {
+               params.ssid = ssid->ssid;
+               params.ssid_len = ssid->ssid_len;
+       }
+       if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
+           params.freq == 0)
+               params.freq = ssid->frequency; /* Initial channel for IBSS */
+       params.wpa_ie = wpa_ie;
+       params.wpa_ie_len = wpa_ie_len;
+       params.pairwise_suite = cipher_pairwise;
+       params.group_suite = cipher_group;
+       params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+       params.auth_alg = algs;
+       params.mode = ssid->mode;
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (ssid->wep_key_len[i])
+                       params.wep_key[i] = ssid->wep_key[i];
+               params.wep_key_len[i] = ssid->wep_key_len[i];
+       }
+       params.wep_tx_keyidx = ssid->wep_tx_keyidx;
+
+       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+           (params.key_mgmt_suite == KEY_MGMT_PSK ||
+            params.key_mgmt_suite == KEY_MGMT_FT_PSK)) {
+               params.passphrase = ssid->passphrase;
+               if (ssid->psk_set)
+                       params.psk = ssid->psk;
+       }
+
+       params.drop_unencrypted = use_crypt;
+
+#ifdef CONFIG_IEEE80211W
+       params.mgmt_frame_protection = ssid->ieee80211w;
+       if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION && bss) {
+               const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+               struct wpa_ie_data ie;
+               if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie) == 0 &&
+                   ie.capabilities &
+                   (WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
+                       wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
+                                  "require MFP");
+                       params.mgmt_frame_protection =
+                               MGMT_FRAME_PROTECTION_REQUIRED;
+               }
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               ret = ieee80211_sta_associate(wpa_s, &params);
+       else
+               ret = wpa_drv_associate(wpa_s, &params);
+       if (ret < 0) {
+               wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
+                       "failed");
+               /* try to continue anyway; new association will be tried again
+                * after timeout */
+               assoc_failed = 1;
+       }
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE) {
+               /* Set the key after the association just in case association
+                * cleared the previously configured key. */
+               wpa_supplicant_set_wpa_none_key(wpa_s, ssid);
+               /* No need to timeout authentication since there is no key
+                * management. */
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+#ifdef CONFIG_IBSS_RSN
+       } else if (ssid->mode == WPAS_MODE_IBSS &&
+                  wpa_s->key_mgmt != WPA_KEY_MGMT_NONE &&
+                  wpa_s->key_mgmt != WPA_KEY_MGMT_WPA_NONE) {
+               ibss_rsn_set_psk(wpa_s->ibss_rsn, ssid->psk);
+               /*
+                * RSN IBSS authentication is per-STA and we can disable the
+                * per-BSSID authentication.
+                */
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+#endif /* CONFIG_IBSS_RSN */
+       } else {
+               /* Timeout for IEEE 802.11 authentication and association */
+               int timeout = 60;
+
+               if (assoc_failed) {
+                       /* give IBSS a bit more time */
+                       timeout = ssid->mode == WPAS_MODE_IBSS ? 10 : 5;
+               } else if (wpa_s->conf->ap_scan == 1) {
+                       /* give IBSS a bit more time */
+                       timeout = ssid->mode == WPAS_MODE_IBSS ? 20 : 10;
+               }
+               wpa_supplicant_req_auth_timeout(wpa_s, timeout, 0);
+       }
+
+       if (wep_keys_set && wpa_drv_get_capa(wpa_s, &capa) == 0 &&
+           capa.flags & WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC) {
+               /* Set static WEP keys again */
+               wpa_set_wep_keys(wpa_s, ssid);
+       }
+
+       if (wpa_s->current_ssid && wpa_s->current_ssid != ssid) {
+               /*
+                * Do not allow EAP session resumption between different
+                * network configurations.
+                */
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
+       }
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = ssid;
+       wpa_s->current_bss = bss;
+       wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+       wpa_supplicant_initiate_eapol(wpa_s);
+       if (old_ssid != wpa_s->current_ssid)
+               wpas_notify_network_changed(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_disassociate - Disassociate the current connection
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @reason_code: IEEE 802.11 reason code for the disassociate frame
+ *
+ * This function is used to request %wpa_supplicant to disassociate with the
+ * current AP.
+ */
+void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
+                                int reason_code)
+{
+       struct wpa_ssid *old_ssid;
+       u8 *addr = NULL;
+
+       if (!is_zero_ether_addr(wpa_s->bssid)) {
+               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+                       ieee80211_sta_disassociate(wpa_s, reason_code);
+               else
+                       wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
+               addr = wpa_s->bssid;
+       }
+       wpa_clear_keys(wpa_s, addr);
+       wpa_supplicant_mark_disassoc(wpa_s);
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = NULL;
+       wpa_s->current_bss = NULL;
+       wpa_sm_set_config(wpa_s->wpa, NULL);
+       eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+       if (old_ssid != wpa_s->current_ssid)
+               wpas_notify_network_changed(wpa_s);
+       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_deauthenticate - Deauthenticate the current connection
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @reason_code: IEEE 802.11 reason code for the deauthenticate frame
+ *
+ * This function is used to request %wpa_supplicant to deauthenticate from the
+ * current AP.
+ */
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+                                  int reason_code)
+{
+       struct wpa_ssid *old_ssid;
+       u8 *addr = NULL;
+
+       if (!is_zero_ether_addr(wpa_s->bssid)) {
+               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+                       ieee80211_sta_deauthenticate(wpa_s, reason_code);
+               else
+                       wpa_drv_deauthenticate(wpa_s, wpa_s->bssid,
+                                              reason_code);
+               addr = wpa_s->bssid;
+       }
+       wpa_clear_keys(wpa_s, addr);
+       wpa_supplicant_mark_disassoc(wpa_s);
+       old_ssid = wpa_s->current_ssid;
+       wpa_s->current_ssid = NULL;
+       wpa_s->current_bss = NULL;
+       wpa_sm_set_config(wpa_s->wpa, NULL);
+       eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+       if (old_ssid != wpa_s->current_ssid)
+               wpas_notify_network_changed(wpa_s);
+       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
+}
+
+
+/**
+ * wpa_supplicant_enable_network - Mark a configured network as enabled
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL
+ *
+ * Enables the specified network or all networks if no network specified.
+ */
+void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
+                                  struct wpa_ssid *ssid)
+{
+       struct wpa_ssid *other_ssid;
+       int was_disabled;
+
+       if (ssid == NULL) {
+               other_ssid = wpa_s->conf->ssid;
+               while (other_ssid) {
+                       if (other_ssid == wpa_s->current_ssid &&
+                           other_ssid->disabled)
+                               wpa_s->reassociate = 1;
+
+                       was_disabled = other_ssid->disabled;
+
+                       other_ssid->disabled = 0;
+
+                       if (was_disabled != other_ssid->disabled)
+                               wpas_notify_network_enabled_changed(
+                                       wpa_s, other_ssid);
+
+                       other_ssid = other_ssid->next;
+               }
+               if (wpa_s->reassociate)
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+       } else if (ssid->disabled) {
+               if (wpa_s->current_ssid == NULL) {
+                       /*
+                        * Try to reassociate since there is no current
+                        * configuration and a new network was made available.
+                        */
+                       wpa_s->reassociate = 1;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
+
+               was_disabled = ssid->disabled;
+
+               ssid->disabled = 0;
+
+               if (was_disabled != ssid->disabled)
+                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+       }
+}
+
+
+/**
+ * wpa_supplicant_disable_network - Mark a configured network as disabled
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL
+ *
+ * Disables the specified network or all networks if no network specified.
+ */
+void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
+                                   struct wpa_ssid *ssid)
+{
+       struct wpa_ssid *other_ssid;
+       int was_disabled;
+
+       if (ssid == NULL) {
+               other_ssid = wpa_s->conf->ssid;
+               while (other_ssid) {
+                       was_disabled = other_ssid->disabled;
+
+                       other_ssid->disabled = 1;
+
+                       if (was_disabled != other_ssid->disabled)
+                               wpas_notify_network_enabled_changed(
+                                       wpa_s, other_ssid);
+
+                       other_ssid = other_ssid->next;
+               }
+               if (wpa_s->current_ssid)
+                       wpa_supplicant_disassociate(
+                               wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+       } else {
+               if (ssid == wpa_s->current_ssid)
+                       wpa_supplicant_disassociate(
+                               wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+               was_disabled = ssid->disabled;
+
+               ssid->disabled = 1;
+
+               if (was_disabled != ssid->disabled)
+                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+       }
+}
+
+
+/**
+ * wpa_supplicant_select_network - Attempt association with a network
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ssid: wpa_ssid structure for a configured network or %NULL for any network
+ */
+void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
+                                  struct wpa_ssid *ssid)
+{
+
+       struct wpa_ssid *other_ssid;
+
+       if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid)
+               wpa_supplicant_disassociate(
+                       wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+       /*
+        * Mark all other networks disabled or mark all networks enabled if no
+        * network specified.
+        */
+       other_ssid = wpa_s->conf->ssid;
+       while (other_ssid) {
+               int was_disabled = other_ssid->disabled;
+
+               other_ssid->disabled = ssid ? (ssid->id != other_ssid->id) : 0;
+
+               if (was_disabled != other_ssid->disabled)
+                       wpas_notify_network_enabled_changed(wpa_s, other_ssid);
+
+               other_ssid = other_ssid->next;
+       }
+       wpa_s->disconnected = 0;
+       wpa_s->reassociate = 1;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       if (ssid)
+               wpas_notify_network_selected(wpa_s, ssid);
+}
+
+
+/**
+ * wpa_supplicant_set_ap_scan - Set AP scan mode for interface
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @ap_scan: AP scan mode
+ * Returns: 0 if succeed or -1 if ap_scan has an invalid value
+ *
+ */
+int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s, int ap_scan)
+{
+
+       int old_ap_scan;
+
+       if (ap_scan < 0 || ap_scan > 2)
+               return -1;
+
+       old_ap_scan = wpa_s->conf->ap_scan;
+       wpa_s->conf->ap_scan = ap_scan;
+
+       if (old_ap_scan != wpa_s->conf->ap_scan)
+               wpas_notify_ap_scan_changed(wpa_s);
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_set_debug_params - Set global debug params
+ * @global: wpa_global structure
+ * @debug_level: debug level
+ * @debug_timestamp: determines if show timestamp in debug data
+ * @debug_show_keys: determines if show keys in debug data
+ * Returns: 0 if succeed or -1 if debug_level has wrong value
+ */
+int wpa_supplicant_set_debug_params(struct wpa_global *global, int debug_level,
+                                   int debug_timestamp, int debug_show_keys)
+{
+
+       int old_level, old_timestamp, old_show_keys;
+
+       /* check for allowed debuglevels */
+       if (debug_level != MSG_MSGDUMP &&
+           debug_level != MSG_DEBUG &&
+           debug_level != MSG_INFO &&
+           debug_level != MSG_WARNING &&
+           debug_level != MSG_ERROR)
+               return -1;
+
+       old_level = wpa_debug_level;
+       old_timestamp = wpa_debug_timestamp;
+       old_show_keys = wpa_debug_show_keys;
+
+       wpa_debug_level = debug_level;
+       wpa_debug_timestamp = debug_timestamp ? 1 : 0;
+       wpa_debug_show_keys = debug_show_keys ? 1 : 0;
+
+       if (wpa_debug_level != old_level)
+               wpas_notify_debug_level_changed(global);
+       if (wpa_debug_timestamp != old_timestamp)
+               wpas_notify_debug_timestamp_changed(global);
+       if (wpa_debug_show_keys != old_show_keys)
+               wpas_notify_debug_show_keys_changed(global);
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_ssid - Get a pointer to the current network structure
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: A pointer to the current network structure or %NULL on failure
+ */
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *entry;
+       u8 ssid[MAX_SSID_LEN];
+       int res;
+       size_t ssid_len;
+       u8 bssid[ETH_ALEN];
+       int wired;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
+               if (ieee80211_sta_get_ssid(wpa_s, ssid, &ssid_len)) {
+                       wpa_printf(MSG_WARNING, "Could not read SSID from "
+                                  "MLME.");
+                       return NULL;
+               }
+       } else {
+               res = wpa_drv_get_ssid(wpa_s, ssid);
+               if (res < 0) {
+                       wpa_printf(MSG_WARNING, "Could not read SSID from "
+                                  "driver.");
+                       return NULL;
+               }
+               ssid_len = res;
+       }
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
+       else if (wpa_drv_get_bssid(wpa_s, bssid) < 0) {
+               wpa_printf(MSG_WARNING, "Could not read BSSID from driver.");
+               return NULL;
+       }
+
+       wired = wpa_s->conf->ap_scan == 0 &&
+               (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED);
+
+       entry = wpa_s->conf->ssid;
+       while (entry) {
+               if (!entry->disabled &&
+                   ((ssid_len == entry->ssid_len &&
+                     os_memcmp(ssid, entry->ssid, ssid_len) == 0) || wired) &&
+                   (!entry->bssid_set ||
+                    os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+                       return entry;
+#ifdef CONFIG_WPS
+               if (!entry->disabled &&
+                   (entry->key_mgmt & WPA_KEY_MGMT_WPS) &&
+                   (entry->ssid == NULL || entry->ssid_len == 0) &&
+                   (!entry->bssid_set ||
+                    os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0))
+                       return entry;
+#endif /* CONFIG_WPS */
+               entry = entry->next;
+       }
+
+       return NULL;
+}
+
+
+static int wpa_supplicant_set_driver(struct wpa_supplicant *wpa_s,
+                                    const char *name)
+{
+       int i;
+       size_t len;
+       const char *pos;
+
+       if (wpa_s == NULL)
+               return -1;
+
+       if (wpa_drivers[0] == NULL) {
+               wpa_printf(MSG_ERROR, "No driver interfaces build into "
+                          "wpa_supplicant.");
+               return -1;
+       }
+
+       if (name == NULL) {
+               /* default to first driver in the list */
+               wpa_s->driver = wpa_drivers[0];
+               wpa_s->global_drv_priv = wpa_s->global->drv_priv[0];
+               return 0;
+       }
+
+       pos = os_strchr(name, ',');
+       if (pos)
+               len = pos - name;
+       else
+               len = os_strlen(name);
+       for (i = 0; wpa_drivers[i]; i++) {
+               if (os_strlen(wpa_drivers[i]->name) == len &&
+                   os_strncmp(name, wpa_drivers[i]->name, len) ==
+                   0) {
+                       wpa_s->driver = wpa_drivers[i];
+                       wpa_s->global_drv_priv = wpa_s->global->drv_priv[i];
+                       return 0;
+               }
+       }
+
+       wpa_printf(MSG_ERROR, "Unsupported driver '%s'.", name);
+       return -1;
+}
+
+
+/**
+ * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant
+ * @ctx: Context pointer (wpa_s); this is the ctx variable registered
+ *     with struct wpa_driver_ops::init()
+ * @src_addr: Source address of the EAPOL frame
+ * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header)
+ * @len: Length of the EAPOL data
+ *
+ * This function is called for each received EAPOL frame. Most driver
+ * interfaces rely on more generic OS mechanism for receiving frames through
+ * l2_packet, but if such a mechanism is not available, the driver wrapper may
+ * take care of received EAPOL frames and deliver them to the core supplicant
+ * code by calling this function.
+ */
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+                            const u8 *buf, size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       wpa_printf(MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
+       wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
+
+       if (wpa_s->wpa_state < WPA_ASSOCIATED) {
+               /*
+                * There is possible race condition between receiving the
+                * association event and the EAPOL frame since they are coming
+                * through different paths from the driver. In order to avoid
+                * issues in trying to process the EAPOL frame before receiving
+                * association information, lets queue it for processing until
+                * the association event is received.
+                */
+               wpa_printf(MSG_DEBUG, "Not associated - Delay processing of "
+                          "received EAPOL frame");
+               wpabuf_free(wpa_s->pending_eapol_rx);
+               wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
+               if (wpa_s->pending_eapol_rx) {
+                       os_get_time(&wpa_s->pending_eapol_rx_time);
+                       os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
+                                 ETH_ALEN);
+               }
+               return;
+       }
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_supplicant_ap_rx_eapol(wpa_s, src_addr, buf, len);
+               return;
+       }
+#endif /* CONFIG_AP */
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+               wpa_printf(MSG_DEBUG, "Ignored received EAPOL frame since "
+                          "no key management is configured");
+               return;
+       }
+
+       if (wpa_s->eapol_received == 0 &&
+           (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) ||
+            !wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+            wpa_s->wpa_state != WPA_COMPLETED) &&
+           (wpa_s->current_ssid == NULL ||
+            wpa_s->current_ssid->mode != IEEE80211_MODE_IBSS)) {
+               /* Timeout for completing IEEE 802.1X and WPA authentication */
+               wpa_supplicant_req_auth_timeout(
+                       wpa_s,
+                       (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+                        wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA ||
+                        wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) ?
+                       70 : 10, 0);
+       }
+       wpa_s->eapol_received++;
+
+       if (wpa_s->countermeasures) {
+               wpa_printf(MSG_INFO, "WPA: Countermeasures - dropped EAPOL "
+                          "packet");
+               return;
+       }
+
+#ifdef CONFIG_IBSS_RSN
+       if (wpa_s->current_ssid &&
+           wpa_s->current_ssid->mode == WPAS_MODE_IBSS) {
+               ibss_rsn_rx_eapol(wpa_s->ibss_rsn, src_addr, buf, len);
+               return;
+       }
+#endif /* CONFIG_IBSS_RSN */
+
+       /* Source address of the incoming EAPOL frame could be compared to the
+        * current BSSID. However, it is possible that a centralized
+        * Authenticator could be using another MAC address than the BSSID of
+        * an AP, so just allow any address to be used for now. The replies are
+        * still sent to the current BSSID (if available), though. */
+
+       os_memcpy(wpa_s->last_eapol_src, src_addr, ETH_ALEN);
+       if (!wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) &&
+           eapol_sm_rx_eapol(wpa_s->eapol, src_addr, buf, len) > 0)
+               return;
+       wpa_drv_poll(wpa_s);
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+               wpa_sm_rx_eapol(wpa_s->wpa, src_addr, buf, len);
+       else if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+               /*
+                * Set portValid = TRUE here since we are going to skip 4-way
+                * handshake processing which would normally set portValid. We
+                * need this to allow the EAPOL state machines to be completed
+                * without going through EAPOL-Key handshake.
+                */
+               eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+       }
+}
+
+
+/**
+ * wpa_supplicant_driver_init - Initialize driver interface parameters
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is called to initialize driver interface parameters.
+ * wpa_drv_init() must have been called before this function to initialize the
+ * driver interface.
+ */
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
+{
+       static int interface_count = 0;
+
+       if (wpa_s->driver->send_eapol) {
+               const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
+               if (addr)
+                       os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
+       } else {
+               wpa_s->l2 = l2_packet_init(wpa_s->ifname,
+                                          wpa_drv_get_mac_addr(wpa_s),
+                                          ETH_P_EAPOL,
+                                          wpa_supplicant_rx_eapol, wpa_s, 0);
+               if (wpa_s->l2 == NULL)
+                       return -1;
+       }
+
+       if (wpa_s->l2 && l2_packet_get_own_addr(wpa_s->l2, wpa_s->own_addr)) {
+               wpa_printf(MSG_ERROR, "Failed to get own L2 address");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "Own MAC address: " MACSTR,
+                  MAC2STR(wpa_s->own_addr));
+
+       if (wpa_s->bridge_ifname[0]) {
+               wpa_printf(MSG_DEBUG, "Receiving packets from bridge interface"
+                          " '%s'", wpa_s->bridge_ifname);
+               wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
+                                             wpa_s->own_addr,
+                                             ETH_P_EAPOL,
+                                             wpa_supplicant_rx_eapol, wpa_s,
+                                             0);
+               if (wpa_s->l2_br == NULL) {
+                       wpa_printf(MSG_ERROR, "Failed to open l2_packet "
+                                  "connection for the bridge interface '%s'",
+                                  wpa_s->bridge_ifname);
+                       return -1;
+               }
+       }
+
+       wpa_clear_keys(wpa_s, NULL);
+
+       /* Make sure that TKIP countermeasures are not left enabled (could
+        * happen if wpa_supplicant is killed during countermeasures. */
+       wpa_drv_set_countermeasures(wpa_s, 0);
+
+       wpa_printf(MSG_DEBUG, "RSN: flushing PMKID list in the driver");
+       wpa_drv_flush_pmkid(wpa_s);
+
+       wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
+       if (wpa_supplicant_enabled_networks(wpa_s->conf)) {
+               wpa_supplicant_req_scan(wpa_s, interface_count, 100000);
+               interface_count++;
+       } else
+               wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_daemon(const char *pid_file)
+{
+       wpa_printf(MSG_DEBUG, "Daemonize..");
+       return os_daemonize(pid_file);
+}
+
+
+static struct wpa_supplicant * wpa_supplicant_alloc(void)
+{
+       struct wpa_supplicant *wpa_s;
+
+       wpa_s = os_zalloc(sizeof(*wpa_s));
+       if (wpa_s == NULL)
+               return NULL;
+       wpa_s->scan_req = 1;
+       wpa_s->new_connection = 1;
+
+       return wpa_s;
+}
+
+
+static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
+                                    struct wpa_interface *iface)
+{
+       const char *ifname, *driver;
+       struct wpa_driver_capa capa;
+
+       wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
+                  "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
+                  iface->confname ? iface->confname : "N/A",
+                  iface->driver ? iface->driver : "default",
+                  iface->ctrl_interface ? iface->ctrl_interface : "N/A",
+                  iface->bridge_ifname ? iface->bridge_ifname : "N/A");
+
+       if (iface->confname) {
+#ifdef CONFIG_BACKEND_FILE
+               wpa_s->confname = os_rel2abs_path(iface->confname);
+               if (wpa_s->confname == NULL) {
+                       wpa_printf(MSG_ERROR, "Failed to get absolute path "
+                                  "for configuration file '%s'.",
+                                  iface->confname);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "Configuration file '%s' -> '%s'",
+                          iface->confname, wpa_s->confname);
+#else /* CONFIG_BACKEND_FILE */
+               wpa_s->confname = os_strdup(iface->confname);
+#endif /* CONFIG_BACKEND_FILE */
+               wpa_s->conf = wpa_config_read(wpa_s->confname);
+               if (wpa_s->conf == NULL) {
+                       wpa_printf(MSG_ERROR, "Failed to read or parse "
+                                  "configuration '%s'.", wpa_s->confname);
+                       return -1;
+               }
+
+               /*
+                * Override ctrl_interface and driver_param if set on command
+                * line.
+                */
+               if (iface->ctrl_interface) {
+                       os_free(wpa_s->conf->ctrl_interface);
+                       wpa_s->conf->ctrl_interface =
+                               os_strdup(iface->ctrl_interface);
+               }
+
+               if (iface->driver_param) {
+                       os_free(wpa_s->conf->driver_param);
+                       wpa_s->conf->driver_param =
+                               os_strdup(iface->driver_param);
+               }
+       } else
+               wpa_s->conf = wpa_config_alloc_empty(iface->ctrl_interface,
+                                                    iface->driver_param);
+
+       if (wpa_s->conf == NULL) {
+               wpa_printf(MSG_ERROR, "\nNo configuration found.");
+               return -1;
+       }
+
+       if (iface->ifname == NULL) {
+               wpa_printf(MSG_ERROR, "\nInterface name is required.");
+               return -1;
+       }
+       if (os_strlen(iface->ifname) >= sizeof(wpa_s->ifname)) {
+               wpa_printf(MSG_ERROR, "\nToo long interface name '%s'.",
+                          iface->ifname);
+               return -1;
+       }
+       os_strlcpy(wpa_s->ifname, iface->ifname, sizeof(wpa_s->ifname));
+
+       if (iface->bridge_ifname) {
+               if (os_strlen(iface->bridge_ifname) >=
+                   sizeof(wpa_s->bridge_ifname)) {
+                       wpa_printf(MSG_ERROR, "\nToo long bridge interface "
+                                  "name '%s'.", iface->bridge_ifname);
+                       return -1;
+               }
+               os_strlcpy(wpa_s->bridge_ifname, iface->bridge_ifname,
+                          sizeof(wpa_s->bridge_ifname));
+       }
+
+       /* RSNA Supplicant Key Management - INITIALIZE */
+       eapol_sm_notify_portEnabled(wpa_s->eapol, FALSE);
+       eapol_sm_notify_portValid(wpa_s->eapol, FALSE);
+
+       /* Initialize driver interface and register driver event handler before
+        * L2 receive handler so that association events are processed before
+        * EAPOL-Key packets if both become available for the same select()
+        * call. */
+       driver = iface->driver;
+next_driver:
+       if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+               return -1;
+
+       wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+       if (wpa_s->drv_priv == NULL) {
+               const char *pos;
+               pos = driver ? os_strchr(driver, ',') : NULL;
+               if (pos) {
+                       wpa_printf(MSG_DEBUG, "Failed to initialize driver "
+                                  "interface - try next driver wrapper");
+                       driver = pos + 1;
+                       goto next_driver;
+               }
+               wpa_printf(MSG_ERROR, "Failed to initialize driver interface");
+               return -1;
+       }
+       if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+               wpa_printf(MSG_ERROR, "Driver interface rejected "
+                          "driver_param '%s'", wpa_s->conf->driver_param);
+               return -1;
+       }
+
+       ifname = wpa_drv_get_ifname(wpa_s);
+       if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
+               wpa_printf(MSG_DEBUG, "Driver interface replaced interface "
+                          "name with '%s'", ifname);
+               os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+       }
+
+       if (wpa_supplicant_init_wpa(wpa_s) < 0)
+               return -1;
+
+       wpa_sm_set_ifname(wpa_s->wpa, wpa_s->ifname,
+                         wpa_s->bridge_ifname[0] ? wpa_s->bridge_ifname :
+                         NULL);
+       wpa_sm_set_fast_reauth(wpa_s->wpa, wpa_s->conf->fast_reauth);
+
+       if (wpa_s->conf->dot11RSNAConfigPMKLifetime &&
+           wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME,
+                            wpa_s->conf->dot11RSNAConfigPMKLifetime)) {
+               wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
+                          "dot11RSNAConfigPMKLifetime");
+               return -1;
+       }
+
+       if (wpa_s->conf->dot11RSNAConfigPMKReauthThreshold &&
+           wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD,
+                            wpa_s->conf->dot11RSNAConfigPMKReauthThreshold)) {
+               wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
+                       "dot11RSNAConfigPMKReauthThreshold");
+               return -1;
+       }
+
+       if (wpa_s->conf->dot11RSNAConfigSATimeout &&
+           wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT,
+                            wpa_s->conf->dot11RSNAConfigSATimeout)) {
+               wpa_printf(MSG_ERROR, "Invalid WPA parameter value for "
+                          "dot11RSNAConfigSATimeout");
+               return -1;
+       }
+
+       if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
+               wpa_s->drv_flags = capa.flags;
+               if (capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
+                       if (ieee80211_sta_init(wpa_s))
+                               return -1;
+               }
+               wpa_s->max_scan_ssids = capa.max_scan_ssids;
+               wpa_s->max_remain_on_chan = capa.max_remain_on_chan;
+       }
+       if (wpa_s->max_remain_on_chan == 0)
+               wpa_s->max_remain_on_chan = 1000;
+
+       if (wpa_supplicant_driver_init(wpa_s) < 0)
+               return -1;
+
+       if (wpa_s->conf->country[0] && wpa_s->conf->country[1] &&
+           wpa_drv_set_country(wpa_s, wpa_s->conf->country)) {
+               wpa_printf(MSG_DEBUG, "Failed to set country");
+               return -1;
+       }
+
+       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
+       if (wpas_wps_init(wpa_s))
+               return -1;
+
+       if (wpa_supplicant_init_eapol(wpa_s) < 0)
+               return -1;
+       wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+       wpa_s->ctrl_iface = wpa_supplicant_ctrl_iface_init(wpa_s);
+       if (wpa_s->ctrl_iface == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to initialize control interface '%s'.\n"
+                          "You may have another wpa_supplicant process "
+                          "already running or the file was\n"
+                          "left by an unclean termination of wpa_supplicant "
+                          "in which case you will need\n"
+                          "to manually remove this file before starting "
+                          "wpa_supplicant again.\n",
+                          wpa_s->conf->ctrl_interface);
+               return -1;
+       }
+
+#ifdef CONFIG_IBSS_RSN
+       wpa_s->ibss_rsn = ibss_rsn_init(wpa_s);
+       if (!wpa_s->ibss_rsn) {
+               wpa_printf(MSG_DEBUG, "Failed to init IBSS RSN");
+               return -1;
+       }
+#endif /* CONFIG_IBSS_RSN */
+
+       if (wpa_bss_init(wpa_s) < 0)
+               return -1;
+
+       return 0;
+}
+
+
+static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
+                                       int notify)
+{
+       if (wpa_s->drv_priv) {
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+
+               wpa_drv_set_countermeasures(wpa_s, 0);
+               wpa_clear_keys(wpa_s, NULL);
+       }
+
+       wpa_supplicant_cleanup(wpa_s);
+
+       if (notify)
+               wpas_notify_iface_removed(wpa_s);
+
+       if (wpa_s->drv_priv)
+               wpa_drv_deinit(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_add_iface - Add a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @iface: Interface configuration options
+ * Returns: Pointer to the created interface or %NULL on failure
+ *
+ * This function is used to add new network interfaces for %wpa_supplicant.
+ * This can be called before wpa_supplicant_run() to add interfaces before the
+ * main event loop has been started. In addition, new interfaces can be added
+ * dynamically while %wpa_supplicant is already running. This could happen,
+ * e.g., when a hotplug network adapter is inserted.
+ */
+struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+                                                struct wpa_interface *iface)
+{
+       struct wpa_supplicant *wpa_s;
+       struct wpa_interface t_iface;
+       struct wpa_ssid *ssid;
+
+       if (global == NULL || iface == NULL)
+               return NULL;
+
+       wpa_s = wpa_supplicant_alloc();
+       if (wpa_s == NULL)
+               return NULL;
+
+       wpa_s->global = global;
+
+       t_iface = *iface;
+       if (global->params.override_driver) {
+               wpa_printf(MSG_DEBUG, "Override interface parameter: driver "
+                          "('%s' -> '%s')",
+                          iface->driver, global->params.override_driver);
+               t_iface.driver = global->params.override_driver;
+       }
+       if (global->params.override_ctrl_interface) {
+               wpa_printf(MSG_DEBUG, "Override interface parameter: "
+                          "ctrl_interface ('%s' -> '%s')",
+                          iface->ctrl_interface,
+                          global->params.override_ctrl_interface);
+               t_iface.ctrl_interface =
+                       global->params.override_ctrl_interface;
+       }
+       if (wpa_supplicant_init_iface(wpa_s, &t_iface)) {
+               wpa_printf(MSG_DEBUG, "Failed to add interface %s",
+                          iface->ifname);
+               wpa_supplicant_deinit_iface(wpa_s, 0);
+               os_free(wpa_s);
+               return NULL;
+       }
+
+       /* Notify the control interfaces about new iface */
+       if (wpas_notify_iface_added(wpa_s)) {
+               wpa_supplicant_deinit_iface(wpa_s, 1);
+               os_free(wpa_s);
+               return NULL;
+       }
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+               wpas_notify_network_added(wpa_s, ssid);
+
+       wpa_s->next = global->ifaces;
+       global->ifaces = wpa_s;
+
+       wpa_printf(MSG_DEBUG, "Added interface %s", wpa_s->ifname);
+
+       return wpa_s;
+}
+
+
+/**
+ * wpa_supplicant_remove_iface - Remove a network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @wpa_s: Pointer to the network interface to be removed
+ * Returns: 0 if interface was removed, -1 if interface was not found
+ *
+ * This function can be used to dynamically remove network interfaces from
+ * %wpa_supplicant, e.g., when a hotplug network adapter is ejected. In
+ * addition, this function is used to remove all remaining interfaces when
+ * %wpa_supplicant is terminated.
+ */
+int wpa_supplicant_remove_iface(struct wpa_global *global,
+                               struct wpa_supplicant *wpa_s)
+{
+       struct wpa_supplicant *prev;
+
+       /* Remove interface from the global list of interfaces */
+       prev = global->ifaces;
+       if (prev == wpa_s) {
+               global->ifaces = wpa_s->next;
+       } else {
+               while (prev && prev->next != wpa_s)
+                       prev = prev->next;
+               if (prev == NULL)
+                       return -1;
+               prev->next = wpa_s->next;
+       }
+
+       wpa_printf(MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
+
+       wpa_supplicant_deinit_iface(wpa_s, 1);
+       os_free(wpa_s);
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_get_iface - Get a new network interface
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * @ifname: Interface name
+ * Returns: Pointer to the interface or %NULL if not found
+ */
+struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
+                                                const char *ifname)
+{
+       struct wpa_supplicant *wpa_s;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (os_strcmp(wpa_s->ifname, ifname) == 0)
+                       return wpa_s;
+       }
+       return NULL;
+}
+
+
+/**
+ * wpa_supplicant_init - Initialize %wpa_supplicant
+ * @params: Parameters for %wpa_supplicant
+ * Returns: Pointer to global %wpa_supplicant data, or %NULL on failure
+ *
+ * This function is used to initialize %wpa_supplicant. After successful
+ * initialization, the returned data pointer can be used to add and remove
+ * network interfaces, and eventually, to deinitialize %wpa_supplicant.
+ */
+struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
+{
+       struct wpa_global *global;
+       int ret, i;
+
+       if (params == NULL)
+               return NULL;
+
+       wpa_debug_open_file(params->wpa_debug_file_path);
+       if (params->wpa_debug_syslog)
+               wpa_debug_open_syslog();
+
+       ret = eap_register_methods();
+       if (ret) {
+               wpa_printf(MSG_ERROR, "Failed to register EAP methods");
+               if (ret == -2)
+                       wpa_printf(MSG_ERROR, "Two or more EAP methods used "
+                                  "the same EAP type.");
+               return NULL;
+       }
+
+       global = os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+       global->params.daemonize = params->daemonize;
+       global->params.wait_for_monitor = params->wait_for_monitor;
+       global->params.dbus_ctrl_interface = params->dbus_ctrl_interface;
+       if (params->pid_file)
+               global->params.pid_file = os_strdup(params->pid_file);
+       if (params->ctrl_interface)
+               global->params.ctrl_interface =
+                       os_strdup(params->ctrl_interface);
+       if (params->override_driver)
+               global->params.override_driver =
+                       os_strdup(params->override_driver);
+       if (params->override_ctrl_interface)
+               global->params.override_ctrl_interface =
+                       os_strdup(params->override_ctrl_interface);
+       wpa_debug_level = global->params.wpa_debug_level =
+               params->wpa_debug_level;
+       wpa_debug_show_keys = global->params.wpa_debug_show_keys =
+               params->wpa_debug_show_keys;
+       wpa_debug_timestamp = global->params.wpa_debug_timestamp =
+               params->wpa_debug_timestamp;
+
+       if (eloop_init()) {
+               wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+               wpa_supplicant_deinit(global);
+               return NULL;
+       }
+
+       global->ctrl_iface = wpa_supplicant_global_ctrl_iface_init(global);
+       if (global->ctrl_iface == NULL) {
+               wpa_supplicant_deinit(global);
+               return NULL;
+       }
+
+       if (wpas_notify_supplicant_initialized(global)) {
+               wpa_supplicant_deinit(global);
+               return NULL;
+       }
+
+       for (i = 0; wpa_drivers[i]; i++)
+               global->drv_count++;
+       if (global->drv_count == 0) {
+               wpa_printf(MSG_ERROR, "No drivers enabled");
+               wpa_supplicant_deinit(global);
+               return NULL;
+       }
+       global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
+       if (global->drv_priv == NULL) {
+               wpa_supplicant_deinit(global);
+               return NULL;
+       }
+       for (i = 0; wpa_drivers[i]; i++) {
+               if (!wpa_drivers[i]->global_init)
+                       continue;
+               global->drv_priv[i] = wpa_drivers[i]->global_init();
+               if (global->drv_priv[i] == NULL) {
+                       wpa_printf(MSG_ERROR, "Failed to initialize driver "
+                                  "'%s'", wpa_drivers[i]->name);
+                       wpa_supplicant_deinit(global);
+                       return NULL;
+               }
+       }
+
+       return global;
+}
+
+
+/**
+ * wpa_supplicant_run - Run the %wpa_supplicant main event loop
+ * @global: Pointer to global data from wpa_supplicant_init()
+ * Returns: 0 after successful event loop run, -1 on failure
+ *
+ * This function starts the main event loop and continues running as long as
+ * there are any remaining events. In most cases, this function is running as
+ * long as the %wpa_supplicant process in still in use.
+ */
+int wpa_supplicant_run(struct wpa_global *global)
+{
+       struct wpa_supplicant *wpa_s;
+
+       if (global->params.daemonize &&
+           wpa_supplicant_daemon(global->params.pid_file))
+               return -1;
+
+       if (global->params.wait_for_monitor) {
+               for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next)
+                       if (wpa_s->ctrl_iface)
+                               wpa_supplicant_ctrl_iface_wait(
+                                       wpa_s->ctrl_iface);
+       }
+
+       eloop_register_signal_terminate(wpa_supplicant_terminate, global);
+       eloop_register_signal_reconfig(wpa_supplicant_reconfig, global);
+
+       eloop_run();
+
+       return 0;
+}
+
+
+/**
+ * wpa_supplicant_deinit - Deinitialize %wpa_supplicant
+ * @global: Pointer to global data from wpa_supplicant_init()
+ *
+ * This function is called to deinitialize %wpa_supplicant and to free all
+ * allocated resources. Remaining network interfaces will also be removed.
+ */
+void wpa_supplicant_deinit(struct wpa_global *global)
+{
+       int i;
+
+       if (global == NULL)
+               return;
+
+       while (global->ifaces)
+               wpa_supplicant_remove_iface(global, global->ifaces);
+
+       if (global->ctrl_iface)
+               wpa_supplicant_global_ctrl_iface_deinit(global->ctrl_iface);
+
+       wpas_notify_supplicant_deinitialized(global);
+
+       eap_peer_unregister_methods();
+#ifdef CONFIG_AP
+       eap_server_unregister_methods();
+#endif /* CONFIG_AP */
+
+       for (i = 0; wpa_drivers[i] && global->drv_priv; i++) {
+               if (!global->drv_priv[i])
+                       continue;
+               wpa_drivers[i]->global_deinit(global->drv_priv[i]);
+       }
+       os_free(global->drv_priv);
+
+       eloop_destroy();
+
+       if (global->params.pid_file) {
+               os_daemonize_terminate(global->params.pid_file);
+               os_free(global->params.pid_file);
+       }
+       os_free(global->params.ctrl_interface);
+       os_free(global->params.override_driver);
+       os_free(global->params.override_ctrl_interface);
+
+       os_free(global);
+       wpa_debug_close_syslog();
+       wpa_debug_close_file();
+}
diff --git a/wpa_supplicant/wpa_supplicant.conf b/wpa_supplicant/wpa_supplicant.conf
new file mode 100644 (file)
index 0000000..0cd5b02
--- /dev/null
@@ -0,0 +1,888 @@
+##### Example wpa_supplicant configuration file ###############################
+#
+# This file describes configuration file format and lists all available option.
+# Please also take a look at simpler configuration examples in 'examples'
+# subdirectory.
+#
+# Empty lines and lines starting with # are ignored
+
+# NOTE! This file may contain password information and should probably be made
+# readable only by root user on multiuser systems.
+
+# Note: All file paths in this configuration file should use full (absolute,
+# not relative to working directory) path in order to allow working directory
+# to be changed. This can happen if wpa_supplicant is run in the background.
+
+# Whether to allow wpa_supplicant to update (overwrite) configuration
+#
+# This option can be used to allow wpa_supplicant to overwrite configuration
+# file whenever configuration is changed (e.g., new network block is added with
+# wpa_cli or wpa_gui, or a password is changed). This is required for
+# wpa_cli/wpa_gui to be able to store the configuration changes permanently.
+# Please note that overwriting configuration file will remove the comments from
+# it.
+#update_config=1
+
+# global configuration (shared by all network blocks)
+#
+# Parameters for the control interface. If this is specified, wpa_supplicant
+# will open a control interface that is available for external programs to
+# manage wpa_supplicant. The meaning of this string depends on which control
+# interface mechanism is used. For all cases, the existance of this parameter
+# in configuration is used to determine whether the control interface is
+# enabled.
+#
+# For UNIX domain sockets (default on Linux and BSD): This is a directory that
+# will be created for UNIX domain sockets for listening to requests from
+# external programs (CLI/GUI, etc.) for status information and configuration.
+# The socket file will be named based on the interface name, so multiple
+# wpa_supplicant processes can be run at the same time if more than one
+# interface is used.
+# /var/run/wpa_supplicant is the recommended directory for sockets and by
+# default, wpa_cli will use it when trying to connect with wpa_supplicant.
+#
+# Access control for the control interface can be configured by setting the
+# directory to allow only members of a group to use sockets. This way, it is
+# possible to run wpa_supplicant as root (since it needs to change network
+# configuration and open raw sockets) and still allow GUI/CLI components to be
+# run as non-root users. However, since the control interface can be used to
+# change the network configuration, this access needs to be protected in many
+# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you
+# want to allow non-root users to use the control interface, add a new group
+# and change this value to match with that group. Add users that should have
+# control interface access to this group. If this variable is commented out or
+# not included in the configuration file, group will not be changed from the
+# value it got by default when the directory or socket was created.
+#
+# When configuring both the directory and group, use following format:
+# DIR=/var/run/wpa_supplicant GROUP=wheel
+# DIR=/var/run/wpa_supplicant GROUP=0
+# (group can be either group name or gid)
+#
+# For UDP connections (default on Windows): The value will be ignored. This
+# variable is just used to select that the control interface is to be created.
+# The value can be set to, e.g., udp (ctrl_interface=udp)
+#
+# For Windows Named Pipe: This value can be used to set the security descriptor
+# for controlling access to the control interface. Security descriptor can be
+# set using Security Descriptor String Format (see http://msdn.microsoft.com/
+# library/default.asp?url=/library/en-us/secauthz/security/
+# security_descriptor_string_format.asp). The descriptor string needs to be
+# prefixed with SDDL=. For example, ctrl_interface=SDDL=D: would set an empty
+# DACL (which will reject all connections). See README-Windows.txt for more
+# information about SDDL string format.
+#
+ctrl_interface=/var/run/wpa_supplicant
+
+# IEEE 802.1X/EAPOL version
+# wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines
+# EAPOL version 2. However, there are many APs that do not handle the new
+# version number correctly (they seem to drop the frames completely). In order
+# to make wpa_supplicant interoperate with these APs, the version number is set
+# to 1 by default. This configuration value can be used to set it to the new
+# version (2).
+eapol_version=1
+
+# AP scanning/selection
+# By default, wpa_supplicant requests driver to perform AP scanning and then
+# uses the scan results to select a suitable AP. Another alternative is to
+# allow the driver to take care of AP scanning and selection and use
+# wpa_supplicant just to process EAPOL frames based on IEEE 802.11 association
+# information from the driver.
+# 1: wpa_supplicant initiates scanning and AP selection; if no APs matching to
+#    the currently enabled networks are found, a new network (IBSS or AP mode
+#    operation) may be initialized (if configured) (default)
+# 0: driver takes care of scanning, AP selection, and IEEE 802.11 association
+#    parameters (e.g., WPA IE generation); this mode can also be used with
+#    non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
+#    APs (i.e., external program needs to control association). This mode must
+#    also be used when using wired Ethernet drivers.
+# 2: like 0, but associate with APs using security policy and SSID (but not
+#    BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
+#    enable operation with hidden SSIDs and optimized roaming; in this mode,
+#    the network blocks in the configuration file are tried one by one until
+#    the driver reports successful association; each network block should have
+#    explicit security policy (i.e., only one option in the lists) for
+#    key_mgmt, pairwise, group, proto variables
+# When using IBSS or AP mode, ap_scan=2 mode can force the new network to be
+# created immediately regardless of scan results. ap_scan=1 mode will first try
+# to scan for existing networks and only if no matches with the enabled
+# networks are found, a new IBSS or AP mode network is created.
+ap_scan=1
+
+# EAP fast re-authentication
+# By default, fast re-authentication is enabled for all EAP methods that
+# support it. This variable can be used to disable fast re-authentication.
+# Normally, there is no need to disable this.
+fast_reauth=1
+
+# OpenSSL Engine support
+# These options can be used to load OpenSSL engines.
+# The two engines that are supported currently are shown below:
+# They are both from the opensc project (http://www.opensc.org/)
+# By default no engines are loaded.
+# make the opensc engine available
+#opensc_engine_path=/usr/lib/opensc/engine_opensc.so
+# make the pkcs11 engine available
+#pkcs11_engine_path=/usr/lib/opensc/engine_pkcs11.so
+# configure the path to the pkcs11 module required by the pkcs11 engine
+#pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so
+
+# Dynamic EAP methods
+# If EAP methods were built dynamically as shared object files, they need to be
+# loaded here before being used in the network blocks. By default, EAP methods
+# are included statically in the build, so these lines are not needed
+#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_tls.so
+#load_dynamic_eap=/usr/lib/wpa_supplicant/eap_md5.so
+
+# Driver interface parameters
+# This field can be used to configure arbitrary driver interace parameters. The
+# format is specific to the selected driver interface. This field is not used
+# in most cases.
+#driver_param="field=value"
+
+# Country code
+# The ISO/IEC alpha2 country code for the country in which this device is
+# currently operating.
+#country=US
+
+# Maximum lifetime for PMKSA in seconds; default 43200
+#dot11RSNAConfigPMKLifetime=43200
+# Threshold for reauthentication (percentage of PMK lifetime); default 70
+#dot11RSNAConfigPMKReauthThreshold=70
+# Timeout for security association negotiation in seconds; default 60
+#dot11RSNAConfigSATimeout=60
+
+# Wi-Fi Protected Setup (WPS) parameters
+
+# Universally Unique IDentifier (UUID; see RFC 4122) of the device
+# If not configured, UUID will be generated based on the local MAC address.
+#uuid=12345678-9abc-def0-1234-56789abcdef0
+
+# Device Name
+# User-friendly description of device; up to 32 octets encoded in UTF-8
+#device_name=Wireless Client
+
+# 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=cmodel
+
+# 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=1-0050F204-1
+
+# OS Version
+# 4-octet operating system version number (hex string)
+#os_version=01020300
+
+# Config Methods
+# List of the supported configuration methods
+# Available methods: usba ethernet label display ext_nfc_token int_nfc_token
+#      nfc_interface push_button keypad
+#config_methods=label display push_button keypad
+
+# 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)
+#wps_cred_processing=0
+
+# Maximum number of BSS entries to keep in memory
+# Default: 200
+# This can be used to limit memory use on the BSS entries (cached scan
+# results). A larger value may be needed in environments that have huge number
+# of APs when using ap_scan=1 mode.
+#bss_max_count=200
+
+
+# filter_ssids - SSID-based scan result filtering
+# 0 = do not filter scan results (default)
+# 1 = only include configured SSIDs in scan results/BSS table
+#filter_ssids=0
+
+
+# network block
+#
+# Each network (usually AP's sharing the same SSID) is configured as a separate
+# block in this configuration file. The network blocks are in preference order
+# (the first match is used).
+#
+# network block fields:
+#
+# disabled:
+#      0 = this network can be used (default)
+#      1 = this network block is disabled (can be enabled through ctrl_iface,
+#          e.g., with wpa_cli or wpa_gui)
+#
+# id_str: Network identifier string for external scripts. This value is passed
+#      to external action script through wpa_cli as WPA_ID_STR environment
+#      variable to make it easier to do network specific configuration.
+#
+# ssid: SSID (mandatory); either as an ASCII string with double quotation or
+#      as hex string; network name
+#
+# scan_ssid:
+#      0 = do not scan this SSID with specific Probe Request frames (default)
+#      1 = scan with SSID-specific Probe Request frames (this can be used to
+#          find APs that do not accept broadcast SSID or use multiple SSIDs;
+#          this will add latency to scanning, so enable this only when needed)
+#
+# bssid: BSSID (optional); if set, this network block is used only when
+#      associating with the AP using the configured BSSID
+#
+# priority: priority group (integer)
+# By default, all networks will get same priority group (0). If some of the
+# networks are more desirable, this field can be used to change the order in
+# which wpa_supplicant goes through the networks when selecting a BSS. The
+# priority groups will be iterated in decreasing priority (i.e., the larger the
+# priority value, the sooner the network is matched against the scan results).
+# Within each priority group, networks will be selected based on security
+# policy, signal strength, etc.
+# Please note that AP scanning with scan_ssid=1 and ap_scan=2 mode are not
+# using this priority to select the order for scanning. Instead, they try the
+# networks in the order that used in the configuration file.
+#
+# mode: IEEE 802.11 operation mode
+# 0 = infrastructure (Managed) mode, i.e., associate with an AP (default)
+# 1 = IBSS (ad-hoc, peer-to-peer)
+# 2 = AP (access point)
+# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP)
+# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). WPA-None requires
+# following network block options:
+# proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not
+# both), and psk must also be set.
+#
+# frequency: Channel frequency in megahertz (MHz) for IBSS, e.g.,
+# 2412 = IEEE 802.11b/g channel 1. This value is used to configure the initial
+# channel for IBSS (adhoc) networks. It is ignored in the infrastructure mode.
+# In addition, this value is only used by the station that creates the IBSS. If
+# an IBSS network with the configured SSID is already present, the frequency of
+# the network will be used instead of this configured value.
+#
+# scan_freq: List of frequencies to scan
+# Space-separated list of frequencies in MHz to scan when searching for this
+# BSS. If the subset of channels used by the network is known, this option can
+# be used to optimize scanning to not occur on channels that the network does
+# not use. Example: scan_freq=2412 2437 2462
+#
+# freq_list: Array of allowed frequencies
+# Space-separated list of frequencies in MHz to allow for selecting the BSS. If
+# set, scan results that do not match any of the specified frequencies are not
+# considered when selecting a BSS.
+#
+# proto: list of accepted protocols
+# WPA = WPA/IEEE 802.11i/D3.0
+# RSN = WPA2/IEEE 802.11i (also WPA2 can be used as an alias for RSN)
+# If not set, this defaults to: WPA RSN
+#
+# key_mgmt: list of accepted authenticated key management protocols
+# WPA-PSK = WPA pre-shared key (this requires 'psk' field)
+# WPA-EAP = WPA using EAP authentication
+# IEEE8021X = IEEE 802.1X using EAP authentication and (optionally) dynamically
+#      generated WEP keys
+# NONE = WPA is not used; plaintext or static WEP could be used
+# WPA-PSK-SHA256 = Like WPA-PSK but using stronger SHA256-based algorithms
+# WPA-EAP-SHA256 = Like WPA-EAP but using stronger SHA256-based algorithms
+# If not set, this defaults to: WPA-PSK WPA-EAP
+#
+# auth_alg: list of allowed IEEE 802.11 authentication algorithms
+# OPEN = Open System authentication (required for WPA/WPA2)
+# SHARED = Shared Key authentication (requires static WEP keys)
+# LEAP = LEAP/Network EAP (only used with LEAP)
+# If not set, automatic selection is used (Open System with LEAP enabled if
+# LEAP is allowed as one of the EAP methods).
+#
+# pairwise: list of accepted pairwise (unicast) ciphers for WPA
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# NONE = Use only Group Keys (deprecated, should not be included if APs support
+#      pairwise keys)
+# If not set, this defaults to: CCMP TKIP
+#
+# group: list of accepted group (broadcast/multicast) ciphers for WPA
+# CCMP = AES in Counter mode with CBC-MAC [RFC 3610, IEEE 802.11i/D7.0]
+# TKIP = Temporal Key Integrity Protocol [IEEE 802.11i/D7.0]
+# WEP104 = WEP (Wired Equivalent Privacy) with 104-bit key
+# WEP40 = WEP (Wired Equivalent Privacy) with 40-bit key [IEEE 802.11]
+# If not set, this defaults to: CCMP TKIP WEP104 WEP40
+#
+# psk: WPA preshared key; 256-bit pre-shared key
+# The key used in WPA-PSK mode can be entered either as 64 hex-digits, i.e.,
+# 32 bytes or as an ASCII passphrase (in which case, the real PSK will be
+# generated using the passphrase and SSID). ASCII passphrase must be between
+# 8 and 63 characters (inclusive).
+# This field is not needed, if WPA-EAP is used.
+# Note: Separate tool, wpa_passphrase, can be used to generate 256-bit keys
+# from ASCII passphrase. This process uses lot of CPU and wpa_supplicant
+# startup and reconfiguration time can be optimized by generating the PSK only
+# only when the passphrase or SSID has actually changed.
+#
+# eapol_flags: IEEE 802.1X/EAPOL options (bit field)
+# Dynamic WEP key required for non-WPA mode
+# bit0 (1): require dynamically generated unicast WEP key
+# bit1 (2): require dynamically generated broadcast WEP key
+#      (3 = require both keys; default)
+# Note: When using wired authentication, eapol_flags must be set to 0 for the
+# authentication to be completed successfully.
+#
+# mixed_cell: This option can be used to configure whether so called mixed
+# cells, i.e., networks that use both plaintext and encryption in the same
+# SSID, are allowed when selecting a BSS form scan results.
+# 0 = disabled (default)
+# 1 = enabled
+#
+# proactive_key_caching:
+# Enable/disable opportunistic PMKSA caching for WPA2.
+# 0 = disabled (default)
+# 1 = enabled
+#
+# wep_key0..3: Static WEP key (ASCII in double quotation, e.g. "abcde" or
+# hex without quotation, e.g., 0102030405)
+# wep_tx_keyidx: Default WEP key index (TX) (0..3)
+#
+# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e DLS) is
+# allowed. This is only used with RSN/WPA2.
+# 0 = disabled (default)
+# 1 = enabled
+#peerkey=1
+#
+# wpa_ptk_rekey: Maximum lifetime for PTK in seconds. This can be used to
+# enforce rekeying of PTK to mitigate some attacks against TKIP deficiencies.
+#
+# Following fields are only used with internal EAP implementation.
+# eap: space-separated list of accepted EAP methods
+#      MD5 = EAP-MD5 (unsecure and does not generate keying material ->
+#                      cannot be used with WPA; to be used as a Phase 2 method
+#                      with EAP-PEAP or EAP-TTLS)
+#       MSCHAPV2 = EAP-MSCHAPv2 (cannot be used separately with WPA; to be used
+#              as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+#       OTP = EAP-OTP (cannot be used separately with WPA; to be used
+#              as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+#       GTC = EAP-GTC (cannot be used separately with WPA; to be used
+#              as a Phase 2 method with EAP-PEAP or EAP-TTLS)
+#      TLS = EAP-TLS (client and server certificate)
+#      PEAP = EAP-PEAP (with tunnelled EAP authentication)
+#      TTLS = EAP-TTLS (with tunnelled EAP or PAP/CHAP/MSCHAP/MSCHAPV2
+#                       authentication)
+#      If not set, all compiled in methods are allowed.
+#
+# identity: Identity string for EAP
+#      This field is also used to configure user NAI for
+#      EAP-PSK/PAX/SAKE/GPSK.
+# anonymous_identity: Anonymous identity string for EAP (to be used as the
+#      unencrypted identity with EAP types that support different tunnelled
+#      identity, e.g., EAP-TTLS)
+# password: Password string for EAP. This field can include either the
+#      plaintext password (using ASCII or hex string) or a NtPasswordHash
+#      (16-byte MD4 hash of password) in hash:<32 hex digits> format.
+#      NtPasswordHash can only be used when the password is for MSCHAPv2 or
+#      MSCHAP (EAP-MSCHAPv2, EAP-TTLS/MSCHAPv2, EAP-TTLS/MSCHAP, LEAP).
+#      EAP-PSK (128-bit PSK), EAP-PAX (128-bit PSK), and EAP-SAKE (256-bit
+#      PSK) is also configured using this field. For EAP-GPSK, this is a
+#      variable length PSK.
+# ca_cert: File path to CA certificate file (PEM/DER). This file can have one
+#      or more trusted CA certificates. If ca_cert and ca_path are not
+#      included, server certificate will not be verified. This is insecure and
+#      a trusted CA certificate should always be configured when using
+#      EAP-TLS/TTLS/PEAP. Full path should be used since working directory may
+#      change when wpa_supplicant is run in the background.
+#
+#      Alternatively, this can be used to only perform matching of the server
+#      certificate (SHA-256 hash of the DER encoded X.509 certificate). In
+#      this case, the possible CA certificates in the server certificate chain
+#      are ignored and only the server certificate is verified. This is
+#      configured with the following format:
+#      hash:://server/sha256/cert_hash_in_hex
+#      For example: "hash://server/sha256/
+#      5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a"
+#
+#      On Windows, trusted CA certificates can be loaded from the system
+#      certificate store by setting this to cert_store://<name>, e.g.,
+#      ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT".
+#      Note that when running wpa_supplicant as an application, the user
+#      certificate store (My user account) is used, whereas computer store
+#      (Computer account) is used when running wpasvc as a service.
+# ca_path: Directory path for CA certificate files (PEM). This path may
+#      contain multiple CA certificates in OpenSSL format. Common use for this
+#      is to point to system trusted CA list which is often installed into
+#      directory like /etc/ssl/certs. If configured, these certificates are
+#      added to the list of trusted CAs. ca_cert may also be included in that
+#      case, but it is not required.
+# client_cert: File path to client certificate file (PEM/DER)
+#      Full path should be used since working directory may change when
+#      wpa_supplicant is run in the background.
+#      Alternatively, a named configuration blob can be used by setting this
+#      to blob://<blob name>.
+# private_key: File path to client private key file (PEM/DER/PFX)
+#      When PKCS#12/PFX file (.p12/.pfx) is used, client_cert should be
+#      commented out. Both the private key and certificate will be read from
+#      the PKCS#12 file in this case. Full path should be used since working
+#      directory may change when wpa_supplicant is run in the background.
+#      Windows certificate store can be used by leaving client_cert out and
+#      configuring private_key in one of the following formats:
+#      cert://substring_to_match
+#      hash://certificate_thumbprint_in_hex
+#      for example: private_key="hash://63093aa9c47f56ae88334c7b65a4"
+#      Note that when running wpa_supplicant as an application, the user
+#      certificate store (My user account) is used, whereas computer store
+#      (Computer account) is used when running wpasvc as a service.
+#      Alternatively, a named configuration blob can be used by setting this
+#      to blob://<blob name>.
+# private_key_passwd: Password for private key file (if left out, this will be
+#      asked through control interface)
+# dh_file: File path to DH/DSA parameters file (in PEM format)
+#      This is an optional configuration file for setting parameters for an
+#      ephemeral DH key exchange. In most cases, the default RSA
+#      authentication does not use this configuration. However, it is possible
+#      setup RSA to use ephemeral DH key exchange. In addition, ciphers with
+#      DSA keys always use ephemeral DH keys. This can be used to achieve
+#      forward secrecy. If the file is in DSA parameters format, it will be
+#      automatically converted into DH params.
+# subject_match: Substring to be matched against the subject of the
+#      authentication server certificate. If this string is set, the server
+#      sertificate is only accepted if it contains this string in the subject.
+#      The subject string is in following format:
+#      /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
+# altsubject_match: Semicolon separated string of entries to be matched against
+#      the alternative subject name of the authentication server certificate.
+#      If this string is set, the server sertificate is only accepted if it
+#      contains one of the entries in an alternative subject name extension.
+#      altSubjectName string is in following format: TYPE:VALUE
+#      Example: EMAIL:server@example.com
+#      Example: DNS:server.example.com;DNS:server2.example.com
+#      Following types are supported: EMAIL, DNS, URI
+# phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters
+#      (string with field-value pairs, e.g., "peapver=0" or
+#      "peapver=1 peaplabel=1")
+#      'peapver' can be used to force which PEAP version (0 or 1) is used.
+#      'peaplabel=1' can be used to force new label, "client PEAP encryption",
+#      to be used during key derivation when PEAPv1 or newer. Most existing
+#      PEAPv1 implementation seem to be using the old label, "client EAP
+#      encryption", and wpa_supplicant is now using that as the default value.
+#      Some servers, e.g., Radiator, may require peaplabel=1 configuration to
+#      interoperate with PEAPv1; see eap_testing.txt for more details.
+#      'peap_outer_success=0' can be used to terminate PEAP authentication on
+#      tunneled EAP-Success. This is required with some RADIUS servers that
+#      implement draft-josefsson-pppext-eap-tls-eap-05.txt (e.g.,
+#      Lucent NavisRadius v4.4.0 with PEAP in "IETF Draft 5" mode)
+#      include_tls_length=1 can be used to force wpa_supplicant to include
+#      TLS Message Length field in all TLS messages even if they are not
+#      fragmented.
+#      sim_min_num_chal=3 can be used to configure EAP-SIM to require three
+#      challenges (by default, it accepts 2 or 3)
+#      result_ind=1 can be used to enable EAP-SIM and EAP-AKA to use
+#      protected result indication.
+#      'crypto_binding' option can be used to control PEAPv0 cryptobinding
+#      behavior:
+#       * 0 = do not use cryptobinding (default)
+#       * 1 = use cryptobinding if server supports it
+#       * 2 = require cryptobinding
+#      EAP-WSC (WPS) uses following options: pin=<Device Password> or
+#      pbc=1.
+# phase2: Phase2 (inner authentication with TLS tunnel) parameters
+#      (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
+#      "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
+# Following certificate/private key fields are used in inner Phase2
+# authentication when using EAP-TTLS or EAP-PEAP.
+# ca_cert2: File path to CA certificate file. This file can have one or more
+#      trusted CA certificates. If ca_cert2 and ca_path2 are not included,
+#      server certificate will not be verified. This is insecure and a trusted
+#      CA certificate should always be configured.
+# ca_path2: Directory path for CA certificate files (PEM)
+# client_cert2: File path to client certificate file
+# private_key2: File path to client private key file
+# private_key2_passwd: Password for private key file
+# dh_file2: File path to DH/DSA parameters file (in PEM format)
+# subject_match2: Substring to be matched against the subject of the
+#      authentication server certificate.
+# altsubject_match2: Substring to be matched against the alternative subject
+#      name of the authentication server certificate.
+#
+# fragment_size: Maximum EAP fragment size in bytes (default 1398).
+#      This value limits the fragment size for EAP methods that support
+#      fragmentation (e.g., EAP-TLS and EAP-PEAP). This value should be set
+#      small enough to make the EAP messages fit in MTU of the network
+#      interface used for EAPOL. The default value is suitable for most
+#      cases.
+#
+# EAP-FAST variables:
+# pac_file: File path for the PAC entries. wpa_supplicant will need to be able
+#      to create this file and write updates to it when PAC is being
+#      provisioned or refreshed. Full path to the file should be used since
+#      working directory may change when wpa_supplicant is run in the
+#      background. Alternatively, a named configuration blob can be used by
+#      setting this to blob://<blob name>
+# phase1: fast_provisioning option can be used to enable in-line provisioning
+#         of EAP-FAST credentials (PAC):
+#         0 = disabled,
+#         1 = allow unauthenticated provisioning,
+#         2 = allow authenticated provisioning,
+#         3 = allow both unauthenticated and authenticated provisioning
+#      fast_max_pac_list_len=<num> option can be used to set the maximum
+#              number of PAC entries to store in a PAC list (default: 10)
+#      fast_pac_format=binary option can be used to select binary format for
+#              storing PAC entries in order to save some space (the default
+#              text format uses about 2.5 times the size of minimal binary
+#              format)
+#
+# wpa_supplicant supports number of "EAP workarounds" to work around
+# interoperability issues with incorrectly behaving authentication servers.
+# These are enabled by default because some of the issues are present in large
+# number of authentication servers. Strict EAP conformance mode can be
+# configured by disabling workarounds with eap_workaround=0.
+
+# Example blocks:
+
+# Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
+network={
+       ssid="simple"
+       psk="very secret passphrase"
+       priority=5
+}
+
+# Same as previous, but request SSID-specific scanning (for APs that reject
+# broadcast SSID)
+network={
+       ssid="second ssid"
+       scan_ssid=1
+       psk="very secret passphrase"
+       priority=2
+}
+
+# Only WPA-PSK is used. Any valid cipher combination is accepted.
+network={
+       ssid="example"
+       proto=WPA
+       key_mgmt=WPA-PSK
+       pairwise=CCMP TKIP
+       group=CCMP TKIP WEP104 WEP40
+       psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+       priority=2
+}
+
+# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying
+network={
+       ssid="example"
+       proto=WPA
+       key_mgmt=WPA-PSK
+       pairwise=TKIP
+       group=TKIP
+       psk="not so secure passphrase"
+       wpa_ptk_rekey=600
+}
+
+# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104
+# or WEP40 as the group cipher will not be accepted.
+network={
+       ssid="example"
+       proto=RSN
+       key_mgmt=WPA-EAP
+       pairwise=CCMP TKIP
+       group=CCMP TKIP
+       eap=TLS
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       priority=1
+}
+
+# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel
+# (e.g., Radiator)
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=PEAP
+       identity="user@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       phase1="peaplabel=1"
+       phase2="auth=MSCHAPV2"
+       priority=10
+}
+
+# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+# unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="user@example.com"
+       anonymous_identity="anonymous@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       priority=2
+}
+
+# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted
+# use. Real identity is sent only within an encrypted TLS tunnel.
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="user@example.com"
+       anonymous_identity="anonymous@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       phase2="auth=MSCHAPV2"
+}
+
+# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner
+# authentication.
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       # Phase1 / outer authentication
+       anonymous_identity="anonymous@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       # Phase 2 / inner authentication
+       phase2="autheap=TLS"
+       ca_cert2="/etc/cert/ca2.pem"
+       client_cert2="/etc/cer/user.pem"
+       private_key2="/etc/cer/user.prv"
+       private_key2_passwd="password"
+       priority=2
+}
+
+# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and
+# group cipher.
+network={
+       ssid="example"
+       bssid=00:11:22:33:44:55
+       proto=WPA RSN
+       key_mgmt=WPA-PSK WPA-EAP
+       pairwise=CCMP
+       group=CCMP
+       psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
+}
+
+# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP
+# and all valid ciphers.
+network={
+       ssid=00010203
+       psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
+}
+
+
+# EAP-SIM with a GSM SIM or USIM
+network={
+       ssid="eap-sim-test"
+       key_mgmt=WPA-EAP
+       eap=SIM
+       pin="1234"
+       pcsc=""
+}
+
+
+# EAP-PSK
+network={
+       ssid="eap-psk-test"
+       key_mgmt=WPA-EAP
+       eap=PSK
+       anonymous_identity="eap_psk_user"
+       password=06b4be19da289f475aa46a33cb793029
+       identity="eap_psk_user@example.com"
+}
+
+
+# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using
+# EAP-TLS for authentication and key generation; require both unicast and
+# broadcast WEP keys.
+network={
+       ssid="1x-test"
+       key_mgmt=IEEE8021X
+       eap=TLS
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       eapol_flags=3
+}
+
+
+# LEAP with dynamic WEP keys
+network={
+       ssid="leap-example"
+       key_mgmt=IEEE8021X
+       eap=LEAP
+       identity="user"
+       password="foobar"
+}
+
+# EAP-IKEv2 using shared secrets for both server and peer authentication
+network={
+       ssid="ikev2-example"
+       key_mgmt=WPA-EAP
+       eap=IKEV2
+       identity="user"
+       password="foobar"
+}
+
+# EAP-FAST with WPA (WPA or WPA2)
+network={
+       ssid="eap-fast-test"
+       key_mgmt=WPA-EAP
+       eap=FAST
+       anonymous_identity="FAST-000102030405"
+       identity="username"
+       password="password"
+       phase1="fast_provisioning=1"
+       pac_file="/etc/wpa_supplicant.eap-fast-pac"
+}
+
+network={
+       ssid="eap-fast-test"
+       key_mgmt=WPA-EAP
+       eap=FAST
+       anonymous_identity="FAST-000102030405"
+       identity="username"
+       password="password"
+       phase1="fast_provisioning=1"
+       pac_file="blob://eap-fast-pac"
+}
+
+# Plaintext connection (no WPA, no IEEE 802.1X)
+network={
+       ssid="plaintext-test"
+       key_mgmt=NONE
+}
+
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X)
+network={
+       ssid="static-wep-test"
+       key_mgmt=NONE
+       wep_key0="abcde"
+       wep_key1=0102030405
+       wep_key2="1234567890123"
+       wep_tx_keyidx=0
+       priority=5
+}
+
+
+# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key
+# IEEE 802.11 authentication
+network={
+       ssid="static-wep-test2"
+       key_mgmt=NONE
+       wep_key0="abcde"
+       wep_key1=0102030405
+       wep_key2="1234567890123"
+       wep_tx_keyidx=0
+       priority=5
+       auth_alg=SHARED
+}
+
+
+# IBSS/ad-hoc network with WPA-None/TKIP.
+network={
+       ssid="test adhoc"
+       mode=1
+       frequency=2412
+       proto=WPA
+       key_mgmt=WPA-NONE
+       pairwise=NONE
+       group=TKIP
+       psk="secret passphrase"
+}
+
+
+# Catch all example that allows more or less all configuration modes
+network={
+       ssid="example"
+       scan_ssid=1
+       key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
+       pairwise=CCMP TKIP
+       group=CCMP TKIP WEP104 WEP40
+       psk="very secret passphrase"
+       eap=TTLS PEAP TLS
+       identity="user@example.com"
+       password="foobar"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+       private_key="/etc/cert/user.prv"
+       private_key_passwd="password"
+       phase1="peaplabel=0"
+}
+
+# Example of EAP-TLS with smartcard (openssl engine)
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TLS
+       proto=RSN
+       pairwise=CCMP TKIP
+       group=CCMP TKIP
+       identity="user@example.com"
+       ca_cert="/etc/cert/ca.pem"
+       client_cert="/etc/cert/user.pem"
+
+       engine=1
+
+       # The engine configured here must be available. Look at
+       # OpenSSL engine support in the global section.
+       # The key available through the engine must be the private key
+       # matching the client certificate configured above.
+
+       # use the opensc engine
+       #engine_id="opensc"
+       #key_id="45"
+
+       # use the pkcs11 engine
+       engine_id="pkcs11"
+       key_id="id_45"
+
+       # Optional PIN configuration; this can be left out and PIN will be
+       # asked through the control interface
+       pin="1234"
+}
+
+# Example configuration showing how to use an inlined blob as a CA certificate
+# data instead of using external file
+network={
+       ssid="example"
+       key_mgmt=WPA-EAP
+       eap=TTLS
+       identity="user@example.com"
+       anonymous_identity="anonymous@example.com"
+       password="foobar"
+       ca_cert="blob://exampleblob"
+       priority=20
+}
+
+blob-base64-exampleblob={
+SGVsbG8gV29ybGQhCg==
+}
+
+
+# Wildcard match for SSID (plaintext APs only). This example select any
+# open AP regardless of its SSID.
+network={
+       key_mgmt=NONE
+}
diff --git a/wpa_supplicant/wpa_supplicant.nsi b/wpa_supplicant/wpa_supplicant.nsi
new file mode 100644 (file)
index 0000000..b9f0162
--- /dev/null
@@ -0,0 +1,112 @@
+!define PRODUCT_NAME "wpa_supplicant"
+!define PRODUCT_VERSION "@WPAVER@"
+!define PRODUCT_PUBLISHER "Jouni Malinen"
+
+Name "${PRODUCT_NAME} ${PRODUCT_VERSION}"
+outfile "../wpa_supplicant-@WPAVER@.exe"
+
+installDir "$PROGRAMFILES\wpa_supplicant"
+
+Page Directory
+Page InstFiles
+
+section -Prerequisites
+       SetOutPath $INSTDIR\Prerequisites
+       MessageBox MB_YESNO "Install WinPcap?" /SD IDYES IDNO endWinPcap
+               File "/opt/Qt-Win/files/WinPcap_4_1_2.exe"
+               ExecWait "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe"
+               Goto endWinPcap
+       endWinPcap:
+sectionEnd
+
+
+section
+       setOutPath $INSTDIR
+
+       File wpa_gui.exe
+       File wpa_gui_de.qm
+       File wpa_cli.exe
+       File COPYING
+       File README
+       File README-Windows.txt
+       File win_example.reg
+       File win_if_list.exe
+       File wpa_passphrase.exe
+       File wpa_supplicant.conf
+       File wpa_supplicant.exe
+       File wpasvc.exe
+
+       File /opt/Qt-Win/files/mingwm10.dll
+       File /opt/Qt-Win/files/libgcc_s_dw2-1.dll
+       File /opt/Qt-Win/files/QtCore4.dll
+       File /opt/Qt-Win/files/QtGui4.dll
+
+       WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_level" 0
+       WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_show_keys" 0
+       WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_timestamp" 0
+       WriteRegDWORD HKLM "Software\wpa_supplicant" "debug_use_file" 0
+
+       WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "ap_scan" 2
+       WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default" "update_config" 1
+       WriteRegDWORD HKLM "Software\wpa_supplicant\configs\default\networks" "dummy" 1
+       DeleteRegValue HKLM "Software\wpa_supplicant\configs\default\networks" "dummy"
+
+       WriteRegDWORD HKLM "Software\wpa_supplicant\interfaces" "dummy" 1
+       DeleteRegValue HKLM "Software\wpa_supplicant\interfaces" "dummy"
+
+       writeUninstaller "$INSTDIR\uninstall.exe"
+
+       WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \
+               "DisplayName" "wpa_supplicant"
+WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant" \
+               "UninstallString" "$INSTDIR\uninstall.exe"
+
+       CreateDirectory "$SMPROGRAMS\wpa_supplicant"
+       CreateShortCut "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk" "$INSTDIR\wpa_gui.exe"
+       CreateShortCut "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk" "$INSTDIR\uninstall.exe"
+
+       ExecWait "$INSTDIR\wpasvc.exe reg"
+sectionEnd
+
+
+Function un.onInit
+       MessageBox MB_YESNO "This will uninstall wpa_supplicant. Continue?" IDYES NoAbort
+       Abort
+  NoAbort:
+FunctionEnd
+
+section "uninstall"
+       DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\wpa_supplicant"
+       delete "$INSTDIR\uninstall.exe"
+
+       ExecWait "$INSTDIR\wpasvc.exe unreg"
+
+       DeleteRegKey HKLM "Software\wpa_supplicant"
+
+       delete "$INSTDIR\wpa_gui.exe"
+       delete "$INSTDIR\wpa_gui_de.qm"
+       delete "$INSTDIR\wpa_cli.exe"
+       delete "$INSTDIR\COPYING"
+       delete "$INSTDIR\README"
+       delete "$INSTDIR\README-Windows.txt"
+       delete "$INSTDIR\win_example.reg"
+       delete "$INSTDIR\win_if_list.exe"
+       delete "$INSTDIR\wpa_passphrase.exe"
+       delete "$INSTDIR\wpa_supplicant.conf"
+       delete "$INSTDIR\wpa_supplicant.exe"
+       delete "$INSTDIR\wpasvc.exe"
+
+       delete "$INSTDIR\mingwm10.dll"
+       delete "$INSTDIR\libgcc_s_dw2-1.dll"
+       delete "$INSTDIR\QtCore4.dll"
+       delete "$INSTDIR\QtGui4.dll"
+
+       delete "$INSTDIR\Prerequisites\WinPcap_4_1_2.exe"
+       rmdir "$INSTDIR\Prerequisites"
+
+       rmdir "$INSTDIR"
+
+       delete "$SMPROGRAMS\wpa_supplicant\wpa_gui.lnk"
+       delete "$SMPROGRAMS\wpa_supplicant\Uninstall.lnk"
+       rmdir "$SMPROGRAMS\wpa_supplicant"
+sectionEnd
diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h
new file mode 100644 (file)
index 0000000..6c36a1a
--- /dev/null
@@ -0,0 +1,512 @@
+/*
+ * wpa_supplicant - Internal definitions
+ * Copyright (c) 2003-2010, 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_SUPPLICANT_I_H
+#define WPA_SUPPLICANT_I_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+
+extern const char *wpa_supplicant_version;
+extern const char *wpa_supplicant_license;
+#ifndef CONFIG_NO_STDOUT_DEBUG
+extern const char *wpa_supplicant_full_license1;
+extern const char *wpa_supplicant_full_license2;
+extern const char *wpa_supplicant_full_license3;
+extern const char *wpa_supplicant_full_license4;
+extern const char *wpa_supplicant_full_license5;
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+struct wpa_sm;
+struct wpa_supplicant;
+struct ibss_rsn;
+struct scan_info;
+struct wpa_bss;
+struct wpa_scan_results;
+
+/*
+ * Forward declarations of private structures used within the ctrl_iface
+ * backends. Other parts of wpa_supplicant do not have access to data stored in
+ * these structures.
+ */
+struct ctrl_iface_priv;
+struct ctrl_iface_global_priv;
+struct wpas_dbus_priv;
+
+/**
+ * struct wpa_interface - Parameters for wpa_supplicant_add_iface()
+ */
+struct wpa_interface {
+       /**
+        * confname - Configuration name (file or profile) name
+        *
+        * This can also be %NULL when a configuration file is not used. In
+        * that case, ctrl_interface must be set to allow the interface to be
+        * configured.
+        */
+       const char *confname;
+
+       /**
+        * ctrl_interface - Control interface parameter
+        *
+        * If a configuration file is not used, this variable can be used to
+        * set the ctrl_interface parameter that would have otherwise been read
+        * from the configuration file. If both confname and ctrl_interface are
+        * set, ctrl_interface is used to override the value from configuration
+        * file.
+        */
+       const char *ctrl_interface;
+
+       /**
+        * driver - Driver interface name, or %NULL to use the default driver
+        */
+       const char *driver;
+
+       /**
+        * driver_param - Driver interface parameters
+        *
+        * If a configuration file is not used, this variable can be used to
+        * set the driver_param parameters that would have otherwise been read
+        * from the configuration file. If both confname and driver_param are
+        * set, driver_param is used to override the value from configuration
+        * file.
+        */
+       const char *driver_param;
+
+       /**
+        * ifname - Interface name
+        */
+       const char *ifname;
+
+       /**
+        * bridge_ifname - Optional bridge interface name
+        *
+        * If the driver interface (ifname) is included in a Linux bridge
+        * device, the bridge interface may need to be used for receiving EAPOL
+        * frames. This can be enabled by setting this variable to enable
+        * receiving of EAPOL frames from an additional interface.
+        */
+       const char *bridge_ifname;
+};
+
+/**
+ * struct wpa_params - Parameters for wpa_supplicant_init()
+ */
+struct wpa_params {
+       /**
+        * daemonize - Run %wpa_supplicant in the background
+        */
+       int daemonize;
+
+       /**
+        * wait_for_monitor - Wait for a monitor program before starting
+        */
+       int wait_for_monitor;
+
+       /**
+        * pid_file - Path to a PID (process ID) file
+        *
+        * If this and daemonize are set, process ID of the background process
+        * will be written to the specified file.
+        */
+       char *pid_file;
+
+       /**
+        * wpa_debug_level - Debugging verbosity level (e.g., MSG_INFO)
+        */
+       int wpa_debug_level;
+
+       /**
+        * wpa_debug_show_keys - Whether keying material is included in debug
+        *
+        * This parameter can be used to allow keying material to be included
+        * in debug messages. This is a security risk and this option should
+        * not be enabled in normal configuration. If needed during
+        * development or while troubleshooting, this option can provide more
+        * details for figuring out what is happening.
+        */
+       int wpa_debug_show_keys;
+
+       /**
+        * wpa_debug_timestamp - Whether to include timestamp in debug messages
+        */
+       int wpa_debug_timestamp;
+
+       /**
+        * ctrl_interface - Global ctrl_iface path/parameter
+        */
+       char *ctrl_interface;
+
+       /**
+        * dbus_ctrl_interface - Enable the DBus control interface
+        */
+       int dbus_ctrl_interface;
+
+       /**
+        * wpa_debug_file_path - Path of debug file or %NULL to use stdout
+        */
+       const char *wpa_debug_file_path;
+
+       /**
+        * wpa_debug_syslog - Enable log output through syslog
+        */
+       int wpa_debug_syslog;
+
+       /**
+        * override_driver - Optional driver parameter override
+        *
+        * This parameter can be used to override the driver parameter in
+        * dynamic interface addition to force a specific driver wrapper to be
+        * used instead.
+        */
+       char *override_driver;
+
+       /**
+        * override_ctrl_interface - Optional ctrl_interface override
+        *
+        * This parameter can be used to override the ctrl_interface parameter
+        * in dynamic interface addition to force a control interface to be
+        * created.
+        */
+       char *override_ctrl_interface;
+};
+
+/**
+ * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
+ *
+ * This structure is initialized by calling wpa_supplicant_init() when starting
+ * %wpa_supplicant.
+ */
+struct wpa_global {
+       struct wpa_supplicant *ifaces;
+       struct wpa_params params;
+       struct ctrl_iface_global_priv *ctrl_iface;
+       struct wpas_dbus_priv *dbus;
+       void **drv_priv;
+       size_t drv_count;
+       struct os_time suspend_time;
+};
+
+
+struct wpa_client_mlme {
+#ifdef CONFIG_CLIENT_MLME
+       enum {
+               IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
+               IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
+               IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
+       } state;
+       u8 prev_bssid[ETH_ALEN];
+       u8 ssid[32];
+       size_t ssid_len;
+       u16 aid;
+       u16 ap_capab, capab;
+       u8 *extra_ie; /* to be added to the end of AssocReq */
+       size_t extra_ie_len;
+       u8 *extra_probe_ie; /* to be added to the end of ProbeReq */
+       size_t extra_probe_ie_len;
+       enum wpa_key_mgmt key_mgmt;
+
+       /* The last AssocReq/Resp IEs */
+       u8 *assocreq_ies, *assocresp_ies;
+       size_t assocreq_ies_len, assocresp_ies_len;
+
+       int auth_tries, assoc_tries;
+
+       unsigned int ssid_set:1;
+       unsigned int bssid_set:1;
+       unsigned int prev_bssid_set:1;
+       unsigned int authenticated:1;
+       unsigned int associated:1;
+       unsigned int probereq_poll:1;
+       unsigned int use_protection:1;
+       unsigned int create_ibss:1;
+       unsigned int mixed_cell:1;
+       unsigned int wmm_enabled:1;
+
+       struct os_time last_probe;
+
+       unsigned int auth_algs; /* bitfield of allowed auth algs
+                                * (WPA_AUTH_ALG_*) */
+       int auth_alg; /* currently used IEEE 802.11 authentication algorithm */
+       int auth_transaction;
+
+       struct os_time ibss_join_req;
+       u8 *probe_resp; /* ProbeResp template for IBSS */
+       size_t probe_resp_len;
+       u32 supp_rates_bits;
+
+       int wmm_last_param_set;
+
+       int sta_scanning;
+       int scan_hw_mode_idx;
+       int scan_channel_idx;
+       enum { SCAN_SET_CHANNEL, SCAN_SEND_PROBE } scan_state;
+       struct os_time last_scan_completed;
+       int scan_oper_channel;
+       int scan_oper_freq;
+       int scan_oper_phymode;
+       u8 scan_ssid[32];
+       size_t scan_ssid_len;
+       int scan_skip_11b;
+       int *scan_freqs;
+
+       struct ieee80211_sta_bss *sta_bss_list;
+#define STA_HASH_SIZE 256
+#define STA_HASH(sta) (sta[5])
+       struct ieee80211_sta_bss *sta_bss_hash[STA_HASH_SIZE];
+
+       int cts_protect_erp_frames;
+
+       enum hostapd_hw_mode phymode; /* current mode */
+       struct hostapd_hw_modes *modes;
+       size_t num_modes;
+       unsigned int hw_modes; /* bitfield of allowed hardware modes;
+                               * (1 << HOSTAPD_MODE_*) */
+       int num_curr_rates;
+       int *curr_rates;
+       int freq; /* The current frequency in MHz */
+       int channel; /* The current IEEE 802.11 channel number */
+
+#ifdef CONFIG_IEEE80211R
+       u8 current_md[6];
+       u8 *ft_ies;
+       size_t ft_ies_len;
+#endif /* CONFIG_IEEE80211R */
+
+       void (*public_action_cb)(void *ctx, const u8 *buf, size_t len,
+                                int freq);
+       void *public_action_cb_ctx;
+
+#else /* CONFIG_CLIENT_MLME */
+       int dummy; /* to keep MSVC happy */
+#endif /* CONFIG_CLIENT_MLME */
+};
+
+/**
+ * struct wpa_supplicant - Internal data for wpa_supplicant interface
+ *
+ * This structure contains the internal data for core wpa_supplicant code. This
+ * should be only used directly from the core code. However, a pointer to this
+ * data is used from other files as an arbitrary context pointer in calls to
+ * core functions.
+ */
+struct wpa_supplicant {
+       struct wpa_global *global;
+       struct wpa_supplicant *next;
+       struct l2_packet_data *l2;
+       struct l2_packet_data *l2_br;
+       unsigned char own_addr[ETH_ALEN];
+       char ifname[100];
+#ifdef CONFIG_CTRL_IFACE_DBUS
+       char *dbus_path;
+#endif /* CONFIG_CTRL_IFACE_DBUS */
+#ifdef CONFIG_CTRL_IFACE_DBUS_NEW
+       char *dbus_new_path;
+#endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
+       char bridge_ifname[16];
+
+       char *confname;
+       struct wpa_config *conf;
+       int countermeasures;
+       os_time_t last_michael_mic_error;
+       u8 bssid[ETH_ALEN];
+       u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
+                                    * field contains the targer BSSID. */
+       int reassociate; /* reassociation requested */
+       int disconnected; /* all connections disabled; i.e., do no reassociate
+                          * before this has been cleared */
+       struct wpa_ssid *current_ssid;
+       struct wpa_bss *current_bss;
+       int ap_ies_from_associnfo;
+       unsigned int assoc_freq;
+
+       /* Selected configuration (based on Beacon/ProbeResp WPA IE) */
+       int pairwise_cipher;
+       int group_cipher;
+       int key_mgmt;
+       int mgmt_group_cipher;
+
+       void *drv_priv; /* private data used by driver_ops */
+       void *global_drv_priv;
+
+       struct wpa_ssid *prev_scan_ssid; /* previously scanned SSID;
+                                         * NULL = not yet initialized (start
+                                         * with wildcard SSID)
+                                         * WILDCARD_SSID_SCAN = wildcard
+                                         * SSID was used in the previous scan
+                                         */
+#define WILDCARD_SSID_SCAN ((struct wpa_ssid *) 1)
+
+       void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+                                struct wpa_scan_results *scan_res);
+       struct dl_list bss; /* struct wpa_bss::list */
+       struct dl_list bss_id; /* struct wpa_bss::list_id */
+       size_t num_bss;
+       unsigned int bss_update_idx;
+       unsigned int bss_next_id;
+
+       struct wpa_driver_ops *driver;
+       int interface_removed; /* whether the network interface has been
+                               * removed */
+       struct wpa_sm *wpa;
+       struct eapol_sm *eapol;
+
+       struct ctrl_iface_priv *ctrl_iface;
+
+       enum wpa_states wpa_state;
+       int scanning;
+       int new_connection;
+       int reassociated_connection;
+
+       int eapol_received; /* number of EAPOL packets received after the
+                            * previous association event */
+
+       struct scard_data *scard;
+
+       unsigned char last_eapol_src[ETH_ALEN];
+
+       int keys_cleared;
+
+       struct wpa_blacklist *blacklist;
+
+       int scan_req; /* manual scan request; this forces a scan even if there
+                      * are no enabled networks in the configuration */
+       int scan_runs; /* number of scan runs since WPS was started */
+
+       struct wpa_client_mlme mlme;
+       unsigned int drv_flags;
+       int max_scan_ssids;
+       unsigned int max_remain_on_chan;
+
+       int pending_mic_error_report;
+       int pending_mic_error_pairwise;
+       int mic_errors_seen; /* Michael MIC errors with the current PTK */
+
+       struct wps_context *wps;
+       int wps_success; /* WPS success event received */
+       struct wps_er *wps_er;
+       int blacklist_cleared;
+
+       struct wpabuf *pending_eapol_rx;
+       struct os_time pending_eapol_rx_time;
+       u8 pending_eapol_rx_src[ETH_ALEN];
+
+       struct ibss_rsn *ibss_rsn;
+
+#ifdef CONFIG_SME
+       struct {
+               u8 ssid[32];
+               size_t ssid_len;
+               int freq;
+               u8 assoc_req_ie[80];
+               size_t assoc_req_ie_len;
+               int mfp;
+               int ft_used;
+               u8 mobility_domain[2];
+               u8 *ft_ies;
+               size_t ft_ies_len;
+               u8 prev_bssid[ETH_ALEN];
+               int prev_bssid_set;
+               int auth_alg;
+       } sme;
+#endif /* CONFIG_SME */
+
+#ifdef CONFIG_AP
+       struct hostapd_iface *ap_iface;
+       void (*ap_configured_cb)(void *ctx, void *data);
+       void *ap_configured_cb_ctx;
+       void *ap_configured_cb_data;
+#endif /* CONFIG_AP */
+
+       struct wpa_ssid *bgscan_ssid;
+       const struct bgscan_ops *bgscan;
+       void *bgscan_priv;
+
+       int connect_without_scan;
+
+       int after_wps;
+       unsigned int wps_freq;
+};
+
+
+/* wpa_supplicant.c */
+int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+
+int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s);
+
+const char * wpa_supplicant_state_txt(enum wpa_states state);
+int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
+                             struct wpa_bss *bss, struct wpa_ssid *ssid,
+                             u8 *wpa_ie, size_t *wpa_ie_len);
+void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
+                             struct wpa_bss *bss,
+                             struct wpa_ssid *ssid);
+void wpa_supplicant_set_non_wpa_policy(struct wpa_supplicant *wpa_s,
+                                      struct wpa_ssid *ssid);
+void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s);
+void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr);
+void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
+                                    int sec, int usec);
+void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
+                             enum wpa_states state);
+struct wpa_ssid * wpa_supplicant_get_ssid(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
+                                  int reason_code);
+void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
+                                int reason_code);
+
+void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
+                                  struct wpa_ssid *ssid);
+void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
+                                   struct wpa_ssid *ssid);
+void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
+                                  struct wpa_ssid *ssid);
+int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
+                              int ap_scan);
+int wpa_supplicant_set_debug_params(struct wpa_global *global,
+                                   int debug_level, int debug_timestamp,
+                                   int debug_show_keys);
+
+void wpa_show_license(void);
+
+struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
+                                                struct wpa_interface *iface);
+int wpa_supplicant_remove_iface(struct wpa_global *global,
+                               struct wpa_supplicant *wpa_s);
+struct wpa_supplicant * wpa_supplicant_get_iface(struct wpa_global *global,
+                                                const char *ifname);
+struct wpa_global * wpa_supplicant_init(struct wpa_params *params);
+int wpa_supplicant_run(struct wpa_global *global);
+void wpa_supplicant_deinit(struct wpa_global *global);
+
+int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid);
+void wpa_supplicant_terminate_proc(struct wpa_global *global);
+void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
+                            const u8 *buf, size_t len);
+enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
+enum wpa_cipher cipher_suite2driver(int cipher);
+
+/* events.c */
+void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
+                           struct wpa_bss *selected,
+                           struct wpa_ssid *ssid);
+
+/* eap_register.c */
+int eap_register_methods(void);
+
+#endif /* WPA_SUPPLICANT_I_H */
diff --git a/wpa_supplicant/wpas_glue.c b/wpa_supplicant/wpas_glue.c
new file mode 100644 (file)
index 0000000..4af0cd0
--- /dev/null
@@ -0,0 +1,685 @@
+/*
+ * WPA Supplicant - Glue code to setup EAPOL and RSN modules
+ * Copyright (c) 2003-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 "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "eloop.h"
+#include "config.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "mlme.h"
+#include "sme.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "wpas_glue.h"
+#include "wps_supplicant.h"
+#include "bss.h"
+#include "scan.h"
+
+
+#ifndef CONFIG_NO_CONFIG_BLOBS
+#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
+static void wpa_supplicant_set_config_blob(void *ctx,
+                                          struct wpa_config_blob *blob)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_config_set_blob(wpa_s->conf, blob);
+       if (wpa_s->conf->update_config) {
+               int ret = wpa_config_write(wpa_s->confname, wpa_s->conf);
+               if (ret) {
+                       wpa_printf(MSG_DEBUG, "Failed to update config after "
+                                  "blob set");
+               }
+       }
+}
+
+
+static const struct wpa_config_blob *
+wpa_supplicant_get_config_blob(void *ctx, const char *name)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpa_config_get_blob(wpa_s->conf, name);
+}
+#endif /* defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA) */
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+#if defined(IEEE8021X_EAPOL) || !defined(CONFIG_NO_WPA)
+static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
+                           const void *data, u16 data_len,
+                           size_t *msg_len, void **data_pos)
+{
+       struct ieee802_1x_hdr *hdr;
+
+       *msg_len = sizeof(*hdr) + data_len;
+       hdr = os_malloc(*msg_len);
+       if (hdr == NULL)
+               return NULL;
+
+       hdr->version = wpa_s->conf->eapol_version;
+       hdr->type = type;
+       hdr->length = host_to_be16(data_len);
+
+       if (data)
+               os_memcpy(hdr + 1, data, data_len);
+       else
+               os_memset(hdr + 1, 0, data_len);
+
+       if (data_pos)
+               *data_pos = hdr + 1;
+
+       return (u8 *) hdr;
+}
+
+
+/**
+ * wpa_ether_send - Send Ethernet frame
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @dest: Destination MAC address
+ * @proto: Ethertype in host byte order
+ * @buf: Frame payload starting from IEEE 802.1X header
+ * @len: Frame payload length
+ * Returns: >=0 on success, <0 on failure
+ */
+static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
+                         u16 proto, const u8 *buf, size_t len)
+{
+       if (wpa_s->l2) {
+               return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
+       }
+
+       return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+}
+#endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
+
+
+#ifdef IEEE8021X_EAPOL
+
+/**
+ * wpa_supplicant_eapol_send - Send IEEE 802.1X EAPOL packet to Authenticator
+ * @ctx: Pointer to wpa_supplicant data (wpa_s)
+ * @type: IEEE 802.1X packet type (IEEE802_1X_TYPE_*)
+ * @buf: EAPOL payload (after IEEE 802.1X header)
+ * @len: EAPOL payload length
+ * Returns: >=0 on success, <0 on failure
+ *
+ * This function adds Ethernet and IEEE 802.1X header and sends the EAPOL frame
+ * to the current Authenticator.
+ */
+static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
+                                    size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       u8 *msg, *dst, bssid[ETH_ALEN];
+       size_t msglen;
+       int res;
+
+       /* TODO: could add l2_packet_sendmsg that allows fragments to avoid
+        * extra copy here */
+
+       if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt) ||
+           wpa_s->key_mgmt == WPA_KEY_MGMT_NONE) {
+               /* Current SSID is not using IEEE 802.1X/EAP, so drop possible
+                * EAPOL frames (mainly, EAPOL-Start) from EAPOL state
+                * machines. */
+               wpa_printf(MSG_DEBUG, "WPA: drop TX EAPOL in non-IEEE 802.1X "
+                          "mode (type=%d len=%lu)", type,
+                          (unsigned long) len);
+               return -1;
+       }
+
+       if (pmksa_cache_get_current(wpa_s->wpa) &&
+           type == IEEE802_1X_TYPE_EAPOL_START) {
+               /* Trying to use PMKSA caching - do not send EAPOL-Start frames
+                * since they will trigger full EAPOL authentication. */
+               wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send "
+                          "EAPOL-Start");
+               return -1;
+       }
+
+       if (is_zero_ether_addr(wpa_s->bssid)) {
+               wpa_printf(MSG_DEBUG, "BSSID not set when trying to send an "
+                          "EAPOL frame");
+               if (wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
+                   !is_zero_ether_addr(bssid)) {
+                       dst = bssid;
+                       wpa_printf(MSG_DEBUG, "Using current BSSID " MACSTR
+                                  " from the driver as the EAPOL destination",
+                                  MAC2STR(dst));
+               } else {
+                       dst = wpa_s->last_eapol_src;
+                       wpa_printf(MSG_DEBUG, "Using the source address of the"
+                                  " last received EAPOL frame " MACSTR " as "
+                                  "the EAPOL destination",
+                                  MAC2STR(dst));
+               }
+       } else {
+               /* BSSID was already set (from (Re)Assoc event, so use it as
+                * the EAPOL destination. */
+               dst = wpa_s->bssid;
+       }
+
+       msg = wpa_alloc_eapol(wpa_s, type, buf, len, &msglen, NULL);
+       if (msg == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "TX EAPOL: dst=" MACSTR, MAC2STR(dst));
+       wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", msg, msglen);
+       res = wpa_ether_send(wpa_s, dst, ETH_P_EAPOL, msg, msglen);
+       os_free(msg);
+       return res;
+}
+
+
+/**
+ * wpa_eapol_set_wep_key - set WEP key for the driver
+ * @ctx: Pointer to wpa_supplicant data (wpa_s)
+ * @unicast: 1 = individual unicast key, 0 = broadcast key
+ * @keyidx: WEP key index (0..3)
+ * @key: Pointer to key data
+ * @keylen: Key length in bytes
+ * Returns: 0 on success or < 0 on error.
+ */
+static int wpa_eapol_set_wep_key(void *ctx, int unicast, int keyidx,
+                                const u8 *key, size_t keylen)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               int cipher = (keylen == 5) ? WPA_CIPHER_WEP40 :
+                       WPA_CIPHER_WEP104;
+               if (unicast)
+                       wpa_s->pairwise_cipher = cipher;
+               else
+                       wpa_s->group_cipher = cipher;
+       }
+       return wpa_drv_set_key(wpa_s, WPA_ALG_WEP,
+                              unicast ? wpa_s->bssid :
+                              (u8 *) "\xff\xff\xff\xff\xff\xff",
+                              keyidx, unicast, (u8 *) "", 0, key, keylen);
+}
+
+
+static void wpa_supplicant_aborted_cached(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_sm_aborted_cached(wpa_s->wpa);
+}
+
+
+static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
+                                   void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       int res, pmk_len;
+       u8 pmk[PMK_LEN];
+
+       wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully",
+                  success ? "" : "un");
+
+       if (wpas_wps_eapol_cb(wpa_s) > 0)
+               return;
+
+       if (!success) {
+               /*
+                * Make sure we do not get stuck here waiting for long EAPOL
+                * timeout if the AP does not disconnect in case of
+                * authentication failure.
+                */
+               wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
+       }
+
+       if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+               return;
+
+       if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
+               return;
+
+       wpa_printf(MSG_DEBUG, "Configure PMK for driver-based RSN 4-way "
+                  "handshake");
+
+       pmk_len = PMK_LEN;
+       res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
+       if (res) {
+               /*
+                * EAP-LEAP is an exception from other EAP methods: it
+                * uses only 16-byte PMK.
+                */
+               res = eapol_sm_get_key(eapol, pmk, 16);
+               pmk_len = 16;
+       }
+
+       if (res) {
+               wpa_printf(MSG_DEBUG, "Failed to get PMK from EAPOL state "
+                          "machines");
+               return;
+       }
+
+       if (wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0, NULL, 0, pmk,
+                           pmk_len)) {
+               wpa_printf(MSG_DEBUG, "Failed to set PMK to the driver");
+       }
+
+       wpa_supplicant_cancel_scan(wpa_s);
+       wpa_supplicant_cancel_auth_timeout(wpa_s);
+       wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+
+}
+
+
+static void wpa_supplicant_notify_eapol_done(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_msg(wpa_s, MSG_DEBUG, "WPA: EAPOL processing complete");
+       if (wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
+               wpa_supplicant_set_state(wpa_s, WPA_4WAY_HANDSHAKE);
+       } else {
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+       }
+}
+
+#endif /* IEEE8021X_EAPOL */
+
+
+#ifndef CONFIG_NO_WPA
+
+static int wpa_get_beacon_ie(struct wpa_supplicant *wpa_s)
+{
+       int ret = 0;
+       struct wpa_bss *curr = NULL, *bss;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       const u8 *ie;
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) != 0)
+                       continue;
+               if (ssid == NULL ||
+                   ((bss->ssid_len == ssid->ssid_len &&
+                     os_memcmp(bss->ssid, ssid->ssid, ssid->ssid_len) == 0) ||
+                    ssid->ssid_len == 0)) {
+                       curr = bss;
+                       break;
+               }
+       }
+
+       if (curr) {
+               ie = wpa_bss_get_vendor_ie(curr, WPA_IE_VENDOR_TYPE);
+               if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+                       ret = -1;
+
+               ie = wpa_bss_get_ie(curr, WLAN_EID_RSN);
+               if (wpa_sm_set_ap_rsn_ie(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0))
+                       ret = -1;
+       } else {
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+static int wpa_supplicant_get_beacon_ie(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_get_beacon_ie(wpa_s) == 0) {
+               return 0;
+       }
+
+       /* No WPA/RSN IE found in the cached scan results. Try to get updated
+        * scan results from the driver. */
+       if (wpa_supplicant_update_scan_results(wpa_s) < 0)
+               return -1;
+
+       return wpa_get_beacon_ie(wpa_s);
+}
+
+
+static u8 * _wpa_alloc_eapol(void *wpa_s, u8 type,
+                            const void *data, u16 data_len,
+                            size_t *msg_len, void **data_pos)
+{
+       return wpa_alloc_eapol(wpa_s, type, data, data_len, msg_len, data_pos);
+}
+
+
+static int _wpa_ether_send(void *wpa_s, const u8 *dest, u16 proto,
+                          const u8 *buf, size_t len)
+{
+       return wpa_ether_send(wpa_s, dest, proto, buf, len);
+}
+
+
+static void _wpa_supplicant_cancel_auth_timeout(void *wpa_s)
+{
+       wpa_supplicant_cancel_auth_timeout(wpa_s);
+}
+
+
+static void _wpa_supplicant_set_state(void *wpa_s, enum wpa_states state)
+{
+       wpa_supplicant_set_state(wpa_s, state);
+}
+
+
+/**
+ * wpa_supplicant_get_state - Get the connection state
+ * @wpa_s: Pointer to wpa_supplicant data
+ * Returns: The current connection state (WPA_*)
+ */
+static enum wpa_states wpa_supplicant_get_state(struct wpa_supplicant *wpa_s)
+{
+       return wpa_s->wpa_state;
+}
+
+
+static enum wpa_states _wpa_supplicant_get_state(void *wpa_s)
+{
+       return wpa_supplicant_get_state(wpa_s);
+}
+
+
+static void _wpa_supplicant_disassociate(void *wpa_s, int reason_code)
+{
+       wpa_supplicant_disassociate(wpa_s, reason_code);
+       /* Schedule a scan to make sure we continue looking for networks */
+       wpa_supplicant_req_scan(wpa_s, 5, 0);
+}
+
+
+static void _wpa_supplicant_deauthenticate(void *wpa_s, int reason_code)
+{
+       wpa_supplicant_deauthenticate(wpa_s, reason_code);
+       /* Schedule a scan to make sure we continue looking for networks */
+       wpa_supplicant_req_scan(wpa_s, 5, 0);
+}
+
+
+static void * wpa_supplicant_get_network_ctx(void *wpa_s)
+{
+       return wpa_supplicant_get_ssid(wpa_s);
+}
+
+
+static int wpa_supplicant_get_bssid(void *ctx, u8 *bssid)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) {
+               os_memcpy(bssid, wpa_s->bssid, ETH_ALEN);
+               return 0;
+       }
+       return wpa_drv_get_bssid(wpa_s, bssid);
+}
+
+
+static int wpa_supplicant_set_key(void *_wpa_s, enum wpa_alg alg,
+                                 const u8 *addr, int key_idx, int set_tx,
+                                 const u8 *seq, size_t seq_len,
+                                 const u8 *key, size_t key_len)
+{
+       struct wpa_supplicant *wpa_s = _wpa_s;
+       if (alg == WPA_ALG_TKIP && key_idx == 0 && key_len == 32) {
+               /* Clear the MIC error counter when setting a new PTK. */
+               wpa_s->mic_errors_seen = 0;
+       }
+       return wpa_drv_set_key(wpa_s, alg, addr, key_idx, set_tx, seq, seq_len,
+                              key, key_len);
+}
+
+
+static int wpa_supplicant_mlme_setprotection(void *wpa_s, const u8 *addr,
+                                            int protection_type,
+                                            int key_type)
+{
+       return wpa_drv_mlme_setprotection(wpa_s, addr, protection_type,
+                                         key_type);
+}
+
+
+static int wpa_supplicant_add_pmkid(void *wpa_s,
+                                   const u8 *bssid, const u8 *pmkid)
+{
+       return wpa_drv_add_pmkid(wpa_s, bssid, pmkid);
+}
+
+
+static int wpa_supplicant_remove_pmkid(void *wpa_s,
+                                      const u8 *bssid, const u8 *pmkid)
+{
+       return wpa_drv_remove_pmkid(wpa_s, bssid, pmkid);
+}
+
+
+#ifdef CONFIG_IEEE80211R
+static int wpa_supplicant_update_ft_ies(void *ctx, const u8 *md,
+                                       const u8 *ies, size_t ies_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               return ieee80211_sta_update_ft_ies(wpa_s, md, ies, ies_len);
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+               return sme_update_ft_ies(wpa_s, md, ies, ies_len);
+       return wpa_drv_update_ft_ies(wpa_s, md, ies, ies_len);
+}
+
+
+static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
+                                        const u8 *target_ap,
+                                        const u8 *ies, size_t ies_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               return ieee80211_sta_send_ft_action(wpa_s, action, target_ap,
+                                                   ies, ies_len);
+       return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len);
+}
+
+
+static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_driver_auth_params params;
+       struct wpa_bss *bss;
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)
+               return -1;
+
+       bss = wpa_bss_get_bssid(wpa_s, target_ap);
+       if (bss == NULL)
+               return -1;
+
+       os_memset(&params, 0, sizeof(params));
+       params.bssid = target_ap;
+       params.freq = bss->freq;
+       params.ssid = bss->ssid;
+       params.ssid_len = bss->ssid_len;
+       params.auth_alg = WPA_AUTH_ALG_FT;
+       params.local_state_change = 1;
+       return wpa_drv_authenticate(wpa_s, &params);
+}
+#endif /* CONFIG_IEEE80211R */
+
+#endif /* CONFIG_NO_WPA */
+
+
+#ifdef IEEE8021X_EAPOL
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void wpa_supplicant_eap_param_needed(void *ctx, const char *field,
+                                           const char *txt)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       char *buf;
+       size_t buflen;
+       int len;
+
+       if (ssid == NULL)
+               return;
+
+       buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       len = os_snprintf(buf, buflen,
+                         WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
+                         field, ssid->id, txt);
+       if (len < 0 || (size_t) len >= buflen) {
+               os_free(buf);
+               return;
+       }
+       if (ssid->ssid && buflen > len + ssid->ssid_len) {
+               os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+               len += ssid->ssid_len;
+               buf[len] = '\0';
+       }
+       buf[buflen - 1] = '\0';
+       wpa_msg(wpa_s, MSG_INFO, "%s", buf);
+       os_free(buf);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define wpa_supplicant_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
+static void wpa_supplicant_port_cb(void *ctx, int authorized)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG, "AP mode active - skip EAPOL Supplicant "
+                          "port status: %s",
+                          authorized ? "Authorized" : "Unauthorized");
+               return;
+       }
+#endif /* CONFIG_AP */
+       wpa_printf(MSG_DEBUG, "EAPOL: Supplicant port status: %s",
+                  authorized ? "Authorized" : "Unauthorized");
+       wpa_drv_set_supp_port(wpa_s, authorized);
+}
+#endif /* IEEE8021X_EAPOL */
+
+
+int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
+{
+#ifdef IEEE8021X_EAPOL
+       struct eapol_ctx *ctx;
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL) {
+               wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
+               return -1;
+       }
+
+       ctx->ctx = wpa_s;
+       ctx->msg_ctx = wpa_s;
+       ctx->eapol_send_ctx = wpa_s;
+       ctx->preauth = 0;
+       ctx->eapol_done_cb = wpa_supplicant_notify_eapol_done;
+       ctx->eapol_send = wpa_supplicant_eapol_send;
+       ctx->set_wep_key = wpa_eapol_set_wep_key;
+       ctx->set_config_blob = wpa_supplicant_set_config_blob;
+       ctx->get_config_blob = wpa_supplicant_get_config_blob;
+       ctx->aborted_cached = wpa_supplicant_aborted_cached;
+       ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
+       ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+       ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+       ctx->wps = wpa_s->wps;
+       ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
+       ctx->port_cb = wpa_supplicant_port_cb;
+       ctx->cb = wpa_supplicant_eapol_cb;
+       ctx->cb_ctx = wpa_s;
+       wpa_s->eapol = eapol_sm_init(ctx);
+       if (wpa_s->eapol == NULL) {
+               os_free(ctx);
+               wpa_printf(MSG_ERROR, "Failed to initialize EAPOL state "
+                          "machines.");
+               return -1;
+       }
+#endif /* IEEE8021X_EAPOL */
+
+       return 0;
+}
+
+
+int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
+{
+#ifndef CONFIG_NO_WPA
+       struct wpa_sm_ctx *ctx;
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL) {
+               wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
+               return -1;
+       }
+
+       ctx->ctx = wpa_s;
+       ctx->msg_ctx = wpa_s;
+       ctx->set_state = _wpa_supplicant_set_state;
+       ctx->get_state = _wpa_supplicant_get_state;
+       ctx->deauthenticate = _wpa_supplicant_deauthenticate;
+       ctx->disassociate = _wpa_supplicant_disassociate;
+       ctx->set_key = wpa_supplicant_set_key;
+       ctx->get_network_ctx = wpa_supplicant_get_network_ctx;
+       ctx->get_bssid = wpa_supplicant_get_bssid;
+       ctx->ether_send = _wpa_ether_send;
+       ctx->get_beacon_ie = wpa_supplicant_get_beacon_ie;
+       ctx->alloc_eapol = _wpa_alloc_eapol;
+       ctx->cancel_auth_timeout = _wpa_supplicant_cancel_auth_timeout;
+       ctx->add_pmkid = wpa_supplicant_add_pmkid;
+       ctx->remove_pmkid = wpa_supplicant_remove_pmkid;
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       ctx->set_config_blob = wpa_supplicant_set_config_blob;
+       ctx->get_config_blob = wpa_supplicant_get_config_blob;
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+       ctx->mlme_setprotection = wpa_supplicant_mlme_setprotection;
+#ifdef CONFIG_IEEE80211R
+       ctx->update_ft_ies = wpa_supplicant_update_ft_ies;
+       ctx->send_ft_action = wpa_supplicant_send_ft_action;
+       ctx->mark_authenticated = wpa_supplicant_mark_authenticated;
+#endif /* CONFIG_IEEE80211R */
+
+       wpa_s->wpa = wpa_sm_init(ctx);
+       if (wpa_s->wpa == NULL) {
+               wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
+                          "machine");
+               return -1;
+       }
+#endif /* CONFIG_NO_WPA */
+
+       return 0;
+}
+
+
+void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
+                                       struct wpa_ssid *ssid)
+{
+       struct rsn_supp_config conf;
+       if (ssid) {
+               os_memset(&conf, 0, sizeof(conf));
+               conf.network_ctx = ssid;
+               conf.peerkey_enabled = ssid->peerkey;
+               conf.allowed_pairwise_cipher = ssid->pairwise_cipher;
+#ifdef IEEE8021X_EAPOL
+               conf.eap_workaround = ssid->eap_workaround;
+               conf.eap_conf_ctx = &ssid->eap;
+#endif /* IEEE8021X_EAPOL */
+               conf.ssid = ssid->ssid;
+               conf.ssid_len = ssid->ssid_len;
+               conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+       }
+       wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
+}
diff --git a/wpa_supplicant/wpas_glue.h b/wpa_supplicant/wpas_glue.h
new file mode 100644 (file)
index 0000000..b571e4d
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * WPA Supplicant - Glue code to setup EAPOL and RSN modules
+ * Copyright (c) 2003-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 WPAS_GLUE_H
+#define WPAS_GLUE_H
+
+int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
+                                       struct wpa_ssid *ssid);
+
+#endif /* WPAS_GLUE_H */
diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c
new file mode 100644 (file)
index 0000000..ba94d33
--- /dev/null
@@ -0,0 +1,1185 @@
+/*
+ * wpa_supplicant / WPS integration
+ * Copyright (c) 2008-2010, 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 "uuid.h"
+#include "crypto/dh_group5.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "common/wpa_ctrl.h"
+#include "eap_common/eap_wsc_common.h"
+#include "eap_peer/eap.h"
+#include "rsn_supp/wpa.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "notify.h"
+#include "blacklist.h"
+#include "bss.h"
+#include "scan.h"
+#include "wps_supplicant.h"
+
+
+#define WPS_PIN_SCAN_IGNORE_SEL_REG 3
+
+static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx);
+static void wpas_clear_wps(struct wpa_supplicant *wpa_s);
+
+
+int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->wps_success &&
+           wpa_s->current_ssid &&
+           eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
+               const u8 *bssid = wpa_s->bssid;
+               if (is_zero_ether_addr(bssid))
+                       bssid = wpa_s->pending_bssid;
+
+               wpa_printf(MSG_DEBUG, "WPS: PIN registration with " MACSTR
+                          " did not succeed - continue trying to find "
+                          "suitable AP", MAC2STR(bssid));
+               wpa_blacklist_add(wpa_s, bssid);
+
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s,
+                                       wpa_s->blacklist_cleared ? 5 : 0, 0);
+               wpa_s->blacklist_cleared = 0;
+               return 1;
+       }
+
+       eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid &&
+           !(wpa_s->current_ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+               wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
+                          "try to associate with the received credential");
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+               wpa_s->after_wps = 5;
+               wpa_s->wps_freq = wpa_s->assoc_freq;
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               return 1;
+       }
+
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS && wpa_s->current_ssid) {
+               wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
+                          "for external credential processing");
+               wpas_clear_wps(wpa_s);
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static void wpas_wps_security_workaround(struct wpa_supplicant *wpa_s,
+                                        struct wpa_ssid *ssid,
+                                        const struct wps_credential *cred)
+{
+       struct wpa_driver_capa capa;
+       struct wpa_bss *bss;
+       const u8 *ie;
+       struct wpa_ie_data adv;
+       int wpa2 = 0, ccmp = 0;
+
+       /*
+        * Many existing WPS APs do not know how to negotiate WPA2 or CCMP in
+        * case they are configured for mixed mode operation (WPA+WPA2 and
+        * TKIP+CCMP). Try to use scan results to figure out whether the AP
+        * actually supports stronger security and select that if the client
+        * has support for it, too.
+        */
+
+       if (wpa_drv_get_capa(wpa_s, &capa))
+               return; /* Unknown what driver supports */
+
+       bss = wpa_bss_get(wpa_s, cred->mac_addr, ssid->ssid, ssid->ssid_len);
+       if (bss == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: The AP was not found from BSS "
+                          "table - use credential as-is");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS: AP found from BSS table");
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+       if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0) {
+               wpa2 = 1;
+               if (adv.pairwise_cipher & WPA_CIPHER_CCMP)
+                       ccmp = 1;
+       } else {
+               ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+               if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &adv) == 0 &&
+                   adv.pairwise_cipher & WPA_CIPHER_CCMP)
+                       ccmp = 1;
+       }
+
+       if (ie == NULL && (ssid->proto & WPA_PROTO_WPA) &&
+           (ssid->pairwise_cipher & WPA_CIPHER_TKIP)) {
+               /*
+                * TODO: This could be the initial AP configuration and the
+                * Beacon contents could change shortly. Should request a new
+                * scan and delay addition of the network until the updated
+                * scan results are available.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: The AP did not yet advertise WPA "
+                          "support - use credential as-is");
+               return;
+       }
+
+       if (ccmp && !(ssid->pairwise_cipher & WPA_CIPHER_CCMP) &&
+           (ssid->pairwise_cipher & WPA_CIPHER_TKIP) &&
+           (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
+               wpa_printf(MSG_DEBUG, "WPS: Add CCMP into the credential "
+                          "based on scan results");
+               if (wpa_s->conf->ap_scan == 1)
+                       ssid->pairwise_cipher |= WPA_CIPHER_CCMP;
+               else
+                       ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+       }
+
+       if (wpa2 && !(ssid->proto & WPA_PROTO_RSN) &&
+           (ssid->proto & WPA_PROTO_WPA) &&
+           (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP)) {
+               wpa_printf(MSG_DEBUG, "WPS: Add WPA2 into the credential "
+                          "based on scan results");
+               if (wpa_s->conf->ap_scan == 1)
+                       ssid->proto |= WPA_PROTO_RSN;
+               else
+                       ssid->proto = WPA_PROTO_RSN;
+       }
+}
+
+
+static int wpa_supplicant_wps_cred(void *ctx,
+                                  const struct wps_credential *cred)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       u8 key_idx = 0;
+       u16 auth_type;
+
+       if ((wpa_s->conf->wps_cred_processing == 1 ||
+            wpa_s->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(wpa_s, MSG_INFO, "%s%s",
+                               WPS_EVENT_CRED_RECEIVED, buf);
+                       os_free(buf);
+               }
+
+               wpas_notify_wps_credential(wpa_s, cred);
+       } else
+               wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_CRED_RECEIVED);
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute",
+                       cred->cred_attr, cred->cred_attr_len);
+
+       if (wpa_s->conf->wps_cred_processing == 1)
+               return 0;
+
+       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));
+
+       auth_type = cred->auth_type;
+       if (auth_type == (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) {
+               wpa_printf(MSG_DEBUG, "WPS: Workaround - convert mixed-mode "
+                          "auth_type into WPA2PSK");
+               auth_type = WPS_AUTH_WPA2PSK;
+       }
+
+       if (auth_type != WPS_AUTH_OPEN &&
+           auth_type != WPS_AUTH_SHARED &&
+           auth_type != WPS_AUTH_WPAPSK &&
+           auth_type != WPS_AUTH_WPA2PSK) {
+               wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
+                          "unsupported authentication type 0x%x",
+                          auth_type);
+               return 0;
+       }
+
+       if (ssid && (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+               wpa_printf(MSG_DEBUG, "WPS: Replace WPS network block based "
+                          "on the received credential");
+               os_free(ssid->eap.identity);
+               ssid->eap.identity = NULL;
+               ssid->eap.identity_len = 0;
+               os_free(ssid->eap.phase1);
+               ssid->eap.phase1 = NULL;
+               os_free(ssid->eap.eap_methods);
+               ssid->eap.eap_methods = NULL;
+       } else {
+               wpa_printf(MSG_DEBUG, "WPS: Create a new network based on the "
+                          "received credential");
+               ssid = wpa_config_add_network(wpa_s->conf);
+               if (ssid == NULL)
+                       return -1;
+               wpas_notify_network_added(wpa_s, ssid);
+       }
+
+       wpa_config_set_network_defaults(ssid);
+
+       os_free(ssid->ssid);
+       ssid->ssid = os_malloc(cred->ssid_len);
+       if (ssid->ssid) {
+               os_memcpy(ssid->ssid, cred->ssid, cred->ssid_len);
+               ssid->ssid_len = cred->ssid_len;
+       }
+
+       switch (cred->encr_type) {
+       case WPS_ENCR_NONE:
+               break;
+       case WPS_ENCR_WEP:
+               if (cred->key_len <= 0)
+                       break;
+               if (cred->key_len != 5 && cred->key_len != 13 &&
+                   cred->key_len != 10 && cred->key_len != 26) {
+                       wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
+                                  "%lu", (unsigned long) cred->key_len);
+                       return -1;
+               }
+               if (cred->key_idx > NUM_WEP_KEYS) {
+                       wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
+                                  cred->key_idx);
+                       return -1;
+               }
+               if (cred->key_idx)
+                       key_idx = cred->key_idx - 1;
+               if (cred->key_len == 10 || cred->key_len == 26) {
+                       if (hexstr2bin((char *) cred->key,
+                                      ssid->wep_key[key_idx],
+                                      cred->key_len / 2) < 0) {
+                               wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
+                                          "%d", key_idx);
+                               return -1;
+                       }
+                       ssid->wep_key_len[key_idx] = cred->key_len / 2;
+               } else {
+                       os_memcpy(ssid->wep_key[key_idx], cred->key,
+                                 cred->key_len);
+                       ssid->wep_key_len[key_idx] = cred->key_len;
+               }
+               ssid->wep_tx_keyidx = key_idx;
+               break;
+       case WPS_ENCR_TKIP:
+               ssid->pairwise_cipher = WPA_CIPHER_TKIP;
+               break;
+       case WPS_ENCR_AES:
+               ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+               break;
+       }
+
+       switch (auth_type) {
+       case WPS_AUTH_OPEN:
+               ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+               ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+               ssid->proto = 0;
+               break;
+       case WPS_AUTH_SHARED:
+               ssid->auth_alg = WPA_AUTH_ALG_SHARED;
+               ssid->key_mgmt = WPA_KEY_MGMT_NONE;
+               ssid->proto = 0;
+               break;
+       case WPS_AUTH_WPAPSK:
+               ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+               ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+               ssid->proto = WPA_PROTO_WPA;
+               break;
+       case WPS_AUTH_WPA:
+               ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+               ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+               ssid->proto = WPA_PROTO_WPA;
+               break;
+       case WPS_AUTH_WPA2:
+               ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+               ssid->key_mgmt = WPA_KEY_MGMT_IEEE8021X;
+               ssid->proto = WPA_PROTO_RSN;
+               break;
+       case WPS_AUTH_WPA2PSK:
+               ssid->auth_alg = WPA_AUTH_ALG_OPEN;
+               ssid->key_mgmt = WPA_KEY_MGMT_PSK;
+               ssid->proto = WPA_PROTO_RSN;
+               break;
+       }
+
+       if (ssid->key_mgmt == WPA_KEY_MGMT_PSK) {
+               if (cred->key_len == 2 * PMK_LEN) {
+                       if (hexstr2bin((const char *) cred->key, ssid->psk,
+                                      PMK_LEN)) {
+                               wpa_printf(MSG_ERROR, "WPS: Invalid Network "
+                                          "Key");
+                               return -1;
+                       }
+                       ssid->psk_set = 1;
+               } else if (cred->key_len >= 8 && cred->key_len < 2 * PMK_LEN) {
+                       os_free(ssid->passphrase);
+                       ssid->passphrase = os_malloc(cred->key_len + 1);
+                       if (ssid->passphrase == NULL)
+                               return -1;
+                       os_memcpy(ssid->passphrase, cred->key, cred->key_len);
+                       ssid->passphrase[cred->key_len] = '\0';
+                       wpa_config_update_psk(ssid);
+               } else {
+                       wpa_printf(MSG_ERROR, "WPS: Invalid Network Key "
+                                  "length %lu",
+                                  (unsigned long) cred->key_len);
+                       return -1;
+               }
+       }
+
+       wpas_wps_security_workaround(wpa_s, ssid, cred);
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+       if (wpa_s->conf->update_config &&
+           wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to update configuration");
+               return -1;
+       }
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+       return 0;
+}
+
+
+static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
+                                        struct wps_event_m2d *m2d)
+{
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_M2D
+               "dev_password_id=%d config_error=%d",
+               m2d->dev_password_id, m2d->config_error);
+       wpas_notify_wps_event_m2d(wpa_s, m2d);
+}
+
+
+static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
+                                         struct wps_event_fail *fail)
+{
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_FAIL "msg=%d", fail->msg);
+       wpas_clear_wps(wpa_s);
+       wpas_notify_wps_event_fail(wpa_s, fail);
+}
+
+
+static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
+{
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_SUCCESS);
+       wpa_s->wps_success = 1;
+       wpas_notify_wps_event_success(wpa_s);
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_add(struct wpa_supplicant *wpa_s,
+                                              struct wps_event_er_ap *ap)
+{
+       char uuid_str[100];
+       char dev_type[WPS_DEV_TYPE_BUFSIZE];
+
+       uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
+       if (ap->pri_dev_type)
+               wps_dev_type_bin2str(ap->pri_dev_type, dev_type,
+                                    sizeof(dev_type));
+       else
+               dev_type[0] = '\0';
+
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_ADD "%s " MACSTR
+               " pri_dev_type=%s wps_state=%d |%s|%s|%s|%s|%s|%s|",
+               uuid_str, MAC2STR(ap->mac_addr), dev_type, ap->wps_state,
+               ap->friendly_name ? ap->friendly_name : "",
+               ap->manufacturer ? ap->manufacturer : "",
+               ap->model_description ? ap->model_description : "",
+               ap->model_name ? ap->model_name : "",
+               ap->manufacturer_url ? ap->manufacturer_url : "",
+               ap->model_url ? ap->model_url : "");
+}
+
+
+static void wpa_supplicant_wps_event_er_ap_remove(struct wpa_supplicant *wpa_s,
+                                                 struct wps_event_er_ap *ap)
+{
+       char uuid_str[100];
+       uuid_bin2str(ap->uuid, uuid_str, sizeof(uuid_str));
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_AP_REMOVE "%s", uuid_str);
+}
+
+
+static void wpa_supplicant_wps_event_er_enrollee_add(
+       struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
+{
+       char uuid_str[100];
+       char dev_type[WPS_DEV_TYPE_BUFSIZE];
+
+       uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
+       if (enrollee->pri_dev_type)
+               wps_dev_type_bin2str(enrollee->pri_dev_type, dev_type,
+                                    sizeof(dev_type));
+       else
+               dev_type[0] = '\0';
+
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD "%s " MACSTR
+               " M1=%d config_methods=0x%x dev_passwd_id=%d pri_dev_type=%s "
+               "|%s|%s|%s|%s|%s|",
+               uuid_str, MAC2STR(enrollee->mac_addr), enrollee->m1_received,
+               enrollee->config_methods, enrollee->dev_passwd_id, dev_type,
+               enrollee->dev_name ? enrollee->dev_name : "",
+               enrollee->manufacturer ? enrollee->manufacturer : "",
+               enrollee->model_name ? enrollee->model_name : "",
+               enrollee->model_number ? enrollee->model_number : "",
+               enrollee->serial_number ? enrollee->serial_number : "");
+}
+
+
+static void wpa_supplicant_wps_event_er_enrollee_remove(
+       struct wpa_supplicant *wpa_s, struct wps_event_er_enrollee *enrollee)
+{
+       char uuid_str[100];
+       uuid_bin2str(enrollee->uuid, uuid_str, sizeof(uuid_str));
+       wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE "%s " MACSTR,
+               uuid_str, MAC2STR(enrollee->mac_addr));
+}
+
+
+static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
+                                    union wps_event_data *data)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       switch (event) {
+       case WPS_EV_M2D:
+               wpa_supplicant_wps_event_m2d(wpa_s, &data->m2d);
+               break;
+       case WPS_EV_FAIL:
+               wpa_supplicant_wps_event_fail(wpa_s, &data->fail);
+               break;
+       case WPS_EV_SUCCESS:
+               wpa_supplicant_wps_event_success(wpa_s);
+               break;
+       case WPS_EV_PWD_AUTH_FAIL:
+               break;
+       case WPS_EV_PBC_OVERLAP:
+               break;
+       case WPS_EV_PBC_TIMEOUT:
+               break;
+       case WPS_EV_ER_AP_ADD:
+               wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap);
+               break;
+       case WPS_EV_ER_AP_REMOVE:
+               wpa_supplicant_wps_event_er_ap_remove(wpa_s, &data->ap);
+               break;
+       case WPS_EV_ER_ENROLLEE_ADD:
+               wpa_supplicant_wps_event_er_enrollee_add(wpa_s,
+                                                        &data->enrollee);
+               break;
+       case WPS_EV_ER_ENROLLEE_REMOVE:
+               wpa_supplicant_wps_event_er_enrollee_remove(wpa_s,
+                                                           &data->enrollee);
+               break;
+       }
+}
+
+
+enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
+{
+       if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
+           eap_is_wps_pin_enrollee(&ssid->eap))
+               return WPS_REQ_ENROLLEE;
+       else
+               return WPS_REQ_REGISTRAR;
+}
+
+
+static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
+{
+       int id;
+       struct wpa_ssid *ssid, *remove_ssid = NULL;
+
+       eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+
+       /* Remove any existing WPS network from configuration */
+       ssid = wpa_s->conf->ssid;
+       while (ssid) {
+               if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+                       if (ssid == wpa_s->current_ssid) {
+                               wpa_s->current_ssid = NULL;
+                               if (ssid != NULL)
+                                       wpas_notify_network_changed(wpa_s);
+                       }
+                       id = ssid->id;
+                       remove_ssid = ssid;
+               } else
+                       id = -1;
+               ssid = ssid->next;
+               if (id >= 0) {
+                       wpas_notify_network_removed(wpa_s, remove_ssid);
+                       wpa_config_remove_network(wpa_s->conf, id);
+               }
+       }
+}
+
+
+static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpa_printf(MSG_INFO, WPS_EVENT_TIMEOUT "Requested operation timed "
+                  "out");
+       wpas_clear_wps(wpa_s);
+}
+
+
+static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
+                                             int registrar, const u8 *bssid)
+{
+       struct wpa_ssid *ssid;
+
+       ssid = wpa_config_add_network(wpa_s->conf);
+       if (ssid == NULL)
+               return NULL;
+       wpas_notify_network_added(wpa_s, ssid);
+       wpa_config_set_network_defaults(ssid);
+       if (wpa_config_set(ssid, "key_mgmt", "WPS", 0) < 0 ||
+           wpa_config_set(ssid, "eap", "WSC", 0) < 0 ||
+           wpa_config_set(ssid, "identity", registrar ?
+                          "\"" WSC_ID_REGISTRAR "\"" :
+                          "\"" WSC_ID_ENROLLEE "\"", 0) < 0) {
+               wpas_notify_network_removed(wpa_s, ssid);
+               wpa_config_remove_network(wpa_s->conf, ssid->id);
+               return NULL;
+       }
+
+       if (bssid) {
+               struct wpa_bss *bss;
+               int count = 0;
+
+               os_memcpy(ssid->bssid, bssid, ETH_ALEN);
+               ssid->bssid_set = 1;
+
+               dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+                       if (os_memcmp(bssid, bss->bssid, ETH_ALEN) != 0)
+                               continue;
+
+                       os_free(ssid->ssid);
+                       ssid->ssid = os_malloc(bss->ssid_len);
+                       if (ssid->ssid == NULL)
+                               break;
+                       os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+                       ssid->ssid_len = bss->ssid_len;
+                       wpa_hexdump_ascii(MSG_DEBUG, "WPS: Picked SSID from "
+                                         "scan results",
+                                         ssid->ssid, ssid->ssid_len);
+                       count++;
+               }
+
+               if (count > 1) {
+                       wpa_printf(MSG_DEBUG, "WPS: More than one SSID found "
+                                  "for the AP; use wildcard");
+                       os_free(ssid->ssid);
+                       ssid->ssid = NULL;
+                       ssid->ssid_len = 0;
+               }
+       }
+
+       return ssid;
+}
+
+
+static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid *selected)
+{
+       struct wpa_ssid *ssid;
+
+       /* Mark all other networks disabled and trigger reassociation */
+       ssid = wpa_s->conf->ssid;
+       while (ssid) {
+               int was_disabled = ssid->disabled;
+               ssid->disabled = ssid != selected;
+               if (was_disabled != ssid->disabled)
+                       wpas_notify_network_enabled_changed(wpa_s, ssid);
+               ssid = ssid->next;
+       }
+       wpa_s->disconnected = 0;
+       wpa_s->reassociate = 1;
+       wpa_s->scan_runs = 0;
+       wpa_s->wps_success = 0;
+       wpa_s->blacklist_cleared = 0;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_ssid *ssid;
+       wpas_clear_wps(wpa_s);
+       ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+       if (ssid == NULL)
+               return -1;
+       wpa_config_set(ssid, "phase1", "\"pbc=1\"", 0);
+       eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
+                              wpa_s, NULL);
+       wpas_wps_reassoc(wpa_s, ssid);
+       return 0;
+}
+
+
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const char *pin)
+{
+       struct wpa_ssid *ssid;
+       char val[128];
+       unsigned int rpin = 0;
+
+       wpas_clear_wps(wpa_s);
+       ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+       if (ssid == NULL)
+               return -1;
+       if (pin)
+               os_snprintf(val, sizeof(val), "\"pin=%s\"", pin);
+       else {
+               rpin = wps_generate_pin();
+               os_snprintf(val, sizeof(val), "\"pin=%08d\"", rpin);
+       }
+       wpa_config_set(ssid, "phase1", val, 0);
+       eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
+                              wpa_s, NULL);
+       wpas_wps_reassoc(wpa_s, ssid);
+       return rpin;
+}
+
+
+#ifdef CONFIG_WPS_OOB
+int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
+                      char *path, char *method, char *name)
+{
+       struct wps_context *wps = wpa_s->wps;
+       struct oob_device_data *oob_dev;
+
+       oob_dev = wps_get_oob_device(device_type);
+       if (oob_dev == NULL)
+               return -1;
+       oob_dev->device_path = path;
+       oob_dev->device_name = name;
+       wps->oob_conf.oob_method = wps_get_oob_method(method);
+
+       if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E) {
+               /*
+                * Use pre-configured DH keys in order to be able to write the
+                * key hash into the OOB file.
+                */
+               wpabuf_free(wps->dh_pubkey);
+               wpabuf_free(wps->dh_privkey);
+               wps->dh_privkey = NULL;
+               wps->dh_pubkey = NULL;
+               dh5_free(wps->dh_ctx);
+               wps->dh_ctx = dh5_init(&wps->dh_privkey, &wps->dh_pubkey);
+               wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192);
+               if (wps->dh_ctx == NULL || wps->dh_pubkey == NULL) {
+                       wpa_printf(MSG_ERROR, "WPS: Failed to initialize "
+                                  "Diffie-Hellman handshake");
+                       return -1;
+               }
+       }
+
+       if (wps->oob_conf.oob_method == OOB_METHOD_CRED)
+               wpas_clear_wps(wpa_s);
+
+       if (wps_process_oob(wps, oob_dev, 0) < 0)
+               return -1;
+
+       if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
+            wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
+           wpas_wps_start_pin(wpa_s, NULL,
+                              wpabuf_head(wps->oob_conf.dev_password)) < 0)
+                       return -1;
+
+       return 0;
+}
+#endif /* CONFIG_WPS_OOB */
+
+
+int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const char *pin, struct wps_new_ap_settings *settings)
+{
+       struct wpa_ssid *ssid;
+       char val[200];
+       char *pos, *end;
+       int res;
+
+       if (!pin)
+               return -1;
+       wpas_clear_wps(wpa_s);
+       ssid = wpas_wps_add_network(wpa_s, 1, bssid);
+       if (ssid == NULL)
+               return -1;
+       pos = val;
+       end = pos + sizeof(val);
+       res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
+       if (res < 0 || res >= end - pos)
+               return -1;
+       pos += res;
+       if (settings) {
+               res = os_snprintf(pos, end - pos, " new_ssid=%s new_auth=%s "
+                                 "new_encr=%s new_key=%s",
+                                 settings->ssid_hex, settings->auth,
+                                 settings->encr, settings->key_hex);
+               if (res < 0 || res >= end - pos)
+                       return -1;
+               pos += res;
+       }
+       res = os_snprintf(pos, end - pos, "\"");
+       if (res < 0 || res >= end - pos)
+               return -1;
+       wpa_config_set(ssid, "phase1", val, 0);
+       eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
+                              wpa_s, NULL);
+       wpas_wps_reassoc(wpa_s, ssid);
+       return 0;
+}
+
+
+static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+                              size_t psk_len)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for "
+                  "STA " MACSTR, MAC2STR(mac_addr));
+       wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
+
+       /* TODO */
+
+       return 0;
+}
+
+
+static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
+                                  const struct wps_device_data *dev)
+{
+       char uuid[40], txt[400];
+       int len;
+       char devtype[WPS_DEV_TYPE_BUFSIZE];
+       if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
+               return;
+       wpa_printf(MSG_DEBUG, "WPS: PIN needed for UUID-E %s", uuid);
+       len = os_snprintf(txt, sizeof(txt), "WPS-EVENT-PIN-NEEDED %s " MACSTR
+                         " [%s|%s|%s|%s|%s|%s]",
+                         uuid, MAC2STR(dev->mac_addr), dev->device_name,
+                         dev->manufacturer, dev->model_name,
+                         dev->model_number, dev->serial_number,
+                         wps_dev_type_bin2str(dev->pri_dev_type, devtype,
+                                              sizeof(devtype)));
+       if (len > 0 && len < (int) sizeof(txt))
+               wpa_printf(MSG_INFO, "%s", txt);
+}
+
+
+static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
+                                   u16 sel_reg_config_methods)
+{
+#ifdef CONFIG_WPS_ER
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->wps_er == NULL)
+               return;
+       wps_er_set_sel_reg(wpa_s->wps_er, sel_reg, dev_passwd_id,
+                          sel_reg_config_methods);
+#endif /* CONFIG_WPS_ER */
+}
+
+
+int wpas_wps_init(struct wpa_supplicant *wpa_s)
+{
+       struct wps_context *wps;
+       struct wps_registrar_config rcfg;
+
+       wps = os_zalloc(sizeof(*wps));
+       if (wps == NULL)
+               return -1;
+
+       wps->cred_cb = wpa_supplicant_wps_cred;
+       wps->event_cb = wpa_supplicant_wps_event;
+       wps->cb_ctx = wpa_s;
+
+       wps->dev.device_name = wpa_s->conf->device_name;
+       wps->dev.manufacturer = wpa_s->conf->manufacturer;
+       wps->dev.model_name = wpa_s->conf->model_name;
+       wps->dev.model_number = wpa_s->conf->model_number;
+       wps->dev.serial_number = wpa_s->conf->serial_number;
+       wps->config_methods =
+               wps_config_methods_str2bin(wpa_s->conf->config_methods);
+       if (wpa_s->conf->device_type &&
+           wps_dev_type_str2bin(wpa_s->conf->device_type,
+                                wps->dev.pri_dev_type) < 0) {
+               wpa_printf(MSG_ERROR, "WPS: Invalid device_type");
+               os_free(wps);
+               return -1;
+       }
+       wps->dev.os_version = WPA_GET_BE32(wpa_s->conf->os_version);
+       wps->dev.rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ; /* TODO: config */
+       os_memcpy(wps->dev.mac_addr, wpa_s->own_addr, ETH_ALEN);
+       if (is_nil_uuid(wpa_s->conf->uuid)) {
+               uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
+               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
+                           wps->uuid, WPS_UUID_LEN);
+       } else
+               os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
+
+       wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
+       wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP;
+
+       os_memset(&rcfg, 0, sizeof(rcfg));
+       rcfg.new_psk_cb = wpas_wps_new_psk_cb;
+       rcfg.pin_needed_cb = wpas_wps_pin_needed_cb;
+       rcfg.set_sel_reg_cb = wpas_wps_set_sel_reg_cb;
+       rcfg.cb_ctx = wpa_s;
+
+       wps->registrar = wps_registrar_init(wps, &rcfg);
+       if (wps->registrar == NULL) {
+               wpa_printf(MSG_DEBUG, "Failed to initialize WPS Registrar");
+               os_free(wps);
+               return -1;
+       }
+
+       wpa_s->wps = wps;
+
+       return 0;
+}
+
+
+void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+
+       if (wpa_s->wps == NULL)
+               return;
+
+#ifdef CONFIG_WPS_ER
+       wps_er_deinit(wpa_s->wps_er, NULL, NULL);
+       wpa_s->wps_er = NULL;
+#endif /* CONFIG_WPS_ER */
+
+       wps_registrar_deinit(wpa_s->wps->registrar);
+       wpabuf_free(wpa_s->wps->dh_pubkey);
+       wpabuf_free(wpa_s->wps->dh_privkey);
+       wpabuf_free(wpa_s->wps->oob_conf.pubkey_hash);
+       wpabuf_free(wpa_s->wps->oob_conf.dev_password);
+       os_free(wpa_s->wps->network_key);
+       os_free(wpa_s->wps);
+       wpa_s->wps = NULL;
+}
+
+
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+                           struct wpa_ssid *ssid, struct wpa_scan_res *bss)
+{
+       struct wpabuf *wps_ie;
+
+       if (!(ssid->key_mgmt & WPA_KEY_MGMT_WPS))
+               return -1;
+
+       wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+       if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+               if (!wps_ie) {
+                       wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
+                       return 0;
+               }
+
+               if (!wps_is_selected_pbc_registrar(wps_ie)) {
+                       wpa_printf(MSG_DEBUG, "   skip - WPS AP "
+                                  "without active PBC Registrar");
+                       wpabuf_free(wps_ie);
+                       return 0;
+               }
+
+               /* TODO: overlap detection */
+               wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+                          "(Active PBC)");
+               wpabuf_free(wps_ie);
+               return 1;
+       }
+
+       if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+               if (!wps_ie) {
+                       wpa_printf(MSG_DEBUG, "   skip - non-WPS AP");
+                       return 0;
+               }
+
+               /*
+                * Start with WPS APs that advertise active PIN Registrar and
+                * allow any WPS AP after third scan since some APs do not set
+                * Selected Registrar attribute properly when using external
+                * Registrar.
+                */
+               if (!wps_is_selected_pin_registrar(wps_ie)) {
+                       if (wpa_s->scan_runs < WPS_PIN_SCAN_IGNORE_SEL_REG) {
+                               wpa_printf(MSG_DEBUG, "   skip - WPS AP "
+                                          "without active PIN Registrar");
+                               wpabuf_free(wps_ie);
+                               return 0;
+                       }
+                       wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+               } else {
+                       wpa_printf(MSG_DEBUG, "   selected based on WPS IE "
+                                  "(Active PIN)");
+               }
+               wpabuf_free(wps_ie);
+               return 1;
+       }
+
+       if (wps_ie) {
+               wpa_printf(MSG_DEBUG, "   selected based on WPS IE");
+               wpabuf_free(wps_ie);
+               return 1;
+       }
+
+       return -1;
+}
+
+
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid,
+                             struct wpa_scan_res *bss)
+{
+       struct wpabuf *wps_ie = NULL;
+       int ret = 0;
+
+       if (eap_is_wps_pbc_enrollee(&ssid->eap)) {
+               wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+               if (wps_ie && wps_is_selected_pbc_registrar(wps_ie)) {
+                       /* allow wildcard SSID for WPS PBC */
+                       ret = 1;
+               }
+       } else if (eap_is_wps_pin_enrollee(&ssid->eap)) {
+               wps_ie = wpa_scan_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+               if (wps_ie &&
+                   (wps_is_selected_pin_registrar(wps_ie) ||
+                    wpa_s->scan_runs >= WPS_PIN_SCAN_IGNORE_SEL_REG)) {
+                       /* allow wildcard SSID for WPS PIN */
+                       ret = 1;
+               }
+       }
+
+       if (!ret && ssid->bssid_set &&
+           os_memcmp(ssid->bssid, bss->bssid, ETH_ALEN) == 0) {
+               /* allow wildcard SSID due to hardcoded BSSID match */
+               ret = 1;
+       }
+
+       wpabuf_free(wps_ie);
+
+       return ret;
+}
+
+
+int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+                             struct wpa_bss *selected, struct wpa_ssid *ssid)
+{
+       const u8 *sel_uuid, *uuid;
+       struct wpabuf *wps_ie;
+       int ret = 0;
+       struct wpa_bss *bss;
+
+       if (!eap_is_wps_pbc_enrollee(&ssid->eap))
+               return 0;
+
+       /* Make sure that only one AP is in active PBC mode */
+       wps_ie = wpa_bss_get_vendor_ie_multi(selected, WPS_IE_VENDOR_TYPE);
+       if (wps_ie)
+               sel_uuid = wps_get_uuid_e(wps_ie);
+       else
+               sel_uuid = NULL;
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               struct wpabuf *ie;
+               if (bss == selected)
+                       continue;
+               ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+               if (!ie)
+                       continue;
+               if (!wps_is_selected_pbc_registrar(ie)) {
+                       wpabuf_free(ie);
+                       continue;
+               }
+               uuid = wps_get_uuid_e(ie);
+               if (sel_uuid == NULL || uuid == NULL ||
+                   os_memcmp(sel_uuid, uuid, 16) != 0) {
+                       ret = 1; /* PBC overlap */
+                       wpabuf_free(ie);
+                       break;
+               }
+
+               /* TODO: verify that this is reasonable dual-band situation */
+
+               wpabuf_free(ie);
+       }
+
+       wpabuf_free(wps_ie);
+
+       return ret;
+}
+
+
+void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss;
+
+       if (wpa_s->disconnected || wpa_s->wpa_state >= WPA_ASSOCIATED)
+               return;
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               struct wpabuf *ie;
+               ie = wpa_bss_get_vendor_ie_multi(bss, WPS_IE_VENDOR_TYPE);
+               if (!ie)
+                       continue;
+               if (wps_is_selected_pbc_registrar(ie))
+                       wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                    WPS_EVENT_AP_AVAILABLE_PBC);
+               else if (wps_is_selected_pin_registrar(ie))
+                       wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                    WPS_EVENT_AP_AVAILABLE_PIN);
+               else
+                       wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                    WPS_EVENT_AP_AVAILABLE);
+               wpabuf_free(ie);
+               break;
+       }
+}
+
+
+int wpas_wps_searching(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid;
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               if ((ssid->key_mgmt & WPA_KEY_MGMT_WPS) && !ssid->disabled)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+                             char *end)
+{
+       struct wpabuf *wps_ie;
+       int ret;
+
+       wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, WPS_DEV_OUI_WFA);
+       if (wps_ie == NULL)
+               return 0;
+
+       ret = wps_attr_text(wps_ie, buf, end);
+       wpabuf_free(wps_ie);
+       return ret;
+}
+
+
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+       if (wpa_s->wps_er) {
+               wps_er_refresh(wpa_s->wps_er);
+               return 0;
+       }
+       wpa_s->wps_er = wps_er_init(wpa_s->wps, wpa_s->ifname);
+       if (wpa_s->wps_er == NULL)
+               return -1;
+       return 0;
+#else /* CONFIG_WPS_ER */
+       return 0;
+#endif /* CONFIG_WPS_ER */
+}
+
+
+int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+       wps_er_deinit(wpa_s->wps_er, NULL, NULL);
+       wpa_s->wps_er = NULL;
+#endif /* CONFIG_WPS_ER */
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_ER
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
+                       const char *pin)
+{
+       u8 u[UUID_LEN];
+       int any = 0;
+
+       if (os_strcmp(uuid, "any") == 0)
+               any = 1;
+       else if (uuid_str2bin(uuid, u))
+               return -1;
+       return wps_registrar_add_pin(wpa_s->wps->registrar, any ? NULL : u,
+                                    (const u8 *) pin, os_strlen(pin), 300);
+}
+
+
+int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid)
+{
+       u8 u[UUID_LEN];
+
+       if (uuid_str2bin(uuid, u))
+               return -1;
+       return wps_er_pbc(wpa_s->wps_er, u);
+}
+
+
+int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
+                     const char *pin)
+{
+       u8 u[UUID_LEN];
+
+       if (uuid_str2bin(uuid, u))
+               return -1;
+       return wps_er_learn(wpa_s->wps_er, u, (const u8 *) pin,
+                           os_strlen(pin));
+}
+
+
+static void wpas_wps_terminate_cb(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "WPS ER: Terminated");
+       eloop_terminate();
+}
+#endif /* CONFIG_WPS_ER */
+
+
+int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_WPS_ER
+       if (wpa_s->wps_er) {
+               wps_er_deinit(wpa_s->wps_er, wpas_wps_terminate_cb, wpa_s);
+               wpa_s->wps_er = NULL;
+               return 1;
+       }
+#endif /* CONFIG_WPS_ER */
+       return 0;
+}
diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h
new file mode 100644 (file)
index 0000000..ba2fb16
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * wpa_supplicant / WPS integration
+ * Copyright (c) 2008-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.
+ */
+
+#ifndef WPS_SUPPLICANT_H
+#define WPS_SUPPLICANT_H
+
+struct wpa_scan_res;
+
+#ifdef CONFIG_WPS
+
+#include "wps/wps.h"
+#include "wps/wps_defs.h"
+
+struct wpa_bss;
+
+struct wps_new_ap_settings {
+       const char *ssid_hex;
+       const char *auth;
+       const char *encr;
+       const char *key_hex;
+};
+
+int wpas_wps_init(struct wpa_supplicant *wpa_s);
+void wpas_wps_deinit(struct wpa_supplicant *wpa_s);
+int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid);
+int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const char *pin);
+int wpas_wps_start_oob(struct wpa_supplicant *wpa_s, char *device_type,
+                      char *path, char *method, char *name);
+int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const char *pin, struct wps_new_ap_settings *settings);
+int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+                           struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid, struct wpa_scan_res *bss);
+int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+                             struct wpa_bss *selected, struct wpa_ssid *ssid);
+void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s);
+int wpas_wps_searching(struct wpa_supplicant *wpa_s);
+int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
+                             char *end);
+int wpas_wps_er_start(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
+int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const char *uuid,
+                       const char *pin);
+int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);
+int wpas_wps_er_learn(struct wpa_supplicant *wpa_s, const char *uuid,
+                     const char *pin);
+int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
+
+#else /* CONFIG_WPS */
+
+static inline int wpas_wps_init(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline u8 wpas_wps_get_req_type(struct wpa_ssid *ssid)
+{
+       return 0;
+}
+
+static inline int wpas_wps_ssid_bss_match(struct wpa_supplicant *wpa_s,
+                                         struct wpa_ssid *ssid,
+                                         struct wpa_scan_res *bss)
+{
+       return -1;
+}
+
+static inline int wpas_wps_ssid_wildcard_ok(struct wpa_supplicant *wpa_s,
+                                           struct wpa_ssid *ssid,
+                                           struct wpa_scan_res *bss)
+{
+       return 0;
+}
+
+static inline int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
+                                           struct wpa_bss *selected,
+                                           struct wpa_ssid *ssid)
+{
+       return 0;
+}
+
+static inline void wpas_wps_notify_scan_results(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_wps_searching(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+#endif /* CONFIG_WPS */
+
+#endif /* WPS_SUPPLICANT_H */
diff --git a/wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj b/wpa_supplicant/xcode/wpa_supplicant.xcodeproj/project.pbxproj
new file mode 100644 (file)
index 0000000..6fea81b
--- /dev/null
@@ -0,0 +1,513 @@
+// !$*UTF8*$!
+{
+       archiveVersion = 1;
+       classes = {
+       };
+       objectVersion = 45;
+       objects = {
+
+/* Begin PBXBuildFile section */
+               881EED0F10DC14EF009E449F /* eap_register.c in Sources */ = {isa = PBXBuildFile; fileRef = 881EED0E10DC14EF009E449F /* eap_register.c */; };
+               8853CB17109F385C00358CEF /* libpcap.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB16109F385C00358CEF /* libpcap.dylib */; };
+               8853CB1B109F389800358CEF /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB1A109F389800358CEF /* libcrypto.dylib */; };
+               8853CB1F109F38BD00358CEF /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB1E109F38BD00358CEF /* CoreFoundation.framework */; };
+               8853CB2E109F3A3900358CEF /* scan_helpers.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CB2D109F3A3900358CEF /* scan_helpers.c */; };
+               8853CB32109F3A9400358CEF /* wpa_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CB31109F3A9400358CEF /* wpa_common.c */; };
+               8853CB36109F3AC700358CEF /* md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CB35109F3AC700358CEF /* md5.c */; };
+               8853CB3C109F3B5800358CEF /* Apple80211.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CB3B109F3B5800358CEF /* Apple80211.framework */; };
+               8853CBFB109F4C6E00358CEF /* eap_gtc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBEC109F4C6E00358CEF /* eap_gtc.c */; };
+               8853CBFC109F4C6E00358CEF /* eap_leap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBED109F4C6E00358CEF /* eap_leap.c */; };
+               8853CBFD109F4C6E00358CEF /* eap_md5.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBEE109F4C6E00358CEF /* eap_md5.c */; };
+               8853CBFE109F4C6E00358CEF /* eap_methods.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBEF109F4C6E00358CEF /* eap_methods.c */; };
+               8853CBFF109F4C6E00358CEF /* eap_mschapv2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF0109F4C6E00358CEF /* eap_mschapv2.c */; };
+               8853CC00109F4C6E00358CEF /* eap_otp.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF1109F4C6E00358CEF /* eap_otp.c */; };
+               8853CC01109F4C6E00358CEF /* eap_peap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF2109F4C6E00358CEF /* eap_peap.c */; };
+               8853CC02109F4C6E00358CEF /* eap_tls_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF3109F4C6E00358CEF /* eap_tls_common.c */; };
+               8853CC03109F4C6E00358CEF /* eap_tls.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF4109F4C6E00358CEF /* eap_tls.c */; };
+               8853CC04109F4C6E00358CEF /* eap_tnc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF5109F4C6E00358CEF /* eap_tnc.c */; };
+               8853CC05109F4C6E00358CEF /* eap_ttls.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF6109F4C6E00358CEF /* eap_ttls.c */; };
+               8853CC06109F4C6E00358CEF /* eap_wsc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF7109F4C6E00358CEF /* eap_wsc.c */; };
+               8853CC07109F4C6E00358CEF /* eap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF8109F4C6E00358CEF /* eap.c */; };
+               8853CC08109F4C6E00358CEF /* mschapv2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBF9109F4C6E00358CEF /* mschapv2.c */; };
+               8853CC09109F4C6E00358CEF /* tncc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CBFA109F4C6E00358CEF /* tncc.c */; };
+               8853CC0E109F4CA100358CEF /* ctrl_iface_unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC0C109F4CA100358CEF /* ctrl_iface_unix.c */; };
+               8853CC0F109F4CA100358CEF /* ctrl_iface.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC0D109F4CA100358CEF /* ctrl_iface.c */; };
+               8853CC11109F4CC800358CEF /* eapol_supp_sm.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC10109F4CC800358CEF /* eapol_supp_sm.c */; };
+               8853CC18109F4D0800358CEF /* chap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC14109F4D0800358CEF /* chap.c */; };
+               8853CC19109F4D0800358CEF /* eap_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC15109F4D0800358CEF /* eap_common.c */; };
+               8853CC1A109F4D0800358CEF /* eap_peap_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC16109F4D0800358CEF /* eap_peap_common.c */; };
+               8853CC1B109F4D0800358CEF /* eap_wsc_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC17109F4D0800358CEF /* eap_wsc_common.c */; };
+               8853CC26109F4D3500358CEF /* wps_attr_build.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC1E109F4D3500358CEF /* wps_attr_build.c */; };
+               8853CC27109F4D3500358CEF /* wps_attr_parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC1F109F4D3500358CEF /* wps_attr_parse.c */; };
+               8853CC28109F4D3500358CEF /* wps_attr_process.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC20109F4D3500358CEF /* wps_attr_process.c */; };
+               8853CC29109F4D3500358CEF /* wps_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC21109F4D3500358CEF /* wps_common.c */; };
+               8853CC2A109F4D3500358CEF /* wps_dev_attr.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC22109F4D3500358CEF /* wps_dev_attr.c */; };
+               8853CC2B109F4D3500358CEF /* wps_enrollee.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC23109F4D3500358CEF /* wps_enrollee.c */; };
+               8853CC2C109F4D3500358CEF /* wps_registrar.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC24109F4D3500358CEF /* wps_registrar.c */; };
+               8853CC2D109F4D3500358CEF /* wps.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC25109F4D3500358CEF /* wps.c */; };
+               8853CC34109F4DE200358CEF /* ms_funcs.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC32109F4DE200358CEF /* ms_funcs.c */; };
+               8853CC35109F4DE200358CEF /* tls_openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC33109F4DE200358CEF /* tls_openssl.c */; };
+               8853CC3C109F4E1D00358CEF /* libssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8853CC3B109F4E1D00358CEF /* libssl.dylib */; };
+               8853CC40109F4E3A00358CEF /* wps_supplicant.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC3F109F4E3A00358CEF /* wps_supplicant.c */; };
+               8853CC44109F4E6200358CEF /* uuid.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC43109F4E6200358CEF /* uuid.c */; };
+               8853CC48109F4E8700358CEF /* ieee802_11_common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC47109F4E8700358CEF /* ieee802_11_common.c */; };
+               8853CC4E109F4ED500358CEF /* sha256.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC4C109F4ED500358CEF /* sha256.c */; };
+               8853CC53109F4F3500358CEF /* aes-cbc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC51109F4F3500358CEF /* aes-cbc.c */; };
+               8853CC54109F4F3500358CEF /* sha1-tlsprf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8853CC52109F4F3500358CEF /* sha1-tlsprf.c */; };
+               88950831109F2FAB004FB35D /* blacklist.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950828109F2FAB004FB35D /* blacklist.c */; };
+               88950832109F2FAB004FB35D /* config_file.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950829109F2FAB004FB35D /* config_file.c */; };
+               88950833109F2FAB004FB35D /* config.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082A109F2FAB004FB35D /* config.c */; };
+               88950834109F2FAB004FB35D /* events.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082B109F2FAB004FB35D /* events.c */; };
+               88950835109F2FAB004FB35D /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082C109F2FAB004FB35D /* main.c */; };
+               88950836109F2FAB004FB35D /* notify.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082D109F2FAB004FB35D /* notify.c */; };
+               88950837109F2FAB004FB35D /* scan.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082E109F2FAB004FB35D /* scan.c */; };
+               88950838109F2FAB004FB35D /* wpa_supplicant.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895082F109F2FAB004FB35D /* wpa_supplicant.c */; };
+               88950839109F2FAB004FB35D /* wpas_glue.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950830109F2FAB004FB35D /* wpas_glue.c */; };
+               88950840109F301A004FB35D /* base64.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083A109F301A004FB35D /* base64.c */; };
+               88950841109F301A004FB35D /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083B109F301A004FB35D /* common.c */; };
+               88950842109F301A004FB35D /* eloop.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083C109F301A004FB35D /* eloop.c */; };
+               88950843109F301A004FB35D /* os_unix.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083D109F301A004FB35D /* os_unix.c */; };
+               88950844109F301A004FB35D /* wpa_debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083E109F301A004FB35D /* wpa_debug.c */; };
+               88950845109F301A004FB35D /* wpabuf.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895083F109F301A004FB35D /* wpabuf.c */; };
+               88950864109F32D1004FB35D /* peerkey.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895085F109F32D1004FB35D /* peerkey.c */; };
+               88950865109F32D1004FB35D /* pmksa_cache.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950860109F32D1004FB35D /* pmksa_cache.c */; };
+               88950866109F32D1004FB35D /* preauth.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950861109F32D1004FB35D /* preauth.c */; };
+               88950867109F32D1004FB35D /* wpa_ie.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950862109F32D1004FB35D /* wpa_ie.c */; };
+               88950868109F32D1004FB35D /* wpa.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950863109F32D1004FB35D /* wpa.c */; };
+               8895086C109F3316004FB35D /* l2_packet_freebsd.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086B109F3316004FB35D /* l2_packet_freebsd.c */; };
+               88950871109F3367004FB35D /* aes-unwrap.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086D109F3367004FB35D /* aes-unwrap.c */; };
+               88950872109F3367004FB35D /* crypto_openssl.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086E109F3367004FB35D /* crypto_openssl.c */; };
+               88950873109F3367004FB35D /* sha1-pbkdf2.c in Sources */ = {isa = PBXBuildFile; fileRef = 8895086F109F3367004FB35D /* sha1-pbkdf2.c */; };
+               88950874109F3367004FB35D /* sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950870109F3367004FB35D /* sha1.c */; };
+               88950885109F3538004FB35D /* driver_osx.m in Sources */ = {isa = PBXBuildFile; fileRef = 88950883109F3538004FB35D /* driver_osx.m */; };
+               88950886109F3538004FB35D /* drivers.c in Sources */ = {isa = PBXBuildFile; fileRef = 88950884109F3538004FB35D /* drivers.c */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+               8DD76FAF0486AB0100D96B5E /* CopyFiles */ = {
+                       isa = PBXCopyFilesBuildPhase;
+                       buildActionMask = 8;
+                       dstPath = /usr/share/man/man1/;
+                       dstSubfolderSpec = 0;
+                       files = (
+                       );
+                       runOnlyForDeploymentPostprocessing = 1;
+               };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+               881EED0E10DC14EF009E449F /* eap_register.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_register.c; path = ../eap_register.c; sourceTree = SOURCE_ROOT; };
+               8853CB16109F385C00358CEF /* libpcap.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpcap.dylib; path = usr/lib/libpcap.dylib; sourceTree = SDKROOT; };
+               8853CB1A109F389800358CEF /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; };
+               8853CB1E109F38BD00358CEF /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
+               8853CB2D109F3A3900358CEF /* scan_helpers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scan_helpers.c; path = ../../src/drivers/scan_helpers.c; sourceTree = SOURCE_ROOT; };
+               8853CB31109F3A9400358CEF /* wpa_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_common.c; path = ../../src/common/wpa_common.c; sourceTree = SOURCE_ROOT; };
+               8853CB35109F3AC700358CEF /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../src/crypto/md5.c; sourceTree = SOURCE_ROOT; };
+               8853CB3B109F3B5800358CEF /* Apple80211.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Apple80211.framework; path = /System/Library/PrivateFrameworks/Apple80211.framework; sourceTree = "<absolute>"; };
+               8853CBEC109F4C6E00358CEF /* eap_gtc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_gtc.c; path = ../../src/eap_peer/eap_gtc.c; sourceTree = SOURCE_ROOT; };
+               8853CBED109F4C6E00358CEF /* eap_leap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_leap.c; path = ../../src/eap_peer/eap_leap.c; sourceTree = SOURCE_ROOT; };
+               8853CBEE109F4C6E00358CEF /* eap_md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_md5.c; path = ../../src/eap_peer/eap_md5.c; sourceTree = SOURCE_ROOT; };
+               8853CBEF109F4C6E00358CEF /* eap_methods.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_methods.c; path = ../../src/eap_peer/eap_methods.c; sourceTree = SOURCE_ROOT; };
+               8853CBF0109F4C6E00358CEF /* eap_mschapv2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_mschapv2.c; path = ../../src/eap_peer/eap_mschapv2.c; sourceTree = SOURCE_ROOT; };
+               8853CBF1109F4C6E00358CEF /* eap_otp.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_otp.c; path = ../../src/eap_peer/eap_otp.c; sourceTree = SOURCE_ROOT; };
+               8853CBF2109F4C6E00358CEF /* eap_peap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_peap.c; path = ../../src/eap_peer/eap_peap.c; sourceTree = SOURCE_ROOT; };
+               8853CBF3109F4C6E00358CEF /* eap_tls_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_tls_common.c; path = ../../src/eap_peer/eap_tls_common.c; sourceTree = SOURCE_ROOT; };
+               8853CBF4109F4C6E00358CEF /* eap_tls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_tls.c; path = ../../src/eap_peer/eap_tls.c; sourceTree = SOURCE_ROOT; };
+               8853CBF5109F4C6E00358CEF /* eap_tnc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_tnc.c; path = ../../src/eap_peer/eap_tnc.c; sourceTree = SOURCE_ROOT; };
+               8853CBF6109F4C6E00358CEF /* eap_ttls.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_ttls.c; path = ../../src/eap_peer/eap_ttls.c; sourceTree = SOURCE_ROOT; };
+               8853CBF7109F4C6E00358CEF /* eap_wsc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_wsc.c; path = ../../src/eap_peer/eap_wsc.c; sourceTree = SOURCE_ROOT; };
+               8853CBF8109F4C6E00358CEF /* eap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap.c; path = ../../src/eap_peer/eap.c; sourceTree = SOURCE_ROOT; };
+               8853CBF9109F4C6E00358CEF /* mschapv2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mschapv2.c; path = ../../src/eap_peer/mschapv2.c; sourceTree = SOURCE_ROOT; };
+               8853CBFA109F4C6E00358CEF /* tncc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tncc.c; path = ../../src/eap_peer/tncc.c; sourceTree = SOURCE_ROOT; };
+               8853CC0C109F4CA100358CEF /* ctrl_iface_unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ctrl_iface_unix.c; path = ../ctrl_iface_unix.c; sourceTree = SOURCE_ROOT; };
+               8853CC0D109F4CA100358CEF /* ctrl_iface.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ctrl_iface.c; path = ../ctrl_iface.c; sourceTree = SOURCE_ROOT; };
+               8853CC10109F4CC800358CEF /* eapol_supp_sm.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eapol_supp_sm.c; path = ../../src/eapol_supp/eapol_supp_sm.c; sourceTree = SOURCE_ROOT; };
+               8853CC14109F4D0800358CEF /* chap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = chap.c; path = ../../src/eap_common/chap.c; sourceTree = SOURCE_ROOT; };
+               8853CC15109F4D0800358CEF /* eap_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_common.c; path = ../../src/eap_common/eap_common.c; sourceTree = SOURCE_ROOT; };
+               8853CC16109F4D0800358CEF /* eap_peap_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_peap_common.c; path = ../../src/eap_common/eap_peap_common.c; sourceTree = SOURCE_ROOT; };
+               8853CC17109F4D0800358CEF /* eap_wsc_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eap_wsc_common.c; path = ../../src/eap_common/eap_wsc_common.c; sourceTree = SOURCE_ROOT; };
+               8853CC1E109F4D3500358CEF /* wps_attr_build.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_attr_build.c; path = ../../src/wps/wps_attr_build.c; sourceTree = SOURCE_ROOT; };
+               8853CC1F109F4D3500358CEF /* wps_attr_parse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_attr_parse.c; path = ../../src/wps/wps_attr_parse.c; sourceTree = SOURCE_ROOT; };
+               8853CC20109F4D3500358CEF /* wps_attr_process.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_attr_process.c; path = ../../src/wps/wps_attr_process.c; sourceTree = SOURCE_ROOT; };
+               8853CC21109F4D3500358CEF /* wps_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_common.c; path = ../../src/wps/wps_common.c; sourceTree = SOURCE_ROOT; };
+               8853CC22109F4D3500358CEF /* wps_dev_attr.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_dev_attr.c; path = ../../src/wps/wps_dev_attr.c; sourceTree = SOURCE_ROOT; };
+               8853CC23109F4D3500358CEF /* wps_enrollee.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_enrollee.c; path = ../../src/wps/wps_enrollee.c; sourceTree = SOURCE_ROOT; };
+               8853CC24109F4D3500358CEF /* wps_registrar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_registrar.c; path = ../../src/wps/wps_registrar.c; sourceTree = SOURCE_ROOT; };
+               8853CC25109F4D3500358CEF /* wps.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps.c; path = ../../src/wps/wps.c; sourceTree = SOURCE_ROOT; };
+               8853CC32109F4DE200358CEF /* ms_funcs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ms_funcs.c; path = ../../src/crypto/ms_funcs.c; sourceTree = SOURCE_ROOT; };
+               8853CC33109F4DE200358CEF /* tls_openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = tls_openssl.c; path = ../../src/crypto/tls_openssl.c; sourceTree = SOURCE_ROOT; };
+               8853CC3B109F4E1D00358CEF /* libssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.dylib; path = usr/lib/libssl.dylib; sourceTree = SDKROOT; };
+               8853CC3F109F4E3A00358CEF /* wps_supplicant.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wps_supplicant.c; path = ../wps_supplicant.c; sourceTree = SOURCE_ROOT; };
+               8853CC43109F4E6200358CEF /* uuid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = uuid.c; path = ../../src/utils/uuid.c; sourceTree = SOURCE_ROOT; };
+               8853CC47109F4E8700358CEF /* ieee802_11_common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = ieee802_11_common.c; path = ../../src/common/ieee802_11_common.c; sourceTree = SOURCE_ROOT; };
+               8853CC4C109F4ED500358CEF /* sha256.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha256.c; path = ../../src/crypto/sha256.c; sourceTree = SOURCE_ROOT; };
+               8853CC51109F4F3500358CEF /* aes-cbc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "aes-cbc.c"; path = "../../src/crypto/aes-cbc.c"; sourceTree = SOURCE_ROOT; };
+               8853CC52109F4F3500358CEF /* sha1-tlsprf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "sha1-tlsprf.c"; path = "../../src/crypto/sha1-tlsprf.c"; sourceTree = SOURCE_ROOT; };
+               88950828109F2FAB004FB35D /* blacklist.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = blacklist.c; path = ../../wpa_supplicant/blacklist.c; sourceTree = SOURCE_ROOT; };
+               88950829109F2FAB004FB35D /* config_file.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = config_file.c; path = ../../wpa_supplicant/config_file.c; sourceTree = SOURCE_ROOT; };
+               8895082A109F2FAB004FB35D /* config.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = config.c; path = ../../wpa_supplicant/config.c; sourceTree = SOURCE_ROOT; };
+               8895082B109F2FAB004FB35D /* events.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = events.c; path = ../../wpa_supplicant/events.c; sourceTree = SOURCE_ROOT; };
+               8895082C109F2FAB004FB35D /* main.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = main.c; path = ../../wpa_supplicant/main.c; sourceTree = SOURCE_ROOT; };
+               8895082D109F2FAB004FB35D /* notify.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = notify.c; path = ../../wpa_supplicant/notify.c; sourceTree = SOURCE_ROOT; };
+               8895082E109F2FAB004FB35D /* scan.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = scan.c; path = ../../wpa_supplicant/scan.c; sourceTree = SOURCE_ROOT; };
+               8895082F109F2FAB004FB35D /* wpa_supplicant.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_supplicant.c; path = ../../wpa_supplicant/wpa_supplicant.c; sourceTree = SOURCE_ROOT; };
+               88950830109F2FAB004FB35D /* wpas_glue.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpas_glue.c; path = ../../wpa_supplicant/wpas_glue.c; sourceTree = SOURCE_ROOT; };
+               8895083A109F301A004FB35D /* base64.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = base64.c; path = ../../src/utils/base64.c; sourceTree = SOURCE_ROOT; };
+               8895083B109F301A004FB35D /* common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = common.c; path = ../../src/utils/common.c; sourceTree = SOURCE_ROOT; };
+               8895083C109F301A004FB35D /* eloop.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = eloop.c; path = ../../src/utils/eloop.c; sourceTree = SOURCE_ROOT; };
+               8895083D109F301A004FB35D /* os_unix.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = os_unix.c; path = ../../src/utils/os_unix.c; sourceTree = SOURCE_ROOT; };
+               8895083E109F301A004FB35D /* wpa_debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_debug.c; path = ../../src/utils/wpa_debug.c; sourceTree = SOURCE_ROOT; };
+               8895083F109F301A004FB35D /* wpabuf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpabuf.c; path = ../../src/utils/wpabuf.c; sourceTree = SOURCE_ROOT; };
+               8895085F109F32D1004FB35D /* peerkey.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = peerkey.c; path = ../../src/rsn_supp/peerkey.c; sourceTree = SOURCE_ROOT; };
+               88950860109F32D1004FB35D /* pmksa_cache.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = pmksa_cache.c; path = ../../src/rsn_supp/pmksa_cache.c; sourceTree = SOURCE_ROOT; };
+               88950861109F32D1004FB35D /* preauth.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = preauth.c; path = ../../src/rsn_supp/preauth.c; sourceTree = SOURCE_ROOT; };
+               88950862109F32D1004FB35D /* wpa_ie.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa_ie.c; path = ../../src/rsn_supp/wpa_ie.c; sourceTree = SOURCE_ROOT; };
+               88950863109F32D1004FB35D /* wpa.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = wpa.c; path = ../../src/rsn_supp/wpa.c; sourceTree = SOURCE_ROOT; };
+               8895086B109F3316004FB35D /* l2_packet_freebsd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = l2_packet_freebsd.c; path = ../../src/l2_packet/l2_packet_freebsd.c; sourceTree = SOURCE_ROOT; };
+               8895086D109F3367004FB35D /* aes-unwrap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "aes-unwrap.c"; path = "../../src/crypto/aes-unwrap.c"; sourceTree = SOURCE_ROOT; };
+               8895086E109F3367004FB35D /* crypto_openssl.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = crypto_openssl.c; path = ../../src/crypto/crypto_openssl.c; sourceTree = SOURCE_ROOT; };
+               8895086F109F3367004FB35D /* sha1-pbkdf2.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = "sha1-pbkdf2.c"; path = "../../src/crypto/sha1-pbkdf2.c"; sourceTree = SOURCE_ROOT; };
+               88950870109F3367004FB35D /* sha1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha1.c; path = ../../src/crypto/sha1.c; sourceTree = SOURCE_ROOT; };
+               88950883109F3538004FB35D /* driver_osx.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = driver_osx.m; path = ../../src/drivers/driver_osx.m; sourceTree = SOURCE_ROOT; };
+               88950884109F3538004FB35D /* drivers.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = drivers.c; path = ../../src/drivers/drivers.c; sourceTree = SOURCE_ROOT; };
+               8DD76FB20486AB0100D96B5E /* wpa_supplicant */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = wpa_supplicant; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+               8DD76FAD0486AB0100D96B5E /* Frameworks */ = {
+                       isa = PBXFrameworksBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               8853CB17109F385C00358CEF /* libpcap.dylib in Frameworks */,
+                               8853CB1B109F389800358CEF /* libcrypto.dylib in Frameworks */,
+                               8853CB1F109F38BD00358CEF /* CoreFoundation.framework in Frameworks */,
+                               8853CB3C109F3B5800358CEF /* Apple80211.framework in Frameworks */,
+                               8853CC3C109F4E1D00358CEF /* libssl.dylib in Frameworks */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+               08FB7794FE84155DC02AAC07 /* wpa_supplicant */ = {
+                       isa = PBXGroup;
+                       children = (
+                               08FB7795FE84155DC02AAC07 /* Source */,
+                               C6A0FF2B0290797F04C91782 /* Documentation */,
+                               1AB674ADFE9D54B511CA2CBB /* Products */,
+                               8853CB16109F385C00358CEF /* libpcap.dylib */,
+                               8853CB1A109F389800358CEF /* libcrypto.dylib */,
+                               8853CB1E109F38BD00358CEF /* CoreFoundation.framework */,
+                               8853CB3B109F3B5800358CEF /* Apple80211.framework */,
+                               8853CC3B109F4E1D00358CEF /* libssl.dylib */,
+                       );
+                       name = wpa_supplicant;
+                       sourceTree = "<group>";
+               };
+               08FB7795FE84155DC02AAC07 /* Source */ = {
+                       isa = PBXGroup;
+                       children = (
+                               881EED0E10DC14EF009E449F /* eap_register.c */,
+                               8853CC51109F4F3500358CEF /* aes-cbc.c */,
+                               8853CC52109F4F3500358CEF /* sha1-tlsprf.c */,
+                               8853CC4C109F4ED500358CEF /* sha256.c */,
+                               8853CC47109F4E8700358CEF /* ieee802_11_common.c */,
+                               8853CC43109F4E6200358CEF /* uuid.c */,
+                               8853CC3F109F4E3A00358CEF /* wps_supplicant.c */,
+                               8853CC32109F4DE200358CEF /* ms_funcs.c */,
+                               8853CC33109F4DE200358CEF /* tls_openssl.c */,
+                               8853CC1E109F4D3500358CEF /* wps_attr_build.c */,
+                               8853CC1F109F4D3500358CEF /* wps_attr_parse.c */,
+                               8853CC20109F4D3500358CEF /* wps_attr_process.c */,
+                               8853CC21109F4D3500358CEF /* wps_common.c */,
+                               8853CC22109F4D3500358CEF /* wps_dev_attr.c */,
+                               8853CC23109F4D3500358CEF /* wps_enrollee.c */,
+                               8853CC24109F4D3500358CEF /* wps_registrar.c */,
+                               8853CC25109F4D3500358CEF /* wps.c */,
+                               8853CC14109F4D0800358CEF /* chap.c */,
+                               8853CC15109F4D0800358CEF /* eap_common.c */,
+                               8853CC16109F4D0800358CEF /* eap_peap_common.c */,
+                               8853CC17109F4D0800358CEF /* eap_wsc_common.c */,
+                               8853CC10109F4CC800358CEF /* eapol_supp_sm.c */,
+                               8853CC0C109F4CA100358CEF /* ctrl_iface_unix.c */,
+                               8853CC0D109F4CA100358CEF /* ctrl_iface.c */,
+                               8853CBEC109F4C6E00358CEF /* eap_gtc.c */,
+                               8853CBED109F4C6E00358CEF /* eap_leap.c */,
+                               8853CBEE109F4C6E00358CEF /* eap_md5.c */,
+                               8853CBEF109F4C6E00358CEF /* eap_methods.c */,
+                               8853CBF0109F4C6E00358CEF /* eap_mschapv2.c */,
+                               8853CBF1109F4C6E00358CEF /* eap_otp.c */,
+                               8853CBF2109F4C6E00358CEF /* eap_peap.c */,
+                               8853CBF3109F4C6E00358CEF /* eap_tls_common.c */,
+                               8853CBF4109F4C6E00358CEF /* eap_tls.c */,
+                               8853CBF5109F4C6E00358CEF /* eap_tnc.c */,
+                               8853CBF6109F4C6E00358CEF /* eap_ttls.c */,
+                               8853CBF7109F4C6E00358CEF /* eap_wsc.c */,
+                               8853CBF8109F4C6E00358CEF /* eap.c */,
+                               8853CBF9109F4C6E00358CEF /* mschapv2.c */,
+                               8853CBFA109F4C6E00358CEF /* tncc.c */,
+                               8853CB35109F3AC700358CEF /* md5.c */,
+                               8853CB31109F3A9400358CEF /* wpa_common.c */,
+                               8853CB2D109F3A3900358CEF /* scan_helpers.c */,
+                               88950883109F3538004FB35D /* driver_osx.m */,
+                               88950884109F3538004FB35D /* drivers.c */,
+                               8895086D109F3367004FB35D /* aes-unwrap.c */,
+                               8895086E109F3367004FB35D /* crypto_openssl.c */,
+                               8895086F109F3367004FB35D /* sha1-pbkdf2.c */,
+                               88950870109F3367004FB35D /* sha1.c */,
+                               8895086B109F3316004FB35D /* l2_packet_freebsd.c */,
+                               8895085F109F32D1004FB35D /* peerkey.c */,
+                               88950860109F32D1004FB35D /* pmksa_cache.c */,
+                               88950861109F32D1004FB35D /* preauth.c */,
+                               88950862109F32D1004FB35D /* wpa_ie.c */,
+                               88950863109F32D1004FB35D /* wpa.c */,
+                               8895083A109F301A004FB35D /* base64.c */,
+                               8895083B109F301A004FB35D /* common.c */,
+                               8895083C109F301A004FB35D /* eloop.c */,
+                               8895083D109F301A004FB35D /* os_unix.c */,
+                               8895083E109F301A004FB35D /* wpa_debug.c */,
+                               8895083F109F301A004FB35D /* wpabuf.c */,
+                               88950828109F2FAB004FB35D /* blacklist.c */,
+                               88950829109F2FAB004FB35D /* config_file.c */,
+                               8895082A109F2FAB004FB35D /* config.c */,
+                               8895082B109F2FAB004FB35D /* events.c */,
+                               8895082C109F2FAB004FB35D /* main.c */,
+                               8895082D109F2FAB004FB35D /* notify.c */,
+                               8895082E109F2FAB004FB35D /* scan.c */,
+                               8895082F109F2FAB004FB35D /* wpa_supplicant.c */,
+                               88950830109F2FAB004FB35D /* wpas_glue.c */,
+                       );
+                       name = Source;
+                       sourceTree = "<group>";
+               };
+               1AB674ADFE9D54B511CA2CBB /* Products */ = {
+                       isa = PBXGroup;
+                       children = (
+                               8DD76FB20486AB0100D96B5E /* wpa_supplicant */,
+                       );
+                       name = Products;
+                       sourceTree = "<group>";
+               };
+               C6A0FF2B0290797F04C91782 /* Documentation */ = {
+                       isa = PBXGroup;
+                       children = (
+                       );
+                       name = Documentation;
+                       sourceTree = "<group>";
+               };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+               8DD76FA90486AB0100D96B5E /* wpa_supplicant */ = {
+                       isa = PBXNativeTarget;
+                       buildConfigurationList = 1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "wpa_supplicant" */;
+                       buildPhases = (
+                               8DD76FAB0486AB0100D96B5E /* Sources */,
+                               8DD76FAD0486AB0100D96B5E /* Frameworks */,
+                               8DD76FAF0486AB0100D96B5E /* CopyFiles */,
+                       );
+                       buildRules = (
+                       );
+                       dependencies = (
+                       );
+                       name = wpa_supplicant;
+                       productInstallPath = "$(HOME)/bin";
+                       productName = wpa_supplicant;
+                       productReference = 8DD76FB20486AB0100D96B5E /* wpa_supplicant */;
+                       productType = "com.apple.product-type.tool";
+               };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+               08FB7793FE84155DC02AAC07 /* Project object */ = {
+                       isa = PBXProject;
+                       buildConfigurationList = 1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "wpa_supplicant" */;
+                       compatibilityVersion = "Xcode 3.1";
+                       hasScannedForEncodings = 1;
+                       mainGroup = 08FB7794FE84155DC02AAC07 /* wpa_supplicant */;
+                       projectDirPath = "";
+                       projectRoot = "";
+                       targets = (
+                               8DD76FA90486AB0100D96B5E /* wpa_supplicant */,
+                       );
+               };
+/* End PBXProject section */
+
+/* Begin PBXSourcesBuildPhase section */
+               8DD76FAB0486AB0100D96B5E /* Sources */ = {
+                       isa = PBXSourcesBuildPhase;
+                       buildActionMask = 2147483647;
+                       files = (
+                               88950831109F2FAB004FB35D /* blacklist.c in Sources */,
+                               88950832109F2FAB004FB35D /* config_file.c in Sources */,
+                               88950833109F2FAB004FB35D /* config.c in Sources */,
+                               88950834109F2FAB004FB35D /* events.c in Sources */,
+                               88950835109F2FAB004FB35D /* main.c in Sources */,
+                               88950836109F2FAB004FB35D /* notify.c in Sources */,
+                               88950837109F2FAB004FB35D /* scan.c in Sources */,
+                               88950838109F2FAB004FB35D /* wpa_supplicant.c in Sources */,
+                               88950839109F2FAB004FB35D /* wpas_glue.c in Sources */,
+                               88950840109F301A004FB35D /* base64.c in Sources */,
+                               88950841109F301A004FB35D /* common.c in Sources */,
+                               88950842109F301A004FB35D /* eloop.c in Sources */,
+                               88950843109F301A004FB35D /* os_unix.c in Sources */,
+                               88950844109F301A004FB35D /* wpa_debug.c in Sources */,
+                               88950845109F301A004FB35D /* wpabuf.c in Sources */,
+                               88950864109F32D1004FB35D /* peerkey.c in Sources */,
+                               88950865109F32D1004FB35D /* pmksa_cache.c in Sources */,
+                               88950866109F32D1004FB35D /* preauth.c in Sources */,
+                               88950867109F32D1004FB35D /* wpa_ie.c in Sources */,
+                               88950868109F32D1004FB35D /* wpa.c in Sources */,
+                               8895086C109F3316004FB35D /* l2_packet_freebsd.c in Sources */,
+                               88950871109F3367004FB35D /* aes-unwrap.c in Sources */,
+                               88950872109F3367004FB35D /* crypto_openssl.c in Sources */,
+                               88950873109F3367004FB35D /* sha1-pbkdf2.c in Sources */,
+                               88950874109F3367004FB35D /* sha1.c in Sources */,
+                               88950885109F3538004FB35D /* driver_osx.m in Sources */,
+                               88950886109F3538004FB35D /* drivers.c in Sources */,
+                               8853CB2E109F3A3900358CEF /* scan_helpers.c in Sources */,
+                               8853CB32109F3A9400358CEF /* wpa_common.c in Sources */,
+                               8853CB36109F3AC700358CEF /* md5.c in Sources */,
+                               8853CBFB109F4C6E00358CEF /* eap_gtc.c in Sources */,
+                               8853CBFC109F4C6E00358CEF /* eap_leap.c in Sources */,
+                               8853CBFD109F4C6E00358CEF /* eap_md5.c in Sources */,
+                               8853CBFE109F4C6E00358CEF /* eap_methods.c in Sources */,
+                               8853CBFF109F4C6E00358CEF /* eap_mschapv2.c in Sources */,
+                               8853CC00109F4C6E00358CEF /* eap_otp.c in Sources */,
+                               8853CC01109F4C6E00358CEF /* eap_peap.c in Sources */,
+                               8853CC02109F4C6E00358CEF /* eap_tls_common.c in Sources */,
+                               8853CC03109F4C6E00358CEF /* eap_tls.c in Sources */,
+                               8853CC04109F4C6E00358CEF /* eap_tnc.c in Sources */,
+                               8853CC05109F4C6E00358CEF /* eap_ttls.c in Sources */,
+                               8853CC06109F4C6E00358CEF /* eap_wsc.c in Sources */,
+                               8853CC07109F4C6E00358CEF /* eap.c in Sources */,
+                               8853CC08109F4C6E00358CEF /* mschapv2.c in Sources */,
+                               8853CC09109F4C6E00358CEF /* tncc.c in Sources */,
+                               8853CC0E109F4CA100358CEF /* ctrl_iface_unix.c in Sources */,
+                               8853CC0F109F4CA100358CEF /* ctrl_iface.c in Sources */,
+                               8853CC11109F4CC800358CEF /* eapol_supp_sm.c in Sources */,
+                               8853CC18109F4D0800358CEF /* chap.c in Sources */,
+                               8853CC19109F4D0800358CEF /* eap_common.c in Sources */,
+                               8853CC1A109F4D0800358CEF /* eap_peap_common.c in Sources */,
+                               8853CC1B109F4D0800358CEF /* eap_wsc_common.c in Sources */,
+                               8853CC26109F4D3500358CEF /* wps_attr_build.c in Sources */,
+                               8853CC27109F4D3500358CEF /* wps_attr_parse.c in Sources */,
+                               8853CC28109F4D3500358CEF /* wps_attr_process.c in Sources */,
+                               8853CC29109F4D3500358CEF /* wps_common.c in Sources */,
+                               8853CC2A109F4D3500358CEF /* wps_dev_attr.c in Sources */,
+                               8853CC2B109F4D3500358CEF /* wps_enrollee.c in Sources */,
+                               8853CC2C109F4D3500358CEF /* wps_registrar.c in Sources */,
+                               8853CC2D109F4D3500358CEF /* wps.c in Sources */,
+                               8853CC34109F4DE200358CEF /* ms_funcs.c in Sources */,
+                               8853CC35109F4DE200358CEF /* tls_openssl.c in Sources */,
+                               8853CC40109F4E3A00358CEF /* wps_supplicant.c in Sources */,
+                               8853CC44109F4E6200358CEF /* uuid.c in Sources */,
+                               8853CC48109F4E8700358CEF /* ieee802_11_common.c in Sources */,
+                               8853CC4E109F4ED500358CEF /* sha256.c in Sources */,
+                               8853CC53109F4F3500358CEF /* aes-cbc.c in Sources */,
+                               8853CC54109F4F3500358CEF /* sha1-tlsprf.c in Sources */,
+                               881EED0F10DC14EF009E449F /* eap_register.c in Sources */,
+                       );
+                       runOnlyForDeploymentPostprocessing = 0;
+               };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+               1DEB928608733DD80010E9CD /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               COPY_PHASE_STRIP = NO;
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"",
+                               );
+                               GCC_DYNAMIC_NO_PIC = NO;
+                               GCC_ENABLE_FIX_AND_CONTINUE = YES;
+                               GCC_MODEL_TUNING = G5;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               INSTALL_PATH = /usr/local/bin;
+                               PRODUCT_NAME = wpa_supplicant;
+                       };
+                       name = Debug;
+               };
+               1DEB928708733DD80010E9CD /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ALWAYS_SEARCH_USER_PATHS = NO;
+                               DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+                               FRAMEWORK_SEARCH_PATHS = (
+                                       "$(inherited)",
+                                       "\"$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks\"",
+                               );
+                               GCC_MODEL_TUNING = G5;
+                               INSTALL_PATH = /usr/local/bin;
+                               PRODUCT_NAME = wpa_supplicant;
+                       };
+                       name = Release;
+               };
+               1DEB928A08733DD80010E9CD /* Debug */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+                               FRAMEWORK_SEARCH_PATHS = /System/Library/PrivateFrameworks;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_OPTIMIZATION_LEVEL = 0;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               HEADER_SEARCH_PATHS = (
+                                       ../../src,
+                                       ../../src/utils,
+                               );
+                               ONLY_ACTIVE_ARCH = YES;
+                               OTHER_CFLAGS = "-DCONFIG_XCODE_DEFAULTS";
+                               PREBINDING = NO;
+                               PRELINK_LIBS = "";
+                               RUN_CLANG_STATIC_ANALYZER = YES;
+                               SDKROOT = macosx10.6;
+                       };
+                       name = Debug;
+               };
+               1DEB928B08733DD80010E9CD /* Release */ = {
+                       isa = XCBuildConfiguration;
+                       buildSettings = {
+                               ARCHS = "$(ARCHS_STANDARD_32_64_BIT)";
+                               FRAMEWORK_SEARCH_PATHS = /System/Library/PrivateFrameworks;
+                               GCC_C_LANGUAGE_STANDARD = gnu99;
+                               GCC_WARN_ABOUT_RETURN_TYPE = YES;
+                               GCC_WARN_UNUSED_VARIABLE = YES;
+                               HEADER_SEARCH_PATHS = (
+                                       ../../src,
+                                       ../../src/utils,
+                               );
+                               OTHER_CFLAGS = "-DCONFIG_XCODE_DEFAULTS";
+                               PREBINDING = NO;
+                               SDKROOT = macosx10.6;
+                       };
+                       name = Release;
+               };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+               1DEB928508733DD80010E9CD /* Build configuration list for PBXNativeTarget "wpa_supplicant" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               1DEB928608733DD80010E9CD /* Debug */,
+                               1DEB928708733DD80010E9CD /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+               1DEB928908733DD80010E9CD /* Build configuration list for PBXProject "wpa_supplicant" */ = {
+                       isa = XCConfigurationList;
+                       buildConfigurations = (
+                               1DEB928A08733DD80010E9CD /* Debug */,
+                               1DEB928B08733DD80010E9CD /* Release */,
+                       );
+                       defaultConfigurationIsVisible = 0;
+                       defaultConfigurationName = Release;
+               };
+/* End XCConfigurationList section */
+       };
+       rootObject = 08FB7793FE84155DC02AAC07 /* Project object */;
+}